Not logged in.  Login/Logout/Register | List snippets | | Create snippet | Upload image | Upload data

104211
LINES

< > BotCompany Repo | #1005016 // Mocha Doom Single Source (include)

JavaX source code [tags: use-pretranspiled] - run with: x30.jar

Libraryless. Compilation Failed (104211L/700K).

package utils;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Array;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Arrays;

import p.Resettable;
import w.InputStreamSugar;

/**
 * Some utilities that emulate C stlib methods or provide convenient functions
 * to do repetitive system and memory related stuff.
 * 
 * @author Maes
 */

public final class C2JUtils {

    static public final char[] strcpy(char[] s1, final char[] s2) {
        for (int i = 0; i < Math.min(s1.length, s2.length); i++) {
            s1[i] = s2[i];
        }
        return s1;
    }

    static public final char[] strcpy(char[] s1, final char[] s2, int off, int len) {
        for (int i = 0; i < len; i++) {
            s1[i] = s2[i + off];
        }
        return s1;
    }

    static public final char[] strcpy(char[] s1, final char[] s2, int off) {
        for (int i = 0; i < Math.min(s1.length, s2.length - off); i++) {
            s1[i] = s2[i + off];
        }
        return s1;
    }

    static public final char[] strcpy(char[] s1, String s2) {
        for (int i = 0; i < Math.min(s1.length, s2.length()); i++) {
            s1[i] = s2.charAt(i);
        }
        return s1;
    }

    
    /** Return a byte[] array from the string's chars,
     *  ANDed to the lowest 8 bits.
     * 
     * @param str
     * @return
     */
    public static final byte[] toByteArray(String str) {
        byte[] retour = new byte[str.length()];
        for (int i = 0; i < str.length(); i++) {
            retour[i] = (byte) (str.charAt(i) & 0xFF);
        }
        return retour;
    }

    /**
     * Finds index of first element of array matching key. Useful whenever an
     * "indexOf" property is required or you encounter the C-ism [pointer-
     * array_base] used to find array indices in O(1) time. However, since this
     * is just a dumb unsorted search, running time is O(n), so use this method
     * only sparingly and in scenarios where it won't occur very frequently
     * -once per level is probably OK-, but watch out for nested loops, and
     * cache the result whenever possible. Consider adding an index or ID type
     * of field to the searched type if you require to use this property too
     * often.
     * 
     * @param array
     * @param key
     * @return
     */

    public static int indexOf(Object[] array, Object key) {
        for (int i = 0; i < array.length; i++) {
            if (array[i] == key) {
                return i;
            }
        }

        return -1;
    }

    /**
     * Emulates C-style "string comparison". "Strings" are considered
     * null-terminated, and comparison is performed only up to the smaller of
     * the two.
     * 
     * @param s1
     * @param s2
     * @return
     */

    static public final boolean strcmp(char[] s1, final char[] s2) {
        boolean match = true;
        for (int i = 0; i < Math.min(s1.length, s2.length); i++) {
            if (s1[i] != s2[i]) {
                match = false;
                break;
            }
        }
        return match;
    }

    static public final boolean strcmp(char[] s1, String s2) {
        return strcmp(s1, s2.toCharArray());
    }

    /**
     * C-like string length (null termination).
     * 
     * @param s1
     * @return
     */
    static public final int strlen(char[] s1) {
        if (s1 == null)
            return 0;
        int len = 0;

        while (s1[len++] > 0) {
            if (len >= s1.length)
                break;
        }

        return len - 1;
    }

    /**
     * Return a new String based on C-like null termination.
     * 
     * @param s
     * @return
     */
    static public final String nullTerminatedString(char[] s) {
        if (s == null)
            return "";
        int len = 0;

        while (s[len++] > 0) {
            if (len >= s.length)
                break;
        }

        return new String(s, 0, len - 1);
    }

    /**
     * Automatically "initializes" arrays of objects with their default
     * constuctor. It's better than doing it by hand, IMO. If you have a better
     * way, be my guest.
     * 
     * @param os
     * @param c
     * @throws Exception
     * @throws
     */

    public static final <T> void initArrayOfObjects(T[] os, Class<T> c) {
        try {
            for (int i = 0; i < os.length; i++) {
                os[i] = c.newInstance();
            }
        } catch (Exception e) {
            e.printStackTrace();
            System.err.println("Failure to allocate " + os.length
                    + " objects of class" + c.getName() + "!");
            System.exit(-1);
        }
    }

    /**
     * Automatically "initializes" arrays of objects with their default
     * constuctor. It's better than doing it by hand, IMO. If you have a better
     * way, be my guest.
     * 
     * @param os
     * @throws Exception
     * @throws
     */

    public static final <T> void initArrayOfObjects(T[] os) {
        Class<T> c = (Class<T>) os.getClass().getComponentType();
        try {
            for (int i = 0; i < os.length; i++) {
                os[i] = (T) c.newInstance();
            }
        } catch (Exception e) {
            e.printStackTrace();
            System.err.println("Failure to allocate " + os.length
                    + " objects of class " + c.getName() + "!");

            System.exit(-1);
        }
    }

    /**
     * Uses reflection to automatically create and initialize an array of
     * objects of the specified class. Does not require casting on "reception".
     * 
     * @param <T>
     * @param c
     * @param num
     * @return
     * @return
     */

    public static final <T> T[] createArrayOfObjects(Class<T> c, int num) {
        T[] os = null;

        os=getNewArray(c,num);

        try {
            for (int i = 0; i < os.length; i++) {
                os[i] = (T) c.newInstance();
            }
        } catch (Exception e) {
            e.printStackTrace();
            System.err.println("Failure to instantiate " + os.length
                    + " objects of class " + c.getName() + "!");
            System.exit(-1);
        }

        return os;
    }
    
    /**
     * Uses reflection to automatically create and initialize an array of
     * objects of the specified class. Does not require casting on "reception".
     * Requires an instance of the desired class. This allows getting around
     * determining the runtime type of parametrized types.
     * 
     * 
     * @param <T>
     * @param instance An instance of a particular class. 
     * @param num
     * @return
     * @return
     */

    @SuppressWarnings("unchecked")
    public static final <T> T[] createArrayOfObjects(T instance, int num) {
        T[] os = null;
        
        Class<T> c=(Class<T>) instance.getClass();

        os=getNewArray(c,num);

        try {
            for (int i = 0; i < os.length; i++) {
                os[i] = (T) c.newInstance();
            }
        } catch (Exception e) {
            e.printStackTrace();
            System.err.println("Failure to instantiate " + os.length
                    + " objects of class " + c.getName() + "!");
            System.exit(-1);
        }

        return os;
    }


    /**
     * Automatically "initializes" arrays of objects with their default
     * constuctor. It's better than doing it by hand, IMO. If you have a better
     * way, be my guest.
     * 
     * @param os
     * @param startpos inclusive
     * @param endpos non-inclusive
     * @throws Exception
     * @throws
     */

    public static final<T> void initArrayOfObjects(T[] os, int startpos,
            int endpos) {
        @SuppressWarnings("unchecked")
		Class<T> c = (Class<T>) os.getClass().getComponentType();
        try {
            for (int i = startpos; i < endpos; i++) {
                os[i] = c.newInstance();
            }
        } catch (Exception e) {
            e.printStackTrace();
            System.err.println("Failure to allocate " + os.length
                    + " objects of class " + c.getName() + "!");

            System.exit(-1);
        }
    }

    /** This method gets eventually inlined, becoming very fast */

    public static final int toUnsignedByte(byte b) {
        return (0x000000FF & b);
    }

    // Optimized array-fill methods designed to operate like C's memset.

    public static final void memset(boolean[] array, boolean value, int len) {
        if (len > 0)
            array[0] = value;
        for (int i = 1; i < len; i += i) {
            System.arraycopy(array, 0, array, i, ((len - i) < i) ? (len - i)
                    : i);
        }
    }

    public static final void memset(char[] array, char value, int len) {
        if (len > 0)
            array[0] = value;
        for (int i = 1; i < len; i += i) {
            System.arraycopy(array, 0, array, i, ((len - i) < i) ? (len - i)
                    : i);
        }
    }

    public static final void memset(int[] array, int value, int len) {
        if (len > 0)
            array[0] = value;
        for (int i = 1; i < len; i += i) {
            System.arraycopy(array, 0, array, i, ((len - i) < i) ? (len - i)
                    : i);
        }
    }
    
    public static final void memset(short[] array, short value, int len) {
        if (len > 0)
            array[0] = value;
        for (int i = 1; i < len; i += i) {
            System.arraycopy(array, 0, array, i, ((len - i) < i) ? (len - i)
                    : i);
        }
    }

    public static final long unsigned(int num) {
        return 0xFFFFFFFFL & num;
    }

    public static final char unsigned(short num) {
        return (char) num;
    }

    /**
     * Convenient alias for System.arraycopy(src, 0, dest, 0, length);
     * 
     * @param dest
     * @param src
     * @param length
     */
    public static final void memcpy(Object dest, Object src, int length) {
        System.arraycopy(src, 0, dest, 0, length);
    }

    public static final boolean testReadAccess(String URI) {
    	InputStream in=null;
    	
        // This is bullshit.
        if (URI == null)
            return false;
        if (URI.length() == 0)
            return false;

        try {
            in=new FileInputStream(URI);
        } catch (Exception e) {
            // Not a file...
            URL u;
			try {
				u = new URL(URI);
			} catch (MalformedURLException e1) {
				return false;
			}
            try {
				in=u.openConnection().getInputStream();
			} catch (IOException e1) {
				return false;
			}

        }
        
        if (in!=null) {
        	try {
				in.close();
			} catch (IOException e) {

			}
        	return true;
        }
        // All is well. Go on...
        return true;

    }
    
    public static final boolean testWriteAccess(String URI) {
    	OutputStream out=null;
    	
        // This is bullshit.
        if (URI == null)
            return false;
        if (URI.length() == 0)
            return false;

        try {
            out=new FileOutputStream(URI);
        } catch (Exception e) {
            // Not a file...
            URL u;
			try {
				u = new URL(URI);
			} catch (MalformedURLException e1) {
				return false;
			}
            try {
				out=u.openConnection().getOutputStream();
			} catch (IOException e1) {
				return false;
			}

        }

        if (out!=null) {
        	try {
				out.close();
			} catch (IOException e) {

			}
        	return true;
        }
        // All is well. Go on...
        return true;
    }

    /**
     * Returns true if flags are included in arg. Synonymous with (flags &
     * arg)!=0
     * 
     * @param flags
     * @param arg
     * @return
     */
    public static final boolean flags(int flags, int arg) {
        return ((flags & arg) != 0);
    }
    
    public static final boolean flags(long flags, long arg) {
        return ((flags & arg) != 0);
    }

    /**
     * Returns 1 for true and 0 for false. Useful, given the amount of
     * "arithmetic" logical functions in legacy code. Synonymous with
     * (expr?1:0);
     * 
     * @param flags
     * @param arg
     * @return
     */
    public static final int eval(boolean expr) {
        return (expr ? 1 : 0);
    }

    /**
     * Returns 1 for non-null and 0 for null objects. Useful, given the amount
     * of "existential" logical functions in legacy code. Synonymous with
     * (expr!=null);
     * 
     * @param flags
     * @param arg
     * @return
     */
    public static final boolean eval(Object expr) {
        return (expr != null);
    }

    /**
     * Returns true for expr!=0, false otherwise.
     * 
     * @param flags
     * @param arg
     * @return
     */
    public static final boolean eval(int expr) {
        return expr != 0;
    }

    /**
     * Returns true for expr!=0, false otherwise.
     * 
     * @param flags
     * @param arg
     * @return
     */
    public static final boolean eval(long expr) {
        return expr != 0;
    }

    public static final void resetAll(Resettable[] r) {
        for (int i = 0; i < r.length; i++) {
            r[i].reset();
        }
    }

    /**
     * Useful for unquoting strings, since StringTokenizer won't do it for us.
     * Returns null upon any failure.
     * 
     * @param s
     * @param c
     * @return
     */

    public static final String unquote(String s, char c) {

        int firstq = s.indexOf(c);
        int lastq = s.lastIndexOf(c);
        // Indexes valid?
        if (isQuoted(s,c))
                return s.substring(firstq + 1, lastq);
        
        return null;
    }

    public static final boolean isQuoted(String s, char c) {

        int q1 = s.indexOf(c);
        int q2 = s.lastIndexOf(c);
        char c1,c2;
        
        // Indexes valid?
        if (q1 != -1 && q2 != -1) {
            if (q1 < q2) {
                c1=s.charAt(q1);
                c2=s.charAt(q2);
                return (c1==c2);
            }
        }
        
        return false;
    }
    
    public static final String unquoteIfQuoted(String s, char c) {

        String tmp = unquote(s, c);
        if (tmp != null)
            return tmp;
        return s;
    }

    /** Return either 0 or a hashcode 
     * 
     * @param o 
     */
    public static final int pointer(Object o) {
        if (o == null)
            return 0;
        else
            return o.hashCode();
    }

 
    public static final boolean checkForExtension(String filename, String ext) {
        
        // Null filenames satisfy null extensions.
        if ((filename == null || filename.isEmpty())
                && (ext == null || ext.isEmpty()))
            return true;
        
        String separator = System.getProperty("file.separator");

        // Remove the path upto the filename.
        int lastSeparatorIndex = filename.lastIndexOf(separator);
        if (lastSeparatorIndex != -1) {
            filename = filename.substring(lastSeparatorIndex + 1);
        }

        String realext = null;

        // Get extension separator. It SHOULD be . on all platforms, right?
        int pos = filename.lastIndexOf('.');

        if (pos >= 0 && pos <= filename.length() - 2) { // Extension present
            
            // Null comparator on valid extension
            if (ext == null || ext.isEmpty()) return false;
            
            realext = filename.substring(pos + 1);
            return realext.compareToIgnoreCase(ext) == 0;
        } else // No extension, and null/empty comparator
        if (ext == null || ext.isEmpty())
            return true;

        // No extension, and non-null/nonempty comparator.
        return false;
    }    
    
    /** Return the filename without extension, and stripped
     * of the path.
     * 
     * @param s
     * @return
     */
    
    public static final String removeExtension(String s) {

        String separator = System.getProperty("file.separator");
        String filename;

        // Remove the path upto the filename.
        int lastSeparatorIndex = s.lastIndexOf(separator);
        if (lastSeparatorIndex == -1) {
            filename = s;
        } else {
            filename = s.substring(lastSeparatorIndex + 1);
        }

        // Remove the extension.
        int extensionIndex = filename.lastIndexOf(".");
        if (extensionIndex == -1)
            return filename;

        return filename.substring(0, extensionIndex);
    }

    
    /**
     * This method is supposed to return the "name" part of a filename. It was
     * intended to return length-limited (max 8 chars) strings to use as lump
     * indicators. There's normally no need to enforce this behavior, as there's
     * nothing preventing the engine from INTERNALLY using lump names with >8
     * chars. However, just to be sure...
     * 
     * @param path
     * @param limit  Set to any value >0 to enforce a length limit
     * @param whole keep extension if set to true
     * @return
     */

    public static final String extractFileBase(String path, int limit, boolean whole) {
    	
    	if (path==null) return path;
    	
        int src = path.length() - 1;

        String separator = System.getProperty("file.separator");
        src = path.lastIndexOf(separator)+1;

        if (src < 0) // No separator
            src = 0;

        int len = path.lastIndexOf('.');
        if (whole || len<0 ) len=path.length()-src; // No extension.
        else  len-= src;        

        // copy UP to the specific number of characters, or all        
        if (limit > 0) len = Math.min(limit, len);
        
        return path.substring(src, src + len);
    }

    /** Maes: File intead of "inthandle" */

    public static long filelength(File handle) {
        try {
            return handle.length();
        } catch (Exception e) {
            System.err.println("Error fstating");
            return -1;
        }

    }

	@SuppressWarnings("unchecked")
	public static final <T> T[] resize(T[] oldarray, int newsize) {
		
		if (oldarray[0]!=null) return resize(oldarray[0],oldarray,newsize); 
		
		T cls=null;
		try {
			cls = (T) oldarray.getClass().getComponentType().newInstance();
			return resize(cls,oldarray,newsize);
		} catch (Exception e) {
			System.err.println("Cannot autodetect type in resizeArray.\n");
			return null;
		}


	}
    
	/** Generic array resizing method. Calls Arrays.copyOf but then also
	 *  uses initArrayOfObject for the "abundant" elements.
	 * 
	 * @param <T>
	 * @param instance
	 * @param oldarray
	 * @param newsize
	 * @return
	 */
	
	public static final <T> T[] resize(T instance,T[] oldarray, int newsize) {
		
		//  Hmm... nope.
		if (newsize<=oldarray.length) return oldarray;
		
		// Copy old array with built-in stuff.
		T[] tmp =Arrays.copyOf(oldarray,newsize);
		
		// Init the null portions as well
		C2JUtils.initArrayOfObjects(tmp, oldarray.length,tmp.length);

		System.out.printf("Old array of type %s resized. New capacity: %d\n"
				,instance.getClass(),newsize);
		
		return tmp;

	}
	
	/** Resize an array without autoinitialization. Same as Arrays.copyOf(..), just
	 * prints a message.
	 *  
	 * @param <T>
	 * @param oldarray
	 * @param newsize
	 * @return
	 */
	
	public static final <T> T[] resizeNoAutoInit(T[] oldarray, int newsize) {
		
		// For non-autoinit types, this is enough.
		T[] tmp =Arrays.copyOf(oldarray,newsize);
		
		System.out.printf("Old array of type %s resized without auto-init. New capacity: %d\n"
				,tmp.getClass().getComponentType(),newsize);
		
		return tmp;

	}
	
	@SuppressWarnings("unchecked")
	public final static<T> T[] getNewArray(T instance,int size){
		
		T[] os=null;
		Class<T> c=(Class<T>) instance.getClass();
		
        try {
            os = (T[]) Array.newInstance(c, size);
        } catch (Exception e) {
            e.printStackTrace();
            System.err.println("Failure to allocate " + size
                    + " objects of class " + c.getName() + "!");
            System.exit(-1);
        }
        
        return os;
	}
	
	public final static<T> T[] getNewArray(int size,T instance){
		@SuppressWarnings("unchecked")
		Class<T> c=(Class<T>) instance.getClass();
		return getNewArray(c,size);
	}
	
	@SuppressWarnings("unchecked")
	public final static<T> T[] getNewArray(Class<T> c,int size){
		T[] os=null;
        try {
            os = (T[]) Array.newInstance(c, size);
        } catch (Exception e) {
            e.printStackTrace();
            System.err.println("Failure to allocate " + size
                    + " objects of class " + c.getName() + "!");
            System.exit(-1);
        }
        
        return os;
	}

    /**
     * Try to guess whether a URI represents a local file, a network any of the
     * above but zipped. Returns
     * 
     * @param URI
     * @return an int with flags set according to InputStreamSugar
     */

    public static int guessResourceType(String URI) {

        int result = 0;
        InputStream in = null;

        // This is bullshit.
        if (URI == null || URI.length() == 0)
            return InputStreamSugar.BAD_URI;

        try {
            in = new FileInputStream(new File(URI));
            // It's a file
            result |= InputStreamSugar.FILE;
        } catch (Exception e) {
            // Not a file...
            URL u;
            try {
                u = new URL(URI);
            } catch (MalformedURLException e1) {
                return InputStreamSugar.BAD_URI;
            }
            try {
                in = u.openConnection().getInputStream();
                result |= InputStreamSugar.NETWORK_FILE;
            } catch (IOException e1) {
                return InputStreamSugar.BAD_URI;
            }

        }

        // Try guessing if it's a ZIP file. A bit lame, really
        // TODO: add proper validation, and maybe MIME type checking
        // for network streams, for cases that we can't really
        // tell from extension alone.

        if (checkForExtension(URI, "zip")) {
            result |= InputStreamSugar.ZIP_FILE;

        }

        try {
            in.close();
        } catch (IOException e) {

        }

        // All is well. Go on...
        return result;
    }

}

package utils;

/** Half-assed way of finding the OS we're running under, shamelessly 
 * ripped from:
 * 
 *  http://www.mkyong.com/java/how-to-detect-os-in-java-systemgetpropertyosname/
 * .
 * This is required, as some things in AWT don't work exactly consistently cross-OS
 * (AWT frame size is the first thing that goes wrong, but also mouse grabbing
 * behavior).
 * 
 * TODO: replace with Apache Commons library?
 *  
 * @author velktron
 *
 */

public class OSValidator{
 
	public static boolean isWindows(){
 
		String os = System.getProperty("os.name").toLowerCase();
		//windows
	    return (os.indexOf( "win" ) >= 0); 
 
	}
 
	public static boolean isMac(){
 
		String os = System.getProperty("os.name").toLowerCase();
		//Mac
	    return (os.indexOf( "mac" ) >= 0); 
 
	}
 
	public static boolean isUnix(){
 
		String os = System.getProperty("os.name").toLowerCase();
		//linux or unix
	    return (os.indexOf( "nix") >=0 || os.indexOf( "nux") >=0);
 
	}
	
	public static boolean isUnknown(){
		return (!isWindows() && !isUnix() && !isMac());
	}
}

package doom;

import defines.skill_t;

/** Groups functions formerly in d_game, 
 * in case you want to provide a different implementation 
 */

public interface IDoomGame {

	 void ExitLevel ();
	 void WorldDone ();

     boolean CheckDemoStatus();
     
     /** Can be called by the startup code or M_Responder.
     A normal game starts at map 1,
     but a warp test can start elsewhere */
     
     public void DeferedInitNew (skill_t skill, int episode, int map);
     
     /** Can be called by the startup code or M_Responder,
     calls P_SetupLevel or W_EnterWorld. */
   public void LoadGame (String name);

   /** Called by M_Responder. */
   public void SaveGame (int slot, String description);
   
   /** Takes a screenshot *NOW*
    * 
    */
   public void ScreenShot() ;
   
   public void StartTitle();
   
   public gameaction_t getGameAction();

   public void setGameAction(gameaction_t ga);
   
   // public void PlayerReborn(int player);
   
   void DeathMatchSpawnPlayer(int playernum);

}

package doom;

public interface NetConsts {

    public static int    NCMD_EXIT=      0x80000000;
    public static int    NCMD_RETRANSMIT     =0x40000000;
    public static int    NCMD_SETUP      =0x20000000;
    public static int   NCMD_KILL    =   0x10000000; // kill game
    public static int    NCMD_CHECKSUM   =   0x0fffffff;

    public static int  DOOMCOM_ID =     0x12345678;


    //Networking and tick handling related. Moved to DEFINES
    //protected static int    BACKUPTICS     = 12;


    // command_t
    public  static short CMD_SEND    = 1;
    public  static short CMD_GET = 2; 
    
}

package doom;

import n.DoomSystemNetworking;
import f.EndLevel;
import f.Finale;
import f.Wiper;
import hu.HU;
import i.IDiskDrawer;
import i.IDoomSystem;
import i.DoomVideoInterface;
import m.IDoomMenu;
import m.IRandom;
import m.IVariablesManager;
import p.AbstractLevelLoader;
import p.Actions;
import rr.ISpriteManager;
import rr.Renderer;
import rr.TextureManager;
import s.IDoomSound;
import s.IMusic;
import s.ISoundDriver;
import st.AbstractStatusBar;
import timing.ITicker;
import v.DoomVideoRenderer;
import w.IWadLoader;
import automap.IAutoMap;

/** Since a lot of stuff requires shared/global access to 
 *  the WadLoader, the Renderer, the Video system etc. and
 *  we're trying to depart from the global/static mentality,
 *  a common sharing is required. Ideally, this would be a perfect
 *  example of where multiple inheritance could be adopted, since most
 *  stuff needs to share this status anyway. The next best thing is
 *  to have local references of any used fields in the classes that use them.
 * 
 *  About generics: T refers to the type of the graphics resources, and is
 *  currently byte[], as all graphics resources are 8-bit indexed. There are
 *  no plans that this will change anytime soon. Some classes should allow 
 *  different types in theory, but it would be too complex and pointless to 
 *  make everything fully compliant at the moment.
 *  
 *  V refers to the type of DISPLAY, and can be 8-bit (byte[]), 16-bit (short[] 
 *  for HiColor and lesser modes such as ARGB4444, etc.), and, in the future, 
 *  int[] (truecolor).
 * 
 *  The general approach is sharing as much code as possible between different 
 *  implementations (e.g. rendering code), and only specialize methods/classes when
 *  the abstraction of generics isn't enough (typically, when you have to assign
 *  directly to primitive arrays or deal with primitive method signatures).
 *  
 *  Classes that have specialized code for indexed and hicolor modes should be top-level
 *  classes in their package, and contain two nested, static, extending classes called
 *  Indexed and HiColor e.g. new MyClass.Indexed() and new MyClass.HiColor(), while any common 
 *  code should reside in MyClass.
 * 
 * @author velktron
 *
 */

public class DoomContext<T,V>{
	
    public DoomMain<T,V> DM;
    public IDoomGame DG;
    public IWadLoader W;
    public IRandom RND;
    public IDoomSystem I;
    public IDoomSound S;
    public ISoundDriver ISND;
    public IMusic IMUS;
    public DoomVideoInterface<V> VI;
    public AbstractStatusBar ST;
    public DoomVideoRenderer<T,V> V;
    public DoomSystemNetworking DNI;
    public IDoomGameNetworking DGN;
    public AbstractLevelLoader LL;
    public IDoomMenu M;
    public Actions P;
    public Renderer<T,V> R;
    public HU HU;
    public IAutoMap<T,V> AM;
    public Finale<T> F;
    public EndLevel<T,V> WI;
    public Wiper<T,V> WIPE;
    public TextureManager<T> TM;
    public ISpriteManager SM;
    public ICommandLineManager CM;
    public ITicker TICK; 
    public IDiskDrawer DD;
    public IVariablesManager VM;
}

package doom;

import java.util.Hashtable;

import utils.C2JUtils;

import defines.GameMode_t;

public class DoomVersions {

	public final static Hashtable<String,GameMode_t> VersionChecker=new Hashtable<String,GameMode_t>();
	
	static {
		VersionChecker.put("doom.wad",GameMode_t.registered);
		VersionChecker.put("doom2.wad",GameMode_t.commercial);
		VersionChecker.put("udoom.wad",GameMode_t.retail);
		VersionChecker.put("tnt.wad",GameMode_t.pack_tnt);
		VersionChecker.put("plutonia.wad",GameMode_t.pack_plut);
		VersionChecker.put("doom1.wad",GameMode_t.shareware);
		VersionChecker.put("xbla.wad",GameMode_t.pack_xbla);
	}
	
	public DoomVersions(){
		
	}
	
	/** Try all versions in given doommwaddir
	 * 
	 */
	
	public void tryThemAll(String doomwaddir){
		 // Commercial.
        doom2wad = (doomwaddir+ "/doom2.wad");

        // Retail.
        doomuwad = (doomwaddir+ "/doomu.wad");    

        // Registered.
        doomwad = (doomwaddir+ "/doom.wad");

        // Shareware.
        doom1wad = (doomwaddir+ "/doom1.wad");

        // Bug, dear Shawn.
        // Insufficient malloc, caused spurious realloc errors.
        plutoniawad = (doomwaddir+ "/plutonia.wad");

        tntwad = (doomwaddir+ "/tnt.wad");

        xblawad = (doomwaddir+ "/xbla.wad");
        
        // French stuff.
        doom2fwad=(doomwaddir+ "/doom2f.wad");
	}
	
	public String 
	doom1wad,
    doomwad,
    doomuwad,
    doom2wad,
    doom2fwad,
    plutoniawad,    
    tntwad,
    xblawad;
	
	/** Try only one IWAD. 
	 * 
	 * @param iwad
	 * @return
	 */

	public GameMode_t tryOnlyOne(String iwad, String doomwaddir) {
		
		// Is it a known and valid version?
		GameMode_t tmp=VersionChecker.get(iwad.toLowerCase());
		if (tmp!=null) {
		// Can we read it?
		if (C2JUtils.testReadAccess(doomwaddir+iwad))
			return tmp; // Yes, so communicate the gamemode back.
		}
		
		// It's either invalid or we can't read it.
		// Fuck that.
		return null;
	}
	
	
}

package doom;

/** Meant to provide a more lightweight alternative to Java's serialization model,
 *  specifically for the purpose of sending
 *  Objects implementing this can return references to one same byte array, with minimal
 *  overhead. Since it's for send-only purposes, it won't matter if it's  modified.
 *   *  
 *  But don't use it in lieu of CacheableDoomObject! 
 * 
 * @author admin
 *
 */

public interface IDatagramSerializable {
    
        /** Packs object into a byte array suitable to send over 
         * datagram networks. Typically, objects cache this array
         * for later use, and is availabe through cached()
         * 
         * @return
         */
    
        public byte[] pack();
        
        /** Packs object into a byte array suitable to send over 
         * datagram networks. The array is supplied externally 
         * (good for daisy-chaining stuff into a single packet).
         * 
         * @return
         */
    
        public void pack(byte[] buf, int offset);
        
        /** Deserializes an object from a given byte buffer.
         *  Only the first (sizeof) bytes will be used, dependant
         *  on each object's implementation. Will NOT also copy
         *  the byte[] caches. 
         */
        
        public void unpack(byte[] buf);
        
        /** Deserializes an object from a given byte buffer.
         *  Only the first (sizeof) bytes will be used, starting
         *  from a specified offset, dependant on each object's 
         *  implementation.
         */
        public void unpack(byte[] buf, int offset);
        
        /** Only use this if you are 100% sure that the object's content
         *  won't have changed since the last call of pack(). 
         *
         * @return Should return the underlying byte[] array directly.
         */
        public byte[] cached();
       
}

package doom;

// Event structure.

public class event_t {
	
	public event_t(){
	}

	   public event_t(event_t t){
	        this.type = t.type;
	        this.data1 = t.data1;
	        this.data2 = t.data2;
	        this.data3 = t.data3;
	    }

	
    public event_t(evtype_t type, int data) {
        this.type = type;
        this.data1 = data;
    }

    public event_t(char c) {
        this.type = evtype_t.ev_keydown;
        this.data1 = c;
    }

    public event_t(evtype_t type, int data1, int data2, int data3) {
        this.type = type;
        this.data1 = data1;
        this.data2 = data2;
        this.data3 = data3;
    }
    
    public void setFrom(event_t t){
        this.type = t.type;
        this.data1 = t.data1;
        this.data2 = t.data2;
        this.data3 = t.data3;
    }

    public evtype_t type;

    /** keys / mouse/joystick buttons, or wheel rotation amount */
    public int data1;

    /** mouse/joystick x move */
    public int data2;

    /** mouse/joystick y move */
    public int data3;

};

package doom;

import static data.Defines.*;
import static g.Keys.*;
import static data.Limits.*;
import java.io.OutputStreamWriter;

import automap.IAutoMap;
import m.IUseVariables;
import m.IVariablesManager;
import m.Settings;
import p.mobj_t;
import rr.Renderer;
import utils.C2JUtils;
import v.DoomVideoRenderer;
import data.mapthing_t;
import defines.*;
import demo.IDoomDemo;
import f.Finale;
import f.Wiper;

/**
 * We need globally shared data structures, for defining the global state
 * variables. MAES: in pure OO style, this should be a global "Doom state"
 * object to be passed along various modules. No ugly globals here!!! Now, some
 * of the variables that appear here were actually defined in separate modules.
 * Pretty much, whatever needs to be shared with other modules was placed here,
 * either as a local definition, or as an extern share. The very least, I'll
 * document where everything is supposed to come from/reside.
 */

public abstract class DoomStatus<T,V> extends DoomContext<T,V> implements IUseVariables {

	public static final int	BGCOLOR=		7;
	public static final int	FGCOLOR		=8;
	public static int   RESENDCOUNT =10;
	public static int   PL_DRONE    =0x80;  // bit flag in doomdata->player

	public String[]		wadfiles=new String[MAXWADFILES];

	boolean         drone;
	
    /** Command line parametersm, actually defined in d_main.c */
    public boolean nomonsters; // checkparm of -nomonsters

    public boolean respawnparm; // checkparm of -respawn

    public boolean fastparm; // checkparm of -fast

    public boolean devparm; // DEBUG: launched with -devparm

 // MAES: declared as "extern", shared with Menu.java
    public  boolean	inhelpscreens;

    boolean		advancedemo;
    
    /////////// Local to doomstat.c ////////////
    // TODO: hide those behind getters
    
    /** Game Mode - identify IWAD as shareware, retail etc. 
     *  This is now hidden behind getters so some cases like plutonia
     *  etc. can be handled more cleanly.
     * */

    private GameMode_t gamemode;
    
    public void setGameMode(GameMode_t mode){
    	this.gamemode=mode;
    }
    
    public GameMode_t getGameMode(){
    	return gamemode;
    }
    
    public boolean isShareware(){
    	return (gamemode== GameMode_t.shareware);
    }    
    
    /** Commercial means Doom 2, Plutonia, TNT, and possibly others like XBLA.
     * 
     * @return
     */
    public boolean isCommercial(){
    	return (gamemode== GameMode_t.commercial ||
    			gamemode== GameMode_t.pack_plut ||
    			gamemode== GameMode_t.pack_tnt ||
    			gamemode== GameMode_t.pack_xbla);
    }
    
    /** Retail means Ultimate.
     * 
     * @return
     */
    public boolean isRetail(){
    	return (gamemode== GameMode_t.retail );
    }
    
    /** Registered is a subset of Ultimate 
     * 
     * @return
     */

    public boolean isRegistered(){
    	return (gamemode== GameMode_t.registered || gamemode== GameMode_t.retail );
    }
    
    public GameMission_t gamemission;

    /** Set if homebrew PWAD stuff has been added. */
    public boolean modifiedgame;

    /** Language. */
    public Language_t language;

    // /////////// Normally found in d_main.c ///////////////

    // Selected skill type, map etc.

    /** Defaults for menu, methinks. */
    public skill_t startskill;

    public int startepisode;

    public int startmap;

    public boolean autostart;

    /** Selected by user */
    public skill_t gameskill;

    public int gameepisode;

    public int gamemap;

    /** Nightmare mode flag, single player. */
    public boolean respawnmonsters;

    /** Netgame? Only true if >1 player. */
    public boolean netgame;

    /**
     * Flag: true only if started as net deathmatch. An enum might handle
     * altdeath/cooperative better. Use altdeath for the "2" value
     */
    public boolean deathmatch;

    /** Use this instead of "deathmatch=2" which is bullshit. */
    public boolean altdeath;
    
    //////////// STUFF SHARED WITH THE RENDERER ///////////////
    
    // -------------------------
    // Status flags for refresh.
    //

    public boolean nodrawers;

    public boolean noblit;
    
    public boolean viewactive;
    
    // Player taking events, and displaying.
    public int consoleplayer;

    public int displayplayer;
    
    // Depending on view size - no status bar?
    // Note that there is no way to disable the
    // status bar explicitely.
    public boolean statusbaractive;

    public boolean automapactive; // In AutoMap mode?

    public boolean menuactive; // Menu overlayed?

    public boolean paused; // Game Pause?

    // -------------------------
    // Internal parameters for sound rendering.
    // These have been taken from the DOS version,
    // but are not (yet) supported with Linux
    // (e.g. no sound volume adjustment with menu.

    // These are not used, but should be (menu).
    // From m_menu.c:
    // Sound FX volume has default, 0 - 15
    // Music volume has default, 0 - 15
    // These are multiplied by 8.
    /** maximum volume for sound */
    public int snd_SfxVolume;

    /** maximum volume for music */
    public int snd_MusicVolume;

    /** Maximum number of sound channels */
    public int numChannels;

    // Current music/sfx card - index useless
    // w/o a reference LUT in a sound module.
    // Ideally, this would use indices found
    // in: /usr/include/linux/soundcard.h
    public int snd_MusicDevice;

    public int snd_SfxDevice;

    // Config file? Same disclaimer as above.
    public int snd_DesiredMusicDevice;

    public int snd_DesiredSfxDevice;

    
    // -------------------------------------
    // Scores, rating.
    // Statistics on a given map, for intermission.
    //
    public int totalkills;

    public int totalitems;

    public int totalsecret;

    /** TNTHOM "cheat" for flashing HOM-detecting BG */
	public boolean flashing_hom;
    
    // Added for prBoom+ code
    public int totallive;
    
    // Timer, for scores.
    public int levelstarttic; // gametic at level start

    public int leveltime; // tics in game play for par

    // --------------------------------------
    // DEMO playback/recording related stuff.
    // No demo, there is a human player in charge?
    // Disable save/end game?
    public boolean usergame;

    // ?
    public boolean demoplayback;

    public boolean demorecording;

    // Quit after playing a demo from cmdline.
    public boolean singledemo;

    /** Set this to GS_DEMOSCREEN upon init, else it will be null*/
    public gamestate_t gamestate=gamestate_t.GS_DEMOSCREEN;

    // -----------------------------
    // Internal parameters, fixed.
    // These are set by the engine, and not changed
    // according to user inputs. Partly load from
    // WAD, partly set at startup time.

    public int gametic;

    // Bookkeeping on players - state.
    public player_t[] players;

    // Alive? Disconnected?
    public boolean[] playeringame = new boolean[MAXPLAYERS];

    public mapthing_t[] deathmatchstarts = new mapthing_t[MAX_DM_STARTS];

    /** pointer into deathmatchstarts */
    public int deathmatch_p;

    /** Player spawn spots. */
    public mapthing_t[] playerstarts = new mapthing_t[MAXPLAYERS];

    /** Intermission stats.
      Parameters for world map / intermission. */
    public wbstartstruct_t wminfo;

    /** LUT of ammunition limits for each kind.
        This doubles with BackPack powerup item.
        NOTE: this "maxammo" is treated like a global.
         */
    public final static int[] maxammo =  {200, 50, 300, 50};

    // -----------------------------------------
    // Internal parameters, used for engine.
    //

    // File handling stuff.
    
    public OutputStreamWriter debugfile;

    // if true, load all graphics at level load
    public boolean precache;

    // wipegamestate can be set to -1
    // to force a wipe on the next draw
    // wipegamestate can be set to -1 to force a wipe on the next draw
    public gamestate_t     wipegamestate = gamestate_t.GS_DEMOSCREEN;
    
    public int mouseSensitivity=15;
    
    // debug flag to cancel adaptiveness
    // Set to true during timedemos.
    public boolean singletics=false;
    
    /* A "fastdemo" is a demo with a clock that tics as
     * fast as possible, yet it maintains adaptiveness and doesn't
     * try to render everything at all costs.
     */
    protected boolean fastdemo;
    protected boolean normaldemo;
    
    protected String loaddemo;
    
    public int bodyqueslot;

    // Needed to store the number of the dummy sky flat.
    // Used for rendering,
    // as well as tracking projectiles etc.
    //public int skyflatnum;

    // TODO: Netgame stuff (buffers and pointers, i.e. indices).

    // TODO: This is ???
    public doomcom_t doomcom;

    // TODO: This points inside doomcom.
    public doomdata_t netbuffer;

    public ticcmd_t[] localcmds = new ticcmd_t[BACKUPTICS];

    public int rndindex;

    public int maketic;

    public int[] nettics = new int[MAXNETNODES];

    public ticcmd_t[][] netcmds;// [MAXPLAYERS][BACKUPTICS];
      
    /** MAES: this WAS NOT in the original. 
     *  Remember to call it!
     */
    protected void initNetGameStuff() {
        //this.netbuffer = new doomdata_t();
        this.doomcom = new doomcom_t();
        this.netcmds = new ticcmd_t[MAXPLAYERS][BACKUPTICS];

        C2JUtils.initArrayOfObjects(localcmds);
        for (int i=0;i<MAXPLAYERS;i++){
        C2JUtils.initArrayOfObjects(netcmds[i]);
        }
        
    }
    
    public abstract void Init();

    // Fields used for selecting variable BPP implementations.
    
    protected abstract Finale<T> selectFinale();
    
    protected abstract DoomVideoRenderer<T,V> selectVideoRenderer();
    
    protected abstract Renderer<T,V> selectRenderer();
    
    protected abstract Wiper<T,V> selectWiper();
    
    protected abstract IAutoMap<T,V> selectAutoMap();
    
    // MAES: Fields specific to DoomGame. A lot of them were
    // duplicated/externalized
    // in d_game.c and d_game.h, so it makes sense adopting a more unified
    // approach.
    protected gameaction_t gameaction=gameaction_t.ga_nothing;

    public boolean sendpause; // send a pause event next tic

    protected boolean sendsave; // send a save event next tic

    protected int starttime;

    protected boolean timingdemo; // if true, exit with report on completion

    public boolean getPaused() {
        return paused;
    }

    public void setPaused(boolean paused) {
        this.paused = paused;
    }

    // ////////// DEMO SPECIFIC STUFF/////////////

    protected String demoname;

    protected boolean netdemo;

    //protected IDemoTicCmd[] demobuffer;

    protected IDoomDemo demobuffer;
    /** pointers */
    // USELESS protected int demo_p;

   // USELESS protected int demoend;

    protected short[][] consistancy = new short[MAXPLAYERS][BACKUPTICS];

    protected byte[] savebuffer;

    /* TODO Proper reconfigurable controls. Defaults hardcoded for now. T3h h4x, d00d. */

    public int key_right=KEY_RIGHTARROW;

    public int key_left=KEY_LEFTARROW;

    public int key_up='w';

    public int key_down='a';

    public int key_strafeleft='s';

    public int key_straferight='d';

    public int key_fire=KEY_CTRL;

    public int key_use=' ';
    
    public int key_strafe=KEY_ALT;

    public int key_speed=KEY_SHIFT;
    
    // Heretic stuff
    public int		key_lookup=KEY_PGUP;
    public int		key_lookdown=KEY_PGDN;
    public int      key_lookcenter=KEY_END;

    public int mousebfire=0;

    public int mousebstrafe=1;

    public int mousebforward=2;

    public int joybfire;

    public int joybstrafe;

    public int joybuse;

    public int joybspeed;
    
    /** Cancel vertical mouse movement by default */
    protected boolean novert=true;

    protected int MAXPLMOVE() {
        return forwardmove[1];
    }

    protected static final int TURBOTHRESHOLD = 0x32;

    /** fixed_t */
    protected final int[] forwardmove = { 0x19, 0x32 }; // + slow turn

    protected final int[] sidemove = { 0x18, 0x28 };

    protected final int[] angleturn = { 640, 1280, 320 };

    protected static final int SLOWTURNTICS = 6;

    protected static final int NUMKEYS = 256;

    protected boolean[] gamekeydown = new boolean[NUMKEYS];

    protected boolean keysCleared;
    
    public boolean alwaysrun;

    protected int turnheld; // for accelerative turning
    protected int lookheld; // for accelerative looking?

    protected boolean[] mousearray = new boolean[4];

    /** This is an alias for mousearray [1+i] */
    protected boolean mousebuttons(int i) {
        return mousearray[1 + i]; // allow [-1]
    }

    protected void mousebuttons(int i, boolean value) {
        mousearray[1 + i] = value; // allow [-1]
    }

    protected void mousebuttons(int i, int value) {
        mousearray[1 + i] = value != 0; // allow [-1]
    }

    /** mouse values are used once */
    protected int mousex, mousey;

    protected int dclicktime;

    protected int dclickstate;

    protected int dclicks;

    protected int dclicktime2, dclickstate2, dclicks2;

    /** joystick values are repeated */
    protected int joyxmove, joyymove;

    protected boolean[] joyarray = new boolean[5];

    protected boolean joybuttons(int i) {
        return joyarray[1 + i]; // allow [-1]
    }

    protected void joybuttons(int i, boolean value) {
        joyarray[1 + i] = value; // allow [-1]
    }

    protected void joybuttons(int i, int value) {
        joyarray[1 + i] = value != 0; // allow [-1]
    }

    protected int savegameslot;

    protected String savedescription;

    protected static final int BODYQUESIZE = 32;

    protected mobj_t[] bodyque = new mobj_t[BODYQUESIZE];

    public String statcopy; // for statistics driver

    
    
    /** Not documented/used in linuxdoom. I supposed it could be used to
     *  ignore mouse input?
     */
    
    public boolean use_mouse,use_joystick;
    
    
    /** More prBoom+ stuff. Used mostly for code uhm..reuse, rather
     *  than to actually change the way stuff works.
     *   
     */
    
    public static int compatibility_level;
    
    public DoomStatus(){
    	players = new player_t[MAXPLAYERS];
    	C2JUtils.initArrayOfObjects(players);

    	this.wminfo=new wbstartstruct_t();
    	initNetGameStuff();
    }

    @Override
    public void registerVariableManager(IVariablesManager manager) {
        this.VM=manager;        
    }

    @Override
    public void update() {

           this.snd_SfxVolume=VM.getSetting(Settings.sfx_volume).getInteger();
           this.snd_MusicVolume=VM.getSetting(Settings.music_volume).getInteger();
           this.alwaysrun=VM.getSetting(Settings.alwaysrun).getBoolean();
                     
          
           // Keys...
           this.key_right=VM.getSetting(Settings.key_right).getChar();
           this.key_left=VM.getSetting(Settings.key_left).getChar();
           this.key_up=VM.getSetting(Settings.key_up).getChar();
           this.key_down=VM.getSetting(Settings.key_down).getChar();
           this.key_strafeleft=VM.getSetting(Settings.key_strafeleft).getChar();
           this.key_straferight=VM.getSetting(Settings.key_straferight).getChar();
           this.key_fire=VM.getSetting(Settings.key_fire).getChar();
           this.key_use=VM.getSetting(Settings.key_use).getChar();
           this.key_strafe=VM.getSetting(Settings.key_strafe).getChar();
           this.key_speed=VM.getSetting(Settings.key_speed).getChar();
           

           // Mouse buttons
           this.use_mouse=VM.getSetting(Settings.use_mouse).getBoolean();
           this.mousebfire=VM.getSetting(Settings.mouseb_fire).getInteger();
           this.mousebstrafe=VM.getSetting(Settings.mouseb_strafe).getInteger();
           this.mousebforward=VM.getSetting(Settings.mouseb_forward).getInteger();
           
           // Joystick

           this.use_joystick=VM.getSetting(Settings.use_joystick).getBoolean();
           this.joybfire=VM.getSetting(Settings.joyb_fire).getInteger();
           this.joybstrafe=VM.getSetting(Settings.joyb_strafe).getInteger();
           this.joybuse=VM.getSetting(Settings.joyb_use).getInteger();
           this.joybspeed=VM.getSetting(Settings.joyb_speed).getInteger();

           // Sound
           this.numChannels=VM.getSetting(Settings.snd_channels).getInteger();

           // Video...so you should wait until video renderer is active.           
           this.V.setUsegamma(VM.getSetting(Settings.usegamma).getInteger());
           
           // These should really be handled by the menu.
           this.M.setShowMessages(VM.getSetting(Settings.show_messages).getBoolean());
           this.M.setScreenBlocks(VM.getSetting(Settings.screenblocks).getInteger());

           // These should be handled by the HU

           for (int i=0;i<=9;i++){
               
           String chatmacro=String.format("chatmacro%d",i);
           this.HU.setChatMacro(i,VM.getSetting(chatmacro).toString());
           }
        }

    @Override
    public void commit() {
        VM.putSetting(Settings.sfx_volume,this.snd_SfxVolume);
        VM.putSetting(Settings.music_volume,this.snd_MusicVolume);
        VM.putSetting(Settings.alwaysrun, this.alwaysrun);
       
       
        // Keys...
        VM.putSetting(Settings.key_right,this.key_right);
        VM.putSetting(Settings.key_left,this.key_left);
        VM.putSetting(Settings.key_up,this.key_up);
        VM.putSetting(Settings.key_down,this.key_down);
        VM.putSetting(Settings.key_strafeleft,this.key_strafeleft);
        VM.putSetting(Settings.key_straferight,this.key_straferight);
        VM.putSetting(Settings.key_fire,this.key_fire);
        VM.putSetting(Settings.key_use,this.key_use);
        VM.putSetting(Settings.key_strafe,this.key_strafe);
        VM.putSetting(Settings.key_speed,this.key_speed);
        

        // Mouse buttons
        VM.putSetting(Settings.use_mouse,this.use_mouse);
        VM.putSetting(Settings.mouseb_fire,this.mousebfire);
        VM.putSetting(Settings.mouseb_strafe,this.mousebstrafe);
        VM.putSetting(Settings.mouseb_forward,this.mousebforward);
        
        // Joystick

        VM.putSetting(Settings.use_joystick,this.use_joystick);
        VM.putSetting(Settings.joyb_fire,this.joybfire);
        VM.putSetting(Settings.joyb_strafe,this.joybstrafe);
        VM.putSetting(Settings.joyb_use,this.joybuse);
        VM.putSetting(Settings.joyb_speed,this.joybspeed);

        // Sound
        VM.putSetting(Settings.snd_channels,this.numChannels);
        
        // Video...         
        VM.putSetting(Settings.usegamma,V.getUsegamma());
        
        // These should really be handled by the menu.
        VM.putSetting(Settings.show_messages,this.M.getShowMessages());
        VM.putSetting(Settings.screenblocks,this.M.getScreenBlocks());
        
        // These should be handled by the HU

        for (int i=0;i<=9;i++){
            
        String chatmacro=String.format("chatmacro%d",i);
        VM.putSetting(chatmacro,this.HU.chat_macros[i]);
        }
        
    }
    

    

}

// $Log: DoomStatus.java,v $
// Revision 1.36  2012/11/06 16:04:58  velktron
// Variables manager less tightly integrated.
//
// Revision 1.35  2012/09/24 17:16:22  velktron
// Massive merge between HiColor and HEAD. There's no difference from now on, and development continues on HEAD.
//
// Revision 1.34.2.3  2012/09/24 16:58:06  velktron
// TrueColor, Generics.
//
// Revision 1.34.2.2  2012/09/20 14:25:13  velktron
// Unified DOOM!!!
//
// Revision 1.34.2.1  2012/09/17 16:06:52  velktron
// Now handling updates of all variables, though those specific to some subsystems should probably be moved???
//
// Revision 1.34  2011/11/01 23:48:10  velktron
// Added tnthom stuff.
//
// Revision 1.33  2011/10/24 02:11:27  velktron
// Stream compliancy
//
// Revision 1.32  2011/10/07 16:01:16  velktron
// Added freelook stuff, using Keys.
//
// Revision 1.31  2011/09/27 16:01:41  velktron
// -complevel_t
//
// Revision 1.30  2011/09/27 15:54:51  velktron
// Added some more prBoom+ stuff.
//
// Revision 1.29  2011/07/28 17:07:04  velktron
// Added always run hack.
//
// Revision 1.28  2011/07/16 10:57:50  velktron
// Merged finnw's changes for enabling polling of ?_LOCK keys.
//
// Revision 1.27  2011/06/14 20:59:47  velktron
// Channel settings now read from default.cfg. Changes in sound creation order.
//
// Revision 1.26  2011/06/04 11:04:25  velktron
// Fixed registered/ultimate identification.
//
// Revision 1.25  2011/06/01 17:35:56  velktron
// Techdemo v1.4a level. Default novert and experimental mochaevents interface.
//
// Revision 1.24  2011/06/01 00:37:58  velktron
// Changed default keys to WASD.
//
// Revision 1.23  2011/05/31 21:45:51  velktron
// Added XBLA version as explicitly supported.
//
// Revision 1.22  2011/05/30 15:50:42  velktron
// Changed to work with new Abstract classes
//
// Revision 1.21  2011/05/26 17:52:11  velktron
// Now using ICommandLineManager
//
// Revision 1.20  2011/05/26 13:39:52  velktron
// Now using ICommandLineManager
//
// Revision 1.19  2011/05/25 17:56:52  velktron
// Introduced some fixes for mousebuttons etc.
//
// Revision 1.18  2011/05/24 17:44:37  velktron
// usemouse added for defaults
//
package doom;

import i.DiskDrawer;
import i.DoomStatusAware;
import i.DoomSystem;
import i.DoomVideoInterface;
import i.Strings;

import java.io.BufferedInputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import n.DummyNetworkDriver;
import static data.dstrings.*;
import p.Actions;
import p.BoomLevelLoader;
import p.mobj_t;
import automap.IAutoMap;
import automap.Map;
import awt.AWTDoom;
import f.EndLevel;
import f.Finale;
import f.Wiper;
import hu.HU;
import m.Menu;
import m.MenuMisc;
import m.DoomRandom;
import m.Settings;
import m.VarsManager;
import static doom.NetConsts.*;
import static doom.englsh.*;
import data.Tables;
import data.dstrings;
import data.mapthing_t;
import data.mobjtype_t;
import defines.*;
import demo.IDemoTicCmd;
import demo.IDoomDemo;
import demo.VanillaDoomDemo;
import demo.VanillaTiccmd;
import data.sounds.musicenum_t;
import data.sounds.sfxenum_t;
import static data.Defines.BACKUPTICS;
import static g.Keys.*;
import static data.Defines.NORMALUNIX;
import static data.Defines.PU_STATIC;
import static data.Defines.VERSION;
import rr.Renderer;
import rr.SimpleTextureManager;
import rr.SpriteManager;
import rr.UnifiedRenderer;
import rr.ViewVars;
import rr.subsector_t;
import rr.parallel.ParallelRenderer;
import rr.parallel.ParallelRenderer2;
import s.AbstractDoomAudio;
import s.ClassicDoomSoundDriver;
import s.ClipSFXModule;
import s.DavidMusicModule;
import s.DavidSFXModule;
import s.DummyMusic;
import s.DummySFX;
import s.DummySoundDriver;
import s.SpeakerDoomSoundDriver;
import s.SuperDoomSoundDriver;
//import s.SpeakerDoomSoundDriver;

import savegame.IDoomSaveGame;
import savegame.IDoomSaveGameHeader;
import savegame.VanillaDSG;
import savegame.VanillaDSGHeader;
import st.StatusBar;
import timing.FastTicker;
import timing.ITicker;
import timing.MilliTicker;
import timing.NanoTicker;
import utils.C2JUtils;
import v.BufferedRenderer;
import v.BufferedRenderer16;
import v.BufferedRenderer32;
import v.ColorTint;
import v.DoomVideoRenderer;
import v.GammaTables;
import v.IVideoScale;
import v.IVideoScaleAware;
import v.PaletteGenerator;
//import v.VideoScaleInfo;
import v.VisualSettings;
import w.DoomBuffer;
import w.WadLoader;
import static data.Defines.*;
import static data.Limits.*;
import static data.Tables.*;
import static data.dstrings.SAVEGAMENAME;
import static data.info.mobjinfo;
import static data.info.states;
import static m.fixed_t.FRACBITS;
import static m.fixed_t.MAPFRACUNIT;
import static utils.C2JUtils.*;

// Emacs style mode select   -*- C++ -*- 
//-----------------------------------------------------------------------------
//
// $Id: DoomMain.java,v 1.110 2016/06/06 22:21:24 velktron Exp $
//
// Copyright (C) 1993-1996 by id Software, Inc.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// DESCRIPTION:
//	DOOM main program (D_DoomMain) and game loop (D_DoomLoop),
//	plus functions to determine game mode (shareware, registered),
//	parse command line parameters, configure game parameters (turbo),
//	and call the startup functions.
//
//  In Mocha Doom, this was unified with d_game and doomstat.c
//
//-----------------------------------------------------------------------------

public abstract class DoomMain<T,V> extends DoomStatus<T,V> implements IDoomGameNetworking, IDoomGame, IDoom, IVideoScaleAware{

    public static final String rcsid = "$Id: DoomMain.java,v 1.110 2016/06/06 22:21:24 velktron Exp $";

    //
    // EVENT HANDLING
    //
    // Events are asynchronous inputs generally generated by the game user.
    // Events can be discarded if no responder claims them
    //
    public event_t[]         events=new event_t[MAXEVENTS];
    public int             eventhead;
    public int 		eventtail;

    /**
     * D_PostEvent
     * Called by the I/O functions when input is detected
     */

    public void PostEvent (event_t ev)
    {
        // TODO create a pool of reusable messages?
        // NEVERMIND we can use the original system.
        events[eventhead].setFrom(ev);
        eventhead = (++eventhead)&(MAXEVENTS-1);
    }


    /**
     * D_ProcessEvents
     * Send all the events of the given timestamp down the responder chain
     */ 
    public void ProcessEvents ()
    {
        event_t	ev;

        // IF STORE DEMO, DO NOT ACCEPT INPUT

        if ( ( isCommercial() )
                && (W.CheckNumForName("MAP01")<0) )
            return; 

        for ( ; eventtail != eventhead ; eventtail = (++eventtail)&(MAXEVENTS-1) )
        {
            ev = events[eventtail];
            if (M.Responder (ev)){
                //epool.checkIn(ev);
                continue;               // menu ate the event
            }
            Responder (ev);
            // We're done with it, return it to the pool.
            //epool.checkIn(ev);
        }
    }

    // "static" to Display, don't move.
    private  boolean		viewactivestate = false;
    private  boolean		menuactivestate = false;
    private  boolean		inhelpscreensstate = false;
    private  boolean		fullscreen = false;
    private  gamestate_t	oldgamestate = gamestate_t.GS_MINUS_ONE;
    private  int			borderdrawcount;

    /**
     * D_Display
     * draw current display, possibly wiping it from the previous
     * @throws IOException 
     */

    public void Display () throws IOException
    {
        int				nowtime;
        int				tics;
        int				wipestart;
        int				y;
        boolean			done;
        boolean			wipe;
        boolean			redrawsbar;

        if (nodrawers)
            return;                    // for comparative timing / profiling

        redrawsbar = false;

        // change the view size if needed
        if (R.getSetSizeNeeded())
        {
            R.ExecuteSetViewSize ();
            oldgamestate = gamestate_t.GS_MINUS_ONE;                      // force background redraw
            borderdrawcount = 3;
        }

        // save the current screen if about to wipe
        if (wipe=(gamestate != wipegamestate))
        {
            wipe = true;
            WIPE.StartScreen(0, 0, SCREENWIDTH, SCREENHEIGHT);
        }
        else
            wipe = false;

        if (gamestate == gamestate_t.GS_LEVEL && eval(gametic))
            HU.Erase();

        // do buffered drawing
        switch (gamestate)
        {
        case GS_LEVEL:
            if (!eval(gametic))
                break;
            if (automapactive)
                AM.Drawer ();
            if (wipe || (!R.isFullHeight() && fullscreen) ||
            		(inhelpscreensstate && !inhelpscreens)
            		|| (DD.justDoneReading()))
                redrawsbar = true; // just put away the help screen
            ST.Drawer (R.isFullHeight(), redrawsbar );
            fullscreen = R.isFullHeight();
            break;

        case GS_INTERMISSION:
            WI.Drawer ();
            break;

        case GS_FINALE:
            F.Drawer ();
            break;

        case GS_DEMOSCREEN:
            PageDrawer ();
            break;
        }

        // draw the view directly
        if (gamestate == gamestate_t.GS_LEVEL && !automapactive && eval(gametic)){
        	if (flashing_hom){
                V.FillRect(gametic%256,0,view.getViewWindowX(),view.getViewWindowY(),
                    view.getScaledViewWidth(),view.getScaledViewHeight());
        	    }
            R.RenderPlayerView (players[displayplayer]);
        	}

        // Automap was active, update only HU.    
        if (gamestate == gamestate_t.GS_LEVEL && eval(gametic))
            HU.Drawer ();

        // clean up border stuff
        if (gamestate != oldgamestate && gamestate != gamestate_t.GS_LEVEL)
            VI.SetPalette (0);

        // see if the border needs to be initially drawn
        if (gamestate == gamestate_t.GS_LEVEL && oldgamestate != gamestate_t.GS_LEVEL)
        {
            viewactivestate = false;        // view was not active
            R.FillBackScreen ();    // draw the pattern into the back screen
        }

        // see if the border needs to be updated to the screen
        if (gamestate == gamestate_t.GS_LEVEL && !automapactive && !R.isFullScreen())
        {
            if (menuactive || menuactivestate || !viewactivestate)
                borderdrawcount = 3;
            if (eval(borderdrawcount))
            {
                R.DrawViewBorder ();    // erase old menu stuff
                borderdrawcount--;
            }

        }

        menuactivestate = menuactive;
        viewactivestate = viewactive;
        inhelpscreensstate = inhelpscreens;
        oldgamestate = wipegamestate = gamestate;

        // draw pause pic
        if (paused)
        {
            if (automapactive)
                y = 4;
            else
                y = view.getViewWindowY()+4;
            V.DrawPatchDirect(view.getViewWindowX()+(view.getScaledViewWidth()-68)/2,
                y,0,W.CachePatchName ("M_PAUSE", PU_CACHE));
        }


        // menus go directly to the screen
        M.Drawer ();          // menu is drawn even on top of everything
        NetUpdate ();         // send out any new accumulation

        // Disk access goes after everything.
        DD.Drawer();
        
        // normal update
        if (!wipe)
        {
            //System.out.print("Tick "+DM.gametic+"\t");
            //System.out.print(DM.players[0]);
            VI.FinishUpdate ();              // page flip or blit buffer
            return;
        }

        // wipe update. At this point, AT LEAST one frame of the game must have been
        // rendered for this to work. 22/5/2011: Fixed a vexing bug with the wiper.
        // Jesus Christ with a Super Shotgun!
        WIPE.EndScreen(0, 0, SCREENWIDTH, SCREENHEIGHT);

        wipestart = TICK.GetTime () - 1;

        do
        {
            do
            {
                nowtime = TICK.GetTime ();
                tics = nowtime - wipestart;
            } while (tics==0); // Wait until a single tic has passed.
            wipestart = nowtime;
            done = WIPE.ScreenWipe(Wiper.wipe.Melt.ordinal()
                , 0, 0, SCREENWIDTH, SCREENHEIGHT, tics);
            ISND.UpdateSound();
            ISND.SubmitSound();             // update sounds after one wipe tic.
            VI.UpdateNoBlit ();
            M.Drawer ();                    // menu is drawn even on top of wipes
            VI.FinishUpdate ();             // page flip or blit buffer
        } while (!done);

    }



    /**
     * D-DoomLoop()
     * Not a globally visible function,
     *  just included for source reference,
     *  called by D_DoomMain, never exits.
     * Manages timing and IO,
     *  calls all ?_Responder, ?_Ticker, and ?_Drawer,
     *  calls I_GetTime, I_StartFrame, and I_StartTic
     * @throws IOException 
     */

    public void DoomLoop () throws IOException
    {
        if (demorecording)
            BeginRecording ();

        if (eval(CM.CheckParm ("-debugfile")))
        {
            String    filename="debug"+consoleplayer+".txt";
            System.out.println("debug output to: "+filename);
            try {
                debugfile = new OutputStreamWriter(new FileOutputStream(filename));
            } catch (FileNotFoundException e) {
                System.err.println("Couldn't open debugfile. Now, that sucks some putrid shit out of John Romero's asshole!");
                e.printStackTrace();
            }
        }
        
        view=R.getView();
        
        while (true)
        {
            // frame syncronous IO operations
            VI.StartFrame ();                

            // process one or more tics
            if (singletics)
            {
                VI.StartTic ();
                ProcessEvents ();
                BuildTiccmd (netcmds[consoleplayer][maketic%BACKUPTICS]);
                if (advancedemo)
                    DoAdvanceDemo ();
                M.Ticker ();
                Ticker ();
                gametic++;
                maketic++;
            }
            else
            {
                DGN.TryRunTics (); // will run at least one tic (in NET)

            }

            S.UpdateSounds (players[consoleplayer].mo);// move positional sounds

            // Update display, next frame, with current state.
            Display ();
            
//#ifndef SNDSERV
	// Sound mixing for the buffer is snychronous.
	ISND.UpdateSound();
//#endif	
	// Synchronous sound output is explicitly called.
//#ifndef SNDINTR
	// Update sound output.
	ISND.SubmitSound();
//#endif
             
        }
    }
    
    // To keep an "eye" on the renderer.
    protected ViewVars view;

    //
    //  DEMO LOOP
    //
    int             demosequence;
    int             pagetic;
    String pagename;


    /**
     * D_PageTicker
     * Handles timing for warped projection
     */
    public final void PageTicker ()
    {
        if (--pagetic < 0)
            AdvanceDemo ();
    }



    /**
     * D_PageDrawer
     */

    public final void PageDrawer ()
    {

        // FIXME: this check wasn't necessary in vanilla, since pagename was 
        // guaranteed(?) not to be null or had a safe default value.  
        if (pagename != null)
            V.DrawPatchSolidScaled (0,0, SAFE_SCALE, SAFE_SCALE,0,W.CachePatchName(pagename, PU_CACHE));
    }


    /**
     * D_AdvanceDemo
     * Called after each demo or intro demosequence finishes
     */

    public void AdvanceDemo ()
    {
        advancedemo = true;
    }


    //
    // This cycles through the demo sequences.
    // FIXME - version dependant demo numbers?
    //
    public void DoAdvanceDemo ()
    {
        players[consoleplayer].playerstate = PST_LIVE;  // not reborn
        advancedemo = false;
        usergame = false;               // no save / end game here
        paused = false;
        gameaction = gameaction_t.ga_nothing;
        
        if ( isRetail()) // Allows access to a 4th demo.
            demosequence = (demosequence+1)%7;
        else
            demosequence = (demosequence+1)%6;

        switch (demosequence)
        {
        case 0:
            if ( isCommercial() )
                pagetic = 35 * 11;
            else
                pagetic = 170;
            gamestate = gamestate_t.GS_DEMOSCREEN;
            
            
            if (W.CheckNumForName("TITLEPIC")!=-1){
                pagename = "TITLEPIC";    
            } else {
                if (W.CheckNumForName("DMENUPIC")!=-1){
                    pagename = "DMENUPIC";
                }
            }
            
            if ( isCommercial() )
                S.StartMusic(musicenum_t.mus_dm2ttl);

            else
                S.StartMusic (musicenum_t.mus_intro); 
            break;
        case 1:
            DeferedPlayDemo ("demo1");
            break;
        case 2:
            pagetic = 200;
            gamestate = gamestate_t.GS_DEMOSCREEN;
            pagename = "CREDIT";
            break;
        case 3:
            DeferedPlayDemo ("demo2");
            break;
        case 4:
            gamestate = gamestate_t.GS_DEMOSCREEN;
            if ( isCommercial())
            {
                pagetic = 35 * 11;
                pagename = "TITLEPIC";
                S.StartMusic(musicenum_t.mus_dm2ttl);
            }
            else
            {
                pagetic = 200;

                if ( isRetail() )
                    pagename = "CREDIT";
                else
                    pagename = "HELP1";
            }
            break;
        case 5:
            DeferedPlayDemo ("demo3");
            break;
            // THE DEFINITIVE DOOM Special Edition demo
        case 6:
            DeferedPlayDemo ("demo4");
            break;
        }
    }



    /**
     * D_StartTitle
     */

    public void StartTitle ()
    {
        gameaction = gameaction_t.ga_nothing;
        demosequence = -1;
        AdvanceDemo ();
    }




    //      printtitle for every printed line
    StringBuffer            title=new StringBuffer();



    /**
     * D_AddFile
     * 
     * Adds file to the end of the wadfiles[] list.
     * Quite crude, we could use a listarray instead.
     * 
     * @param file
     */
    void AddFile (String file)
    {
        int     numwadfiles;
        String newfile;

        for (numwadfiles = 0 ; eval(wadfiles[numwadfiles]) ; numwadfiles++)
            ;

        newfile = new String(file);

        wadfiles[numwadfiles] = newfile;
    }
    

    /**
     * IdentifyVersion
     * Checks availability of IWAD files by name,
     * to determine whether registered/commercial features
     * should be executed (notably loading PWAD's).
     */

    public String IdentifyVersion ()
    {

        String home;
        String doomwaddir;
        DoomVersions vcheck=new DoomVersions();
        
        // By default.
        language=Language_t.english;
        
        // First, check for -iwad parameter.
        // If valid, then it trumps all others.
        
        int p;
        p=CM.CheckParm("-iwad");
        if (eval(p))
        {
        	System.out.println("-iwad specified. Will be used with priority\n");
            String test=CM.getArgv(p+1);
            
            // It might be quoted.
            test=C2JUtils.unquoteIfQuoted(test,'"');
            
            String separator=System.getProperty("file.separator");
            doomwaddir=test.substring(0, 1+test.lastIndexOf(separator));
            String iwad=test.substring( 1+test.lastIndexOf(separator));
            GameMode_t attempt=vcheck.tryOnlyOne(iwad,doomwaddir);
            // Note: at this point we can't distinguish between "doom" retail
            // and "doom" ultimate yet.
            if (attempt!=null) {
            	AddFile(doomwaddir+iwad);
            	this.setGameMode(attempt);
            	return (doomwaddir+iwad);
            }
        } else {
        // Unix-like checking. Might come in handy sometimes.   
        // This should ALWAYS be activated, else doomwaddir etc. won't be defined.

        doomwaddir = System.getenv("DOOMWADDIR");
        if (doomwaddir!=null){
                System.out.println("DOOMWADDIR found. Will be used with priority\n");
        }
        
        home = System.getenv("HOME");
        if (NORMALUNIX){
            if (!eval(home))
                I.Error("Please set $HOME to your home directory");
        }
        
        Settings.basedefault=home+"/.doomrc";   

        // None found, using current.
        if (!eval(doomwaddir))
            doomwaddir = ".";
    
        	vcheck.tryThemAll(doomwaddir);
        }

        // MAES: Interesting. I didn't know of that :-o
        if (eval(CM.CheckParm ("-shdev")))
        {
            setGameMode(GameMode_t.shareware);
            devparm = true;
            AddFile (dstrings.DEVDATA+"doom1.wad");
            AddFile (dstrings.DEVMAPS+"data_se/texture1.lmp");
            AddFile (dstrings.DEVMAPS+"data_se/pnames.lmp");
            Settings.basedefault=dstrings.DEVDATA+"default.cfg";
            return (dstrings.DEVDATA+"doom1.wad");
        }

        if (eval(CM.CheckParm ("-regdev")))
        {
            setGameMode(GameMode_t.registered);
            devparm = true;
            AddFile (dstrings.DEVDATA+"doom.wad");
            AddFile (dstrings.DEVMAPS+"data_se/texture1.lmp");
            AddFile (dstrings.DEVMAPS+"data_se/texture2.lmp");
            AddFile (dstrings.DEVMAPS+"data_se/pnames.lmp");
            Settings.basedefault=dstrings.DEVDATA+"default.cfg";
            return (dstrings.DEVDATA+"doom.wad");
        }

        if (eval(CM.CheckParm ("-comdev")))
        {
            setGameMode(GameMode_t.commercial);
            devparm = true;
            /* I don't bother
	if(plutonia)
	    D_AddFile (DEVDATA"plutonia.wad");
	else if(tnt)
	    D_AddFile (DEVDATA"tnt.wad");
	else*/

            AddFile (dstrings.DEVDATA+"doom2.wad");	    
            AddFile (dstrings.DEVMAPS+"cdata/texture1.lmp");
            AddFile (dstrings.DEVMAPS+"cdata/pnames.lmp");
            Settings.basedefault=dstrings.DEVDATA+"default.cfg";
            return (dstrings.DEVDATA+"doom2.wad");
        }


        if ( testReadAccess(vcheck.doom2fwad))
        {
            setGameMode(GameMode_t.commercial);
            // C'est ridicule!
            // Let's handle languages in config files, okay?
            language = Language_t.french;
            System.out.println("French version\n");
            AddFile (vcheck.doom2fwad);
            return vcheck.doom2fwad;
        }


        if ( testReadAccess(vcheck.doom2wad))
        {
            setGameMode(GameMode_t.commercial);
            AddFile (vcheck.doom2wad);
            return vcheck.doom2wad;
        }

        if ( testReadAccess (vcheck.plutoniawad) )
        {
            setGameMode(GameMode_t.pack_plut);
            AddFile (vcheck.plutoniawad);
            return vcheck.plutoniawad;
        }

        if ( testReadAccess ( vcheck.tntwad) )
        {
            setGameMode(GameMode_t.pack_tnt);
            AddFile (vcheck.tntwad);
            return vcheck.tntwad;
        }
        
        if ( testReadAccess ( vcheck.tntwad) )
        {
            setGameMode(GameMode_t.pack_xbla);
            AddFile (vcheck.xblawad);
            return vcheck.xblawad;
        }

        if ( testReadAccess (vcheck.doomuwad) )
        {
            // TODO auto-detect ultimate Doom even from doom.wad
        	// Maes: this is done later on.
            setGameMode(GameMode_t.retail);
            AddFile (vcheck.doomuwad);
            return vcheck.doomuwad;
        }

        if ( testReadAccess (vcheck.doomwad) )
        {
            setGameMode(GameMode_t.registered);
            AddFile (vcheck.doomwad);
            return vcheck.doomwad;
        }

        if ( testReadAccess (vcheck.doom1wad) )
        {
            setGameMode(GameMode_t.shareware);
            AddFile (vcheck.doom1wad);
            return vcheck.doom1wad;
        }

        // MAES: Maybe we should add FreeDoom here later.

        System.out.println("Game mode indeterminate.\n");
        setGameMode(GameMode_t.indetermined);

        return null;
        // We don't abort. Let's see what the PWAD contains.
        //exit(1);
        //I_Error ("Game mode indeterminate\n");
    }




    /**
     * D_DoomMain
     *
     * Completes the job started by Init, which only created the
     * instances of the various stuff and registered the "status aware" 
     * stuff. Here everything priority-critical is 
     * called and created in more detail.
     *
     *
     */

	public void Start() throws IOException
    {
        int             p;
        StringBuffer file=new StringBuffer();

        // TODO: This may modify the command line by appending more stuff
        // from an external file. Since it can affect other stuff too,
        // maybe it should be outside of Start() and into i.Main ?
        CM.FindResponseFile ();

        String iwadfilename=IdentifyVersion ();
        
        // Sets unbuffered output in C. Not needed here. setbuf (stdout, NULL);
        modifiedgame = false;

        nomonsters = eval(CM.CheckParm ("-nomonsters"));
        respawnparm = eval(CM.CheckParm ("-respawn"));
        fastparm = eval(CM.CheckParm ("-fast"));
        devparm = eval(CM.CheckParm ("-devparm"));
        if (eval(CM.CheckParm ("-altdeath")))
            //deathmatch = 2;
            altdeath=true;
        else if (eval(CM.CheckParm ("-deathmatch")))
            deathmatch = true;

        // MAES: Check for Ultimate Doom in "doom.wad" filename.
        WadLoader tmpwad=new WadLoader();
        try {
			tmpwad.InitFile(iwadfilename,true);
		} catch (Exception e2) {
			// TODO Auto-generated catch block
			e2.printStackTrace();
		}
		// Check using a reloadable hack.
        CheckForUltimateDoom(tmpwad);    
       
        // MAES: better extract a method for this.
        GenerateTitle();

        // Print ticker info. It has already been set at Init() though.
        if (eval(CM.CheckParm("-millis"))){
            System.out.println("ITicker: Using millisecond accuracy timer.");
        } else if (eval(CM.CheckParm("-fasttic"))){
                System.out.println("ITicker: Using fastest possible timer.");
            }
        else {
            System.out.println("ITicker: Using nanosecond accuracy timer.");
        }

        System.out.println(title.toString());

        if (devparm)
            System.out.println(D_DEVSTR);

        // Running from CDROM?
        if (eval(CM.CheckParm("-cdrom")))
        {
            System.out.println(D_CDROM);
            //System.get("c:\\doomdata",0);
            System.out.println (Settings.basedefault+"c:/doomdata/default.cfg");
        }	

        // turbo option
        if ( eval(p=CM.CheckParm ("-turbo")) )
        {
            int     scale = 200;
            //int forwardmove[2];
            // int sidemove[2];

            if (p<CM.getArgc()-1)
                scale = Integer.parseInt(CM.getArgv(p+1));
            if (scale < 10)
                scale = 10;
            if (scale > 400)
                scale = 400;
            System.out.println("turbo scale: "+scale);
            forwardmove[0] = forwardmove[0]*scale/100;
            forwardmove[1] = forwardmove[1]*scale/100;
            sidemove[0] = sidemove[0]*scale/100;
            sidemove[1] = sidemove[1]*scale/100;
        }

        // add any files specified on the command line with -file wadfile
        // to the wad list
        //
        // convenience hack to allow -wart e m to add a wad file
        // prepend a tilde to the filename so wadfile will be reloadable
        p = CM.CheckParm ("-wart");
        if (eval(p))
        {
            char[] tmp=CM.getArgv(p).toCharArray();
            tmp[4]= 'p';// big hack, change to -warp
            CM.setArgv(p,new String(tmp));    
            GameMode_t gamemode=getGameMode();
            // Map name handling.
            switch (gamemode )
            {
            case shareware:
            case retail:
            case registered:
                file.append("~");
                file.append(DEVMAPS);
                file.append(String.format("E%cM%c.wad", CM.getArgv(p+1), CM.getArgv(p+2)));
                file.append(String.format("Warping to Episode %s, Map %s.\n",
                    CM.getArgv(p+1),CM.getArgv(p+2)));
                break;

            case commercial:
            default:
                p = Integer.parseInt(CM.getArgv(p+1));
                if (p<10){
                    file.append("~");
                    file.append(DEVMAPS);
                    file.append(String.format("cdata/map0%d.wad", p));
                }
                else
                {
                    file.append("~");
                    file.append(DEVMAPS);
                    file.append(String.format("cdata/map%d.wad", p));
                }
                break;
            }
            AddFile (file.toString());
        }

        p = CM.CheckParm ("-file");
        if (eval(p))
        {
            // the parms after p are wadfile/lump names,
            // until end of parms or another - preceded parm
            modifiedgame = true;            // homebrew levels
            // MAES 1/6/2011: Avoid + to avoid clashing with +map
            while (++p != CM.getArgc() && CM.getArgv(p).charAt(0) != '-' && CM.getArgv(p).charAt(0) != '+')
                AddFile (C2JUtils.unquoteIfQuoted(CM.getArgv(p),'"'));
        }
        
        p = CM.CheckParm ("-playdemo");
                
        if (eval(p)) normaldemo=true;
        else {
            p=CM.CheckParm ("-fastdemo");        
            if (eval(p)){
            System.out.println("Fastdemo mode. Boundless clock!");
            fastdemo=true;
            this.TICK=new FastTicker();            
            }
            else        
                if (!eval(p)) {
                    p = CM.CheckParm ("-timedemo");
                    if (eval(p)) singletics=true;
                }
        }

        // If any of the previous succeeded, try grabbing the filename.
        if ((normaldemo||fastdemo||singletics) && p < CM.getArgc()-1)
        {   
            loaddemo=CM.getArgv(p+1);
            AddFile (loaddemo+".lmp");
            System.out.printf("Playing demo %s.lmp.\n",loaddemo);
            autostart=true;
        }
        
        // Subsequent uses of loaddemo use only the lump name.
        loaddemo=C2JUtils.extractFileBase(loaddemo,0,true);

        // get skill / episode / map from parms
        // FIXME: should get them FROM THE DEMO itself.
        startskill = skill_t.sk_medium;
        startepisode = 1;
        startmap = 1;
        //autostart = false;


        p = CM.CheckParm ("-novert");
        if (eval(p) && p < CM.getArgc()-1)
        {
            novert=!(CM.getArgv(p+1).toLowerCase().compareTo("disable")==0);
            if (!novert) System.out.println("-novert ENABLED (default)");
            else System.out.println("-novert DISABLED. Hope you know what you're doing...");
        }

        p = CM.CheckParm ("-skill");
        if (eval(p) && p < CM.getArgc()-1)
        {
            startskill = skill_t.values()[CM.getArgv(p+1).charAt(0)-'1'];
            autostart = true;
        }

        p = CM.CheckParm ("-episode");
        if (eval(p) && p < CM.getArgc()-1)
        {
            startepisode = CM.getArgv(p+1).charAt(0)-'0';
            startmap = 1;
            autostart = true;
        }

        p = CM.CheckParm ("-timer");
        if (eval(p) && p < CM.getArgc()-1 && deathmatch)
        {
            int     time;
            time = Integer.parseInt(CM.getArgv(p+1));
            System.out.print("Levels will end after "+time+" minute");
            if (time>1)
                System.out.print("s");
            System.out.print(".\n");
        }

        // OK, and exactly how is this enforced?
        p = CM.CheckParm ("-avg");
        if (eval(p) && p < CM.getArgc()-1 && deathmatch)
            System.out.print("Austin Virtual Gaming: Levels will end after 20 minutes\n");

        // MAES 31/5/2011: added support for +map variation.
        p = CM.CheckParm ("-warp");
        if (eval(p) && p < CM.getArgc()-1)
        {
            if (isCommercial())
                startmap = Integer.parseInt(CM.getArgv(p+1));
            else
            {
                int eval=11;
                try {
                    eval=Integer.parseInt(CM.getArgv(p+1));
                } catch (Exception e){
                    // swallow exception. No warp.
                }

                if (eval>99) eval%=100;

                if (eval<10) {
                    startepisode = 1;
                    startmap = 1;
                }
                else {
                    startepisode = eval/10;
                    startmap = eval%10;
                }
            }
            autostart = true;
        }

        // Maes: 1/6/2011 Added +map support
        p = CM.CheckParm ("+map");
        if (eval(p)) 
        {
            if (isCommercial()) {
                startmap = parseAsMapXX(CM.getArgv(p+1));
                if (startmap!=-1){
                	autostart = true;
            	}
            }
            else
            {
                int eval=parseAsExMx(CM.getArgv(p+1));
                if (eval!=-1){

                    startepisode = Math.max(1,eval/10);
                    startmap = Math.max(1,eval%10);
                    autostart = true;
                }
            }
            
        }
        
        // init subsystems
        System.out.print("V_Init: allocate screens.\n");
        V.Init ();
        

        System.out.print("Z_Init: Init zone memory allocation daemon. \n");
        // DUMMY: Z_Init ();

        System.out.print("W_Init: Init WADfiles.\n");
        try {
            W.InitMultipleFiles(wadfiles,true);
        } catch (Exception e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
        }

        //
        System.out.print("VI_Init: set colormaps.\n");
        
        // MAES: FIX for incomplete palette lumps such as those in EGADOOM.
        // Generate the palette programmatically _anyway_
        byte[] pal=PaletteGenerator.generatePalette(PaletteGenerator.playpal,
        		256, ColorTint.tints);
        // Copy over the one you read from disk...
        int pallump=W.GetNumForName("PLAYPAL");
        byte[] tmppal=W.CacheLumpNumAsRawBytes(pallump, PU_STATIC);
        if (tmppal!=null)
        System.arraycopy(tmppal, 0, pal, 0, pal.length);
        
        V.createPalettes(pal, GammaTables.gammatables, 14, 256, 3, 5);
        
        W.InjectLumpNum(pallump,new DoomBuffer(ByteBuffer.wrap(pal)));
        // set it, create it, but don't make it visible yet.
        
        VI=selectVideoInterface();

        VI.InitGraphics();

        // MAES: Before we begin calling the various Init() stuff,
        // we need to make sure that objects that support the IVideoScaleAware
        // interface get set and initialized.

        initializeVideoScaleStuff();

        // MAES: The status bar needs this update because it can "force"
        // the video renderer to assign it a scratchpad screen (Screen 4).
        // Since we're at it, let's update everything, it's easy!
        
        this.updateStatusHolders(this);
        
        // Update variables and stuff NOW.
        this.update();

        // Check for -file in shareware
        CheckForPWADSInShareware();
        

        // Iff additonal PWAD files are used, printmodified banner
        
        if (modifiedgame)
        // Generate WAD loading alert. Abort upon denial.
        if (!I.GenerateAlert(Strings.MODIFIED_GAME_TITLE,Strings.MODIFIED_GAME_DIALOG)) {
        	  W.CloseAllHandles();
        	  System.exit(-2);
          }
        
        // Check and printwhich version is executed.
        switch ( getGameMode() )
        {
        case shareware:
        case indetermined:
            System.out.print("===========================================================================\n");
            System.out.print("                                Shareware!\n");
            System.out.print("===========================================================================\n");
            break;
        case registered:
        case retail:
        case commercial:
        case pack_tnt:
        case pack_plut:
        case pack_xbla:
            System.out.print("===========================================================================\n");
            System.out.print("                 Commercial product - do not distribute!\n");
            System.out.print("         Please report software piracy to the SPA: 1-800-388-PIR8\n");
            System.out.print("===========================================================================\n");
            break;

        default:
            // Ouch.
            break;
        }

        System.out.print("Tables.InitTables: Init trigonometric LUTs.\n");
        Tables.InitTables();

        System.out.print("M_Init: Init miscellaneous info.\n");
        M.Init ();

        System.out.print("R_Init: Init DOOM refresh daemon - ");
        R.Init ();
        
        System.out.print("AM_Init: Init Automap colors - ");
        AM.Init (); // Called here to set up configurable colors.

        System.out.print("\nP_Init: Init Playloop state.\n");
        P.Init ();

        System.out.print("I_Init: Setting up machine state.\n");
        I.Init ();

        System.out.print("D_CheckNetGame: Checking network game status.\n");
        CheckNetGame ();

        System.out.print("S_Init: Setting up sound.\n");
        
      // Sound "drivers" before the game sound controller.
        
        if (CM.CheckParmBool("-nomusic") || CM.CheckParmBool("-nosound"))
            this.IMUS=new DummyMusic();
        else
            this.IMUS=new DavidMusicModule();
        
        if (CM.CheckParmBool("-nosfx") ||  CM.CheckParmBool("-nosound"))
            this.ISND=new DummySFX();
        else {
            // Switch between possible sound drivers.
            // Crudish.
            if (CM.CheckParmBool("-audiolines"))
                this.ISND=	new DavidSFXModule(this,numChannels);
            else // PC Speaker emulation 
            if (CM.CheckParmBool("-speakersound"))
                this.ISND=  new SpeakerDoomSoundDriver(this,numChannels);
            else
            if (CM.CheckParmBool("-clipsound"))
                this.ISND=  new ClipSFXModule(this,numChannels);
            else  // This is the default
            if (CM.CheckParmBool("-classicsound"))
                this.ISND=  new ClassicDoomSoundDriver(this,numChannels);
            else  // This is the default
                this.ISND=  new SuperDoomSoundDriver(this,numChannels);

            }
        
        // Check for sound init failure and revert to dummy
        if (!ISND.InitSound()){
        	 System.err.println ("S_InitSound: failed. Reverting to dummy...\n");
        	this.ISND=new DummySFX();
        }
        
        if (!(CM.CheckParmBool("-nosound") || (ISND instanceof DummySFX)))// Obviously, nomusic && nosfx = nosound.
        	this.S=new AbstractDoomAudio(this,numChannels);
        else
        	// Saves a lot of distance calculations, 
        	// if we're not to output any sound at all.
            // TODO: create a Dummy that can handle music alone.
        	this.S=new DummySoundDriver();
        
        IMUS.InitMusic();
        S.Init (snd_SfxVolume *8, snd_MusicVolume *8 );

        // Hook audio to users.
        this.updateStatusHolders(this);
        
        System.out.print("HU_Init: Setting up heads up display.\n");
        HU.Init();

        System.out.print("ST_Init: Init status bar.\n");
        ST.Init ();

        // check for a driver that wants intermission stats
        p = CM.CheckParm ("-statcopy");
        if (eval(p) && p<CM.getArgc()-1)
        {
            // TODO: this should be chained to a logger
            //statcopy = CM.getArgv(p+1);
            System.out.print("External statistics registered.\n");
        }

        // start the apropriate game based on parms
        p = CM.CheckParm ("-record");

        if (eval(p) && p < CM.getArgc()-1)
        {
            RecordDemo (CM.getArgv(p+1));
            autostart = true;
        }
        
        // NOW it's safe to init the disk reader.
        DD.Init();

        // MAES: at this point everything should be set and initialized, so it's
        // time to make the players aware of the general status of Doom.
        //_D_ gonna try to initialize here, because it is needed to play a demo
        for (int i=0;i<MAXPLAYERS;i++){
            players[i].updateStatus(this);
        }

        //p = CM.CheckParm ("-timedemo");
        if (singletics)
        {
            TimeDemo (loaddemo);
            autostart = true;
            DoomLoop ();  // never returns
        }

        p = CM.CheckParm ("-loadgame");
        if (eval(p) && p < CM.getArgc()-1)
        {
            file.delete(0, file.length());
            if (eval(CM.CheckParm("-cdrom"))){
                file.append("c:\\doomdata\\");
                file.append(SAVEGAMENAME);
                file.append("%c.dsg");
                file.append(CM.getArgv(p+1).charAt(0));
            }
            else
            {
                file.append(String.format("%s%c.dsg",SAVEGAMENAME,
                    CM.getArgv(p+1).charAt(0)));

            }
            LoadGame(file.toString());
        }



        if ( gameaction != gameaction_t.ga_loadgame )
        {
            if (autostart || netgame)
                InitNew (startskill, startepisode, startmap);
            else
                StartTitle ();                // start up intro loop

        }


        if (fastdemo||normaldemo)
        {
            singledemo = true;              // quit after one demo
            if (fastdemo) timingdemo=true;
            InitNew (startskill, startepisode, startmap);
            gamestate=gamestate_t.GS_DEMOSCREEN;
            DeferedPlayDemo (loaddemo);
            DoomLoop ();  // never returns
        }

        
        
        DoomLoop ();  // never returns
    }


	protected abstract DoomVideoInterface<V> selectVideoInterface();


    protected int parseAsMapXX(String argv) {
    	
    	if (argv.length()!=5) return -1; // Nah.
    	if (argv.toLowerCase().lastIndexOf("map")!=0) return -1; // Meh.
    	int map;
    	try {
    		map=Integer.parseInt(argv.substring(3));
    	} catch (NumberFormatException e){
    		return -1; // eww
    	}
    	
		return map;
	}

    protected int parseAsExMx(String argv) {
    	
    	if (argv.length()!=4) return -1; // Nah.
    	if (argv.toLowerCase().lastIndexOf("e")!=0) return -1; // Meh.
    	if (argv.toLowerCase().lastIndexOf("m")!=2) return -1; // Meh.
    	int episode,mission;
    	try {
    		episode=Integer.parseInt(argv.substring(1,2));
    		mission=Integer.parseInt(argv.substring(3,4));
    	} catch (NumberFormatException e){
    		return -1; // eww
    	}
    	
		return episode*10+mission;
	}
    
	List<IVideoScaleAware> videoScaleChildren;

    public  void initializeVideoScaleStuff() {

        videoScaleChildren=new ArrayList<IVideoScaleAware>();

        // The automap...
        videoScaleChildren.add(this.AM);
        // The finale...
        videoScaleChildren.add(this.F);
        // The wiper...
        videoScaleChildren.add(this.WIPE);
        // The heads up...
        videoScaleChildren.add(this.HU);
        // The menu...
        videoScaleChildren.add(this.M);
        // The renderer (also has dependent children!)
        videoScaleChildren.add(this.R);
        // The Status Bar
        videoScaleChildren.add(this.ST);
        // Even the video renderer needs some initialization?
        videoScaleChildren.add(this.V);
        // wiper
        videoScaleChildren.add(this.WI);
        // disk drawer
        videoScaleChildren.add(this.DD);
        
        for(IVideoScaleAware i:videoScaleChildren){
            if (i!=null){
                i.setVideoScale(this.vs);
                i.initScaling();
            }
        }


    }


    /**
     * 
     */
    protected void CheckForPWADSInShareware() {
        if (modifiedgame)
        {
            // These are the lumps that will be checked in IWAD,
            // if any one is not present, execution will be aborted.
            String[] name=
            {
                    "e2m1","e2m2","e2m3","e2m4","e2m5","e2m6","e2m7","e2m8","e2m9",
                    "e3m1","e3m3","e3m3","e3m4","e3m5","e3m6","e3m7","e3m8","e3m9",
                    "dphoof","bfgga0","heada1","cybra1","spida1d1"
            };
            int i;

            // Oh yes I can.
            if ( isShareware())
                System.out.println("\nYou cannot -file with the shareware version. Register!");

            // Check for fake IWAD with right name,
            // but w/o all the lumps of the registered version. 
            if (isRegistered())
                for (i = 0;i < 23; i++)
                    if (W.CheckNumForName(name[i].toUpperCase())<0)
                        I.Error("\nThis is not the registered version: "+name[i]);
        }
    }

    /** Check whether the "doom.wad" we actually loaded
     *  is ultimate Doom's, by checking if it contains 
     *  e4m1 - e4m9.
     * 
     */
    protected void CheckForUltimateDoom(WadLoader W) {
        if (isRegistered())
        {
            // These are the lumps that will be checked in IWAD,
            // if any one is not present, execution will be aborted.
            String[] lumps=
            {
                    "e4m1","e4m2","e4m3","e4m4","e4m5","e4m6","e4m7","e4m8","e4m9"
            };

            // Check for fake IWAD with right name,
            // but w/o all the lumps of the registered version. 
            if (!CheckForLumps(lumps,W)) return;
            // Checks passed, so we can set the mode to Ultimate
            setGameMode(GameMode_t.retail);
        }

    }


    /** Check if ALL of the lumps exist.
     * 
     * @param name
     * @return
     */
    protected boolean CheckForLumps(String[] name, WadLoader W) {
        for (int i = 0;i < name.length; i++)
            if (W.CheckNumForName(name[i].toUpperCase())<0) {
                // Even one is missing? Not OK.
                return false; 
            }
        return true;
    }


    /**
     * 
     */
    protected void GenerateTitle() {
        switch ( getGameMode() )
        {
        case retail:
            title.append("                         ");
            title.append("The Ultimate DOOM Startup v");
            title.append(VERSION/100);
            title.append(".");
            title.append(VERSION%100);
            title.append("                           ");
            break;
        case shareware:
            title.append("                            ");
            title.append("DOOM Shareware Startup v");
            title.append(VERSION/100);
            title.append(".");
            title.append(VERSION%100);
            title.append("                           ");
            break;
        case registered:
            title.append("                            ");
            title.append("DOOM Registered Startup v");
            title.append(VERSION/100);
            title.append(".");
            title.append(VERSION%100);
            title.append("                           ");
            break;
        case commercial:
            title.append("                            ");
            title.append("DOOM 2: Hell on Earth v");
            title.append(VERSION/100);
            title.append(".");
            title.append(VERSION%100);
            title.append("                           ");

            break;
        case pack_plut:
            title.append("                            ");
            title.append("DOOM 2: Plutonia Experiment v");
            title.append(VERSION/100);
            title.append(".");
            title.append(VERSION%100);
            title.append("                           ");
            break;
        case pack_tnt:
            title.append("                            ");
            title.append("DOOM 2: TNT - Evilution v");
            title.append(VERSION/100);
            title.append(".");
            title.append(VERSION%100);
            title.append("                           ");
            break;
        case pack_xbla:
            title.append("                            ");
            title.append("DOOM 2: No Rest for the Living v");
            title.append(VERSION/100);
            title.append(".");
            title.append(VERSION%100);
            title.append("                           ");
            break;

        default:
            title.append("                            ");
            title.append("Public DOOM - v");
            title.append(VERSION/100);
            title.append(".");
            title.append(VERSION%100);
            title.append("                           ");
            break;
        }
    }

    // Used in BuildTiccmd.
    protected ticcmd_t   base=new ticcmd_t();

    /**
     * G_BuildTiccmd
     * Builds a ticcmd from all of the available inputs
     * or reads it from the demo buffer. 
     * If recording a demo, write it out .
     * 
     * The CURRENT event to process is written to the various 
     * gamekeydown etc. arrays by the Responder method.
     * So look there for any fuckups in constructing them.
     * 
     */ 

    private void BuildTiccmd (ticcmd_t cmd) 
    { 
        int     i; 
        boolean strafe;
        boolean bstrafe; 
        int     speed, tspeed,lspeed; 
        int     forward;
        int     side;
        int look;

        //base = I_BaseTiccmd ();     // empty, or external driver
        // memcpy (cmd,base,sizeof(*cmd));
        base.copyTo(cmd);

        cmd.consistancy =  consistancy[consoleplayer][maketic%BACKUPTICS]; 

        strafe = gamekeydown[key_strafe] || mousebuttons(mousebstrafe) 
        || joybuttons(joybstrafe); 
        speed = ((gamekeydown[key_speed]^alwaysrun) || joybuttons(joybspeed))?1:0;

        forward = side = look= 0;
        
        // use two stage accelerative turning
        // on the keyboard and joystick
        if (joyxmove < 0 || joyxmove > 0 ||
        	gamekeydown[key_right] || gamekeydown[key_left]) 
            turnheld += ticdup; 
        else 
            turnheld = 0; 

        if (turnheld < SLOWTURNTICS) 
            tspeed = 2;             // slow turn 
        else 
            tspeed = speed;

        if(gamekeydown[key_lookdown] || gamekeydown[key_lookup])
    	{
    		lookheld += ticdup;
    	}
    	else
    	{
    		lookheld = 0;
    	}
    	if(lookheld < SLOWTURNTICS)
    	{
    		lspeed = 1;
    	}
    	else
    	{
    		lspeed = 2;
    	}
        
        // let movement keys cancel each other out
        if (strafe) 
        { 
            if (gamekeydown[key_right]) 
            {
                // fprintf(stderr, "strafe right\n");
                side += sidemove[speed]; 
            }
            if (gamekeydown[key_left]) 
            {
                //  fprintf(stderr, "strafe left\n");
                side -= sidemove[speed]; 
            }
            if (joyxmove > 0) 
                side += sidemove[speed]; 
            if (joyxmove < 0) 
                side -= sidemove[speed]; 

        } 
        else 
        { 
            if (gamekeydown[key_right]) 
                cmd.angleturn -= angleturn[tspeed]; 
            if (gamekeydown[key_left]) 
                cmd.angleturn += angleturn[tspeed]; 
            if (joyxmove > 0) 
                cmd.angleturn -= angleturn[tspeed]; 
            if (joyxmove < 0) 
                cmd.angleturn += angleturn[tspeed]; 
        } 

        if (gamekeydown[key_up]) 
        {
            //System.err.print("up\n");
            forward += forwardmove[speed]; 
        }
        if (gamekeydown[key_down]) 
        {
            //System.err.print("down\n");
            forward -= forwardmove[speed]; 
        }        
        
        if (joyymove < 0) 
            forward += forwardmove[speed]; 
        if (joyymove > 0) 
            forward -= forwardmove[speed]; 
        if (gamekeydown[key_straferight]) 
            side += sidemove[speed]; 
        if (gamekeydown[key_strafeleft]) 
            side -= sidemove[speed];

    	// Look up/down/center keys
    	if(gamekeydown[key_lookup])
    	{
    		System.err.print("Look up\n");
    		look = lspeed;
    	}
    	
    	if(gamekeydown[key_lookdown])
    	{
    		System.err.print("Look down\n");
    		look = -lspeed;
    	}
    	
    	if(gamekeydown[key_lookcenter])
    	{
    		System.err.print("Center look\n");
    		look = TOCENTER;
    	}
    	
        // buttons
        cmd.chatchar = HU.dequeueChatChar(); 

        if (gamekeydown[key_fire] || mousebuttons(mousebfire) 
                || joybuttons(joybfire)) 
            cmd.buttons |= BT_ATTACK; 

        if (gamekeydown[key_use] || joybuttons(joybuse) ) 
        { 
            cmd.buttons |= BT_USE;
            // clear double clicks if hit use button 
            dclicks = 0;                   
        } 

        // chainsaw overrides 
        for (i=0 ; i<NUMWEAPONS-1 ; i++)        
            if (gamekeydown['1'+i]) 
            { 
                //System.out.println("Attempting weapon change (building ticcmd)");
                cmd.buttons |= BT_CHANGE; 
                cmd.buttons |= i<<BT_WEAPONSHIFT; 
                break; 
            }

        // mouse
        if (mousebuttons(mousebforward)) 
            forward += forwardmove[speed];

        // forward double click (operator precedence? && over >
        if (mousebuttons(mousebforward) != (dclickstate!=0) && (dclicktime > 1) ) 
        { 
            dclickstate = mousebuttons(mousebforward)?1:0; 
            if (dclickstate!=0) 
                dclicks++; 
            if (dclicks == 2) 
            { 
                cmd.buttons |= BT_USE; 
                dclicks = 0; 
            } 
            else 
                dclicktime = 0; 
        } 
        else 
        { 
            dclicktime += ticdup; 
            if (dclicktime > 20) 
            { 
                dclicks = 0; 
                dclickstate = 0; 
            } 
        }

        // strafe double click
        bstrafe = mousebuttons(mousebstrafe) || joybuttons(joybstrafe); 
        if ((bstrafe != (dclickstate2!=0)) && dclicktime2 > 1 ) 
        { 
            dclickstate2 = bstrafe?1:0; 
            if (dclickstate2!=0) 
                dclicks2++; 
            if (dclicks2 == 2) 
            { 
                cmd.buttons |= BT_USE; 
                dclicks2 = 0; 
            } 
            else 
                dclicktime2 = 0; 
        } 
        else 
        { 
            dclicktime2 += ticdup; 
            if (dclicktime2 > 20) 
            { 
                dclicks2 = 0; 
                dclickstate2 = 0; 
            } 
        } 

        // By default, no vertical mouse movement
        if (!novert) forward += mousey; 

        if (strafe) 
            side += mousex*2; 
        else 
            cmd.angleturn -= mousex*0x8; 

        mousex = mousey = 0; 

        if (forward > MAXPLMOVE()) 
            forward = MAXPLMOVE(); 
        else if (forward < -MAXPLMOVE()) 
            forward = -MAXPLMOVE(); 
        if (side > MAXPLMOVE()) 
            side = MAXPLMOVE(); 
        else if (side < -MAXPLMOVE()) 
            side = -MAXPLMOVE(); 

        cmd.forwardmove += forward; 
        cmd.sidemove += side;

    	if(players[consoleplayer].playerstate == PST_LIVE)
    	{
    		if(look < 0)
    		{
    			look += 16;
    		}
    		
    		cmd.lookfly = (char) look;
    	}
        
        // special buttons
        if (sendpause) 
        { 
            sendpause = false; 
            cmd.buttons = BT_SPECIAL | BTS_PAUSE; 
        } 

        if (sendsave) 
        { 
            sendsave = false; 
            cmd.buttons = (char) (BT_SPECIAL | BTS_SAVEGAME | (savegameslot<<BTS_SAVESHIFT)); 
        } 
    } 


    //
    // G_DoLoadLevel 
    //
    //extern  gamestate_t     wipegamestate; 

    public boolean DoLoadLevel () 
    { 
        int             i; 

        // Set the sky map.
        // First thing, we have a dummy sky texture name,
        //  a flat. The data is in the WAD only because
        //  we look for an actual index, instead of simply
        //  setting one.
        TM.setSkyFlatNum(TM.FlatNumForName ( SKYFLATNAME ));

        // DOOM determines the sky texture to be used
        // depending on the current episode, and the game version.
        if (isCommercial()
                || ( gamemission == GameMission_t.pack_tnt )
                || ( gamemission == GameMission_t.pack_plut ) )
        {
            TM.setSkyTexture(TM.TextureNumForName ("SKY3"));
            if (gamemap < 12)
                TM.setSkyTexture(TM.TextureNumForName ("SKY1"));
            else
                if (gamemap < 21)
                    TM.setSkyTexture(TM.TextureNumForName ("SKY2"));
        }

        levelstarttic = gametic;        // for time calculation

        if (wipegamestate == gamestate_t.GS_LEVEL) 
            wipegamestate = gamestate_t.GS_MINUS_ONE;             // force a wipe 

        gamestate = gamestate_t.GS_LEVEL; 

        for (i=0 ; i<MAXPLAYERS ; i++) 
        { 
            if (playeringame[i] && players[i].playerstate == PST_DEAD) 
                players[i].playerstate = PST_REBORN; 
            // I don't give a shit if it's not super-duper optimal. 
            Arrays.fill(players[i].frags, 0);

        } 

        try {
        LL.SetupLevel (gameepisode, gamemap, 0, gameskill);
        } catch (Exception e){
        	e.printStackTrace();
        	// Failure loading level.
        	return false;
        }
        
        displayplayer = consoleplayer;      // view the guy you are playing    
        gameaction = gameaction_t.ga_nothing; 
        //Z_CheckHeap ();

        // clear cmd building stuff
        Arrays.fill(gamekeydown, false);
        keysCleared = true;
        joyxmove = joyymove = 0; 
        mousex = mousey = 0; 
        sendpause = sendsave = paused = false; 
        Arrays.fill (mousearray, false);
        Arrays.fill(joyarray, false); 
        
        
        // killough 5/13/98: in case netdemo has consoleplayer other than green
        ST.Start();
        HU.Start();
        
        // killough: make -timedemo work on multilevel demos
        // Move to end of function to minimize noise -- killough 2/22/98:

        if (timingdemo)
          {
            if (first)
              {
                starttime = RealTime.GetTime();
                first=false;
              }
          }
        
        // Try reclaiming some memory from limit-expanded buffers.
        R.resetLimits();
        return true;
    } 
    
    protected boolean first=true;
    
    // Maes: needed because a caps lock down signal is issued
    // as soon as the app gains focus. This causes lock keys to respond.
    // With this hack, they are ignored (?) the first time this happens.
    public boolean justfocused=true;

    /**
     * G_Responder  
     * Get info needed to make ticcmd_ts for the players.
     */

    public boolean Responder (event_t ev) 
    { 
        // allow spy mode changes even during the demo
        if (gamestate == gamestate_t.GS_LEVEL && ev.type == evtype_t.ev_keydown 
                && ev.data1 == KEY_F12 && (singledemo || !deathmatch) )
        {
            // spy mode 
            do 
            { 
                displayplayer++; 
                if (displayplayer == MAXPLAYERS) 
                    displayplayer = 0; 
            } while (!playeringame[displayplayer] && displayplayer != consoleplayer); 
            return true; 
        }

        // any other key pops up menu if in demos
        if (gameaction == gameaction_t.ga_nothing && !singledemo && 
                (demoplayback || gamestate == gamestate_t.GS_DEMOSCREEN) 
        ) 
        { 
            if (ev.type == evtype_t.ev_keydown ||  
                    (ev.type == evtype_t.ev_mouse && ev.data1!=0) || 
                    (ev.type == evtype_t.ev_joystick && ev.data1!=0) ) 
            { 
                M.StartControlPanel (); 
                return true; 
            } 
            return false; 
        } 

        if (gamestate == gamestate_t.GS_LEVEL) 
        { 

            if (devparm && ev.type == evtype_t.ev_keydown && ev.data1 == ';') 
            { 
                DeathMatchSpawnPlayer (0); 
                return true; 
            } 

            //automapactive=true;

            if (HU.Responder (ev)) 
                return true;    // chat ate the event 
            if (ST.Responder (ev)) 
                return true;    // status window ate it
            if (AM.Responder (ev)) 
                return true;    // automap ate it 

        } 

        if (gamestate == gamestate_t.GS_FINALE) 
        { 
            if (F.Responder (ev)) 
                return true;    // finale ate the event 
        } 

        switch (ev.type) 
        {
        case ev_clear:
        	// PAINFULLY and FORCEFULLY clear the buttons.
            Arrays.fill(gamekeydown, false);
            keysCleared = true;
            return false; // Nothing more to do here. 
        case ev_keydown: 
            if (ev.data1 == KEY_PAUSE) 
            { 
                sendpause = true; 
                return true; 
            }
            
            /* CAPS lock will only go through as a keyup event
            if (ev.data1 == KEY_CAPSLOCK) 
            { 
                if (justfocused) justfocused=false;
                    else
                // If caps are turned on, turn autorun on anyway.
                if (!alwaysrun) {
                    alwaysrun=true;
                    players[consoleplayer].message=String.format("Always run: %s",alwaysrun);
                    }
                return true; 
            } */
            
            if (ev.data1 <NUMKEYS) 
                gamekeydown[ev.data1] = true;
            return true;    // eat key down events 

        case ev_keyup:
            if (ev.data1 == KEY_CAPSLOCK) 
            { 
                if (justfocused) justfocused=false;
                    else
                    {
                    // Just toggle it. It's too hard to read the state.
                    alwaysrun=!alwaysrun;
                    players[consoleplayer].message=String.format("Always run: %s",alwaysrun);
                    }
            }
            
            if (ev.data1 <NUMKEYS) 
                gamekeydown[ev.data1] = false; 
            return false;   // always let key up events filter down 

        case ev_mouse:
            // Ignore them at the responder level
            if (use_mouse){
            mousebuttons(0, ev.data1 & 1); 
            mousebuttons(1, ev.data1 & 2); 
            mousebuttons(2, ev.data1 & 4);
            mousex = ev.data2*(mouseSensitivity+5)/10; 
            mousey = ev.data3*(mouseSensitivity+5)/10;
            }
            return true;    // eat events 

        case ev_joystick:
            if (use_joystick){
            joybuttons(0, ev.data1 & 1); 
            joybuttons(1, ev.data1 & 2); 
            joybuttons(2,ev.data1 & 4); 
            joybuttons(3,ev.data1 & 8); 
            joyxmove = ev.data2; 
            joyymove = ev.data3; 
            }
            return true;    // eat events 

        default: 
            break; 
        } 

        return false; 
    } 


    private final String turbomessage="is turbo!"; 

    /**
     * G_Ticker
     * Make ticcmd_ts for the players.
     */

    public void Ticker () 
    { 
        int     i;
        int     buf; 
        ticcmd_t   cmd;

        // do player reborns if needed
        for (i=0 ; i<MAXPLAYERS ; i++) 
            if (playeringame[i] && players[i].playerstate == PST_REBORN) 
                DoReborn (i);

        // do things to change the game state
        while (gameaction != gameaction_t.ga_nothing) 
        { 
            switch (gameaction) 
            { 
            case ga_loadlevel: 
                DoLoadLevel ();
                break; 
            case ga_newgame: 
                DoNewGame (); 
                break; 
            case ga_loadgame:
                DoLoadGame (); 
                break; 
            case ga_savegame: 
                DoSaveGame (); 
                break; 
            case ga_playdemo: 
                DoPlayDemo (); 
                break; 
            case ga_completed: 
                DoCompleted (); 
                break; 
            case ga_victory: 
                F.StartFinale (); 
                break; 
            case ga_worlddone: 
                DoWorldDone (); 
                break; 
            case ga_screenshot: 
                ScreenShot (); 
                gameaction = gameaction_t.ga_nothing; 
                break; 
            case ga_nothing: 
                break; 
            } 
        }

        // get commands, check consistancy,
        // and build new consistancy check
        buf = (gametic/ticdup)%BACKUPTICS; 

        for (i=0 ; i<MAXPLAYERS ; i++)
        {
            if (playeringame[i]) 
            { 
                cmd = players[i].cmd; 
                //System.out.println("Current command:"+cmd);

                //memcpy (cmd, &netcmds[i][buf], sizeof(ticcmd_t));
                netcmds[i][buf].copyTo(cmd);

                // MAES: this is where actual demo commands are being issued or created!
                // Essentially, a demo is a sequence of stored ticcmd_t with a header.
                // Knowing that, it's possible to objectify it.
                if (demoplayback) 
                    ReadDemoTiccmd (cmd); 
                if (demorecording) 
                    WriteDemoTiccmd (cmd);

                // check for turbo cheats
                if (cmd.forwardmove > TURBOTHRESHOLD 
                        && ((gametic&31)==0) && ((gametic>>5)&3) == i )
                {

                    //extern char *player_names[4];
                    //sprintf (turbomessage, "%s is turbo!",player_names[i]);
                    players[consoleplayer].message = hu.HU.player_names[i]+turbomessage;
                }

                if (netgame && !netdemo && (gametic%ticdup)==0 ) 
                { 
                    if (gametic > BACKUPTICS 
                            && consistancy[i][buf] != cmd.consistancy) 
                    { 
                        I.Error ("consistency failure (%d should be %d)",
                            cmd.consistancy, consistancy[i][buf]); 
                    } 
                    if (players[i].mo!=null) 
                        consistancy[i][buf] = (short) players[i].mo.x; 
                    else 
                        consistancy[i][buf] = (short) RND.getIndex(); 
                } 
            }
        }

        // check for special buttons
        for (i=0 ; i<MAXPLAYERS ; i++)
        {
            if (playeringame[i]) 
            { 
                if ((players[i].cmd.buttons & BT_SPECIAL)!=0) 
                { 
                    switch (players[i].cmd.buttons & BT_SPECIALMASK) 
                    { 
                    case BTS_PAUSE:
                        // MAES: fixed stupid ^pause bug.
                        paused = !paused; 
                        if (paused)
                            S.PauseSound ();
                        else
                            S.ResumeSound (); 
                        break; 

                    case BTS_SAVEGAME: 
                        if (savedescription==null) 
                            savedescription=new String( "NET GAME"); 
                        savegameslot =  
                            (players[i].cmd.buttons & BTS_SAVEMASK)>>BTS_SAVESHIFT; 
        gameaction = gameaction_t.ga_savegame; 
        break; 
                    } 
                } 
            }
        }

        // do main actions
        switch (gamestate) 
        { 
        case GS_LEVEL: 
            P.Ticker (); 
            ST.Ticker (); 
            AM.Ticker (); 
            HU.Ticker ();            
            break; 

        case GS_INTERMISSION: 
            WI.Ticker (); 
            break; 

        case GS_FINALE: 
            F.Ticker (); 
            break; 

        case GS_DEMOSCREEN: 
            PageTicker (); 
            break; 
        }        
    } 


    //
    // PLAYER STRUCTURE FUNCTIONS
    // also see P_SpawnPlayer in P_Things
    //

    /**
     * G_InitPlayer
     * Called at the start.
     * Called by the game initialization functions.
     *
     * MAES: looks like dead code. It's never called.
     *
     */

    protected void InitPlayer (int player) 
    { 
        player_t   p; 

        // set up the saved info         
        p = players[player]; 

        // clear everything else to defaults 
        p.PlayerReborn (); 

    } 
    
    //
    // G_CheckSpot  
    // Returns false if the player cannot be respawned
    // at the given mapthing_t spot  
    // because something is occupying it 
    //
    //void P_SpawnPlayer (mapthing_t* mthing); 

    private boolean
    CheckSpot
    ( int       playernum,
            mapthing_t   mthing ) 
    { 
        int     x,y; // fixed_t 
        subsector_t    ss; 
        int        an; // angle 
        mobj_t     mo; 


        if (players[playernum].mo==null)
        {
            // first spawn of level, before corpses
            for (int i=0 ; i<playernum ; i++)
                if (players[i].mo.x == mthing.x << FRACBITS
                        && players[i].mo.y == mthing.y << FRACBITS)
                    return false;   
            return true;
        }

        x = mthing.x << FRACBITS; 
        y = mthing.y << FRACBITS; 

        if (!P.CheckPosition (players[playernum].mo, x, y) )
            return false;

        // flush an old corpse if needed 
        if (bodyqueslot >= BODYQUESIZE) 
            P.RemoveMobj (bodyque[bodyqueslot%BODYQUESIZE]); 
        bodyque[bodyqueslot%BODYQUESIZE] = players[playernum].mo; 
        bodyqueslot++; 

        // spawn a teleport fog 
        ss = LL.PointInSubsector (x,y); 
        // Angles stored in things are supposed to be "sanitized" against rollovers.
        an = (int) (( ANG45 * (mthing.angle/45) ) >>> ANGLETOFINESHIFT); 

        mo = P.SpawnMobj (x+20*finecosine[an], y+20*finesine[an] 
                                                             , ss.sector.floorheight 
                                                             , mobjtype_t.MT_TFOG); 

        if (players[consoleplayer].viewz != 1) ; 
        S.StartSound (mo, sfxenum_t.sfx_telept);  // don't start sound on first frame 

        return true; 
    } 


    //
    // G_DeathMatchSpawnPlayer 
    // Spawns a player at one of the random death match spots 
    // called at level load and each death 
    //
    @Override
    public void DeathMatchSpawnPlayer (int playernum) 
    { 
        int             i,j; 
        int             selections; 

        selections = deathmatch_p; 
        if (selections < 4) 
            I.Error ("Only %d deathmatch spots, 4 required", selections); 

        for (j=0 ; j<20 ; j++) 
        { 
            i = RND.P_Random() % selections; 
            if (CheckSpot (playernum, deathmatchstarts[i]) ) 
            { 
                deathmatchstarts[i].type = (short) (playernum+1); 
                P.SpawnPlayer (deathmatchstarts[i]); 
                return; 
            } 
        } 

        // no good spot, so the player will probably get stuck
        // MAES: seriously, fuck him.
        P.SpawnPlayer (playerstarts[playernum]); 
    } 

    //
    // G_DoReborn 
    // 

    public void DoReborn (int playernum) 
    { 
        int                             i; 

        if (!netgame)
        {
            // reload the level from scratch
            gameaction = gameaction_t.ga_loadlevel;  
        }
        else 
        {
            // respawn at the start

            // first dissasociate the corpse 
            players[playernum].mo.player = null;   

            // spawn at random spot if in death match 
            if (deathmatch) 
            { 
                DeathMatchSpawnPlayer (playernum); 
                return; 
            } 

            if (CheckSpot (playernum, playerstarts[playernum]) ) 
            { 
                P.SpawnPlayer (playerstarts[playernum]); 
                return; 
            }

            // try to spawn at one of the other players spots 
            for (i=0 ; i<MAXPLAYERS ; i++)
            {
                if (CheckSpot (playernum, playerstarts[i]) ) 
                { 
                    playerstarts[i].type = (short) (playernum+1); // fake as other player 
                    P.SpawnPlayer (playerstarts[i]); 
                    playerstarts[i].type = (short) (i+1);     // restore 
                    return; 
                }       
                // he's going to be inside something.  Too bad.
                // MAES: Yeah, they're like, fuck him.
            }
            P.SpawnPlayer (playerstarts[playernum]); 
        } 
    } 

    /** DOOM Par Times [4][10] */
    final int[][] pars = 
    { 
            {0}, 
            {0,30,75,120,90,165,180,180,30,165}, 
            {0,90,90,90,120,90,360,240,30,170}, 
            {0,90,45,90,150,90,90,165,30,135} 
    }; 

    /** DOOM II Par Times */
    final int[] cpars =
    {
            30,90,120,120,90,150,120,120,270,90,    //  1-10
            210,150,150,150,210,150,420,150,210,150,    // 11-20
            240,150,180,150,150,300,330,420,300,180,    // 21-30
            120,30                  // 31-32
    };


    //
    // G_DoCompleted 
    //
    boolean     secretexit; 

    public final void ExitLevel () 
    { 
        secretexit = false; 
        gameaction = gameaction_t.ga_completed; 
    } 

    // Here's for the german edition.
    public void SecretExitLevel () 
    { 
        // IF NO WOLF3D LEVELS, NO SECRET EXIT!
        if ( isCommercial()
                && (W.CheckNumForName("MAP31")<0))
            secretexit = false;
        else
            secretexit = true; 
        gameaction =  gameaction_t.ga_completed; 
    } 

    protected void DoCompleted () 
    { 
        int             i; 

        gameaction =  gameaction_t.ga_nothing; 

        for (i=0 ; i<MAXPLAYERS ; i++) 
            if (playeringame[i]) 
                players[i].PlayerFinishLevel ();        // take away cards and stuff 

        if (automapactive) 
            AM.Stop (); 

        if ( !isCommercial())
            switch(gamemap)
            {
            case 8:
                // MAES: end of episode
                gameaction =  gameaction_t.ga_victory;
                return;
            case 9:
                // MAES: end of secret level
                for (i=0 ; i<MAXPLAYERS ; i++) 
                    players[i].didsecret = true; 
                break;
            }

        /*  Hmmm - why? MAES: Clearly redundant.
        if ( (gamemap == 8)
                && (!isCommercial()) ) 
        {
            // victory 
            gameaction =  gameaction_t.ga_victory; 
            return; 
        } 

        if ( (gamemap == 9)
                && !isCommercial() ) 
        {
            // exit secret level 
            for (i=0 ; i<MAXPLAYERS ; i++) 
                players[i].didsecret = true; 
        } 
         */


        wminfo.didsecret = players[consoleplayer].didsecret; 
        wminfo.epsd = gameepisode -1; 
        wminfo.last = gamemap -1;

        // wminfo.next is 0 biased, unlike gamemap
        if ( isCommercial())
        {
            if (secretexit)
                switch(gamemap)
                {
                case 15: wminfo.next = 30; break;
                case 31: wminfo.next = 31; break;
                }
            else
                switch(gamemap)
                {
                case 31:
                case 32: wminfo.next = 15; break;
                default: wminfo.next = gamemap;
                }
        }
        else
        {
            if (secretexit) 
                wminfo.next = 8;    // go to secret level 
            else if (gamemap == 9) 
            {
                // returning from secret level 
                switch (gameepisode) 
                { 
                case 1: 
                    wminfo.next = 3; 
                    break; 
                case 2: 
                    wminfo.next = 5; 
                    break; 
                case 3: 
                    wminfo.next = 6; 
                    break; 
                case 4:
                    wminfo.next = 2;
                    break;
                }                
            } 
            else 
                wminfo.next = gamemap;          // go to next level 
        }

        wminfo.maxkills = totalkills; 
        wminfo.maxitems = totalitems; 
        wminfo.maxsecret = totalsecret; 
        wminfo.maxfrags = 0; 
        if ( isCommercial() )
            wminfo.partime = 35*cpars[gamemap-1]; 
        else if (gameepisode >= pars.length)
            wminfo.partime = 0;
        else
            wminfo.partime = 35*pars[gameepisode][gamemap]; 
        wminfo.pnum = consoleplayer; 

        for (i=0 ; i<MAXPLAYERS ; i++) 
        { 
            wminfo.plyr[i].in = playeringame[i]; 
            wminfo.plyr[i].skills = players[i].killcount; 
            wminfo.plyr[i].sitems = players[i].itemcount; 
            wminfo.plyr[i].ssecret = players[i].secretcount; 
            wminfo.plyr[i].stime = leveltime;
            C2JUtils.memcpy (wminfo.plyr[i].frags, players[i].frags 
                , wminfo.plyr[i].frags.length); 
        } 

        gamestate = gamestate_t.GS_INTERMISSION; 
        viewactive = false; 
        automapactive = false; 

        if (statcopy!=null)
            C2JUtils.memcpy (statcopy, wminfo,1);

        WI.Start (wminfo); 
    } 


    /**
     * G_WorldDone 
     */

    public void WorldDone () 
    { 
        gameaction = gameaction_t.ga_worlddone; 

        if (secretexit) 
            players[consoleplayer].didsecret = true; 

        if ( isCommercial() )
        {
            switch (gamemap)
            {
            case 15:
            case 31:
                if (!secretexit)
                    break;
            case 6:
            case 11:
            case 20:
            case 30:
                F.StartFinale ();
                break;
            }
        }
    } 

    public void DoWorldDone () 
    {        
        gamestate = gamestate_t.GS_LEVEL; 
        gamemap = wminfo.next+1; 
        DoLoadLevel (); 
        gameaction = gameaction_t.ga_nothing; 
        viewactive = true; 
    } 



    //
    // G_InitFromSavegame
    // Can be called by the startup code or the menu task. 
    //
    //extern boolean setsizeneeded;
    //void R_ExecuteSetViewSize (void);

    String    savename;

    public void LoadGame (String name) 
    { 
        savename=new String(name); 
        gameaction = gameaction_t.ga_loadgame; 
    } 



    /** This is fugly. Making a "savegame object" will make at least certain comparisons
     *  easier, and avoid writing code twice.
     */

    protected void DoLoadGame () 
    { 
        try{ 
            int     i;  
            StringBuffer vcheck=new StringBuffer();
            VanillaDSGHeader header=new VanillaDSGHeader();
            IDoomSaveGame dsg=new VanillaDSG();
            dsg.updateStatus(this.DM);
            
            gameaction = gameaction_t.ga_nothing; 

            DataInputStream f=new DataInputStream(new BufferedInputStream(new FileInputStream(savename))); 

            header.read(f);
            f.close();
    
            // skip the description field 
            vcheck.append("version ");
            vcheck.append(VERSION);

            if (vcheck.toString().compareTo(header.getVersion())!=0) {
            	f.close();
            	return; // bad version
            }

            // Ok so far, reopen stream.
            f=new DataInputStream(new BufferedInputStream(new FileInputStream(savename)));        
            gameskill = header.getGameskill(); 
            gameepisode = header.getGameepisode(); 
            gamemap = header.getGamemap(); 
            for (i=0 ; i<MAXPLAYERS ; i++) 
                playeringame[i] = header.getPlayeringame()[i]; 

            // load a base level 
            InitNew (gameskill, gameepisode, gamemap); 
            
            if (gameaction == gameaction_t.ga_failure) {
            	// failure to load. Abort.
            	f.close();
            	return;
            }

            gameaction = gameaction_t.ga_nothing;
            
            // get the times 
            leveltime = header.getLeveltime(); 

            // dearchive all the modifications
            boolean ok=dsg.doLoad(f);
            f.close();

            // MAES: this will cause a forced exit.
            // The problem is that the status will have already been altered 
            // (perhaps VERY badly) so it makes no sense to progress.
            // If you want it bullet-proof, you could implement
            // a "tentative loading" subsystem, which will only alter the game
            // if everything works out without errors. But who cares :-p
            if (!ok) 
                I.Error("Bad savegame");

            // done 
            //Z_Free (savebuffer); 

            if (R.getSetSizeNeeded())
                R.ExecuteSetViewSize ();

            // draw the pattern into the back screen
            R.FillBackScreen ();   
        } catch (Exception e){
            e.printStackTrace();
        }
    } 


    //
    // G_SaveGame
    // Called by the menu task.
    // Description is a 24 byte text string 
    //
    public void
    SaveGame
    ( int   slot,
            String description ) 
    { 
        savegameslot = slot; 
        savedescription=new String(description); 
        sendsave = true; 
    } 

    protected void DoSaveGame () 
    { 
     
        try{
     String    name; 
     //char[]    name2=new char[VERSIONSIZE]; 
     String   description;
     StringBuffer build=new StringBuffer();
     IDoomSaveGameHeader header=new VanillaDSGHeader();
     IDoomSaveGame dsg=new VanillaDSG();
     dsg.updateStatus(this.DM);
     
     if (eval(CM.CheckParm("-cdrom"))) {
         build.append("c:\\doomdata\\");
         build.append(SAVEGAMENAME);
         build.append("%d.dsg");
     } else {         
      build.append(SAVEGAMENAME);
      build.append("%d.dsg"); 
     }
     
     name=String.format(build.toString(), savegameslot);
     
     description = savedescription; 

     header.setName(description);
     header.setVersion(String.format("version %d",VERSION));
     header.setGameskill(gameskill);
     header.setGameepisode(gameepisode);
     header.setGamemap(gamemap);
     header.setPlayeringame(playeringame);
     header.setLeveltime(leveltime);
     dsg.setHeader(header);
     
     // Try opening a save file. No intermediate buffer (performance?)
     DataOutputStream f=new DataOutputStream(new FileOutputStream(name));
     boolean ok=dsg.doSave(f);
     f.close();
        } catch (Exception e){
            e.printStackTrace();
        }
     // Saving is not as destructive as loading.
     
     gameaction = gameaction_t.ga_nothing; 
        savedescription = "";      

        players[consoleplayer].message = GGSAVED; 

        // draw the pattern into the back screen
        R.FillBackScreen ();
     

    } 



    skill_t d_skill; 
    int     d_episode; 
    int     d_map; 

    public void
    DeferedInitNew
    ( skill_t   skill,
            int       episode,
            int       map) 
    { 
        d_skill = skill; 
        d_episode = episode; 
        d_map = map; 
        gameaction = gameaction_t.ga_newgame; 
    } 


    public void DoNewGame () 
    {
        demoplayback = false; 
        netdemo = false;
        netgame = false;
        deathmatch = false;
        playeringame[1] = playeringame[2] = playeringame[3] = false;
        respawnparm = false;
        fastparm = false;
        nomonsters = false;
        consoleplayer = 0;
        InitNew (d_skill, d_episode, d_map); 
        gameaction = gameaction_t.ga_nothing; 
    } 


    /**
     * G_InitNew
     * Can be called by the startup code or the menu task,
     * consoleplayer, displayplayer, playeringame[] should be set. 
     */

    public void InitNew
    ( skill_t   skill,
            int       episode,
            int       map ) 
    { 
        int             i; 

        if (paused) 
        { 
            paused = false; 
            S.ResumeSound (); 
        } 


        if (skill.ordinal() > skill_t.sk_nightmare.ordinal()) 
            skill = skill_t.sk_nightmare;


        // This was quite messy with SPECIAL and commented parts.
        // Supposedly hacks to make the latest edition work.
        // It might not work properly.
        if (episode < 1)
            episode = 1; 

        if ( isRetail() )
        {
            if (episode > 4)
                episode = 4;
        }
        else if ( isShareware() )
        {
            if (episode > 1) 
                episode = 1; // only start episode 1 on shareware
        }  
        else
        {
            if (episode > 3)
                episode = 3;
        }



        if (map < 1) 
            map = 1;

        if ( (map > 9)
                && ( !isCommercial()))
            map = 9; 

        RND.ClearRandom (); 

        if (skill == skill_t.sk_nightmare || respawnparm )
            respawnmonsters = true;
        else
            respawnmonsters = false;

        // If on nightmare/fast monsters make everything MOAR pimp.

        if (fastparm || (skill == skill_t.sk_nightmare && gameskill != skill_t.sk_nightmare) )
        { 
            for (i=statenum_t.S_SARG_RUN1.ordinal() ; i<=statenum_t.S_SARG_PAIN2.ordinal() ; i++) 
                states[i].tics >>= 1; 
            mobjinfo[mobjtype_t.MT_BRUISERSHOT.ordinal()].speed = 20*MAPFRACUNIT; 
            mobjinfo[mobjtype_t.MT_HEADSHOT.ordinal()].speed = 20*MAPFRACUNIT; 
            mobjinfo[mobjtype_t.MT_TROOPSHOT.ordinal()].speed = 20*MAPFRACUNIT; 
        } 
        else if (skill != skill_t.sk_nightmare && gameskill == skill_t.sk_nightmare) 
        { 
            for (i=statenum_t.S_SARG_RUN1.ordinal() ; i<=statenum_t.S_SARG_PAIN2.ordinal() ; i++) 
                states[i].tics <<= 1; 
            mobjinfo[mobjtype_t.MT_BRUISERSHOT.ordinal()].speed = 15*MAPFRACUNIT; 
            mobjinfo[mobjtype_t.MT_HEADSHOT.ordinal()].speed = 10*MAPFRACUNIT; 
            mobjinfo[mobjtype_t.MT_TROOPSHOT.ordinal()].speed = 10*MAPFRACUNIT; 
        } 


        // force players to be initialized upon first level load         
        for (i=0 ; i<MAXPLAYERS ; i++) 
            players[i].playerstate = PST_REBORN; 

        usergame = true;                // will be set false if a demo 
        paused = false; 
        demoplayback = false; 
        automapactive = false; 
        viewactive = true; 
        gameepisode = episode; 
        gamemap = map; 
        gameskill = skill; 

        viewactive = true;

        // set the sky map for the episode
        if ( isCommercial())
        {
            TM.setSkyTexture(TM.TextureNumForName ("SKY3"));
            if (gamemap < 12)
                TM.setSkyTexture(TM.TextureNumForName ("SKY1"));
            else
                if (gamemap < 21)
                    TM.setSkyTexture(TM.TextureNumForName ("SKY2"));
        }
        else
            switch (episode) 
            { 
            case 1: 
                TM.setSkyTexture(TM.TextureNumForName ("SKY1")); 
                break; 
            case 2: 
                TM.setSkyTexture(TM.TextureNumForName ("SKY2"));
                break; 
            case 3: 
                TM.setSkyTexture(TM.TextureNumForName ("SKY3")); 
                break; 
            case 4:   // Special Edition sky
                TM.setSkyTexture(TM.TextureNumForName ("SKY4"));
                break;
            } 

        if (!DoLoadLevel ()) levelLoadFailure();
    } 

    protected void levelLoadFailure(){
    	boolean endgame=I.GenerateAlert(Strings.LEVEL_FAILURE_TITLE, Strings.LEVEL_FAILURE_CAUSE);
    	
    	if (endgame){         	// Initiate endgame
    	gameaction=gameaction_t.ga_failure;
    	gamestate = gamestate_t.GS_DEMOSCREEN; 
    	M.ClearMenus();
    	StartTitle();
    	} else {
    	// Shutdown immediately.
    	I.Quit();	
    	}
    }

    //
    // DEMO RECORDING 
    // 
    public  void ReadDemoTiccmd (ticcmd_t cmd) 
    { 
        IDemoTicCmd democmd=demobuffer.getNextTic();
        if (democmd == null) 
        {
            // end of demo data stream 
            CheckDemoStatus ();

            // Force status resetting
            this.demobuffer.resetDemo();
            return; 
        }

        democmd.decode(cmd); 
    } 

    public void WriteDemoTiccmd (ticcmd_t cmd) 
    { 
        if (gamekeydown['q'])           // press q to end demo recording 
            CheckDemoStatus ();
        IDemoTicCmd reccmd=new VanillaTiccmd();
        reccmd.encode(cmd);     
        demobuffer.putTic(reccmd);

        // MAES: Useless, we can't run out of space anymore (at least not in theory).

        /*   demobuffer[demo_p++] = cmd.forwardmove; 
     demobuffer[demo_p++] = cmd.sidemove; 
     demobuffer[demo_p++] = (byte) ((cmd.angleturn+128)>>8); 
     demobuffer[demo_p++] = (byte) cmd.buttons; 
     demo_p -= 4; 
     if (demo_p > demoend - 16)
     {
     // no more space 
     CheckDemoStatus (); 
     return; 
     } */ 

        //ReadDemoTiccmd (cmd);         // make SURE it is exactly the same
        // MAES: this is NOT the way to do in Mocha, because we are not manipulating
        // the demo index directly anymore. Instead, decode what we have just saved.     
        reccmd.decode(cmd);

    } 



    /**
     * G_RecordDemo 
     */ 
    public void RecordDemo (String name) 
    { 

        StringBuffer buf=new StringBuffer();
        usergame = false; 
        buf.append(name); 
        buf.append(".lmp");
        demoname=buf.toString();
        demobuffer = new VanillaDoomDemo(); 
        demorecording = true; 
    } 


    public void BeginRecording () 
    {         
        demobuffer.setVersion(VERSION);
        demobuffer.setSkill(gameskill); 
        demobuffer.setEpisode(gameepisode);
        demobuffer.setMap(gamemap);
        demobuffer.setDeathmatch(deathmatch);
        demobuffer.setRespawnparm(respawnparm);
        demobuffer.setFastparm(fastparm);
        demobuffer.setNomonsters(nomonsters);
        demobuffer.setConsoleplayer(consoleplayer);
        demobuffer.setPlayeringame(playeringame);
    }
    
    String   defdemoname;
    
    /**
     * G_PlayDemo 
     */

    public void DeferedPlayDemo (String name) 
    { 
        defdemoname = name; 
        gameaction = gameaction_t.ga_playdemo; 
    } 

    public void DoPlayDemo () 
    { 

        skill_t skill;
        boolean fail=false;
        int             i, episode, map; 

        gameaction = gameaction_t.ga_nothing;
        // MAES: Yeah, it's OO all the way now, baby ;-)
        try {
        demobuffer = (IDoomDemo) W.CacheLumpName(defdemoname.toUpperCase(), PU_STATIC,VanillaDoomDemo.class);
        } catch (Exception e){
            fail=true;
        }
        
        fail=(demobuffer.getSkill()==null);

        if (fail || demobuffer.getVersion()!= VERSION)
        {
            System.err.println("Demo is from a different game version!\n");
            System.err.println("Version code read: "+demobuffer.getVersion());
            gameaction = gameaction_t.ga_nothing;
            return;
        }

        skill = demobuffer.getSkill(); 
        episode = demobuffer.getEpisode(); 
        map = demobuffer.getMap(); 
        deathmatch = demobuffer.isDeathmatch();
        respawnparm = demobuffer.isRespawnparm();
        fastparm = demobuffer.isFastparm();
        nomonsters = demobuffer.isNomonsters();
        consoleplayer = demobuffer.getConsoleplayer();
        // Do this, otherwise previously loaded demos will be stuck at their end.
        demobuffer.resetDemo();
        
        boolean[] pigs=demobuffer.getPlayeringame();
        for (i=0 ; i<MAXPLAYERS ; i++) 
            playeringame[i] = pigs[i]; 
        if (playeringame[1]) 
        {
            netgame = true; 
            netdemo = true; 
        }

        // don't spend a lot of time in loadlevel 
        precache = false;
        InitNew (skill, episode, map); 
        precache = true;

        usergame = false; 
        demoplayback = true; 

    }

    //
    // G_TimeDemo 
    //
    public void TimeDemo (String name) 
    {    
        nodrawers = CM.CheckParm ("-nodraw")!=0; 
        noblit = CM.CheckParm ("-noblit")!=0; 
        timingdemo = true; 
        singletics = true;
        defdemoname = name;
        gameaction = gameaction_t.ga_playdemo; 
    } 

    
    /** G_CheckDemoStatus
     * 
     * Called after a death or level completion to allow demos to be cleaned up
     * Returns true if a new demo loop action will take place
     *  
     */ 

    public boolean CheckDemoStatus () 
    { 
        int endtime; 
        
        if (timingdemo) 
        {
            endtime = RealTime.GetTime ();
            // killough -- added fps information and made it work for longer demos:
            long realtics=endtime-starttime;    
            
            this.commit();
            VM.SaveDefaults(VM.getDefaultFile());
            I.Error ("timed %d gametics in %d realtics = %f frames per second",gametic 
                , realtics, gametic*(double)(TICRATE)/realtics); 
        }

        if (demoplayback) 
        {
            if (singledemo) 
                I.Quit ();

            // Z_ChangeTag (demobuffer, PU_CACHE); 
            demoplayback = false; 
            netdemo = false;
            netgame = false;
            deathmatch = false;
            playeringame[1] = playeringame[2] = playeringame[3] = false;
            respawnparm = false;
            fastparm = false;
            nomonsters = false;
            consoleplayer = 0;
            AdvanceDemo (); 
            return true; 
        } 

        if (demorecording) 
        { 
            //demobuffer[demo_p++] = (byte) DEMOMARKER; 

            MenuMisc.WriteFile(demoname, demobuffer); 
            //Z_Free (demobuffer); 
            demorecording = false; 
            I.Error ("Demo %s recorded",demoname); 
        } 

        return false; 
    } 

    /** This should always be available for real timing */
    protected ITicker RealTime;
    
    public DoomMain(){
        // Init game status...
        super();
        C2JUtils.initArrayOfObjects(events,event_t.class);
        this.I=new DoomSystem();
        gamestate=gamestate_t.GS_DEMOSCREEN;        
        
        this.RealTime=new MilliTicker();
    }

   
    
    protected void updateStatusHolders(DoomStatus<T,V> DS){
        for (DoomStatusAware dsa : status_holders){
            dsa.updateStatus(DS);
        }
    }

    /** A list with objects that do hold status and need to be aware of 
     *  it.
     */
    protected List<DoomStatusAware> status_holders;

 


    /* Since it's so intimately tied, it's less troublesome to merge the "main" and "network"
     *  code. 
     * 
     */


    /** To be initialized by the DoomNetworkingInterface via a setter */
    //private  doomcom_t   doomcom;   
    //private  doomdata_t  netbuffer;      // points inside doomcom
    protected StringBuilder sb=new StringBuilder();


    //
    // NETWORKING
    //
    // gametic is the tic about to (or currently being) run
    // maketic is the tick that hasn't had control made for it yet
    // nettics[] has the maketics for all players 
    //
    // a gametic cannot be run until nettics[] > gametic for all players
    //

    //ticcmd_t[]  localcmds= new ticcmd_t[BACKUPTICS];

    //ticcmd_t [][]       netcmds=new ticcmd_t [MAXPLAYERS][BACKUPTICS];
    int[]           nettics=new int[MAXNETNODES];
    boolean[]       nodeingame=new boolean[MAXNETNODES];        // set false as nodes leave game
    boolean[]       remoteresend=new boolean[MAXNETNODES];      // set when local needs tics
    int[]       resendto=new int[MAXNETNODES];          // set when remote needs tics
    int[]       resendcount=new int[MAXNETNODES];

    int[]       nodeforplayer=new int[MAXPLAYERS];

    int             maketic;
    int     lastnettic;
    int     skiptics;
    protected int     ticdup;


    public int getTicdup() {
        return ticdup;
    }


    public void setTicdup(int ticdup) {
        this.ticdup = ticdup;
    }






    int     maxsend; // BACKUPTICS/(2*ticdup)-1;


    //void D_ProcessEvents (void); 
    //void G_BuildTiccmd (ticcmd_t *cmd); 
    //void D_DoAdvanceDemo (void);

    // _D_
    boolean     reboundpacket = false;
    doomdata_t  reboundstore = new doomdata_t();


    // 
    //
    //123

    /** MAES: interesting. After testing it was found to return the following size:
     *  (8*(netbuffer.numtics+1));
     */

    int NetbufferSize ()
    {
        //    return (int)(((doomdata_t)0).cmds[netbuffer.numtics]);
        return (8*(netbuffer.numtics+1));
    }


    protected long NetbufferChecksum ()
    {
        long        c;
        int     i,l;

        c = 0x1234567L;

        // FIXME -endianess?
        if (NORMALUNIX)
            return 0;           // byte order problems


        /* Here it was trying to get the length of a doomdata_t struct up to retransmit from.
         * l = (NetbufferSize () - (int)&(((doomdata_t *)0)->retransmitfrom))/4;
         * (int)&(((doomdata_t *)0)->retransmitfrom) evaluates to "4"
         * Therefore, l= (netbuffersize - 4)/4
         * 
         */
        l = (NetbufferSize () - 4)/4;
        for (i=0 ; i<l ; i++)
            // TODO: checksum would be better computer in the netbuffer itself.
            // The C code actually takes all fields into account.
            c += 0;// TODO: (netbuffer->retransmitfrom)[i] * (i+1);

        return c & NCMD_CHECKSUM;
    }
    //
    //
    //
    protected int ExpandTics (int low)
    {
        int delta;

        delta = low - (maketic&0xff);

        if (delta >= -64 && delta <= 64)
            return (maketic&~0xff) + low;
        if (delta > 64)
            return (maketic&~0xff) - 256 + low;
        if (delta < -64)
            return (maketic&~0xff) + 256 + low;

        I.Error ("ExpandTics: strange value %d at maketic %d",low,maketic);
        return 0;
    }



    /**
     * HSendPacket
     *
     * Will send out a packet to all involved parties. A special
     * case is the rebound storage, which acts as a local "echo"
     * which is then picked up by the host itself. This is
     * necessary to simulate a 1-node network.
     * @throws IOException 
     *
     */
    void
    HSendPacket
    (int   node,
            int   flags )
    {
        netbuffer.checksum = (int) (NetbufferChecksum () | flags);

        // Local node's comms are sent to rebound packet, which is 
        // then picked up again. THIS IS VITAL FOR SINGLE-PLAYER
        // SPEED THROTTLING TOO, AS IT RELIES ON NETWORK ACKS/BUSY
        // WAITING.
        if (node==0)
        {
            // _D_
            reboundstore.copyFrom(netbuffer);
            reboundpacket = true;
            return;
        }

        if (DM.demoplayback)
            return;

        if (!DM.netgame)
            I.Error ("Tried to transmit to another node");

        doomcom.command = CMD_SEND;
        doomcom.remotenode = (short) node;
        doomcom.datalength = (short) NetbufferSize ();

        if (DM.debugfile!=null)
        {
            int     i;
            int     realretrans;
            if (flags(netbuffer.checksum , NCMD_RETRANSMIT))
                realretrans = ExpandTics (netbuffer.retransmitfrom);
            else
                realretrans = -1;

            logger(debugfile,"send ("+ExpandTics(netbuffer.starttic)+", "+netbuffer.numtics + ", R "+
                realretrans+ "["+ doomcom.datalength+"]");

            for (i=0 ; i<doomcom.datalength ; i++)

                // TODO: get a serialized string representation.
            	logger(debugfile,netbuffer.toString()+"\n");
        }

        // This should execute a "send" command for the current stuff in doomcom.
        DNI.NetCmd ();
    }

    //
    // HGetPacket
    // Returns false if no packet is waiting
    //
    private boolean HGetPacket () 
    {   
        // Fugly way of "clearing" the buffer.
        sb.setLength(0);
        if (reboundpacket)
        {
            // FIXME: MAES: this looks like a struct copy 
            netbuffer.copyFrom(reboundstore);
            doomcom.remotenode = 0;
            reboundpacket = false;
            return true;
        }

        // If not actually a netgame (e.g. single player, demo) return.
        if (!DM.netgame)
            return false;

        if (DM.demoplayback)
            return false;

        doomcom.command = CMD_GET;
        DNI.NetCmd ();

        // Invalid node?
        if (doomcom.remotenode == -1)
            return false;

        if (doomcom.datalength != NetbufferSize ())
        {
            if (eval(debugfile))
            	logger(debugfile,"bad packet length "+doomcom.datalength+"\n");
            return false;
        }

        if (NetbufferChecksum () != (netbuffer.checksum&NCMD_CHECKSUM) )
        {
            if (eval(debugfile))
            	logger(debugfile,"bad packet checksum\n");
            return false;
        }

        if (eval(debugfile))
        {
            int     realretrans;
            int i;

            if (flags(netbuffer.checksum , NCMD_SETUP))
            	logger(debugfile,"setup packet\n");
            else
            {
                if (flags(netbuffer.checksum , NCMD_RETRANSMIT))
                    realretrans = ExpandTics (netbuffer.retransmitfrom);
                else
                    realretrans = -1;

                sb.append("get ");
                sb.append(doomcom.remotenode);
                sb.append(" = (");
                sb.append(ExpandTics(netbuffer.starttic));
                sb.append(" + ");
                sb.append(netbuffer.numtics);
                sb.append(", R ");
                sb.append(realretrans);
                sb.append(")[");
                sb.append(doomcom.datalength);
                sb.append("]");

                logger(debugfile,sb.toString());

                // Trick: force update of internal buffer.
                netbuffer.pack();

                /* TODO: Could it be actually writing stuff beyond the boundaries of a single doomdata object?
                 * A doomcom object has a lot of header info, and a single "raw" data placeholder, which by now
                 * should be inside netbuffer....right?
                 * 
                 * 
                 */

                try{
                    for (i=0 ; i<doomcom.datalength ; i++) {
                    	debugfile.write(Integer.toHexString(netbuffer.cached()[i]));
                    	debugfile.write('\n');
                    }
                }
                catch( IOException e){
                    // "Drown" IOExceptions here.
                }
            }
        }
        return true;    
    }


    ////    GetPackets

    StringBuilder exitmsg=new StringBuilder(80);

    public void GetPackets () 
    {
        int     netconsole;
        int     netnode;
        ticcmd_t    src, dest;
        int     realend;
        int     realstart;

        while ( HGetPacket() )
        {
            if (flags(netbuffer.checksum , NCMD_SETUP))
                continue;       // extra setup packet

            netconsole = netbuffer.player & ~PL_DRONE;
            netnode = doomcom.remotenode;

            // to save bytes, only the low byte of tic numbers are sent
            // Figure out what the rest of the bytes are
            realstart = ExpandTics (netbuffer.starttic);        
            realend = (realstart+netbuffer.numtics);

            // check for exiting the game
            if (flags(netbuffer.checksum , NCMD_EXIT))
            {
                if (!nodeingame[netnode])
                    continue;
                nodeingame[netnode] = false;
                playeringame[netconsole] = false;
                exitmsg.insert(0, "Player 1 left the game");
                exitmsg.setCharAt(7,(char) (exitmsg.charAt(7)+netconsole));
                players[consoleplayer].message = exitmsg.toString();
                if (demorecording)
                    DM.CheckDemoStatus ();
                continue;
            }

            // check for a remote game kill
            if (flags(netbuffer.checksum , NCMD_KILL))
                I.Error ("Killed by network driver");

            nodeforplayer[netconsole] = netnode;

            // check for retransmit request
            if ( resendcount[netnode] <= 0 
                    && flags(netbuffer.checksum , NCMD_RETRANSMIT) )
            {
                resendto[netnode] = ExpandTics(netbuffer.retransmitfrom);
                if (eval(debugfile)){
                    sb.setLength(0);
                    sb.append("retransmit from ");
                    sb.append(resendto[netnode]);
                    sb.append('\n');
                    logger(debugfile,sb.toString());
                    resendcount[netnode] = RESENDCOUNT;
                }
            }
            else
                resendcount[netnode]--;

            // check for out of order / duplicated packet       
            if (realend == nettics[netnode])
                continue;

            if (realend < nettics[netnode])
            {
                if (eval(debugfile)){
                    sb.setLength(0);
                    sb.append("out of order packet (");
                    sb.append(realstart);
                    sb.append(" + ");
                    sb.append(netbuffer.numtics);
                    sb.append(")\n");
                    logger(debugfile,sb.toString());
                }
                continue;
            }

            // check for a missed packet
            if (realstart > nettics[netnode])
            {
                // stop processing until the other system resends the missed tics
                if (eval(debugfile)) {
                    sb.setLength(0);
                    sb.append("missed tics from ");
                    sb.append(netnode);
                    sb.append(" (");
                    sb.append(realstart);
                    sb.append(" - ");
                    sb.append(nettics[netnode]);
                    sb.append(")\n");
                    logger(debugfile,sb.toString());
                }
                remoteresend[netnode] = true;
                continue;
            }

            // update command store from the packet
            {
                int     start;

                remoteresend[netnode] = false;

                start = nettics[netnode] - realstart;       
                src = netbuffer.cmds[start];

                while (nettics[netnode] < realend)
                {
                    dest = netcmds[netconsole][nettics[netnode]%BACKUPTICS];
                    nettics[netnode]++;
                    // MAES: this is a struct copy.
                    src.copyTo(dest);
                    // Advance src
                    start++;

                    //_D_: had to add this (see linuxdoom source). That fixed that damn consistency failure!!!
                    if (start < netbuffer.cmds.length)
                        src = netbuffer.cmds[start];

                }
            }
        }
    }

    protected void logger(OutputStreamWriter debugfile, String string) {
    		try {
				debugfile.write(string);
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		
	}

	int      gametime;

    @Override
    public void NetUpdate ()
    {
        int             nowtime;
        int             newtics;
        int             i,j;
        int             realstart;
        int             gameticdiv;

        // check time
        nowtime = TICK.GetTime ()/ticdup;
        newtics = nowtime - gametime;
        gametime = nowtime;

        if (newtics <= 0)   // nothing new to update
        {
            // listen for other packets
            GetPackets ();
            return;
        } else {


            if (skiptics <= newtics)
            {
                newtics -= skiptics;
                skiptics = 0;
            }
            else
            {
                skiptics -= newtics;
                newtics = 0;
            }

            netbuffer.player = (byte) consoleplayer;

            // build new ticcmds for console player
            gameticdiv = gametic/ticdup;
            for (i=0 ; i<newtics ; i++)
            {
                VI.StartTic ();
                ProcessEvents ();
                if (maketic - gameticdiv >= BACKUPTICS/2-1)
                    break;          // can't hold any more

                //System.out.printf ("mk:%d ",maketic);
                BuildTiccmd (localcmds[maketic%BACKUPTICS]);
                maketic++;
            }


            if (singletics)
                return;         // singletic update is syncronous

            // send the packet to the other nodes
            for (i=0 ; i<doomcom.numnodes ; i++)
                if (nodeingame[i])
                {
                    netbuffer.starttic = (byte) (realstart = resendto[i]);
                    netbuffer.numtics = (byte) (maketic - realstart);
                    if (netbuffer.numtics > BACKUPTICS)
                        I.Error ("NetUpdate: netbuffer.numtics > BACKUPTICS");

                    resendto[i] = maketic - doomcom.extratics;

                    for (j=0 ; j< netbuffer.numtics ; j++)
                        localcmds[(realstart+j)%BACKUPTICS].copyTo(netbuffer.cmds[j]);
                    // MAES: one of _D_ fixes.
                    //netbuffer.cmds[j] = localcmds[(realstart+j)%BACKUPTICS];

                    if (remoteresend[i])
                    {
                        netbuffer.retransmitfrom = (byte) nettics[i];
                        HSendPacket (i, NCMD_RETRANSMIT);
                    }
                    else
                    {
                        netbuffer.retransmitfrom = 0;
                        HSendPacket (i, 0);
                    }
                }
            GetPackets();
        }

    }



    //
    // CheckAbort
    //
    private void CheckAbort ()
    {
        event_t ev;
        int     stoptic;

        stoptic = TICK.GetTime () + 2; 
        while (TICK.GetTime() < stoptic) 
            VI.StartTic (); 

        VI.StartTic ();
        for ( ; eventtail != eventhead 
        ; eventtail = (++eventtail)&(MAXEVENTS-1) ) 
        { 
            ev = events[eventtail]; 
            if (ev.type == evtype_t.ev_keydown && ev.data1 == KEY_ESCAPE)
                I.Error ("Network game synchronization aborted.");
        } 
    }

    boolean[] gotinfo=new boolean[MAXNETNODES];

    /**
     * D_ArbitrateNetStart
     * @throws IOException 
     *
     * 
     */
    public void ArbitrateNetStart () throws IOException
    {
        int     i;
        autostart = true;

        // Clear it up...
        Arrays.fill(gotinfo,false);

        if (doomcom.consoleplayer!=0)
        {
            // listen for setup info from key player
            System.out.println("listening for network start info...\n");
            while (true)
            {
                CheckAbort ();
                if (!HGetPacket ())
                    continue;
                if (flags(netbuffer.checksum , NCMD_SETUP))
                {
                    if (netbuffer.player != VERSION)
                        I.Error ("Different DOOM versions cannot play a net game!");
                    startskill = skill_t.values()[netbuffer.retransmitfrom & 15];

                    // Deathmatch
                    if (((netbuffer.retransmitfrom & 0xc0) >> 6)==1) 
                        deathmatch = true;
                    else
                        // Cooperative
                        if (((netbuffer.retransmitfrom & 0xc0) >> 6)==2) 
                            altdeath = true;

                    nomonsters = (netbuffer.retransmitfrom & 0x20) > 0;
                    respawnparm = (netbuffer.retransmitfrom & 0x10) > 0;
                    startmap = netbuffer.starttic & 0x3f;
                    startepisode = netbuffer.starttic >> 6;
            return;
                }
            }
        }
        else
        {
            // key player, send the setup info
            System.out.println("sending network start info...\n");
            do
            {
                CheckAbort ();
                for (i=0 ; i<doomcom.numnodes ; i++)
                {
                    netbuffer.retransmitfrom = (byte) startskill.ordinal();
                    if (deathmatch)
                        netbuffer.retransmitfrom |= (1<<6);
                    else
                        if (altdeath)
                            netbuffer.retransmitfrom |= (2<<6);
                    if (nomonsters)
                        netbuffer.retransmitfrom |= 0x20;
                    if (respawnparm)
                        netbuffer.retransmitfrom |= 0x10;
                    netbuffer.starttic = (byte) (startepisode * 64 + startmap);
                    netbuffer.player = VERSION;
                    netbuffer.numtics = 0;
                    HSendPacket (i, NCMD_SETUP);
                }

                //#if 1
                for(i = 10 ; (i>0)  &&  HGetPacket(); --i)
                {
                    if((netbuffer.player&0x7f) < MAXNETNODES)
                        gotinfo[netbuffer.player&0x7f] = true;
                }
                /*
        while (HGetPacket ())
        {
        gotinfo[netbuffer.player&0x7f] = true;
        }
                 */

                for (i=1 ; i<doomcom.numnodes ; i++)
                    if (!gotinfo[i])
                        break;
            } while (i < doomcom.numnodes);
        }
    }

    //
    // D_CheckNetGame
    // Works out player numbers among the net participants
    //

    private void CheckNetGame () throws IOException
    {
        int             i;

        for (i=0 ; i<MAXNETNODES ; i++)
        {
            nodeingame[i] = false;
            nettics[i] = 0;
            remoteresend[i] = false;    // set when local needs tics
            resendto[i] = 0;        // which tic to start sending
        }

        // I_InitNetwork sets doomcom and netgame
        DNI.InitNetwork ();
        if (doomcom.id != DOOMCOM_ID)
            I.Error ("Doomcom buffer invalid!");

        // Maes: This is the only place where netbuffer is definitively set to something
        netbuffer = doomcom.data;
        consoleplayer = displayplayer = doomcom.consoleplayer;
        if (netgame)
            ArbitrateNetStart ();

        System.out.printf ("startskill %s  deathmatch: %s  startmap: %d  startepisode: %d\n",
            startskill.toString(), Boolean.toString(deathmatch), startmap, startepisode);

        // read values out of doomcom
        ticdup = doomcom.ticdup;
        // MAES: ticdup must not be zero at this point. Obvious, no?
        maxsend = BACKUPTICS/(2*ticdup)-1;
        if (maxsend<1)
            maxsend = 1;

        for (i=0 ; i<doomcom.numplayers ; i++)
            playeringame[i] = true;
        for (i=0 ; i<doomcom.numnodes ; i++)
            nodeingame[i] = true;

        System.out.printf ("player %d of %d (%d nodes)\n",(consoleplayer+1), doomcom.numplayers, doomcom.numnodes);

    }


    //
    // D_QuitNetGame
    // Called before quitting to leave a net game
    // without hanging the other players
    //
    @Override
    public void QuitNetGame () throws IOException
    {
        int             i, j;

        if (eval(debugfile))
            try {
                debugfile.close();
            } catch (IOException e) {
                e.printStackTrace();
            }

            if (!netgame || !usergame || consoleplayer == -1 || demoplayback)
                return;

            // send a bunch of packets for security
            netbuffer.player = (byte) consoleplayer;
            netbuffer.numtics = 0;
            for (i=0 ; i<4 ; i++)
            {
                for (j=1 ; j<doomcom.numnodes ; j++)
                    if (nodeingame[j])
                        HSendPacket (j, NCMD_EXIT);
                I.WaitVBL (1);
            }
    }



    //
    // TryRunTics
    //
    int[] frametics=new int[4];
    int frameon;
    boolean[] frameskip=new boolean[4];
    int oldnettics;
    int  oldentertics;


    @Override
    public void TryRunTics () throws IOException
    {
        int     i;
        int     lowtic;
        int     entertic;

        int     realtics;
        int     availabletics;
        int     counts;
        int     numplaying;


        // get real tics        
        entertic = TICK.GetTime ()/ticdup;
        realtics = entertic - oldentertics;
        oldentertics = entertic;

        //System.out.printf("Entertic %d, realtics %d, oldentertics %d\n",entertic,realtics,oldentertics);

        // get available tics
        NetUpdate ();

        lowtic = MAXINT;
        numplaying = 0;
        for (i=0 ; i<doomcom.numnodes ; i++)
        {
            if (nodeingame[i])
            {
                numplaying++;
                if (nettics[i] < lowtic)
                    lowtic = nettics[i];
            }
        }
        availabletics = lowtic - gametic/ticdup;

        // decide how many tics to run
        if (realtics < availabletics-1)
            counts = realtics+1;
        else if (realtics < availabletics)
            counts = realtics;
        else
            counts = availabletics;

        if (counts < 1)
            counts = 1;

        frameon++;

        if (eval(debugfile)){
            sb.setLength(0);
            sb.append( "=======real: ");
            sb.append(realtics);
            sb.append("  avail: ");
            sb.append(availabletics);
            sb.append("  game: ");
            sb.append(counts);
            sb.append("\n");
            debugfile.write(sb.toString());
        }

        if (!demoplayback)
        {   
            // ideally nettics[0] should be 1 - 3 tics above lowtic
            // if we are consistantly slower, speed up time
            for (i=0 ; i<MAXPLAYERS ; i++)
                if (playeringame[i])
                    break;
            if (consoleplayer == i)
            {
                // the key player does not adapt
            }
            else
            {
                if (nettics[0] <= nettics[nodeforplayer[i]])
                {
                    gametime--;
                    System.out.print("-");
                }
                frameskip[frameon&3] = oldnettics > nettics[nodeforplayer[i]];
                oldnettics = nettics[0];
                if (frameskip[0] && frameskip[1] && frameskip[2] && frameskip[3])
                {
                    skiptics = 1;
                    System.out.print("+");
                }
            }
        }// demoplayback

        // wait for new tics if needed
        while (lowtic < gametic/ticdup + counts)    
        {
            NetUpdate ();   
            lowtic = MAXINT;

            // Finds the node with the lowest number of tics.
            for (i=0 ; i<doomcom.numnodes ; i++)
                if (nodeingame[i] && nettics[i] < lowtic)
                    lowtic = nettics[i];

            if (lowtic < gametic/ticdup)
                I.Error ("TryRunTics: lowtic < gametic");

            // don't stay in here forever -- give the menu a chance to work
            int time=TICK.GetTime();
            if (time/ticdup - entertic >= 20)
            {
                M.Ticker ();
                return;
            }
        }

        // run the count * ticdup dics
        while (counts-->0)
        {
            for (i=0 ; i<ticdup ; i++)
            {
                if (gametic/ticdup > lowtic)
                    I.Error ("gametic>lowtic");
                if (advancedemo)
                    DM.DoAdvanceDemo ();
                M.Ticker ();
                Ticker ();
                gametic++;

                // modify command for duplicated tics
                if (i != ticdup-1)
                {
                    ticcmd_t    cmd;
                    int         buf;
                    int         j;

                    buf = (gametic/ticdup)%BACKUPTICS; 
                    for (j=0 ; j<MAXPLAYERS ; j++)
                    {
                        cmd = netcmds[j][buf];
                        cmd.chatchar = 0;
                        if (flags(cmd.buttons , BT_SPECIAL))
                            cmd.buttons = 0;
                    }
                }
            }
            NetUpdate ();   // check for new console commands
        }
    }


    @Override
    public doomcom_t getDoomCom() {
        return this.doomcom;
    }


    @Override
    public void setDoomCom(doomcom_t doomcom) {
        this.doomcom=doomcom;
    }


    @Override
    public void setGameAction(gameaction_t action) {
        this.gameaction=action;
    }


    @Override
    public gameaction_t getGameAction() {       
        return this.gameaction;
    }

    ////////////////////////////VIDEO SCALE STUFF ////////////////////////////////

    protected int SCREENWIDTH;
    protected int SCREENHEIGHT;
    protected int SAFE_SCALE;
    protected IVideoScale vs;



    @Override
    public void setVideoScale(IVideoScale vs) {
        this.vs=vs;
    }

    @Override
    public void initScaling() {
        this.SCREENHEIGHT=vs.getScreenHeight();
        this.SCREENWIDTH=vs.getScreenWidth();
        this.SAFE_SCALE=vs.getSafeScaling();
    }


    public void setCommandLineArgs(ICommandLineManager cM) {
        this.CM=cM;

    }


    public boolean shouldPollLockingKeys() {
        if (keysCleared) {
            keysCleared = false;
            return true;
        }
        return false;
    }
    
    /**
     * Since this is a fully OO implementation, we need a way to create
     * the instances of the Refresh daemon, the Playloop, the Wadloader 
     * etc. which however are now completely independent of each other
     * (well, ALMOST), and are typically only passed context when 
     * instantiated.
     * 
     *  If you instantiate one too early, it will have null context.
     *  
     *  The trick is to construct objects in the correct order. Some of
     *  them have Init() methods which are NOT yet safe to call.
     * 
     */

    public void Init(){
       
        // The various objects that need to "sense" the global status
        // end up here. This allows one-call updates.
        status_holders=new ArrayList<DoomStatusAware>();
        
        // Doommain is both "main" and handles most of the game status.
        this.DM=this;
        this.DG = this;
        this.DGN=this; // DoomMain also handles its own Game Networking.
             
        // Set ticker. It is a shared status object, but not a holder itself.
        if (eval(CM.CheckParm("-millis"))){

            TICK=new MilliTicker();
        }
        else if (eval(CM.CheckParm("-fasttic"))){
            TICK=new FastTicker();
        } else {
            TICK=new NanoTicker();
        }
        
        // Network "driver"
        status_holders.add((DoomStatusAware) (this.DNI=new DummyNetworkDriver(this)));
        
        // Random number generator, but we can have others too.
        this.RND=new DoomRandom();
        
        // Sound can be left until later, in Start

        this.W=new WadLoader(this.I); // The wadloader is a "weak" status holder.
        status_holders.add(this.WIPE=selectWiper());   

        // Then the menu...
        status_holders.add(this.HU=new HU(this));
        status_holders.add(this.M=new Menu(this));
        status_holders.add(this.LL=new BoomLevelLoader(this));
        
        // This will set R.
        this.R= selectRenderer();
        status_holders.add(this.R);
        status_holders.add((this.P=new Actions(this)));
        status_holders.add(this.ST=new StatusBar(this));
        status_holders.add(this.AM=selectAutoMap()); // Call Init later.
        // Let the renderer pick its own. It makes linking easier.
        this.TM=R.getTextureManager();
        this.SM=R.getSpriteManager();        

        //_D_: well, for EndLevel and Finale to work, they need to be instanciated somewhere!
        // it seems to fit perfectly here
        status_holders.add(this.WI = new EndLevel(this));    
        status_holders.add(this.F = selectFinale());
        
        // TODO: find out if we have requests for a specific resolution,
        // and try honouring them as closely as possible.       

        // 23/5/2011: Experimental dynamic resolution subsystem
        vs=VisualSettings.parse(CM);
        
        // Initializing actually sets drawing positions, constants,
        // etc. for main. Children will be set later in Start().
        this.initScaling();
        
        this.V=selectVideoRenderer();

        status_holders.add((DoomStatusAware) this.I);
        status_holders.add((DoomStatusAware) this.V);
        
        // Disk access visualizer
        
        status_holders.add((DoomStatusAware) (this.DD=new DiskDrawer(this,DiskDrawer.STDISK)));
        
        updateStatusHolders(this);


    }
    


    public static final class HiColor extends DoomMain<byte[], short[]>{
    
        public HiColor(){
            super();
        }
        
        protected IAutoMap<byte[], short[]> selectAutoMap() {
            return new Map.HiColor(this);
        }
        
        protected final Finale<byte[]> selectFinale(){
            return new Finale<byte[]>(this);
        }
        
        protected final DoomVideoRenderer<byte[],short[]> selectVideoRenderer(){
            return new BufferedRenderer16(SCREENWIDTH,SCREENHEIGHT);
        }
        
        protected final DoomVideoInterface<short[]> selectVideoInterface(){
            return new AWTDoom.HiColor(this,V);
        }
        
        protected final Wiper<byte[],short[]> selectWiper(){
            return new Wiper.HiColor(this);
        }
        
        protected final Renderer<byte[],short[]> selectRenderer() {
            // Serial or parallel renderer (serial is default, but can be forced)
            if (eval(CM.CheckParm("-serialrenderer"))){
                return new UnifiedRenderer.HiColor(this);    
            } else 
                // Parallel. Either with default values (2,1) or user-specified.
                if (CM.CheckParmBool("-parallelrenderer")||CM.CheckParmBool("-parallelrenderer2")){        
                    int p = CM.CheckParm ("-parallelrenderer");
                    if (p<1) p=CM.CheckParm("-parallelrenderer2");
                    
                    if (p < CM.getArgc()-1)
                    {
                        // Next THREE args must be numbers.
                        int walls=1, floors=1,masked=2;
                        startmap = Integer.parseInt(CM.getArgv(p+1));
                        // Try parsing walls.
                        try {
                            walls=Integer.parseInt(CM.getArgv(p+1));
                        } catch (Exception e){
                            // OK, move on anyway.
                        }

                        // Try parsing floors. If wall succeeded, but floors
                        // not, it will default to 1.
                        try {
                            floors=Integer.parseInt(CM.getArgv(p+2));
                        } catch (Exception e){
                            // OK, move on anyway.
                        }
                        
                        try {
                            masked=Integer.parseInt(CM.getArgv(p+3));
                        } catch (Exception e){
                            // OK, move on anyway.
                        }

                        // In the worst case, we will use the defaults.
                        if  (CM.CheckParmBool("-parallelrenderer"))
                            // TODO: temporarily disabled
                        	return new ParallelRenderer.HiColor(this,walls,floors,masked);  
                       //     return (Renderer<byte[], short[]>) new ParallelRenderer(this,walls,floors,masked);
                       // else
                       //     return (Renderer<byte[], short[]>) new ParallelRenderer2(this,walls,floors,masked);
                    }
                } else {
                    // Force serial
                    return new UnifiedRenderer.HiColor(this);   
                }
            
            return null;
        }

        /**
        *  M_Screenshot
        *  
        *  Currently saves PCX screenshots, and only in devparm.
        *  Very oldschool ;-)
        *  
        *  TODO: add non-devparm hotkey for screenshots, sequential screenshot
        *  messages, option to save as either PCX or PNG. Also, request
        *  current palette from VI (otherwise gamma settings and palette effects
        *  don't show up).
        *  
        */

       public void ScreenShot ()
       {
           int     i;
           short[]  linear;
           String format=new String("DOOM%d%d%d%d.png");
           String lbmname = null;
           
           // munge planar buffer to linear
           linear = (short[]) V.getScreen(DoomVideoRenderer.SCREEN_WS);
           VI.ReadScreen (linear);

           // find a file name to save it to
           
           int[] digit=new int[4];
           
           for (i=0 ; i<=9999 ; i++)
           {
           digit[0] = ((i/1000 )%10);
           digit[1] =  ((i/100)%10);
           digit[2] =  ((i/10)%10);
           digit[3] =  (i%10);
           lbmname=String.format(format, digit[0],digit[1],digit[2],digit[3]);
           if (!C2JUtils.testReadAccess(lbmname))
               break;  // file doesn't exist
           }
           if (i==10000)
           I.Error ("M_ScreenShot: Couldn't create a PNG");

           // save the pcx file
           MenuMisc.WritePNGfile (lbmname, linear,
                 SCREENWIDTH, SCREENHEIGHT);

           players[consoleplayer].message = "screen shot";
       }

    }
    
    public static final class Indexed extends DoomMain<byte[], byte[]>{
        
        public Indexed(){
            super();
        }
        
        protected IAutoMap<byte[], byte[]> selectAutoMap() {
            return new Map.Indexed(this);
        }
        
        protected final Finale<byte[]> selectFinale(){
            return new Finale<byte[]>(this);
        }
        
        protected final DoomVideoRenderer<byte[],byte[]> selectVideoRenderer(){
            return new BufferedRenderer(SCREENWIDTH,SCREENHEIGHT);
        }
        
        protected final DoomVideoInterface<byte[]> selectVideoInterface(){
            return new AWTDoom.Indexed(this,V);
        }
        
        protected final Wiper<byte[],byte[]> selectWiper(){
            return new Wiper.Indexed(this);
        }        
        protected final Renderer<byte[],byte[]> selectRenderer() {
            // Serial or parallel renderer (serial is default, but can be forced)
            if (eval(CM.CheckParm("-serialrenderer"))){
                return new UnifiedRenderer.Indexed(this);    
            } else 
                // Parallel. Either with default values (2,1) or user-specified.
                if (CM.CheckParmBool("-parallelrenderer")||CM.CheckParmBool("-parallelrenderer2")){        
                    int p = CM.CheckParm ("-parallelrenderer");
                    if (p<1) p=CM.CheckParm("-parallelrenderer2");
                    
                    if (p < CM.getArgc()-1)
                    {
                        // Next THREE args must be numbers.
                        int walls=1, floors=1,masked=2;
                        startmap = Integer.parseInt(CM.getArgv(p+1));
                        // Try parsing walls.
                        try {
                            walls=Integer.parseInt(CM.getArgv(p+1));
                        } catch (Exception e){
                            // OK, move on anyway.
                        }

                        // Try parsing floors. If wall succeeded, but floors
                        // not, it will default to 1.
                        try {
                            floors=Integer.parseInt(CM.getArgv(p+2));
                        } catch (Exception e){
                            // OK, move on anyway.
                        }
                        
                        try {
                            masked=Integer.parseInt(CM.getArgv(p+3));
                        } catch (Exception e){
                            // OK, move on anyway.
                        }

                        // In the worst case, we will use the defaults.
                        if  (CM.CheckParmBool("-parallelrenderer"))                            
                            // TODO: temporarily disabled
                            //return new UnifiedRenderer.Indexed(this);   
                            return new ParallelRenderer.Indexed(this,walls,floors,masked);
                        //else
                          //  return new ParallelRenderer2(this,walls,floors,masked);
                    }
                } else {
                    // Force serial
                    return new UnifiedRenderer.Indexed(this);   
                }
            
            return null;
        }

        /**
        *  M_Screenshot
        *  
        *  Currently saves PCX screenshots, and only in devparm.
        *  Very oldschool ;-)
        *  
        *  TODO: add non-devparm hotkey for screenshots, sequential screenshot
        *  messages, option to save as either PCX or PNG. Also, request
        *  current palette from VI (otherwise gamma settings and palette effects
        *  don't show up).
        *  
        */

       public void ScreenShot ()
       {
           int     i;
           byte[]  linear;
           String format=new String("DOOM%d%d%d%d.png");
           String lbmname = null;
           
           // munge planar buffer to linear
           linear = (byte[]) V.getScreen(DoomVideoRenderer.SCREEN_WS);
           VI.ReadScreen (linear);

           // find a file name to save it to
           
           int[] digit=new int[4];
           
           for (i=0 ; i<=9999 ; i++)
           {
           digit[0] = ((i/1000 )%10);
           digit[1] =  ((i/100)%10);
           digit[2] =  ((i/10)%10);
           digit[3] =  (i%10);
           lbmname=String.format(format, digit[0],digit[1],digit[2],digit[3]);
           if (!C2JUtils.testReadAccess(lbmname))
               break;  // file doesn't exist
           }
           if (i==10000)
           I.Error ("M_ScreenShot: Couldn't create a PNG");

           // save the pcx file
           MenuMisc.WritePNGfile(lbmname, linear,
                 SCREENWIDTH, SCREENHEIGHT,V.getPalette());

           players[consoleplayer].message = "screen shot";
       }
        
    }
 
    public static final class TrueColor extends DoomMain<byte[], int[]>{
        
        public TrueColor(){
            super();
        }
        
        protected IAutoMap<byte[], int[]> selectAutoMap() {
            return new Map.TrueColor(this);
        }
        
        protected final Finale<byte[]> selectFinale(){
            return new Finale<byte[]>(this);
        }
        
        protected final DoomVideoRenderer<byte[],int[]> selectVideoRenderer(){
            return new BufferedRenderer32(SCREENWIDTH,SCREENHEIGHT);
        }
        
        protected final DoomVideoInterface<int[]> selectVideoInterface(){
            return new AWTDoom.TrueColor(this,V);
        }
        
        protected final Wiper<byte[],int[]> selectWiper(){
            return new Wiper.TrueColor(this);
        }
        
        protected final Renderer<byte[],int[]> selectRenderer() {
            // Serial or parallel renderer (serial is default, but can be forced)
            if (eval(CM.CheckParm("-serialrenderer"))){
                return new UnifiedRenderer.TrueColor(this);    
            } else 
                // Parallel. Either with default values (2,1) or user-specified.
                if (CM.CheckParmBool("-parallelrenderer")||CM.CheckParmBool("-parallelrenderer2")){        
                    int p = CM.CheckParm ("-parallelrenderer");
                    if (p<1) p=CM.CheckParm("-parallelrenderer2");
                    
                    if (p < CM.getArgc()-1)
                    {
                        // Next THREE args must be numbers.
                        int walls=1, floors=1,masked=2;
                        startmap = Integer.parseInt(CM.getArgv(p+1));
                        // Try parsing walls.
                        try {
                            walls=Integer.parseInt(CM.getArgv(p+1));
                        } catch (Exception e){
                            // OK, move on anyway.
                        }

                        // Try parsing floors. If wall succeeded, but floors
                        // not, it will default to 1.
                        try {
                            floors=Integer.parseInt(CM.getArgv(p+2));
                        } catch (Exception e){
                            // OK, move on anyway.
                        }
                        
                        try {
                            masked=Integer.parseInt(CM.getArgv(p+3));
                        } catch (Exception e){
                            // OK, move on anyway.
                        }

                        // In the worst case, we will use the defaults.
                        if  (CM.CheckParmBool("-parallelrenderer"))
                            // TODO: temporarily disabled
                        	return new ParallelRenderer.TrueColor(this,walls,floors,masked);   
                       //     return (Renderer<byte[], short[]>) new ParallelRenderer(this,walls,floors,masked);
                       // else
                       //     return (Renderer<byte[], short[]>) new ParallelRenderer2(this,walls,floors,masked);
                    }
                } else {
                    // Force serial
                    return new UnifiedRenderer.TrueColor(this);   
                }
            
            return null;
        }

        /**
        *  M_Screenshot
        *  
        *  Currently saves PCX screenshots, and only in devparm.
        *  Very oldschool ;-)
        *  
        *  TODO: add non-devparm hotkey for screenshots, sequential screenshot
        *  messages, option to save as either PCX or PNG. Also, request
        *  current palette from VI (otherwise gamma settings and palette effects
        *  don't show up).
        *  
        */

       public void ScreenShot ()
       {
           int     i;
           int[]  linear;
           String format=new String("DOOM%d%d%d%d.png");
           String lbmname = null;
           
           // munge planar buffer to linear
           linear = (int[]) V.getScreen(DoomVideoRenderer.SCREEN_WS);
           VI.ReadScreen (linear);

           // find a file name to save it to
           
           int[] digit=new int[4];
           
           for (i=0 ; i<=9999 ; i++)
           {
           digit[0] = ((i/1000 )%10);
           digit[1] =  ((i/100)%10);
           digit[2] =  ((i/10)%10);
           digit[3] =  (i%10);
           lbmname=String.format(format, digit[0],digit[1],digit[2],digit[3]);
           if (!C2JUtils.testReadAccess(lbmname))
               break;  // file doesn't exist
           }
           if (i==10000)
           I.Error ("M_ScreenShot: Couldn't create a PNG");

           // save the pcx file
           MenuMisc.WritePNGfile (lbmname, linear,
                 SCREENWIDTH, SCREENHEIGHT);

           players[consoleplayer].message = "screen shot";
       }

    }
    
}

//$Log: DoomMain.java,v $
//Revision 1.110  2016/06/06 22:21:24  velktron
//Use coalescing
//
//Revision 1.109  2012/11/06 16:04:58  velktron
//Variables manager less tightly integrated.
//
//Revision 1.108  2012/11/05 17:25:29  velktron
//Fixed tinting system according to SodaHolic's advice.
//
//Revision 1.107  2012/09/27 16:53:46  velktron
//Stupid brokeness prevented -loadgame from working.
//
//Revision 1.106  2012/09/26 23:15:20  velktron
//Parallel renderer restored...sort of.
//
//Revision 1.105  2012/09/26 15:54:22  velktron
//Spritemanager is set up by renderer.
//
//Revision 1.104  2012/09/24 22:36:49  velktron
//Fixed HOM detection.
//
//Revision 1.103  2012/09/24 17:16:22  velktron
//Massive merge between HiColor and HEAD. There's no difference from now on, and development continues on HEAD.
//
//Revision 1.101.2.11  2012/09/24 16:58:06  velktron
//TrueColor, Generics.
//
//Revision 1.101.2.10  2012/09/21 16:17:25  velktron
//More generic.
//
//Revision 1.101.2.9  2012/09/20 14:25:13  velktron
//Unified DOOM!!!
//

package doom;

/** A shiny new and enterprisey (yeah right) interface for
 *  command-line handling. No more argv/argc nastiness!
 *  
 * @author velktron
 *
 */

public interface ICommandLineManager {

    public abstract String getArgv(int index);

    public abstract int getArgc();

    /**
     * M_CheckParm Checks for the given parameter in the program's command line
     * arguments. Returns the argument number (1 to argc-1) or 0 if not present
     * 
     * OK, now WHY ON EARTH was this be defined in m_menu.c?
     * 
     * MAES: this needs to be modified for Java, or else bump myargc one element up.
     * 
     */

    public abstract int CheckParm(String check);

    void FindResponseFile();

    public abstract void setArgv(int index, String string);

	boolean CheckParmBool(String check);

}
package doom;
import defines.*;

//
// PSPRITE ACTIONS for waepons.
// This struct controls the weapon animations.
//
// Each entry is:
//   ammo/amunition type
//  upstate
//  downstate
// readystate
// atkstate, i.e. attack/fire/hit frame
// flashstate, muzzle flash
//

public class weaponinfo_t {

    /*    
    public weaponinfo_t(ammotype_t ammo, int upstate, int downstate,
            int readystate, int atkstate, int flashstate) {
        super();
        this.ammo = ammo;
        this.upstate = upstate;
        this.downstate = downstate;
        this.readystate = readystate;
        this.atkstate = atkstate;
        this.flashstate = flashstate;
    }*/
        public ammotype_t  ammo;
             
        
        public weaponinfo_t(ammotype_t ammo, statenum_t upstate,
                statenum_t downstate, statenum_t readystate,
                statenum_t atkstate, statenum_t flashstate) {
            super();
            this.ammo = ammo;
            this.upstate = upstate;
            this.downstate = downstate;
            this.readystate = readystate;
            this.atkstate = atkstate;
            this.flashstate = flashstate;
        }
        
        public statenum_t     upstate;
        public statenum_t     downstate;
        public statenum_t     readystate;
        public statenum_t     atkstate;
        public statenum_t     flashstate;
        
        
        /*
        public int     upstate;
        public int     downstate;
        public int     readystate;
        public int     atkstate;
        public int     flashstate;
        */
    
}

package doom;

/** The possible events according to Doom */

public enum evtype_t {
        ev_null,
        ev_keydown,
        ev_keyup,
        ev_mouse,
        ev_joystick,
        ev_mousewheel, // extension
        ev_clear // Forcibly clear all button input (e.g. when losing focus)
    };

package doom;

/** Mocha Doom uses the think_t type as an ENUM for available action functions. This
 * makes enumerations etc. easy to keep track of.
 * 
 * @author velktron
 *
 */

public enum think_t {
    /** Here's your "decent NOP" function */
	NOP,
	A_Light0(2),
    A_WeaponReady(2),
    A_Lower(2),
    A_Raise(2),
    A_Punch(2),
    A_ReFire(2),
    A_FirePistol(2),
    A_Light1(2),
    A_FireShotgun(2),
    A_Light2(2),
    A_FireShotgun2(2),
    A_CheckReload(2),
    A_OpenShotgun2(2),
    A_LoadShotgun2(2),
    A_CloseShotgun2(2),
    A_FireCGun(2),
    A_GunFlash(2),
    A_FireMissile(2),
    A_Saw(2),
    A_FirePlasma(2),
    A_BFGsound(2),
    A_FireBFG(2),
    A_BFGSpray(1),
    A_Explode(1),
    A_Pain(1),
    A_PlayerScream(1),
    A_Fall(1),
    A_XScream(1),
    A_Look(1),
    A_Chase(1),
    A_FaceTarget(1),
    A_PosAttack(1),
    A_Scream(1),
    A_SPosAttack(1),
    A_VileChase(1),
    A_VileStart(1),
    A_VileTarget(1),
    A_VileAttack(1),
    A_StartFire(1),
    A_Fire(1),
    A_FireCrackle(1),
    A_Tracer(1),
    A_SkelWhoosh(1),
  //Historically, "think_t" is yet another
  //function pointer to a routine to handle
  //an actor.

  //
  //Experimental stuff.
  //To compile this as "ANSI C with classes"
  //we will need to handle the various
  //action functions cleanly.
  //
  //typedef  void (*actionf_v)();
  //typedef  void (*actionf_p1)( void* );
  //typedef  void (*actionf_p2)( void*, void* );

  /*typedef union
  {
  actionf_p1  acp1;
  actionf_v   acv;
  actionf_p2  acp2;

  } actionf_t;

  */

    A_SkelFist(1),
    A_SkelMissile(1),
    A_FatRaise(1),
    A_FatAttack1(1),
    A_FatAttack2(1),
    A_FatAttack3(1),
    A_BossDeath(1),
    A_CPosAttack(1),
    A_CPosRefire(1),
    A_TroopAttack(1),
    A_SargAttack(1),
    A_HeadAttack(1),
    A_BruisAttack(1),
    A_SkullAttack(1),
    A_Metal(1),
    A_SpidRefire(1),
    A_BabyMetal(1),
    A_BspiAttack(1),
    A_Hoof(1),
    A_CyberAttack(1),
    A_PainAttack(1),
    A_PainDie(1),
    A_KeenDie(1),
    A_BrainPain(1),
    A_BrainScream(1),
    A_BrainDie(1),
    A_BrainAwake(1),
    A_BrainSpit(1),
    A_SpawnSound(1),
    A_SpawnFly(1),
    A_BrainExplode(1),
    P_MobjThinker(1),
    T_FireFlicker(1),
	T_LightFlash(1),
	T_StrobeFlash(1),
	T_Glow(1),
	T_MoveCeiling(1),
	T_MoveFloor(1),
	T_VerticalDoor(1),
	T_PlatRaise(1), 
	T_SlidingDoor(1),
	// The following are dummies that exist only for demo sync debugging
	DeathMatchSpawnPlayer,
	PlayerInSpecialSector, 
	SpawnLightFlash, 
	SpawnStrobeFlash, 
	ExplodeMissile,
	CheckMissileRange,
	DoPlat,
	CheckMissileSpawn,
	DamageMobj, 
	KillMobj, 
	NewChaseDir, 
	P_GunShot, 
	PIT_ChangeSector, 
	PIT_CheckThing, 
	TryWalk, 
	SpawnBlood, 
	SpawnMapThing, 
	SpawnMobj, 
	SpawnMissile,
	SpawnPuff;
    
	
	think_t(){
	    type=0; // Default, but Doom has no "type 0" functions!
	}
	
    think_t(int type){
        this.type=type;
    }

    /** 0 for void, 1 for acp1, 2 for acp2 */
    public int getType() {
        return type;
    }

    private int type;
    
    public String ToString(){
        return this.name()+" Type: "+type;
    }
    
    public static final int acpv=0;
    public static final int acp1=1;
    public static final int acp2=2;
	
}

package doom;

import java.io.IOException;

/** Doom is actually tied to its networking module.
 *  Therefore, no matter where and how you implement it, these functions
 *  need to be callable from within many modules.
 *  
 *  This is the so called "game networking" which is internal and game-specific,
 *  and not system networking which deals with the low level sockets and packet 
 *  stuff. You'll need DoomSystemNetworking for that one.
 *  
 * @author Velktron
 *
 */

public interface IDoomGameNetworking {
	
	public void TryRunTics() throws IOException;
	
	/**
	 * NetUpdate
	 * Builds ticcmds for console player,
	 * sends out a packet
	 * @throws IOException 
	 */

	public void NetUpdate ();
	
	public doomcom_t getDoomCom();
	
	public void setDoomCom(doomcom_t doomcom);
	
	public int getTicdup();

	public void setTicdup(int ticdup);

}

package doom;

import i.DoomStatusAware;
import i.IDoomSystem;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Arrays;

import m.IRandom;

import data.Tables;
import data.state_t;
import defines.*;
import data.sounds.sfxenum_t;
import static data.Defines.*;
import static data.Tables.*;
import static m.fixed_t.*;
import static data.info.*;
import static p.mobj_t.*;
import p.Actions;
import p.mobj_t;
import p.pspdef_t;
import rr.LightsAndColors;
import rr.Renderer;
import rr.sector_t;
import s.IDoomSound;
import utils.C2JUtils;
import w.DoomBuffer;
import w.DoomIO;
import w.IPackableDoomObject;
import w.IReadableDoomObject;
import static utils.C2JUtils.*;
import static data.Limits.*;
import static doom.items.weaponinfo;
import static p.mobj_t.MF_SHADOW;

/**
 * Extended player object info: player_t The player data structure depends on a
 * number of other structs: items (internal inventory), animation states
 * (closely tied to the sprites used to represent them, unfortunately). 
 * 
 * #include "d_items.h" 
 * #include "p_pspr.h" 
 * 
 * In addition, the player is just a special
 * case of the generic moving object/actor. 
 * NOTE: this doesn't mean it needs to extend it, although it would be
 * possible.
 * 
 * #include "p_mobj.h" 
 * 
 * Finally, for odd reasons, the player input is buffered within 
 * the player data struct, as  commands per game tick. 
 * 
 * #include "d_ticcmd.h"
 */

public class player_t /*extends mobj_t */
        implements Cloneable ,DoomStatusAware, IReadableDoomObject, IPackableDoomObject
        {
	
    /** Probably doomguy needs to know what the fuck is going on */
    private DoomStatus DS;
    private IDoomGame DG;
    private Actions P;
    private Renderer<?,?> R;
    private IRandom RND;
    private IDoomSystem I;
    private IDoomSound S;
	
    /* Fugly hack to "reset" the player. Not worth the fugliness.
    public static player_t nullplayer;
    static {
        nullplayer = new player_t();
    }
    */

    public player_t() {
        powers = new int[NUMPOWERS];
        frags = new int[MAXPLAYERS];
        ammo = new int[NUMAMMO];
        //maxammo = new int[NUMAMMO];
        maxammo= new int[NUMAMMO];
        cards = new boolean[card_t.NUMCARDS.ordinal()];
        weaponowned = new boolean[NUMWEAPONS];
        psprites = new pspdef_t[NUMPSPRITES];
        C2JUtils.initArrayOfObjects(psprites);
        this.mo=new mobj_t();
        // If a player doesn't reference himself through his object, he will have an existential crisis.
        this.mo.player=this;
        readyweapon=weapontype_t.wp_fist;
        this.cmd=new ticcmd_t();
        //weaponinfo=new weaponinfo_t();
    }

    public final static int CF_NOCLIP = 1; // No damage, no health loss.

    public final static int CF_GODMODE = 2;

    public final static int CF_NOMOMENTUM = 4; // Not really a cheat, just a debug aid.

    
    /** The "mobj state" of the player is stored here, even though he "inherits"
     *  all mobj_t properties (except being a thinker). However, for good or bad,
     *  his mobj properties are modified by accessing player.mo
     */
    public mobj_t mo;

    /** playerstate_t */
    public int playerstate;

    public ticcmd_t cmd;

    /**
     * Determine POV, including viewpoint bobbing during movement. (fixed_t)
     * Focal origin above r.z
     */
    public int viewz;
    
    /** (fixed_t) Base height above floor for viewz. */
    public int viewheight;

    /** (fixed_t) Bob/squat speed. */
    public int deltaviewheight;

    /** (fixed_t) bounded/scaled total momentum. */
    public int bob;

    // Heretic stuff
	public int			flyheight;
	public int			lookdir;
	public boolean		centering;
    
    /**
     * This is only used between levels, mo->health is used during levels.
     * CORRECTION: this is also used by the automap widget.
     * MAES: fugly hax, as even passing "Integers" won't work, as they are immutable.
     * Fuck that, I'm doing it the fugly MPI Java way!
     */
    public int[] health = new int[1];

    /** has to be passed around :-( */
    public int[] armorpoints = new int[1];

    /** Armor type is 0-2. */
    public int armortype;

    /** Power ups. invinc and invis are tic counters. */
    public int[] powers;

    public boolean[] cards;

    public boolean backpack;

    // Frags, kills of other players.
    public int[] frags;

    public weapontype_t readyweapon;

    // Is wp_nochange if not changing.
    public weapontype_t pendingweapon;

    public boolean[] weaponowned;

    public int[] ammo;

    public int[] maxammo;

    /** True if button down last tic. */
    public boolean attackdown;

    public boolean usedown;

    // Bit flags, for cheats and debug.
    // See cheat_t, above.
    public int cheats;

    // Refired shots are less accurate.
    public int refire;

    // For intermission stats.
    public int killcount;

    public int itemcount;

    public int secretcount;

    // Hint messages.
    public String message;

    // For screen flashing (red or bright).
    public int damagecount;

    public int bonuscount;

    // Who did damage (NULL for floors/ceilings).
    public mobj_t attacker;

    // So gun flashes light up areas.
    public int extralight;

    /**
     * Current PLAYPAL, ??? can be set to REDCOLORMAP for pain, etc. MAES: "int"
     * my ass. It's yet another pointer alias into colormaps. Ergo, array and
     * pointer.
     */

    // public byte[] fixedcolormap;
    public int fixedcolormap;

    // Player skin colorshift,
    // 0-3 for which color to draw player.
    public int colormap;

    // TODO: Overlay view sprites (gun, etc).
    public pspdef_t[] psprites;

    // True if secret level has been done.
    public boolean didsecret;

    /** It's probably faster to clone the null player */

    public void reset() {

        Arrays.fill(this.ammo, 0);
        Arrays.fill(this.armorpoints, 0);
        Arrays.fill(this.cards, false);
        Arrays.fill(this.frags, 0);
        Arrays.fill(this.health, 0);
        Arrays.fill(this.maxammo, 0);
        Arrays.fill(this.powers, 0);
        Arrays.fill(this.weaponowned, false);
        //Arrays.fill(this.psprites, null);
        this.cheats=0; // Forgot to clear up cheats flag...
        this.armortype = 0;
        this.attackdown = false;
        this.attacker = null;
        this.backpack = false;
        this.bob = 0;

    }

    @Override
    public player_t clone()
            throws CloneNotSupportedException {
        return (player_t) super.clone();
    }
        
    /** 16 pixels of bob */
    private static int MAXBOB = 0x100000;

    /**
     * P_Thrust Moves the given origin along a given angle.
     * 
     * @param angle
     *        (angle_t)
     * @param move
     *        (fixed_t)
     */

    public void Thrust(long angle, int move) {
        mo.momx += FixedMul(move, finecosine(angle));
        mo.momy += FixedMul(move, finesine( angle));
    }

    protected final static int PLAYERTHRUST=2048/TIC_MUL;
    
    /**
     * P_MovePlayer
     */
    public void MovePlayer() {
        ticcmd_t cmd = this.cmd;

        mo.angle += (cmd.angleturn << 16);
        mo.angle&=BITS32;
        
        // Do not let the player control movement
        // if not onground.
        onground = (mo.z <= mo.floorz);

        if (cmd.forwardmove != 0 && onground)
            Thrust(mo.angle, cmd.forwardmove * PLAYERTHRUST);

        if (cmd.sidemove != 0 && onground)
            Thrust((mo.angle - ANG90)&BITS32, cmd.sidemove * PLAYERTHRUST);

        if ((cmd.forwardmove != 0 || cmd.sidemove != 0)
                && mo.state == states[statenum_t.S_PLAY.ordinal()]) {
            this.mo.SetMobjState(statenum_t.S_PLAY_RUN1);
        }
        
        // Freelook code ripped off Heretic. Sieg heil!
        
        int look = cmd.lookfly&15;
        
    	if(look > 7)
    	{
    		look -= 16;
    	}
    	if(look!=0)
    	{
    		if(look == TOCENTER)
    		{
    			centering = true;
    		}
    		else
    		{
    			lookdir += 5*look;
    			if(lookdir > 90 || lookdir < -110)
    			{
    				lookdir -= 5*look;
    			}
    		}
    	}
    	
    	// Centering is done over several tics
    	if(centering)
    	{
    		if(lookdir > 0)
    		{
    			lookdir -= 8;
    		}
    		else if(lookdir < 0)
    		{
    			lookdir += 8;
    		}
    		if(Math.abs(lookdir) < 8)
    		{
    			lookdir = 0;
    			centering = false;
    		}
    	}
    	/* Flight stuff from Heretic
    	fly = cmd.lookfly>>4;
    		
    	if(fly > 7)
    	{
    		fly -= 16;
    	}
    	if(fly && player->powers[pw_flight])
    	{
    		if(fly != TOCENTER)
    		{
    			player->flyheight = fly*2;
    			if(!(player->mo->flags2&MF2_FLY))
    			{
    				player->mo->flags2 |= MF2_FLY;
    				player->mo->flags |= MF_NOGRAVITY;
    			}
    		}
    		else
    		{
    			player->mo->flags2 &= ~MF2_FLY;
    			player->mo->flags &= ~MF_NOGRAVITY;
    		}
    	}
    	else if(fly > 0)
    	{
    		P_PlayerUseArtifact(player, arti_fly);
    	}
    	if(player->mo->flags2&MF2_FLY)
    	{
    		player->mo->momz = player->flyheight*FRACUNIT;
    		if(player->flyheight)
    		{
    			player->flyheight /= 2;
    		}
    	} */
    }


    //
    // GET STUFF
    //

    
    // a weapon is found with two clip loads,
    // a big item has five clip loads
    
    public static final int[] clipammo = { 10, 4, 20, 1 };

    /**
     * P_GiveAmmo Num is the number of clip loads, not the individual count (0=
     * 1/2 clip).
     * 
     * @return false if the ammo can't be picked up at all
     * @param ammo
     *        intended to be ammotype_t.
     */
        
    public boolean GiveAmmo(ammotype_t amm, int num) {
        int oldammo;
        int ammo = amm.ordinal();
        if (ammo == ammotype_t.am_noammo.ordinal())
            return false;

        if (ammo < 0 || ammo > NUMAMMO)
            I.Error("P_GiveAmmo: bad type %i", ammo);

        if (this.ammo[ammo] == maxammo[ammo])
            return false;

        if (num != 0)
            num *= clipammo[ammo];
        else
            num = clipammo[ammo] / 2;

        if (DS.gameskill == skill_t.sk_baby
                ||DS.gameskill == skill_t.sk_nightmare) {
            // give double ammo in trainer mode,
            // you'll need in nightmare
            num <<= 1;
        }

        oldammo = this.ammo[ammo];
        this.ammo[ammo] += num;

        if (this.ammo[ammo] > maxammo[ammo])
            this.ammo[ammo] = maxammo[ammo];

        // If non zero ammo,
        // don't change up weapons,
        // player was lower on purpose.
        if (oldammo != 0)
            return true;

        // We were down to zero,
        // so select a new weapon.
        // Preferences are not user selectable.
        switch (ammotype_t.values()[ammo]) {
        case am_clip:
            if (readyweapon == weapontype_t.wp_fist) {
                if (weaponowned[weapontype_t.wp_chaingun.ordinal()])
                    pendingweapon = weapontype_t.wp_chaingun;
                else
                    pendingweapon = weapontype_t.wp_pistol;
            }
            break;

        case am_shell:
            if (readyweapon == weapontype_t.wp_fist
                    || readyweapon == weapontype_t.wp_pistol) {
                if (weaponowned[weapontype_t.wp_shotgun.ordinal()])
                    pendingweapon = weapontype_t.wp_shotgun;
            }
            break;

        case am_cell:
            if (readyweapon == weapontype_t.wp_fist
                    || readyweapon == weapontype_t.wp_pistol) {
                if (weaponowned[weapontype_t.wp_plasma.ordinal()])
                    pendingweapon = weapontype_t.wp_plasma;
            }
            break;

        case am_misl:
            if (readyweapon == weapontype_t.wp_fist) {
                if (weaponowned[weapontype_t.wp_missile.ordinal()])
                    pendingweapon = weapontype_t.wp_missile;
            }
        default:
            break;
        }

        return true;
    }

    public static final int BONUSADD = 6;

    /**
     * P_GiveWeapon
     * The weapon name may have a MF_DROPPED flag ored in.
     */
    
    public boolean GiveWeapon(weapontype_t weapn, boolean dropped) {
        boolean gaveammo;
        boolean gaveweapon;
        int weapon = weapn.ordinal();

        if (DS.netgame && (DS.deathmatch != true) // ???? was "2"
                && !dropped) {
            // leave placed weapons forever on net games
            if (weaponowned[weapon])
                return false;

            bonuscount += BONUSADD;
            weaponowned[weapon] = true;

            if (DS.deathmatch)
                GiveAmmo(weaponinfo[weapon].ammo, 5);
            else
                GiveAmmo(weaponinfo[weapon].ammo, 2);
            pendingweapon = weapn;

            if (this ==DS.players[DS.consoleplayer])
                S.StartSound (null, sfxenum_t.sfx_wpnup);
                return false;
        }

        if (weaponinfo[weapon].ammo != ammotype_t.am_noammo) {
            // give one clip with a dropped weapon,
            // two clips with a found weapon
            if (dropped)
                gaveammo = GiveAmmo(weaponinfo[weapon].ammo, 1);
            else
                gaveammo = GiveAmmo(weaponinfo[weapon].ammo, 2);
        } else
            gaveammo = false;

        if (weaponowned[weapon])
            gaveweapon = false;
        else {
            gaveweapon = true;
            weaponowned[weapon] = true;
            pendingweapon = weapn;
        }

        return (gaveweapon || gaveammo);
    }

    /**
     * P_GiveBody Returns false if the body isn't needed at all
     */

    public boolean GiveBody(int num) {
        if (this.health[0] >= MAXHEALTH)
            return false;

        health[0] += num;
        if (health[0] > MAXHEALTH)
            health[0] = MAXHEALTH;
        mo.health = health[0];

        return true;
    }

    /**
     * P_GiveArmor Returns false if the armor is worse than the current armor.
     */

    public boolean GiveArmor(int armortype) {
        int hits;

        hits = armortype * 100;
        if (armorpoints[0] >= hits)
            return false; // don't pick up

        this.armortype = armortype;
        armorpoints[0] = hits;

        return true;
    }

    /**
     * P_GiveCard
     */
    
    public void GiveCard(card_t crd) {
        int card = crd.ordinal();
        if (cards[card])
            return;

        bonuscount = BONUSADD;
        cards[card] = true;
    }

    //
    // P_GivePower
    //
    public boolean GivePower( int /* powertype_t */power) // MAES:
                                                                          // I
                                                                          // didn't
                                                                          // change
                                                                          // this!
    {
        if (power == pw_invulnerability) {
            powers[power] = INVULNTICS;
            return true;
        }

        if (power == pw_invisibility) {
            powers[power] = INVISTICS;
            mo.flags |= MF_SHADOW;
            return true;
        }

        if (power == pw_infrared) {
            powers[power] = INFRATICS;
            return true;
        }

        if (power == pw_ironfeet) {
            powers[power] = IRONTICS;
            return true;
        }

        if (power == pw_strength) {
            GiveBody(100);
            powers[power] = 1;
            return true;
        }

        if (powers[power] != 0)
            return false; // already got it

        powers[power] = 1;
        return true;
    }
 
    /**
     * G_PlayerFinishLevel
     * Called when a player completes a level.
     */

    public final void PlayerFinishLevel () 
    { 
        Arrays.fill(powers, 0);
        Arrays.fill(cards,false);       
        mo.flags &= ~mobj_t.MF_SHADOW;     // cancel invisibility 
        extralight = 0;          // cancel gun flashes 
        fixedcolormap = 0;       // cancel ir gogles 
        damagecount = 0;         // no palette changes 
        bonuscount = 0;
        lookdir = 0; // From heretic
    } 
    

    /**
     * P_PlayerInSpecialSector
     * Called every tic frame
     *  that the player origin is in a special sector
     */
    
    protected void PlayerInSpecialSector ()
    {
        sector_t   sector;
        
        sector = mo.subsector.sector;

        // Falling, not all the way down yet?
        if (mo.z != sector.floorheight)
        return; 

        // Has hitten ground.
        switch (sector.special)
        {
          case 5:
        // HELLSLIME DAMAGE
        if (powers[pw_ironfeet]==0)
            if (!flags(DS.leveltime,0x1f))
                P.DamageMobj (mo,null, null, 10);
        break;
        
          case 7:
        // NUKAGE DAMAGE
        if (powers[pw_ironfeet]==0)
            if (!flags(DS.leveltime,0x1f))
                P.DamageMobj (mo, null, null, 5);
        break;
        
          case 16:
        // SUPER HELLSLIME DAMAGE
          case 4:
        // STROBE HURT
        if (!eval(powers[pw_ironfeet])
            || (RND.P_Random()<5) )
        {
            if (!flags(DS.leveltime,0x1f))
            P.DamageMobj (mo, null, null, 20);
        }
        break;
                
          case 9:
        // SECRET SECTOR
        secretcount++;
        sector.special = 0;
        break;
                
          case 11:
        // EXIT SUPER DAMAGE! (for E1M8 finale)
        cheats &= ~CF_GODMODE;

        if (!flags(DS.leveltime,0x1f))
            P.DamageMobj (mo, null, null, 20);

        if (health[0] <= 10)
            DG.ExitLevel();
        break;
                
          default:
        I.Error ("P_PlayerInSpecialSector: unknown special %d", sector.special);
        break;
        };
    }    
   
 // Index of the special effects (INVUL inverse) map.
public static final int INVERSECOLORMAP		=LightsAndColors.LIGHTLEVELS;
    

 
 //
//P_CalcHeight
//Calculate the walking / running height adjustment
//
public void CalcHeight () 
{
  int     angle;
  int bob; // fixed
  
  // Regular movement bobbing
  // (needs to be calculated for gun swing
  // even if not on ground)
  // OPTIMIZE: tablify angle
  // Note: a LUT allows for effects
  //  like a ramp with low health.
  this.bob =
  FixedMul (mo.momx, mo.momx)
  + FixedMul (mo.momy,mo.momy);
  
  this.bob >>= 2;

        
  if (this.bob>MAXBOB)
	  this.bob = MAXBOB;

  
  
  if (flags(cheats ,CF_NOMOMENTUM) || !onground)
  {
  viewz = mo.z + VIEWHEIGHT;

  if (viewz > mo.ceilingz-4*FRACUNIT)
      viewz = mo.ceilingz-4*FRACUNIT;

  viewz = mo.z + viewheight;
  return;
  }
      
  angle = (FINEANGLES/20*DS.leveltime)&FINEMASK;
  bob = FixedMul ( this.bob/2, finesine[angle]);

  
  // move viewheight
  if (playerstate == PST_LIVE)
  {
  viewheight += deltaviewheight;

  if (viewheight > VIEWHEIGHT)
  {
      viewheight = VIEWHEIGHT;
      deltaviewheight = 0;
  }

  if (viewheight < VIEWHEIGHT/2)
  {
      viewheight = VIEWHEIGHT/2;
      if (deltaviewheight <= 0)
      deltaviewheight = 1;
  }
  
  if (deltaviewheight!=0)    
  {
      deltaviewheight += FRACUNIT/4;
      if (deltaviewheight==0)
      deltaviewheight = 1;
  }
  }
  viewz = mo.z + viewheight + bob;

  if (viewz > mo.ceilingz-4*FRACUNIT)
  viewz = mo.ceilingz-4*FRACUNIT;
} 
 
 private static final long ANG5   = (ANG90/18);

 /**
  * P_DeathThink
  * Fall on your face when dying.
  * Decrease POV height to floor height.
  * 
  * DOOMGUY IS SO AWESOME THAT HE THINKS EVEN WHEN DEAD!!!
  * 
  */

 
public void DeathThink ()
{
  long     angle; //angle_t
  long     delta;

  MovePsprites ();
  
  // fall to the ground
  if (viewheight > 6*FRACUNIT)
  viewheight -= FRACUNIT;

  if (viewheight < 6*FRACUNIT)
  viewheight = 6*FRACUNIT;

  deltaviewheight = 0;
  onground = (mo.z <= mo.floorz);
  CalcHeight ();
  
  if (attacker!=null && attacker != mo)
  {
  angle = R.PointToAngle2 (mo.x,
               mo.y,
               attacker.x,
               attacker.y);
  
  delta = Tables.addAngles(angle, - mo.angle);
  
  if (delta < ANG5 || delta > -ANG5)
  {
      // Looking at killer,
      //  so fade damage flash down.
      mo.angle = angle;

      if (damagecount!=0)
      damagecount--;
  }
  else if (delta < ANG180)
      mo.angle += ANG5;
  else
      mo.angle -= ANG5;
  }
  else if (damagecount!=0)
  damagecount--;
  

  if (flags(cmd.buttons ,BT_USE))
  playerstate = PST_REBORN;
}
 
//
// P_MovePsprites
// Called every tic by player thinking routine.
//
public void MovePsprites () 
{

    pspdef_t    psp;
    @SuppressWarnings("unused") // Shut up compiler
	state_t state=null;    
 
    for (int i=0 ; i<NUMPSPRITES ; i++)
    {
    	   psp = psprites[i];
    // a null state means not active
    if ( (state = psp.state)!=null )  
    {
        // drop tic count and possibly change state

        // a -1 tic count never changes
        if (psp.tics != -1) 
        {
        psp.tics--;
        if (!eval(psp.tics))
            this.SetPsprite (i, psp.state.nextstate);
        }               
    }
    }
    
    psprites[ps_flash].sx = psprites[ps_weapon].sx;
    psprites[ps_flash].sy = psprites[ps_weapon].sy;
}

/**
/* P_SetPsprite
*/

public void
SetPsprite
( int       position,
  statenum_t    newstate ) 
{
    pspdef_t    psp;
    state_t state;
    
    psp = psprites[position];
    
    do
    {
    if (newstate==null)
    {
        // object removed itself
        psp.state = null;
        break;  
    }
    
    state = states[newstate.ordinal()];
    psp.state = state;
    psp.tics = (int) state.tics;    // could be 0

    if (state.misc1!=0)
    {
        // coordinate set
        psp.sx = (int) (state.misc1 << FRACBITS);
        psp.sy = (int) (state.misc2 << FRACBITS);
    }
    
    // Call action routine.
    // Modified handling.
    if (state.acp2!=null)
    {
        state.acp2.invoke(this, psp);
        if (psp.state==null)
        break;
    }
    
    newstate = psp.state.nextstate;
    
    } while (psp.tics==0);
    // an initial state of 0 could cycle through
}


    /** Accessory method to identify which "doomguy" we are.
     *  Because we can't use the [target.player-players] syntax
     *  in order to get an array index, in Java.
     *  
     *  If -1 is returned, then we have existential problems.
     *
     */    
    
    public int identify(){
        
        
        if (id>=0) return id;
        int i;    
        // Let's assume that we know jack.
            for (i=0;i<DS.players.length;i++)
                if (this==DS.players[i]) break;
            
            return id=i;
        
    }
    
    private int id=-1;
    
    private boolean onground;


        	
    /* psprnum_t enum */
    public static int ps_weapon=0,
        ps_flash=1,
        NUMPSPRITES=2;  
    
    public static int  LOWERSPEED  =   MAPFRACUNIT*6;
    public static int  RAISESPEED  =   MAPFRACUNIT*6;

    public static int  WEAPONBOTTOM    =128*FRACUNIT;
    public static int  WEAPONTOP       =32*FRACUNIT;


    // plasma cells for a bfg attack
    private static int  BFGCELLS        =40;        


    /*
     P_SetPsprite
    
    
    public void
    SetPsprite
    ( player_t  player,
      int       position,
      statenum_t    newstate ) 
    {
        pspdef_t    psp;
        state_t state;
        
        psp = psprites[position];
        
        do
        {
        if (newstate==null)
        {
            // object removed itself
            psp.state = null;
            break;  
        }
        
        state = states[newstate.ordinal()];
        psp.state = state;
        psp.tics = (int) state.tics;    // could be 0

        if (state.misc1!=0)
        {
            // coordinate set
            psp.sx = (int) (state.misc1 << FRACBITS);
            psp.sy = (int) (state.misc2 << FRACBITS);
        }
        
        // Call action routine.
        // Modified handling.
        if (state.action.getType()==acp2)
        {
            P.A.dispatch(state.action,this, psp);
            if (psp.state==null)
            break;
        }
        
        newstate = psp.state.nextstate;
        
        } while (psp.tics==0);
        // an initial state of 0 could cycle through
    }
    */
    
    /** fixed_t */
    int     swingx, swingy;

    /**P_CalcSwing
     * 
     * @param player
     */
    public void CalcSwing (player_t   player)
    {
        int swing; // fixed_t
        int     angle;
        
        // OPTIMIZE: tablify this.
        // A LUT would allow for different modes,
        //  and add flexibility.

        swing = this.bob;

        angle = (FINEANGLES/70*DS.leveltime)&FINEMASK;
        swingx = FixedMul ( swing, finesine[angle]);

        angle = (FINEANGLES/70*DS.leveltime+FINEANGLES/2)&FINEMASK;
        swingy = -FixedMul ( swingx, finesine[angle]);
    }



    //
    // P_BringUpWeapon
    // Starts bringing the pending weapon up
    // from the bottom of the screen.
    // Uses player
    //
    public void BringUpWeapon ()
    {
        statenum_t  newstate=statenum_t.S_NULL;
        
        if (pendingweapon == weapontype_t.wp_nochange)
        pendingweapon = readyweapon;
            
        if (pendingweapon == weapontype_t.wp_chainsaw)
        S.StartSound (mo, sfxenum_t.sfx_sawup);
            
        newstate = weaponinfo[pendingweapon.ordinal()].upstate;

        pendingweapon = weapontype_t.wp_nochange;
        psprites[ps_weapon].sy = WEAPONBOTTOM;

        this.SetPsprite ( ps_weapon, newstate);
    }

    /**
     * P_CheckAmmo
     * Returns true if there is enough ammo to shoot.
     * If not, selects the next weapon to use.
     */
    
    public boolean CheckAmmo ()
    {
        ammotype_t      ammo;
        int         count;

        ammo = weaponinfo[readyweapon.ordinal()].ammo;

        // Minimal amount for one shot varies.
        if (readyweapon == weapontype_t.wp_bfg)
        count = BFGCELLS;
        else if (readyweapon == weapontype_t.wp_supershotgun)
        count = 2;  // Double barrel.
        else
        count = 1;  // Regular.

        // Some do not need ammunition anyway.
        // Return if current ammunition sufficient.
        if (ammo == ammotype_t.am_noammo || this.ammo[ammo.ordinal()] >= count)
        return true;
            
        // Out of ammo, pick a weapon to change to.
        // Preferences are set here.
        do
        {
        if (weaponowned[weapontype_t.wp_plasma.ordinal()]
            && (this.ammo[ammotype_t.am_cell.ordinal()]!=0)
            && !DS.isShareware() )
        {
            pendingweapon = weapontype_t.wp_plasma;
        }
        else if (weaponowned[weapontype_t.wp_supershotgun.ordinal()] 
             && this.ammo[ammotype_t.am_shell.ordinal()]>2
             && DS.isCommercial() )
        {
            pendingweapon = weapontype_t.wp_supershotgun;
        }
        else if (weaponowned[weapontype_t.wp_chaingun.ordinal()]
             && this.ammo[ammotype_t.am_clip.ordinal()]!=0)
        {
            pendingweapon = weapontype_t.wp_chaingun;
        }
        else if (weaponowned[weapontype_t.wp_shotgun.ordinal()]
             && this.ammo[ammotype_t.am_shell.ordinal()]!=0)
        {
            pendingweapon = weapontype_t.wp_shotgun;
        }
        else if (this.ammo[ammotype_t.am_clip.ordinal()]!=0)
        {
            pendingweapon = weapontype_t.wp_pistol;
        }
        else if (weaponowned[weapontype_t.wp_chainsaw.ordinal()])
        {
            pendingweapon = weapontype_t.wp_chainsaw;
        }
        else if (weaponowned[weapontype_t.wp_missile.ordinal()]
             && this.ammo[ammotype_t.am_misl.ordinal()]!=0)
        {
            pendingweapon = weapontype_t.wp_missile;
        }
        else if (weaponowned[weapontype_t.wp_bfg.ordinal()]
             && this.ammo[ammotype_t.am_cell.ordinal()]>40
             && !DS.isShareware() )
        {
            pendingweapon = weapontype_t.wp_bfg;
        }
        else
        {
            // If everything fails.
            pendingweapon = weapontype_t.wp_fist;
        }
        
        } while (pendingweapon == weapontype_t.wp_nochange);

        // Now set appropriate weapon overlay.
        this.SetPsprite (
              ps_weapon,
              weaponinfo[readyweapon.ordinal()].downstate);

        return false;   
    }


    /**
     * P_DropWeapon
     * Player died, so put the weapon away.
     */
    
    public void DropWeapon ()
    {
        this.SetPsprite (
              ps_weapon,
              weaponinfo[readyweapon.ordinal()].downstate);
    }

    /**
     * P_SetupPsprites
     * Called at start of level for each 
     */
    
    public void SetupPsprites () 
    {
        int i;
        
        // remove all psprites
        for (i=0 ; i<NUMPSPRITES ; i++)
        psprites[i].state = null;
            
        // spawn the gun
        pendingweapon = readyweapon;
        BringUpWeapon ();
    }
    
    /**
     *  P_PlayerThink
     */
    public void PlayerThink (player_t player)
    {
     ticcmd_t       cmd;
     weapontype_t    newweapon;
     
     // fixme: do this in the cheat code
     if (flags(player.cheats , player_t.CF_NOCLIP))
     player.mo.flags |= MF_NOCLIP;
     else
     player.mo.flags &= ~MF_NOCLIP;
     
     // chain saw run forward
     cmd = player.cmd;
     if (flags(player.mo.flags , MF_JUSTATTACKED))
     {
     cmd.angleturn = 0;
     cmd.forwardmove = (0xc800/512);
     cmd.sidemove = 0;
     player.mo.flags &= ~MF_JUSTATTACKED;
     }
             
     
     if (player.playerstate == PST_DEAD)
     {
     player.DeathThink ();
     return;
     }
     
     // Move around.
     // Reactiontime is used to prevent movement
     //  for a bit after a teleport.
     if (eval(player.mo.reactiontime))
     player.mo.reactiontime--;
     else
         player.MovePlayer ();
     
         player.CalcHeight ();

     if (eval(player.mo.subsector.sector.special))
         player.PlayerInSpecialSector ();
     
     // Check for weapon change.

     // A special event has no other buttons.
     if (flags(cmd.buttons , BT_SPECIAL))
     cmd.buttons = 0;           
         
     if (flags(cmd.buttons , BT_CHANGE))
     {
     // The actual changing of the weapon is done
     //  when the weapon psprite can do it
     //  (read: not in the middle of an attack).
    // System.out.println("Weapon change detected, attempting to perform");
         
     newweapon = weapontype_t.values()[(cmd.buttons&BT_WEAPONMASK)>>BT_WEAPONSHIFT];

     // If chainsaw is available, it won't change back to the fist 
     // unless player also has berserk.
     if (newweapon == weapontype_t.wp_fist
         && player.weaponowned[weapontype_t.wp_chainsaw.ordinal()]
         && !(player.readyweapon == weapontype_t.wp_chainsaw
          && eval(player.powers[pw_strength])))
     {
         newweapon = weapontype_t.wp_chainsaw;
     }
     
     // Will switch between SG and SSG in Doom 2.
     
     if ( DS.isCommercial()
         && newweapon == weapontype_t.wp_shotgun 
         && player.weaponowned[weapontype_t.wp_supershotgun.ordinal()]
         && player.readyweapon != weapontype_t.wp_supershotgun)
     {
         newweapon = weapontype_t.wp_supershotgun;
     }
     

     if (player.weaponowned[newweapon.ordinal()]
         && newweapon != player.readyweapon)
     {
         // Do not go to plasma or BFG in shareware,
         //  even if cheated.
         if ((newweapon != weapontype_t.wp_plasma
          && newweapon != weapontype_t.wp_bfg)
         || !DS.isShareware() )
         {
         player.pendingweapon = newweapon;
         }
     }
     }
     
     // check for use
     if (flags(cmd.buttons , BT_USE))
     {
     if (!player.usedown)
     {
         P.UseLines (player);
         player.usedown = true;
     }
     }
     else
     player.usedown = false;
     
     // cycle psprites
     player.MovePsprites ();
     
     // Counters, time dependent power ups.

     // Strength counts up to diminish fade.
     if (eval(player.powers[pw_strength]))
     player.powers[pw_strength]++;  
         
     if (eval(player.powers[pw_invulnerability]))
     player.powers[pw_invulnerability]--;

     if (eval(player.powers[pw_invisibility]))
     if (! eval(--player.powers[pw_invisibility]) )
         player.mo.flags &= ~MF_SHADOW;
             
     if (eval(player.powers[pw_infrared]))
     player.powers[pw_infrared]--;
         
     if (eval(player.powers[pw_ironfeet]))
     player.powers[pw_ironfeet]--;
         
     if (eval(player.damagecount))
     player.damagecount--;
         
     if (eval(player.bonuscount))
     player.bonuscount--;

     
     // Handling colormaps.
     if (eval(player.powers[pw_invulnerability]))
     {
     if (player.powers[pw_invulnerability] > 4*32
         || flags(player.powers[pw_invulnerability],8) )
         player.fixedcolormap = player_t.INVERSECOLORMAP;
     else
         player.fixedcolormap = 0;
     }
     else if (eval(player.powers[pw_infrared]))
     {
     if (player.powers[pw_infrared] > 4*32
         || flags(player.powers[pw_infrared],8) )
     {
         // almost full bright
         player.fixedcolormap = 1;
     }
     else
         player.fixedcolormap = 0;
     }
     else
     player.fixedcolormap = 0;
    }

    /**
     * G_PlayerReborn
     * Called after a player dies 
     * almost everything is cleared and initialized 
     *
     *
     */

    public void PlayerReborn () 
    { 
        int     i; 
        int[]     frags=new int [MAXPLAYERS]; 
        int     killcount;
        int     itemcount;
        int     secretcount; 

        // System.arraycopy(players[player].frags, 0, frags, 0, frags.length);
        // We save the player's frags here...
        C2JUtils.memcpy (frags,this.frags,frags.length); 
        killcount = this.killcount; 
        itemcount = this.itemcount; 
        secretcount = this.secretcount; 

        //MAES: we need to simulate an erasure, possibly without making
        // a new object.memset (p, 0, sizeof(*p));
        //players[player]=(player_t) player_t.nullplayer.clone();
        // players[player]=new player_t();
        this.reset();

        // And we copy the old frags into the "new" player. 
        C2JUtils.memcpy(this.frags, frags, this.frags.length); 

        this.killcount = killcount; 
        this.itemcount = itemcount; 
        this.secretcount = secretcount; 

        usedown = attackdown = true;  // don't do anything immediately 
        playerstate = PST_LIVE;       
        health[0] = MAXHEALTH; 
        readyweapon = pendingweapon = weapontype_t.wp_pistol; 
        weaponowned[weapontype_t.wp_fist.ordinal()] = true; 
        weaponowned[weapontype_t.wp_pistol.ordinal()] = true;        
        ammo[ammotype_t.am_clip.ordinal()] = 50; 
        lookdir = 0; // From Heretic

        for (i=0 ; i<NUMAMMO ; i++) 
        	this.maxammo[i] = DoomStatus.maxammo[i]; 

    }

    
    /** Called by Actions ticker */
    public void PlayerThink() {
        PlayerThink(this);       
    }

	@Override
	public void updateStatus(DoomStatus DS) {
	    this.DS=DS;
	    this.DG=DS.DG;
	    this.P=DS.P;
	    this.R=DS.R;
	    this.RND=DS.RND;
	    this.I=DS.I;
	    this.S=DS.S;
	}

	public String toString(){
		sb.setLength(0);
		sb.append("player");
		sb.append(" momx ");
		sb.append(this.mo.momx);
		sb.append(" momy ");
		sb.append(this.mo.momy);
		sb.append(" x ");
		sb.append(this.mo.x);
		sb.append(" y ");
		sb.append(this.mo.y);
		return sb.toString();
	}

	private static StringBuilder sb=new StringBuilder();

    public void read(DataInputStream f) throws IOException{

            // Careful when loading/saving:
            // A player only carries a pointer to a mobj, which is "saved"
            // but later discarded at load time, at least in vanilla. In any case,
           // it has the size of a 32-bit integer, so make sure you skip it.
           // TODO: OK, so vanilla's monsters lost "state" when saved, including non-Doomguy
            //  infighting. Did they "remember" Doomguy too?
            // ANSWER: they didn't.
        
            // The player is special in that it unambigously allows identifying
            // its own map object in an absolute way. Once we identify
            // at least one (e.g. object #45 is pointer 0x43545345) then, since
            // map objects are stored in a nice serialized order.
            this.p_mobj= DoomIO.readLEInt(f); // player mobj pointer
            
            this.playerstate=DoomIO.readLEInt(f);
            this.cmd.read(f);
            this.viewz=DoomIO.readLEInt(f);
            this.viewheight= DoomIO.readLEInt(f);
            this.deltaviewheight= DoomIO.readLEInt(f);
            this.bob=DoomIO.readLEInt(f);
            this.health[0]=DoomIO.readLEInt(f);
            this.armorpoints[0]=DoomIO.readLEInt(f); 
            this.armortype=DoomIO.readLEInt(f); 
            DoomIO.readIntArray(f,this.powers, ByteOrder.LITTLE_ENDIAN); 
            DoomIO.readBooleanIntArray(f,this.cards);
            this.backpack=DoomIO.readIntBoolean(f);
            DoomIO.readIntArray(f,frags, ByteOrder.LITTLE_ENDIAN);
            this.readyweapon=weapontype_t.values()[DoomIO.readLEInt(f)];
            this.pendingweapon=weapontype_t.values()[DoomIO.readLEInt(f)];
            DoomIO.readBooleanIntArray(f,this.weaponowned);
            DoomIO.readIntArray(f,ammo,ByteOrder.LITTLE_ENDIAN);
            DoomIO.readIntArray(f,maxammo,ByteOrder.LITTLE_ENDIAN);
            // Read these as "int booleans"
            this.attackdown=DoomIO.readIntBoolean(f);
            this.usedown=DoomIO.readIntBoolean(f);
            this.cheats=DoomIO.readLEInt(f);
            this.refire=DoomIO.readLEInt(f);
            // For intermission stats.
            this.killcount=DoomIO.readLEInt(f);
            this.itemcount=DoomIO.readLEInt(f);
            this.secretcount=DoomIO.readLEInt(f);
            // Hint messages.
            f.skipBytes(4);
            // For screen flashing (red or bright).
            this.damagecount=DoomIO.readLEInt(f);
            this.bonuscount=DoomIO.readLEInt(f);
            // Who did damage (NULL for floors/ceilings).
            // TODO: must be properly denormalized before saving/loading
            f.skipBytes(4); // TODO: waste a read for attacker mobj.
            // So gun flashes light up areas.
            this.extralight=DoomIO.readLEInt(f);
            // Current PLAYPAL, ???
            //  can be set to REDCOLORMAP for pain, etc.
            this.fixedcolormap=DoomIO.readLEInt(f);
            this.colormap=DoomIO.readLEInt(f);
            // PSPDEF _is_ readable.
            for (pspdef_t p: this.psprites)
                p.read(f);
            this.didsecret=DoomIO.readIntBoolean(f);
            // Total size should be 280 bytes.
        }
    
    public void write(DataOutputStream f) throws IOException{

        // It's much more convenient to pre-buffer, since
        // we'll be writing all Little Endian stuff.
        ByteBuffer b=ByteBuffer.allocate(280);
        this.pack(b);
        // Total size should be 280 bytes.
        // Write everything nicely and at once.        
        f.write(b.array());
    }
    
    // Used to disambiguate between objects
    public int p_mobj;

    @Override
    public void pack(ByteBuffer buf)
            throws IOException {
        
        ByteOrder bo=ByteOrder.LITTLE_ENDIAN;
        buf.order(bo);
        // The player is special in that it unambiguously allows identifying
        // its own map object in an absolute way. Once we identify
        // at least one (e.g. object #45 is pointer 0x43545345) then, since
        // map objects are stored in a nice serialized order by using
        // their next/prev pointers, you can reconstruct their
        // relationships a posteriori.
        // Store our own hashcode or "pointer" if you wish.
        buf.putInt(pointer(mo));
        buf.putInt(playerstate);
        cmd.pack(buf);
        buf.putInt(viewz);        
        buf.putInt(viewheight);
        buf.putInt(deltaviewheight);
        buf.putInt(bob);
        buf.putInt(health[0]);
        buf.putInt(armorpoints[0]); 
        buf.putInt(armortype);
        DoomBuffer.putIntArray(buf,this.powers,this.powers.length,bo); 
        DoomBuffer.putBooleanIntArray(buf,this.cards,this.cards.length, bo);
        DoomBuffer.putBooleanInt(buf,backpack,bo);
        DoomBuffer.putIntArray(buf,this.frags,this.frags.length,bo);
        buf.putInt(readyweapon.ordinal());
        buf.putInt(pendingweapon.ordinal());
        DoomBuffer.putBooleanIntArray(buf,this.weaponowned,this.weaponowned.length, bo);
        DoomBuffer.putIntArray(buf,this.ammo,this.ammo.length, bo);
        DoomBuffer.putIntArray(buf,this.maxammo,this.maxammo.length, bo);
        // Read these as "int booleans"
        DoomBuffer.putBooleanInt(buf,attackdown,bo);
        DoomBuffer.putBooleanInt(buf,usedown,bo);        
        buf.putInt(cheats);
        buf.putInt(refire);
        // For intermission stats.
        buf.putInt(this.killcount);
        buf.putInt(this.itemcount);
        buf.putInt(this.secretcount);
        // Hint messages.
        buf.putInt(0);
        // For screen flashing (red or bright).
        buf.putInt(this.damagecount);
        buf.putInt(this.bonuscount);
        // Who did damage (NULL for floors/ceilings).
        // TODO: must be properly denormalized before saving/loading
        buf.putInt(pointer(attacker));
        // So gun flashes light up areas.
        buf.putInt(this.extralight);
        // Current PLAYPAL, ???
        //  can be set to REDCOLORMAP for pain, etc.
        buf.putInt(this.fixedcolormap);
        buf.putInt(this.colormap);
        // PSPDEF _is_ readable.
        for (pspdef_t p: this.psprites)
            p.pack(buf);
        buf.putInt(this.didsecret?1:0);
        
    }
        
    }
package doom;

/** killough 8/29/98: threads of thinkers, for more efficient searches
 * cph 2002/01/13: for consistency with the main thinker list, keep objects
 * pending deletion on a class list too
 */


public enum th_class {
      th_delete,
      th_misc,
      th_friends,
      th_enemies,
      th_all;
      
      public static final int NUMTHCLASS=th_class.values().length;
}

package doom;

import java.nio.ByteBuffer;

import utils.C2JUtils;
import w.DoomBuffer;

public class doomdata_t implements IDatagramSerializable {

    public static final int DOOMDATALEN=8+data.Defines.BACKUPTICS*ticcmd_t.TICCMDLEN;
    
     // High bit is retransmit request.
     /** MAES: was "unsigned" */
     public int        checksum; 
     
     /* CAREFUL!!! Those "bytes" are actually unsigned
      * 
      */
     
     /** Only valid if NCMD_RETRANSMIT. */     
     public byte        retransmitfrom;
     
     public byte        starttic;
     public byte        player;
     public byte        numtics;
     public ticcmd_t[]        cmds;
     
    public doomdata_t(){
        cmds=new ticcmd_t[data.Defines.BACKUPTICS];
        C2JUtils.initArrayOfObjects(cmds);
        // Enough space for its own header + the ticcmds;
        buffer=new byte[DOOMDATALEN];
        // This "pegs" the ByteBuffer to this particular array.
        // Separate updates are not necessary.
        bbuf=ByteBuffer.wrap(buffer);
    }
    
    // Used for datagram serialization.
    private byte[] buffer;
    private ByteBuffer bbuf;

    
    
    @Override
    public byte[] pack() {        
        
        bbuf.rewind();
        
        // Why making it harder?
        bbuf.putInt(checksum);
        bbuf.put(retransmitfrom);
        bbuf.put(starttic);
        bbuf.put(player);
        bbuf.put(numtics);
        
        // FIXME: it's probably more efficient to use System.arraycopy ? 
        // Or are the packets too small anyway? At most we'll be sending "doomdata_t's"
        
        for (int i=0;i<cmds.length;i++){
            bbuf.put(cmds[i].pack());
        }
        
        return bbuf.array();
    
    }

    @Override
    public void pack(byte[] buf, int offset) {
        
        // No need to make it harder...just pack it and slap it in.
        byte[] tmp=this.pack();
        System.arraycopy(tmp, 0, buf, offset, tmp.length);        
    }

    @Override
    public void unpack(byte[] buf) {
        unpack(buf,0);
    }

    @Override
    public void unpack(byte[] buf, int offset) {
        checksum=DoomBuffer.getBEInt(buf);
        offset=+4;
        retransmitfrom=buf[offset++];
        starttic=buf[offset++];
        player=buf[offset++];
        numtics=buf[offset++];
        
        for (int i=0;i<cmds.length;i++){
            cmds[i].unpack(buf,offset);
            offset+=ticcmd_t.TICCMDLEN;
            }
        
    }
    
    public void selfUnpack(){
        unpack(this.buffer);
    }
    
    public void copyFrom(doomdata_t source) {        
        this.checksum=source.checksum;
        this.numtics=source.numtics;
        this.player=source.player;
        this.retransmitfrom=source.retransmitfrom;
        this.starttic=source.starttic;
        
        // MAES: this was buggy as hell, and didn't work at all, which
        // in turn prevented other subsystems such as speed throttling and
        // networking to work.
        // 
        // This should be enough to alter the ByteBuffer too.
        //System.arraycopy(source.cached(), 0, this.buffer, 0, DOOMDATALEN);
        // This should set all fields
        //selfUnpack();        
        }
    
    @Override
     public byte[] cached(){
         return this.buffer;
     }
    
    StringBuilder sb=new StringBuilder();
    public String toString(){
        sb.setLength(0);
        sb.append("doomdata_t ");
        sb.append(retransmitfrom);
        sb.append(" starttic ");
        sb.append(starttic);
        sb.append(" player ");
        sb.append(player);
        sb.append(" numtics ");
        sb.append(numtics);
        return sb.toString();
        
    }

 }

package doom;
// Emacs style mode select   -*- C++ -*- 
//-----------------------------------------------------------------------------
//
// $Id: net.java,v 1.5 2011/02/11 00:11:13 velktron Exp $
//
// Copyright (C) 1993-1996 by id Software, Inc.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// $Log: net.java,v $
// Revision 1.5  2011/02/11 00:11:13  velktron
// A MUCH needed update to v1.3.
//
// Revision 1.1  2010/06/30 08:58:50  velktron
// Let's see if this stuff will finally commit....
//
//
// Most stuff is still  being worked on. For a good place to start and get an idea of what is being done, I suggest checking out the "testers" package.
//
// Revision 1.1  2010/06/29 11:07:34  velktron
// Release often, release early they say...
//
// Commiting ALL stuff done so far. A lot of stuff is still broken/incomplete, and there's still mixed C code in there. I suggest you load everything up in Eclpise and see what gives from there.
//
// A good place to start is the testers/ directory, where you  can get an idea of how a few of the implemented stuff works.
//
//
// DESCRIPTION:
//	DOOM Network game communication and protocol,
//	all OS independend parts.
//
//-----------------------------------------------------------------------------


//static const char rcsid[] = "$Id: net.java,v 1.5 2011/02/11 00:11:13 velktron Exp $";


//#include "m_menu.h"
//#include "i_system.h"
//#include "i_video.h"
//#include "i_net.h"
//#include "g_game.h"
import static data.Defines.*;
import data.doomstat.*;

//
//Network play related stuff.
//There is a data struct that stores network
//communication related stuff, and another
//one that defines the actual packets to
//be transmitted.
//


public class net{

protected static int	NCMD_EXIT=		0x80000000;
protected static int   	NCMD_RETRANSMIT		=0x40000000;
protected static int   	NCMD_SETUP		=0x20000000;
protected static int   NCMD_KILL	=	0x10000000;	// kill game
protected static int   	NCMD_CHECKSUM	= 	0x0fffffff;

protected static int  DOOMCOM_ID =     0x12345678;

//Max computers/players in a game.
protected static int    MAXNETNODES   =  8;


//Networking and tick handling related.
protected static int    BACKUPTICS     = 12;


// commant_t
protected  static int CMD_SEND    = 1;
protected  static int CMD_GET = 2; 

doomcom_t	doomcom;	
doomdata_t	netbuffer;		// points inside doomcom


//
// NETWORKING
//
// gametic is the tic about to (or currently being) run
// maketic is the tick that hasn't had control made for it yet
// nettics[] has the maketics for all players 
//
// a gametic cannot be run until nettics[] > gametic for all players
//
public static int	RESENDCOUNT	=10;
public static int	PL_DRONE	=0x80;	// bit flag in doomdata->player

ticcmd_t[]	localcmds= new ticcmd_t[BACKUPTICS];

ticcmd_t [][]       netcmds=new ticcmd_t [MAXPLAYERS][BACKUPTICS];
int[]         	nettics=new int[MAXNETNODES];
boolean[]		nodeingame=new boolean[MAXNETNODES];		// set false as nodes leave game
boolean[]		remoteresend=new boolean[MAXNETNODES];		// set when local needs tics
int[]		resendto=new int[MAXNETNODES];			// set when remote needs tics
int[]		resendcount=new int[MAXNETNODES];

int[]		nodeforplayer=new int[MAXPLAYERS];

int             maketic;
int		lastnettic;
int		skiptics;
int		ticdup;		
int		maxsend;	// BACKUPTICS/(2*ticdup)-1


//void D_ProcessEvents (void); 
//void G_BuildTiccmd (ticcmd_t *cmd); 
//void D_DoAdvanceDemo (void);
 
boolean		reboundpacket;
doomdata_t	reboundstore;



// 
//
//123

/** MAES: interesting. After testing it was found to return the following size:
 * 
 */

int NetbufferSize ()
{
//    return (int)(((doomdata_t)0).cmds[netbuffer.numtics]);
    return (8*(netbuffer.numtics+1));
}

}
/*

//1
// Checksum 
//
unsigned NetbufferChecksum (void)
{
    unsigned		c;
    int		i,l;

    c = 0x1234567;

    // FIXME -endianess?
#ifdef NORMALUNIX
    return 0;			// byte order problems
#endif

    l = (NetbufferSize () - (int)&(((doomdata_t *)0)->retransmitfrom))/4;
    for (i=0 ; i<l ; i++)
	c += ((unsigned *)&netbuffer->retransmitfrom)[i] * (i+1);

    return c & NCMD_CHECKSUM;
}

//
//
//
int ExpandTics (int low)
{
    int	delta;
	
    delta = low - (maketic&0xff);
	
    if (delta >= -64 && delta <= 64)
	return (maketic&~0xff) + low;
    if (delta > 64)
	return (maketic&~0xff) - 256 + low;
    if (delta < -64)
	return (maketic&~0xff) + 256 + low;
		
    I_Error ("ExpandTics: strange value %i at maketic %i",low,maketic);
    return 0;
}



//
// HSendPacket
//
void
HSendPacket
 (int	node,
  int	flags )
{
    netbuffer->checksum = NetbufferChecksum () | flags;

    if (!node)
    {
	reboundstore = *netbuffer;
	reboundpacket = true;
	return;
    }

    if (demoplayback)
	return;

    if (!netgame)
	I_Error ("Tried to transmit to another node");
		
    doomcom->command = CMD_SEND;
    doomcom->remotenode = node;
    doomcom->datalength = NetbufferSize ();
	
    if (debugfile)
    {
	int		i;
	int		realretrans;
	if (netbuffer->checksum & NCMD_RETRANSMIT)
	    realretrans = ExpandTics (netbuffer->retransmitfrom);
	else
	    realretrans = -1;

	fprintf (debugfile,"send (%i + %i, R %i) [%i] ",
		 ExpandTics(netbuffer->starttic),
		 netbuffer->numtics, realretrans, doomcom->datalength);
	
	for (i=0 ; i<doomcom->datalength ; i++)
	    fprintf (debugfile,"%i ",((byte *)netbuffer)[i]);

	fprintf (debugfile,"\n");
    }

    I_NetCmd ();
}

//
// HGetPacket
// Returns false if no packet is waiting
//
boolean HGetPacket (void)
{	
    if (reboundpacket)
    {
	*netbuffer = reboundstore;
	doomcom->remotenode = 0;
	reboundpacket = false;
	return true;
    }

    if (!netgame)
	return false;

    if (demoplayback)
	return false;
		
    doomcom->command = CMD_GET;
    I_NetCmd ();
    
    if (doomcom->remotenode == -1)
	return false;

    if (doomcom->datalength != NetbufferSize ())
    {
	if (debugfile)
	    fprintf (debugfile,"bad packet length %i\n",doomcom->datalength);
	return false;
    }
	
    if (NetbufferChecksum () != (netbuffer->checksum&NCMD_CHECKSUM) )
    {
	if (debugfile)
	    fprintf (debugfile,"bad packet checksum\n");
	return false;
    }

    if (debugfile)
    {
	int		realretrans;
	int	i;
			
	if (netbuffer->checksum & NCMD_SETUP)
	    fprintf (debugfile,"setup packet\n");
	else
	{
	    if (netbuffer->checksum & NCMD_RETRANSMIT)
		realretrans = ExpandTics (netbuffer->retransmitfrom);
	    else
		realretrans = -1;
	    
	    fprintf (debugfile,"get %i = (%i + %i, R %i)[%i] ",
		     doomcom->remotenode,
		     ExpandTics(netbuffer->starttic),
		     netbuffer->numtics, realretrans, doomcom->datalength);

	    for (i=0 ; i<doomcom->datalength ; i++)
		fprintf (debugfile,"%i ",((byte *)netbuffer)[i]);
	    fprintf (debugfile,"\n");
	}
    }
    return true;	
}


//
// GetPackets
//
char    exitmsg[80];

void GetPackets (void)
{
    int		netconsole;
    int		netnode;
    ticcmd_t	*src, *dest;
    int		realend;
    int		realstart;
				 
    while ( HGetPacket() )
    {
	if (netbuffer->checksum & NCMD_SETUP)
	    continue;		// extra setup packet
			
	netconsole = netbuffer->player & ~PL_DRONE;
	netnode = doomcom->remotenode;
	
	// to save bytes, only the low byte of tic numbers are sent
	// Figure out what the rest of the bytes are
	realstart = ExpandTics (netbuffer->starttic);		
	realend = (realstart+netbuffer->numtics);
	
	// check for exiting the game
	if (netbuffer->checksum & NCMD_EXIT)
	{
	    if (!nodeingame[netnode])
		continue;
	    nodeingame[netnode] = false;
	    playeringame[netconsole] = false;
	    strcpy (exitmsg, "Player 1 left the game");
	    exitmsg[7] += netconsole;
	    players[consoleplayer].message = exitmsg;
	    if (demorecording)
		G_CheckDemoStatus ();
	    continue;
	}
	
	// check for a remote game kill
	if (netbuffer->checksum & NCMD_KILL)
	    I_Error ("Killed by network driver");

	nodeforplayer[netconsole] = netnode;
	
	// check for retransmit request
	if ( resendcount[netnode] <= 0 
	     && (netbuffer->checksum & NCMD_RETRANSMIT) )
	{
	    resendto[netnode] = ExpandTics(netbuffer->retransmitfrom);
	    if (debugfile)
		fprintf (debugfile,"retransmit from %i\n", resendto[netnode]);
	    resendcount[netnode] = RESENDCOUNT;
	}
	else
	    resendcount[netnode]--;
	
	// check for out of order / duplicated packet		
	if (realend == nettics[netnode])
	    continue;
			
	if (realend < nettics[netnode])
	{
	    if (debugfile)
		fprintf (debugfile,
			 "out of order packet (%i + %i)\n" ,
			 realstart,netbuffer->numtics);
	    continue;
	}
	
	// check for a missed packet
	if (realstart > nettics[netnode])
	{
	    // stop processing until the other system resends the missed tics
	    if (debugfile)
		fprintf (debugfile,
			 "missed tics from %i (%i - %i)\n",
			 netnode, realstart, nettics[netnode]);
	    remoteresend[netnode] = true;
	    continue;
	}

	// update command store from the packet
        {
	    int		start;

	    remoteresend[netnode] = false;
		
	    start = nettics[netnode] - realstart;		
	    src = &netbuffer->cmds[start];

	    while (nettics[netnode] < realend)
	    {
		dest = &netcmds[netconsole][nettics[netnode]%BACKUPTICS];
		nettics[netnode]++;
		*dest = *src;
		src++;
	    }
	}
    }
}


//
// NetUpdate
// Builds ticcmds for console player,
// sends out a packet
//
int      gametime;

void NetUpdate (void)
{
    int             nowtime;
    int             newtics;
    int				i,j;
    int				realstart;
    int				gameticdiv;
    
    // check time
    nowtime = I_GetTime ()/ticdup;
    newtics = nowtime - gametime;
    gametime = nowtime;
	
    if (newtics <= 0) 	// nothing new to update
	goto listen; 

    if (skiptics <= newtics)
    {
	newtics -= skiptics;
	skiptics = 0;
    }
    else
    {
	skiptics -= newtics;
	newtics = 0;
    }
	
		
    netbuffer->player = consoleplayer;
    
    // build new ticcmds for console player
    gameticdiv = gametic/ticdup;
    for (i=0 ; i<newtics ; i++)
    {
	I_StartTic ();
	D_ProcessEvents ();
	if (maketic - gameticdiv >= BACKUPTICS/2-1)
	    break;          // can't hold any more
	
	//printf ("mk:%i ",maketic);
	G_BuildTiccmd (&localcmds[maketic%BACKUPTICS]);
	maketic++;
    }


    if (singletics)
	return;         // singletic update is syncronous
    
    // send the packet to the other nodes
    for (i=0 ; i<doomcom->numnodes ; i++)
	if (nodeingame[i])
	{
	    netbuffer->starttic = realstart = resendto[i];
	    netbuffer->numtics = maketic - realstart;
	    if (netbuffer->numtics > BACKUPTICS)
		I_Error ("NetUpdate: netbuffer->numtics > BACKUPTICS");

	    resendto[i] = maketic - doomcom->extratics;

	    for (j=0 ; j< netbuffer->numtics ; j++)
		netbuffer->cmds[j] = 
		    localcmds[(realstart+j)%BACKUPTICS];
					
	    if (remoteresend[i])
	    {
		netbuffer->retransmitfrom = nettics[i];
		HSendPacket (i, NCMD_RETRANSMIT);
	    }
	    else
	    {
		netbuffer->retransmitfrom = 0;
		HSendPacket (i, 0);
	    }
	}
    
    // listen for other packets
  listen:
    GetPackets ();
}



//
// CheckAbort
//
void CheckAbort (void)
{
    event_t *ev;
    int		stoptic;
	
    stoptic = I_GetTime () + 2; 
    while (I_GetTime() < stoptic) 
	I_StartTic (); 
	
    I_StartTic ();
    for ( ; eventtail != eventhead 
	      ; eventtail = (++eventtail)&(MAXEVENTS-1) ) 
    { 
	ev = &events[eventtail]; 
	if (ev->type == ev_keydown && ev->data1 == KEY_ESCAPE)
	    I_Error ("Network game synchronization aborted.");
    } 
}


//
// D_ArbitrateNetStart
//
void D_ArbitrateNetStart (void)
{
    int		i;
    boolean	gotinfo[MAXNETNODES];
	
    autostart = true;
    memset (gotinfo,0,sizeof(gotinfo));
	
    if (doomcom->consoleplayer)
    {
	// listen for setup info from key player
	printf ("listening for network start info...\n");
	while (1)
	{
	    CheckAbort ();
	    if (!HGetPacket ())
		continue;
	    if (netbuffer->checksum & NCMD_SETUP)
	    {
		if (netbuffer->player != VERSION)
		    I_Error ("Different DOOM versions cannot play a net game!");
		startskill = netbuffer->retransmitfrom & 15;
		deathmatch = (netbuffer->retransmitfrom & 0xc0) >> 6;
		nomonsters = (netbuffer->retransmitfrom & 0x20) > 0;
		respawnparm = (netbuffer->retransmitfrom & 0x10) > 0;
		startmap = netbuffer->starttic & 0x3f;
		startepisode = netbuffer->starttic >> 6;
		return;
	    }
	}
    }
    else
    {
	// key player, send the setup info
	printf ("sending network start info...\n");
	do
	{
	    CheckAbort ();
	    for (i=0 ; i<doomcom->numnodes ; i++)
	    {
		netbuffer->retransmitfrom = startskill;
		if (deathmatch)
		    netbuffer->retransmitfrom |= (deathmatch<<6);
		if (nomonsters)
		    netbuffer->retransmitfrom |= 0x20;
		if (respawnparm)
		    netbuffer->retransmitfrom |= 0x10;
		netbuffer->starttic = startepisode * 64 + startmap;
		netbuffer->player = VERSION;
		netbuffer->numtics = 0;
		HSendPacket (i, NCMD_SETUP);
	    }

#if 1
	    for(i = 10 ; i  &&  HGetPacket(); --i)
	    {
		if((netbuffer->player&0x7f) < MAXNETNODES)
		    gotinfo[netbuffer->player&0x7f] = true;
	    }
#else
	    while (HGetPacket ())
	    {
		gotinfo[netbuffer->player&0x7f] = true;
	    }
#endif

	    for (i=1 ; i<doomcom->numnodes ; i++)
		if (!gotinfo[i])
		    break;
	} while (i < doomcom->numnodes);
    }
}

//
// D_CheckNetGame
// Works out player numbers among the net participants
//
extern	int			viewangleoffset;

void D_CheckNetGame (void)
{
    int             i;
	
    for (i=0 ; i<MAXNETNODES ; i++)
    {
	nodeingame[i] = false;
       	nettics[i] = 0;
	remoteresend[i] = false;	// set when local needs tics
	resendto[i] = 0;		// which tic to start sending
    }
	
    // I_InitNetwork sets doomcom and netgame
    I_InitNetwork ();
    if (doomcom->id != DOOMCOM_ID)
	I_Error ("Doomcom buffer invalid!");
    
    netbuffer = &doomcom->data;
    consoleplayer = displayplayer = doomcom->consoleplayer;
    if (netgame)
	D_ArbitrateNetStart ();

    printf ("startskill %i  deathmatch: %i  startmap: %i  startepisode: %i\n",
	    startskill, deathmatch, startmap, startepisode);
	
    // read values out of doomcom
    ticdup = doomcom->ticdup;
    maxsend = BACKUPTICS/(2*ticdup)-1;
    if (maxsend<1)
	maxsend = 1;
			
    for (i=0 ; i<doomcom->numplayers ; i++)
	playeringame[i] = true;
    for (i=0 ; i<doomcom->numnodes ; i++)
	nodeingame[i] = true;
	
    printf ("player %i of %i (%i nodes)\n",
	    consoleplayer+1, doomcom->numplayers, doomcom->numnodes);

}


//
// D_QuitNetGame
// Called before quitting to leave a net game
// without hanging the other players
//
void D_QuitNetGame (void)
{
    int             i, j;
	
    if (debugfile)
	fclose (debugfile);
		
    if (!netgame || !usergame || consoleplayer == -1 || demoplayback)
	return;
	
    // send a bunch of packets for security
    netbuffer->player = consoleplayer;
    netbuffer->numtics = 0;
    for (i=0 ; i<4 ; i++)
    {
	for (j=1 ; j<doomcom->numnodes ; j++)
	    if (nodeingame[j])
		HSendPacket (j, NCMD_EXIT);
	I_WaitVBL (1);
    }
}



//
// TryRunTics
//
int	frametics[4];
int	frameon;
int	frameskip[4];
int	oldnettics;

extern	boolean	advancedemo;

public static void TryRunTics ()
{
    int		i;
    int		lowtic;
    int		entertic;
    static int	oldentertics;
    int		realtics;
    int		availabletics;
    int		counts;
    int		numplaying;
    
    // get real tics		
    entertic = I_GetTime ()/ticdup;
    realtics = entertic - oldentertics;
    oldentertics = entertic;
    
    // get available tics
    NetUpdate ();
	
    lowtic = MAXINT;
    numplaying = 0;
    for (i=0 ; i<doomcom->numnodes ; i++)
    {
	if (nodeingame[i])
	{
	    numplaying++;
	    if (nettics[i] < lowtic)
		lowtic = nettics[i];
	}
    }
    availabletics = lowtic - gametic/ticdup;
    
    // decide how many tics to run
    if (realtics < availabletics-1)
	counts = realtics+1;
    else if (realtics < availabletics)
	counts = realtics;
    else
	counts = availabletics;
    
    if (counts < 1)
	counts = 1;
		
    frameon++;

    if (debugfile)
	fprintf (debugfile,
		 "=======real: %i  avail: %i  game: %i\n",
		 realtics, availabletics,counts);

    if (!demoplayback)
    {	
	// ideally nettics[0] should be 1 - 3 tics above lowtic
	// if we are consistantly slower, speed up time
	for (i=0 ; i<MAXPLAYERS ; i++)
	    if (playeringame[i])
		break;
	if (consoleplayer == i)
	{
	    // the key player does not adapt
	}
	else
	{
	    if (nettics[0] <= nettics[nodeforplayer[i]])
	    {
		gametime--;
		// printf ("-");
	    }
	    frameskip[frameon&3] = (oldnettics > nettics[nodeforplayer[i]]);
	    oldnettics = nettics[0];
	    if (frameskip[0] && frameskip[1] && frameskip[2] && frameskip[3])
	    {
		skiptics = 1;
		// printf ("+");
	    }
	}
    }// demoplayback
	
    // wait for new tics if needed
    while (lowtic < gametic/ticdup + counts)	
    {
	NetUpdate ();   
	lowtic = MAXINT;
	
	for (i=0 ; i<doomcom->numnodes ; i++)
	    if (nodeingame[i] && nettics[i] < lowtic)
		lowtic = nettics[i];
	
	if (lowtic < gametic/ticdup)
	    I_Error ("TryRunTics: lowtic < gametic");
				
	// don't stay in here forever -- give the menu a chance to work
	if (I_GetTime ()/ticdup - entertic >= 20)
	{
	    M_Ticker ();
	    return;
	} 
    }
    
    // run the count * ticdup dics
    while (counts--)
    {
	for (i=0 ; i<ticdup ; i++)
	{
	    if (gametic/ticdup > lowtic)
		I_Error ("gametic>lowtic");
	    if (advancedemo)
		D_DoAdvanceDemo ();
	    M_Ticker ();
	    G_Ticker ();
	    gametic++;
	    
	    // modify command for duplicated tics
	    if (i != ticdup-1)
	    {
		ticcmd_t	*cmd;
		int			buf;
		int			j;
				
		buf = (gametic/ticdup)%BACKUPTICS; 
		for (j=0 ; j<MAXPLAYERS ; j++)
		{
		    cmd = &netcmds[j][buf];
		    cmd->chatchar = 0;
		    if (cmd->buttons & BT_SPECIAL)
			cmd->buttons = 0;
		}
	    }
	}
	NetUpdate ();	// check for new console commands
    }
}
}*/
package doom;

public class doomcom_t {
	
		public doomcom_t(){
			this.data=new doomdata_t();
			
		}

        // Supposed to be DOOMCOM_ID?
        // Maes: was "long", but they intend 32-bit "int" here. Hurray for C's consistency!
        public int        id;
        
        // DOOM executes an int to execute commands.
        public short       intnum;     
        // Communication between DOOM and the driver.
        // Is CMD_SEND or CMD_GET.
        public short       command;
        // Is dest for send, set by get (-1 = no packet).
        public short       remotenode;
        
        // Number of bytes in doomdata to be sent
        public short       datalength;

        // Info common to all nodes.
        // Console is allways node 0.
        public short       numnodes;
        // Flag: 1 = no duplication, 2-5 = dup for slow nets.
        public short       ticdup;
        // Flag: 1 = send a backup tic in every packet.
        public short       extratics;
        // Flag: 1 = deathmatch.
        public short       deathmatch;
        // Flag: -1 = new game, 0-5 = load savegame
        public short       savegame;
        public short       episode;    // 1-3
        public short       map;        // 1-9
        public short       skill;      // 1-5

        // Info specific to this node.
        public short       consoleplayer;
        public short       numplayers;
        
        // These are related to the 3-display mode,
        //  in which two drones looking left and right
        //  were used to render two additional views
        //  on two additional computers.
        // Probably not operational anymore.
        // 1 = left, 0 = center, -1 = right
        public short       angleoffset;
        // 1 = drone
        public short       drone;      

        // The packet data to be sent.
        public doomdata_t      data;
        
    }

package doom;

public class pic_t {
    
    public pic_t(byte width, byte height, byte data) {
        super();
        this.width = width;
        this.height = height;
        this.data = data;
    }
    
    public byte        width;
    public byte        height;
    public byte        data;
}


package doom;

/** The defined weapons,
 * including a marker indicating
 *  user has not changed weapon.
 */
public enum weapontype_t
{
    wp_fist,
    wp_pistol,
    wp_shotgun,
    wp_chaingun,
    wp_missile,
    wp_plasma,
    wp_bfg,
    wp_chainsaw,
    wp_supershotgun,

    NUMWEAPONS,
    
    // No pending weapon change.
    wp_nochange;
    
    public String toString(){
        return this.name();
    }
    
}

package doom;

import static utils.C2JUtils.eval;

import java.io.BufferedInputStream;
import java.io.DataInputStream;
import java.io.FileInputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.StringTokenizer;

import w.DoomIO;

/**
 * A class to handle the command-line args in an unified manner, and without
 * them being tied to DoomMain or DoomStatus.
 * 
 * 
 * @author velktron
 *
 */

public class CommandLine implements ICommandLineManager {

    
    /* What bullshit, those are supposed to carry over from the very first main */
    protected int myargc;
    protected String[] myargv;
    
    public CommandLine(String[] argv){

        // Bump argcount up by 1 to maintain CheckParm behavior
        // In C, argument 0 is the executable itself, and most
        // of Doom's code is tailored to reflect that, with 0
        // meaning "not found" with regards to a parameter.
        // It's easier to change this here.
    	// TODO: this is really bad practice, eliminate once a 
    	// cvar system is in place.

        myargv=new String[argv.length+1];
        System.arraycopy(argv, 0, myargv, 1, argv.length);        
        myargc=argv.length+1;
        cvars=new HashMap<String, Object>();
    }
    
    /* (non-Javadoc)
     * @see doom.ICommandLineManager#getArgv(int)
     */
    @Override
    public String getArgv(int index){
        if (index>myargc) return null;
        else return myargv[index];
    }
    
    /* (non-Javadoc)
     * @see doom.ICommandLineManager#getArgc()
     */
    @Override
    public int getArgc(){
        return myargc;
    }
    

    /* (non-Javadoc)
     * @see doom.ICommandLineManager#CheckParm(java.lang.String)
     */

    @Override
    public int CheckParm(String check) {
        int i;

        for (i = 1; i < myargc; i++) {
            if (check.compareToIgnoreCase(myargv[i]) == 0)
                return i;
        }

        return 0;
    }
    
    @Override
    public boolean CheckParmBool(String check) {
        int i;

        for (i = 1; i < myargc; i++) {
            if (check.compareToIgnoreCase(myargv[i]) == 0)
                return true;
        }

        return false;
    }
    
    /**
     * Find a Response File
     * 
     * Not very well documented, but Doom apparently could use a sort of 
     * script file with command line arguments inside, if you prepend @ to
     * the command-like argument itself. The arguments themselves could
     * be separated by any sort of whitespace or ASCII characters exceeding "z"
     * in value.
     * 
     * E.g. doom @crap
     * 
     * would load a file named "crap".
     * 
     * Now, the original function is crap for several reasons: for one,
     * it will bomb if more than 100 arguments <i>total</i> are formed.
     * Memory allocation will also fail because the tokenizer used only
     * stops at file size limit, not at maximum parsed arguments limit
     * (MACARGVS = 100).
     * 
     * This is the wiki's entry:
     * 
     * doom @<response>
     * This parameter tells the Doom engine to read from a response file, 
     * a text file that may store additional command line parameters. 
     * The file may have any name that is valid to the system, optionally 
     * with an extension. The parameters are typed as in the command line 
     * (-episode 2, for example), but one per line, where up to 100 lines
     *  may be used. The additional parameters may be disabled for later 
     *  use by placing a vertical bar (the | character) between the 
     *  prefixing dash (-) and the rest of the parameter name.
     * 
     * 
     */
    @Override
    public void FindResponseFile ()
    {
        try{

            for (int i = 1;i < getArgc();i++)
                if (getArgv(i).charAt(0)=='@')
                {
                    DataInputStream handle;
                    // save o       
                    int             size;
                    int             indexinfile;
                    char[]   infile=null;
                    char[]    file=null;
                    // Fuck that, we're doing it properly.
                    ArrayList<String>  parsedargs=new ArrayList<String>();
                    ArrayList<String>    moreargs=new ArrayList<String>();
                    String    firstargv;

                    // READ THE RESPONSE FILE INTO MEMORY
                    handle = new DataInputStream(new BufferedInputStream(new FileInputStream(myargv[i].substring(1))));
                    if (!eval(handle))
                    {
                        System.out.print ("\nNo such response file!");
                        System.exit(1);
                    }
                    System.out.println("Found response file "+myargv[i].substring(1));
                    size = (int) handle.available();
                    file=new char[size];
                    
                    DoomIO.readNonUnicodeCharArray(handle, file,size);
                    handle.close();

                    // Save first argument.
                    firstargv = myargv[0];

                    // KEEP ALL CMDLINE ARGS FOLLOWING @RESPONSEFILE ARG
                    // This saves the old references.
                    for (int k = i+1; k < myargc; k++)
                        moreargs.add(myargv[k]);

                    infile = file;
                    indexinfile = 0;
                    indexinfile++;  // SKIP PAST ARGV[0] (KEEP IT)
                    // HMM? StringBuffer build=new StringBuffer();

                    /* MAES: the code here looked like some primitive tokenizer.
           that assigned C-strings to memory locations.
           Instead, we'll tokenize the file input correctly here.
                     */

                    StringTokenizer tk=new StringTokenizer(String.copyValueOf(infile));



                    //myargv = new String[tk.countTokens()+argc];
                    parsedargs.add(firstargv);

                    while(tk.hasMoreTokens())
                    {
                        parsedargs.add(tk.nextToken());
                    }

                    // Append the other args to the end.
                    parsedargs.addAll(moreargs);

                    /* NOW the original myargv is reset, but the old values still survive in 
                     * the listarray.*/

                    myargv= new String[parsedargs.size()];
                    myargv=parsedargs.toArray(myargv);
                    myargc = myargv.length;

                    // DISPLAY ARGS
                    System.out.println(myargc+" command-line args:");
                    for (int k=0;k<myargc;k++)
                        System.out.println(myargv[k]);

                    // Stops at the first one. Pity, because we could do funky recursive stuff with that :-p
                    break;
                }
        } catch (Exception e){
            e.printStackTrace();
        }
    }

    @Override
    public void setArgv(int index, String string) {
        this.myargv[index]=string;
        
    }
    
    private HashMap<String, Object> cvars;
    
    public boolean cvarExists(String name){
    	return cvars.containsKey(name);
    	}
    
    public Object removeCvar(String name){
    	return cvars.remove(name);
    	}
    
    public void putCvar(String name, int value){
    	cvars.put(name, value);
    	}

    public void putCvar(String name, double value){
    	cvars.put(name, value);
    	}
    
    public void putCvar(String name, String value){
    	cvars.put(name, value);
    	}

    public void putCvar(String name, boolean value){
    	cvars.put(name, value);
    	}

    public void putCvar(String name, String[] value){
    	cvars.put(name, value);
    	}
    
    public Integer getInt(String name){
    	Object stuff=cvars.get(name);
    	if (stuff !=null){
    		if (stuff instanceof Integer){
    			return (Integer) stuff;
    		}
    	}    	
    	return null;
    }
    
    public Double getFloat(String name){
    	Object stuff=cvars.get(name);
    	if (stuff !=null){
    		if (stuff instanceof Double){
    			return (Double) stuff;
    		}
    	}    	
    	return null;
    }
    
    public String getString(String name){
    	Object stuff=cvars.get(name);
    	if (stuff !=null){
    		if (stuff instanceof String){
    			return (String) stuff;
    		}
    	}    	
    	return null;
    }
    
    public String[] getStringArray(String name){
    	Object stuff=cvars.get(name);
    	if (stuff !=null){
    		if (stuff instanceof String[]){
    			return (String[]) stuff;
    		}
    	}    	
    	return null;
    }
    
    public Boolean getBoolean(String name){
    	Object stuff=cvars.get(name);
    	if (stuff !=null){
    		if (stuff instanceof Boolean){
    			return (Boolean) stuff;
    		}
    	}    	
    	return null;
    }
    
    public <K> void setCvar(String name, K value){
    	if (cvars.containsKey(name)){
    		cvars.put(name, value);
    	}
    }

    public static String checkParameterCouple(String check, String[] myargv) {
        int found=-1;

        for (int i = 0; i < myargv.length; i++) {
            if (check.compareToIgnoreCase(myargv[i]) == 0){
                found=i; // found. Break on first.
                break;
            }
        }
        
        // Found, and there's room to spare for one more?
        if ((found>=0)&&(found<myargv.length-1)){
            if (myargv[found+1]!=null){
                // Not null, not empty and not a parameter switch
                if ((myargv[found+1].length()>=1) &&
                    (myargv[found+1].charAt(0)!='-'))                        
                        return myargv[found+1];
            }
        }

        // Well duh.
        return null;
    }
 
    /** Is a parameter based on an prefix identifier e.g. '-'
     * 
     * @param what
     * @param identifier
     * @return
     */
    public static boolean isParameter(String what, char identifier){
        if (what!=null && what.length()>-0){
        	return (what.charAt(0)!=identifier); 
        }
        
        return false;        
    }
    
    public static int parameterMultipleValues(String check, String[] myargv) {
        int found=-1;

        // It's not even a valid parameter name
        if (!isParameter(check,'-')) return -1;
        
        // Does it exist?
        if ((found=checkParm(check,myargv))==-1) return found;
        
        // Found, and there are still some to spare
        int rest=myargv.length-found-1;
        int count=0;
        
        for (int i=found+1;i<myargv.length;i++){
        	if (isParameter(myargv[i],'-')) break;
        		else
        	count++; // not a parameter, count up        	
        }
        
        // Well duh.
        return count;
    }
    
    
    public static int checkParm(String check, String[] params) {
        int i;

        for (i = 0; i < params.length; i++) {
            if (check.compareToIgnoreCase(params[i]) == 0)
                return i;
        }

        return -1;
    }
    
}

package doom;

import java.io.DataInputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

import static utils.C2JUtils.pointer;
import w.CacheableDoomObject;
import w.IPackableDoomObject;
import w.IReadableDoomObject;
import p.ActionType1;
import p.ActionType2;
import p.ActionTypeSS;

public class thinker_t implements CacheableDoomObject,IReadableDoomObject,IPackableDoomObject{
   
   public thinker_t prev;
   public thinker_t next;
   public think_t     function;

   /* killough 8/29/98: we maintain thinkers in several equivalence classes,
    * according to various criteria, so as to allow quicker searches.
    */

   /** Next, previous thinkers in same class */
   
   public thinker_t cnext, cprev;

   
   // Thinkers can either have one parameter of type (mobj_t),
   // Or otherwise be sector specials, flickering lights etc.
   // Those are atypical and need special handling.
   public ActionType1     acp1;
   public ActionType2     acp2;
   public ActionTypeSS     acpss;
   
   /** extra fields, to use when archiving/unarchiving for
    * identification. Also in blocklinks, etc.
    */
   public int id,previd, nextid,functionid;
   
   
   
@Override
public void read(DataInputStream f)
        throws IOException {
	readbuffer.position(0);
	readbuffer.order(ByteOrder.LITTLE_ENDIAN);
	f.read(readbuffer.array());
	unpack(readbuffer);
	}

/** This adds 12 bytes */

@Override
public void pack(ByteBuffer b)
        throws IOException {
    // It's possible to reconstruct even by hashcodes.
    // As for the function, that should be implied by the mobj_t type.
	b.order(ByteOrder.LITTLE_ENDIAN);
	b.putInt(pointer(prev));
    b.putInt(pointer(next));
    b.putInt(pointer(function));
    //System.out.printf("Packed thinker %d %d %d\n",pointer(prev),pointer(next),pointer(function));
	}

@Override
public void unpack(ByteBuffer b)
        throws IOException {
    // We are supposed to archive pointers to other thinkers,
    // but they are rather useless once on disk.
	b.order(ByteOrder.LITTLE_ENDIAN);
    previd=b.getInt();
    nextid=b.getInt();
    functionid=b.getInt();
    //System.out.printf("Unpacked thinker %d %d %d\n",pointer(previd),pointer(nextid),pointer(functionid));
	}

 private static ByteBuffer readbuffer=ByteBuffer.allocate(12);

}
package doom;
// Emacs style mode select   -*- C++ -*- 
//-----------------------------------------------------------------------------
//
// $Id: items.java,v 1.3 2010/12/20 17:15:08 velktron Exp $
//
// Copyright (C) 1993-1996 by id Software, Inc.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// $Log: items.java,v $
// Revision 1.3  2010/12/20 17:15:08  velktron
// Made the renderer more OO -> TextureManager and other changes as well.
//
// Revision 1.2  2010/08/19 23:14:49  velktron
// Automap
//
// Revision 1.1  2010/06/30 08:58:50  velktron
// Let's see if this stuff will finally commit....
//
//
// Most stuff is still  being worked on. For a good place to start and get an idea of what is being done, I suggest checking out the "testers" package.
//
// Revision 1.1  2010/06/29 11:07:34  velktron
// Release often, release early they say...
//
// Commiting ALL stuff done so far. A lot of stuff is still broken/incomplete, and there's still mixed C code in there. I suggest you load everything up in Eclpise and see what gives from there.
//
// A good place to start is the testers/ directory, where you  can get an idea of how a few of the implemented stuff works.
//
//
// DESCRIPTION:
//
//-----------------------------------------------------------------------------

import defines.*;

public class items{

public static weaponinfo_t[]	weaponinfo=
{
    new weaponinfo_t(
	// fist
	ammotype_t.am_noammo,
	statenum_t.S_PUNCHUP,
	statenum_t.S_PUNCHDOWN,
	statenum_t.S_PUNCH,
	statenum_t.S_PUNCH1,
	statenum_t.S_NULL
    ),	
    new weaponinfo_t(
	// pistol
    ammotype_t.am_clip,
	statenum_t.S_PISTOLUP,
	statenum_t.S_PISTOLDOWN,
	statenum_t.S_PISTOL,
	statenum_t.S_PISTOL1,
	statenum_t.S_PISTOLFLASH
	), new weaponinfo_t(
	// shotgun
    ammotype_t.am_shell,
    statenum_t.S_SGUNUP,
    statenum_t.S_SGUNDOWN,
    statenum_t.S_SGUN,
    statenum_t.S_SGUN1,
    statenum_t.S_SGUNFLASH1
    ),
    new weaponinfo_t(
	// chaingun
    ammotype_t.am_clip,
	statenum_t.S_CHAINUP,
	statenum_t.S_CHAINDOWN,
	statenum_t.S_CHAIN,
	statenum_t.S_CHAIN1,
	statenum_t.S_CHAINFLASH1
    ),
    new weaponinfo_t(
	// missile launcher
        ammotype_t.am_misl,
	statenum_t.S_MISSILEUP,
	statenum_t.S_MISSILEDOWN,
	statenum_t.S_MISSILE,
	statenum_t.S_MISSILE1,
	statenum_t.S_MISSILEFLASH1
    ),
    new weaponinfo_t(
	// plasma rifle
        ammotype_t.am_cell,
	statenum_t.S_PLASMAUP,
	statenum_t.S_PLASMADOWN,
	statenum_t.S_PLASMA,
	statenum_t.S_PLASMA1,
	statenum_t.S_PLASMAFLASH1
    ),
    new weaponinfo_t(
	// bfg 9000
        ammotype_t.am_cell,
	statenum_t.S_BFGUP,
	statenum_t.S_BFGDOWN,
	statenum_t.S_BFG,
	statenum_t.S_BFG1,
	statenum_t.S_BFGFLASH1
    ),
    new weaponinfo_t(
	// chainsaw
        ammotype_t.am_noammo,
	statenum_t.S_SAWUP,
	statenum_t.S_SAWDOWN,
	statenum_t.S_SAW,
	statenum_t.S_SAW1,
	statenum_t.S_NULL
    ),
    new weaponinfo_t(
	// super shotgun
    ammotype_t.am_shell,
    statenum_t.S_DSGUNUP,
	statenum_t.S_DSGUNDOWN,
	statenum_t.S_DSGUN,
	statenum_t.S_DSGUN1,
	statenum_t.S_DSGUNFLASH1
    )
    };
}









package doom;

public enum gameaction_t {
        ga_nothing,
        ga_loadlevel,
        ga_newgame,
        ga_loadgame,
        ga_savegame,
        ga_playdemo,
        ga_completed,
        ga_victory,
        ga_worlddone,
        ga_screenshot,
        ga_failure // HACK: communicate failures silently
    }

package doom;

// Emacs style mode select   -*- C++ -*- 
//-----------------------------------------------------------------------------
//
// $Id: englsh.java,v 1.5 2011/05/31 21:46:20 velktron Exp $
//
// Copyright (C) 1993-1996 by id Software, Inc.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// DESCRIPTION:
//  Printed strings for translation.
//  English language support (default).
//
//-----------------------------------------------------------------------------

//
//  Printed strings for translation
//

//
// D_Main.C
//

public class englsh{
public final static String  D_DEVSTR="Development mode ON.\n";
public final static String  D_CDROM="CD-ROM Version: default.cfg from c:\\doomdata\n";

//
//  M_Menu.C
//
public final static String  PRESSKEY ="press a key.";
public final static String  PRESSYN ="press y or n.";
public final static String  QUITMSG="are you sure you want to\nquit this great game?";
public final static String  LOADNET ="you can't do load while in a net game!\n\n"+PRESSKEY;
public final static String  QLOADNET="you can't quickload during a netgame!\n\n"+PRESSKEY;
public final static String  QSAVESPOT="you haven't picked a quicksave slot yet!\n\n"+PRESSKEY;
public final static String  SAVEDEAD ="you can't save if you aren't playing!\n\n"+PRESSKEY;
public final static String  QSPROMPT ="quicksave over your game named\n\n'%s'?\n\n"+PRESSYN;
public final static String  QLPROMPT="do you want to quickload the game named\n\n'%s'?\n\n"+PRESSYN;

public final static String  NEWGAME = "you can't start a new game\nwhile in a network game.\n\n"+PRESSKEY;

public final static String  NIGHTMARE ="are you sure? this skill level\nisn't even remotely fair.\n\n"+PRESSYN;

public final static String  SWSTRING = "this is the shareware version of doom.\n\nyou need to order the entire trilogy.\n\n"+PRESSKEY;

public final static String  MSGOFF="Messages OFF";
public final static String  MSGON   ="Messages ON";
public final static String  NETEND="you can't end a netgame!\n\n"+PRESSKEY;
public final static String  ENDGAME="are you sure you want to end the game?\n\n"+PRESSYN;

public final static String  DOSY    ="(press y to quit)";

public final static String  DETAILHI="High detail";
public final static String  DETAILLO="Low detail";
public final static String  GAMMALVL0="Gamma correction OFF";
public final static String  GAMMALVL1="Gamma correction level 1";
public final static String  GAMMALVL2="Gamma correction level 2";
public final static String  GAMMALVL3="Gamma correction level 3";
public final static String  GAMMALVL4="Gamma correction level 4";
public final static String  EMPTYSTRING="empty slot";

//
//  P_inter.C
//
public final static String  GOTARMOR="Picked up the armor.";
public final static String  GOTMEGA="Picked up the MegaArmor!";
public final static String  GOTHTHBONUS="Picked up a health bonus.";
public final static String  GOTARMBONUS="Picked up an armor bonus.";
public final static String  GOTSTIM="Picked up a stimpack.";
public final static String  GOTMEDINEED="Picked up a medikit that you REALLY need!";
public final static String  GOTMEDIKIT="Picked up a medikit.";
public final static String  GOTSUPER="Supercharge!";

public final static String  GOTBLUECARD="Picked up a blue keycard.";
public final static String  GOTYELWCARD="Picked up a yellow keycard.";
public final static String  GOTREDCARD="Picked up a red keycard.";
public final static String  GOTBLUESKUL="Picked up a blue skull key.";
public final static String  GOTYELWSKUL="Picked up a yellow skull key.";
public final static String  GOTREDSKULL="Picked up a red skull key.";

public final static String  GOTINVUL="Invulnerability!";
public final static String  GOTBERSERK="Berserk!";
public final static String  GOTINVIS="Partial Invisibility";
public final static String  GOTSUIT="Radiation Shielding Suit";
public final static String  GOTMAP="Computer Area Map";
public final static String  GOTVISOR="Light Amplification Visor";
public final static String  GOTMSPHERE="MegaSphere!";

public final static String  GOTCLIP="Picked up a clip.";
public final static String  GOTCLIPBOX="Picked up a box of bullets.";
public final static String  GOTROCKET="Picked up a rocket.";
public final static String  GOTROCKBOX="Picked up a box of rockets.";
public final static String  GOTCELL="Picked up an energy cell.";
public final static String  GOTCELLBOX="Picked up an energy cell pack.";
public final static String  GOTSHELLS="Picked up 4 shotgun shells.";
public final static String  GOTSHELLBOX="Picked up a box of shotgun shells.";
public final static String  GOTBACKPACK="Picked up a backpack full of ammo!";

public final static String  GOTBFG9000="You got the BFG9000!  Oh, yes.";
public final static String  GOTCHAINGUN="You got the chaingun!";
public final static String  GOTCHAINSAW="A chainsaw!  Find some meat!";
public final static String  GOTLAUNCHER="You got the rocket launcher!";
public final static String  GOTPLASMA="You got the plasma gun!";
public final static String  GOTSHOTGUN="You got the shotgun!";
public final static String  GOTSHOTGUN2="You got the super shotgun!";

//
// P_Doors.C
//
public final static String  PD_BLUEO="You need a blue key to activate this object";
public final static String  PD_REDO="You need a red key to activate this object";
public final static String  PD_YELLOWO="You need a yellow key to activate this object";
public final static String  PD_BLUEK="You need a blue key to open this door";
public final static String  PD_REDK="You need a red key to open this door";
public final static String  PD_YELLOWK="You need a yellow key to open this door";

//
//  G_game.C
//
public final static String  GGSAVED="game saved.";

//
//  HU_stuff.C
//
public final static String  HUSTR_MSGU="[Message unsent]";

public final static String  HUSTR_E1M1="E1M1: Hangar";
public final static String  HUSTR_E1M2="E1M2: Nuclear Plant";
public final static String  HUSTR_E1M3="E1M3: Toxin Refinery";
public final static String  HUSTR_E1M4="E1M4: Command Control";
public final static String  HUSTR_E1M5="E1M5: Phobos Lab";
public final static String  HUSTR_E1M6="E1M6: Central Processing";
public final static String  HUSTR_E1M7="E1M7: Computer Station";
public final static String  HUSTR_E1M8="E1M8: Phobos Anomaly";
public final static String  HUSTR_E1M9="E1M9: Military Base";

public final static String  HUSTR_E2M1="E2M1: Deimos Anomaly";
public final static String  HUSTR_E2M2="E2M2: Containment Area";
public final static String  HUSTR_E2M3="E2M3: Refinery";
public final static String  HUSTR_E2M4="E2M4: Deimos Lab";
public final static String  HUSTR_E2M5="E2M5: Command Center";
public final static String  HUSTR_E2M6="E2M6: Halls of the Damned";
public final static String  HUSTR_E2M7="E2M7: Spawning Vats";
public final static String  HUSTR_E2M8="E2M8: Tower of Babel";
public final static String  HUSTR_E2M9="E2M9: Fortress of Mystery";

public final static String  HUSTR_E3M1="E3M1: Hell Keep";
public final static String  HUSTR_E3M2="E3M2: Slough of Despair";
public final static String  HUSTR_E3M3="E3M3: Pandemonium";
public final static String  HUSTR_E3M4="E3M4: House of Pain";
public final static String  HUSTR_E3M5="E3M5: Unholy Cathedral";
public final static String  HUSTR_E3M6="E3M6: Mt. Erebus";
public final static String  HUSTR_E3M7="E3M7: Limbo";
public final static String  HUSTR_E3M8="E3M8: Dis";
public final static String  HUSTR_E3M9="E3M9: Warrens";

public final static String  HUSTR_E4M1="E4M1: Hell Beneath";
public final static String  HUSTR_E4M2="E4M2: Perfect Hatred";
public final static String  HUSTR_E4M3="E4M3: Sever The Wicked";
public final static String  HUSTR_E4M4="E4M4: Unruly Evil";
public final static String  HUSTR_E4M5="E4M5: They Will Repent";
public final static String  HUSTR_E4M6="E4M6: Against Thee Wickedly";
public final static String  HUSTR_E4M7="E4M7: And Hell Followed";
public final static String  HUSTR_E4M8="E4M8: Unto The Cruel";
public final static String  HUSTR_E4M9="E4M9: Fear";

public final static String  HUSTR_1="level 1: entryway";
public final static String  HUSTR_2="level 2: underhalls";
public final static String  HUSTR_3="level 3: the gantlet";
public final static String  HUSTR_4="level 4: the focus";
public final static String  HUSTR_5="level 5: the waste tunnels";
public final static String  HUSTR_6="level 6: the crusher";
public final static String  HUSTR_7="level 7: dead simple";
public final static String  HUSTR_8="level 8: tricks and traps";
public final static String  HUSTR_9="level 9: the pit";
public final static String  HUSTR_10="level 10: refueling base";
public final static String  HUSTR_11="level 11: 'o' of destruction!";

public final static String  HUSTR_12="level 12: the factory";
public final static String  HUSTR_13="level 13: downtown";
public final static String  HUSTR_14="level 14: the inmost dens";
public final static String  HUSTR_15="level 15: industrial zone";
public final static String  HUSTR_16="level 16: suburbs";
public final static String  HUSTR_17="level 17: tenements";
public final static String  HUSTR_18="level 18: the courtyard";
public final static String  HUSTR_19="level 19: the citadel";
public final static String  HUSTR_20="level 20: gotcha!";

public final static String  HUSTR_21="level 21: nirvana";
public final static String  HUSTR_22="level 22: the catacombs";
public final static String  HUSTR_23="level 23: barrels o' fun";
public final static String  HUSTR_24="level 24: the chasm";
public final static String  HUSTR_25="level 25: bloodfalls";
public final static String  HUSTR_26="level 26: the abandoned mines";
public final static String  HUSTR_27="level 27: monster condo";
public final static String  HUSTR_28="level 28: the spirit world";
public final static String  HUSTR_29="level 29: the living end";
public final static String  HUSTR_30="level 30: icon of sin";

public final static String  HUSTR_31="level 31: wolfenstein";
public final static String  HUSTR_32="level 32: grosse";
public final static String  HUSTR_33="level 33: betray";

public final static String  PHUSTR_1="level 1: congo";
public final static String  PHUSTR_2="level 2: well of souls";
public final static String  PHUSTR_3="level 3: aztec";
public final static String  PHUSTR_4="level 4: caged";
public final static String  PHUSTR_5="level 5: ghost town";
public final static String  PHUSTR_6="level 6: baron's lair";
public final static String  PHUSTR_7="level 7: caughtyard";
public final static String  PHUSTR_8="level 8: realm";
public final static String  PHUSTR_9="level 9: abattoire";
public final static String  PHUSTR_10="level 10: onslaught";
public final static String  PHUSTR_11="level 11: hunted";

public final static String  PHUSTR_12="level 12: speed";
public final static String  PHUSTR_13="level 13: the crypt";
public final static String  PHUSTR_14="level 14: genesis";
public final static String  PHUSTR_15="level 15: the twilight";
public final static String  PHUSTR_16="level 16: the omen";
public final static String  PHUSTR_17="level 17: compound";
public final static String  PHUSTR_18="level 18: neurosphere";
public final static String  PHUSTR_19="level 19: nme";
public final static String  PHUSTR_20="level 20: the death domain";

public final static String  PHUSTR_21="level 21: slayer";
public final static String  PHUSTR_22="level 22: impossible mission";
public final static String  PHUSTR_23="level 23: tombstone";
public final static String  PHUSTR_24="level 24: the final frontier";
public final static String  PHUSTR_25="level 25: the temple of darkness";
public final static String  PHUSTR_26="level 26: bunker";
public final static String  PHUSTR_27="level 27: anti-christ";
public final static String  PHUSTR_28="level 28: the sewers";
public final static String  PHUSTR_29="level 29: odyssey of noises";
public final static String  PHUSTR_30="level 30: the gateway of hell";

public final static String  PHUSTR_31="level 31: cyberden";
public final static String  PHUSTR_32="level 32: go 2 it";

public final static String  THUSTR_1="level 1: system control";
public final static String  THUSTR_2="level 2: human bbq";
public final static String  THUSTR_3="level 3: power control";
public final static String  THUSTR_4="level 4: wormhole";
public final static String  THUSTR_5="level 5: hanger";
public final static String  THUSTR_6="level 6: open season";
public final static String  THUSTR_7="level 7: prison";
public final static String  THUSTR_8="level 8: metal";
public final static String  THUSTR_9="level 9: stronghold";
public final static String  THUSTR_10="level 10: redemption";
public final static String  THUSTR_11="level 11: storage facility";

public final static String  THUSTR_12="level 12: crater";
public final static String  THUSTR_13="level 13: nukage processing";
public final static String  THUSTR_14="level 14: steel works";
public final static String  THUSTR_15="level 15: dead zone";
public final static String  THUSTR_16="level 16: deepest reaches";
public final static String  THUSTR_17="level 17: processing area";
public final static String  THUSTR_18="level 18: mill";
public final static String  THUSTR_19="level 19: shipping/respawning";
public final static String  THUSTR_20="level 20: central processing";

public final static String  THUSTR_21="level 21: administration center";
public final static String  THUSTR_22="level 22: habitat";
public final static String  THUSTR_23="level 23: lunar mining project";
public final static String  THUSTR_24="level 24: quarry";
public final static String  THUSTR_25="level 25: baron's den";
public final static String  THUSTR_26="level 26: ballistyx";
public final static String  THUSTR_27="level 27: mount pain";
public final static String  THUSTR_28="level 28: heck";
public final static String  THUSTR_29="level 29: river styx";
public final static String  THUSTR_30="level 30: last call";

public final static String  THUSTR_31="level 31: pharaoh";
public final static String  THUSTR_32="level 32: caribbean";

public final static String  HUSTR_CHATMACRO1="I'm ready to kick butt!";
public final static String  HUSTR_CHATMACRO2="I'm OK.";
public final static String  HUSTR_CHATMACRO3="I'm not looking too good!";
public final static String  HUSTR_CHATMACRO4="Help!";
public final static String  HUSTR_CHATMACRO5="You suck!";
public final static String  HUSTR_CHATMACRO6="Next time, scumbag...";
public final static String  HUSTR_CHATMACRO7="Come here!";
public final static String  HUSTR_CHATMACRO8="I'll take care of it.";
public final static String  HUSTR_CHATMACRO9="Yes";
public final static String  HUSTR_CHATMACRO0="No";

public final static String  HUSTR_TALKTOSELF1="You mumble to yourself";
public final static String  HUSTR_TALKTOSELF2="Who's there?";
public final static String  HUSTR_TALKTOSELF3="You scare yourself";
public final static String  HUSTR_TALKTOSELF4="You start to rave";
public final static String  HUSTR_TALKTOSELF5="You've lost it...";

public final static String  HUSTR_MESSAGESENT="[Message Sent]";

// The following should NOT be changed unless it seems
// just AWFULLY necessary

public final static String  HUSTR_PLRGREEN="Green: ";
public final static String  HUSTR_PLRINDIGO="Indigo: ";
public final static String  HUSTR_PLRBROWN="Brown: ";
public final static String  HUSTR_PLRRED    ="Red: ";

public final static char  HUSTR_KEYGREEN = 'g';
public final static char  HUSTR_KEYINDIGO ='i';
public final static char  HUSTR_KEYBROWN = 'b';
public final static char  HUSTR_KEYRED   = 'r';

//
//  AM_map.C
//

public final static String  AMSTR_FOLLOWON="Follow Mode ON";
public final static String  AMSTR_FOLLOWOFF="Follow Mode OFF";

public final static String  AMSTR_GRIDON="Grid ON";
public final static String  AMSTR_GRIDOFF="Grid OFF";

public final static String  AMSTR_MARKEDSPOT="Marked Spot";
public final static String  AMSTR_MARKSCLEARED="All Marks Cleared";

//
//  ST_stuff.C
//

public final static String  STSTR_MUS   ="Music Change";
public final static String  STSTR_NOMUS ="IMPOSSIBLE SELECTION";
public final static String  STSTR_DQDON ="Degreelessness Mode On";
public final static String  STSTR_DQDOFF="Degreelessness Mode Off";

public final static String  STSTR_KFAADDED="Very Happy Ammo Added";
public final static String  STSTR_FAADDED="Ammo (no keys) Added";

public final static String  STSTR_NCON  ="No Clipping Mode ON";
public final static String  STSTR_NCOFF ="No Clipping Mode OFF";

public final static String  STSTR_BEHOLD="inVuln, Str, Inviso, Rad, Allmap, or Lite-amp";
public final static String  STSTR_BEHOLDX="Power-up Toggled";

public final static String  STSTR_CHOPPERS="... doesn't suck - GM";
public final static String  STSTR_CLEV  ="Changing Level...";

//
//  F_Finale.C
//
public final static String  E1TEXT =(
"Once you beat the big badasses and\n"+
"clean out the moon base you're supposed\n"+
"to win, aren't you? Aren't you? Where's\n"+
"your fat reward and ticket home? What\n"+
"the hell is this? It's not supposed to\n"+
"end this way!\n"+
"\n" +
"It stinks like rotten meat, but looks\n"+
"like the lost Deimos base.  Looks like\n"+
"you're stuck on The Shores of Hell.\n"+
"The only way out is through.\n"+
"\n"+
"To continue the DOOM experience, play\n"+
"The Shores of Hell and its amazing\n"+
"sequel, Inferno!\n");


public final static String  E2TEXT =(
"You've done it! The hideous cyber-\n"+
"demon lord that ruled the lost Deimos\n"+
"moon base has been slain and you\n"+
"are triumphant! But ... where are\n"+
"you? You clamber to the edge of the\n"+
"moon and look down to see the awful\n"+
"truth.\n" +
"\n"+
"Deimos floats above Hell itself!\n"+
"You've never heard of anyone escaping\n"+
"from Hell, but you'll make the bastards\n"+
"sorry they ever heard of you! Quickly,\n"+
"you rappel down to  the surface of\n"+
"Hell.\n"+
"\n" +
"Now, it's on to the final chapter of\n"+
"DOOM! -- Inferno.");


public final static String  E3TEXT =(
"The loathsome spiderdemon that\n"+
"masterminded the invasion of the moon\n"+
"bases and caused so much death has had\n"+
"its ass kicked for all time.\n"+
"\n"+
"A hidden doorway opens and you enter.\n"+
"You've proven too tough for Hell to\n"+
"contain, and now Hell at last plays\n"+
"fair -- for you emerge from the door\n"+
"to see the green fields of Earth!\n"+
"Home at last.\n" +
"\n"+
"You wonder what's been happening on\n"+
"Earth while you were battling evil\n"+
"unleashed. It's good that no Hell-\n"+
"spawn could have come through that\n"+
"door with you ...");


public final static String  E4TEXT =(
"the spider mastermind must have sent forth\n"+
"its legions of hellspawn before your\n"+
"final confrontation with that terrible\n"+
"beast from hell.  but you stepped forward\n"+
"and brought forth eternal damnation and\n"+
"suffering upon the horde as a true hero\n"+
"would in the face of something so evil.\n"+
"\n"+
"besides, someone was gonna pay for what\n"+
"happened to daisy, your pet rabbit.\n"+
"\n"+
"but now, you see spread before you more\n"+
"potential pain and gibbitude as a nation\n"+
"of demons run amok among our cities.\n"+
"\n"+
"next stop, hell on earth!");


// after level 6, put this:

public final static String  C1TEXT =(
"YOU HAVE ENTERED DEEPLY INTO THE INFESTED\n" +
"STARPORT. BUT SOMETHING IS WRONG. THE\n" +
"MONSTERS HAVE BROUGHT THEIR OWN REALITY\n" +
"WITH THEM, AND THE STARPORT'S TECHNOLOGY\n" +
"IS BEING SUBVERTED BY THEIR PRESENCE.\n" +
"\n"+
"AHEAD, YOU SEE AN OUTPOST OF HELL, A\n" +
"FORTIFIED ZONE. IF YOU CAN GET PAST IT,\n" +
"YOU CAN PENETRATE INTO THE HAUNTED HEART\n" +
"OF THE STARBASE AND FIND THE CONTROLLING\n" +
"SWITCH WHICH HOLDS EARTH'S POPULATION\n" +
"HOSTAGE.");

// After level 11, put this:

public final static String  C2TEXT =(
"YOU HAVE WON! YOUR VICTORY HAS ENABLED\n" +
"HUMANKIND TO EVACUATE EARTH AND ESCAPE\n"+
"THE NIGHTMARE.  NOW YOU ARE THE ONLY\n"+
"HUMAN LEFT ON THE FACE OF THE PLANET.\n"+
"CANNIBAL MUTATIONS, CARNIVOROUS ALIENS,\n"+
"AND EVIL SPIRITS ARE YOUR ONLY NEIGHBORS.\n"+
"YOU SIT BACK AND WAIT FOR DEATH, CONTENT\n"+
"THAT YOU HAVE SAVED YOUR SPECIES.\n"+
"\n"+
"BUT THEN, EARTH CONTROL BEAMS DOWN A\n"+
"MESSAGE FROM SPACE: \"SENSORS HAVE LOCATED\n"+
"THE SOURCE OF THE ALIEN INVASION. IF YOU\n"+
"GO THERE, YOU MAY BE ABLE TO BLOCK THEIR\n"+
"ENTRY.  THE ALIEN BASE IS IN THE HEART OF\n"+
"YOUR OWN HOME CITY, NOT FAR FROM THE\n"+
"STARPORT.\" SLOWLY AND PAINFULLY YOU GET\n"+
"UP AND RETURN TO THE FRAY.");


// After level 20, put this:

public final static String  C3TEXT =(
"YOU ARE AT THE CORRUPT HEART OF THE CITY,\n"+
"SURROUNDED BY THE CORPSES OF YOUR ENEMIES.\n"+
"YOU SEE NO WAY TO DESTROY THE CREATURES'\n"+
"ENTRYWAY ON THIS SIDE, SO YOU CLENCH YOUR\n"+
"TEETH AND PLUNGE THROUGH IT.\n"+
"\n"+
"THERE MUST BE A WAY TO CLOSE IT ON THE\n"+
"OTHER SIDE. WHAT DO YOU CARE IF YOU'VE\n"+
"GOT TO GO THROUGH HELL TO GET TO IT?");


// After level 29, put this:

public final static String  C4TEXT =(
"THE HORRENDOUS VISAGE OF THE BIGGEST\n"+
"DEMON YOU'VE EVER SEEN CRUMBLES BEFORE\n"+
"YOU, AFTER YOU PUMP YOUR ROCKETS INTO\n"+
"HIS EXPOSED BRAIN. THE MONSTER SHRIVELS\n"+
"UP AND DIES, ITS THRASHING LIMBS\n"+
"DEVASTATING UNTOLD MILES OF HELL'S\n"+
"SURFACE.\n"+
"\n"+
"YOU'VE DONE IT. THE INVASION IS OVER.\n"+
"EARTH IS SAVED. HELL IS A WRECK. YOU\n"+
"WONDER WHERE BAD FOLKS WILL GO WHEN THEY\n"+
"DIE, NOW. WIPING THE SWEAT FROM YOUR\n"+
"FOREHEAD YOU BEGIN THE LONG TREK BACK\n"+
"HOME. REBUILDING EARTH OUGHT TO BE A\n"+
"LOT MORE FUN THAN RUINING IT WAS.\n");



// Before level 31, put this:

public final static String  C5TEXT =(
"CONGRATULATIONS, YOU'VE FOUND THE SECRET\n"+
"LEVEL! LOOKS LIKE IT'S BEEN BUILT BY\n"+
"HUMANS, RATHER THAN DEMONS. YOU WONDER\n"+
"WHO THE INMATES OF THIS CORNER OF HELL\n"+
"WILL BE.");


// Before level 32, put this:

public final static String  C6TEXT =(
"CONGRATULATIONS, YOU'VE FOUND THE\n"+
"SUPER SECRET LEVEL!  YOU'D BETTER\n"+
"BLAZE THROUGH THIS ONE!\n");


// after map 06 

public final static String  P1TEXT  =(
"You gloat over the steaming carcass of the\n"+
"Guardian.  With its death, you've wrested\n"+
"the Accelerator from the stinking claws\n"+
"of Hell.  You relax and glance around the\n"+
"room.  Damn!  There was supposed to be at\n"+
"least one working prototype, but you can't\n"+
"see it. The demons must have taken it.\n"+
"\n"+
"You must find the prototype, or all your\n"+
"struggles will have been wasted. Keep\n"+
"moving, keep fighting, keep killing.\n"+
"Oh yes, keep living, too.");


// after map 11

public final static String  P2TEXT =(
"Even the deadly Arch-Vile labyrinth could\n"+
"not stop you, and you've gotten to the\n"+
"prototype Accelerator which is soon\n"+
"efficiently and permanently deactivated.\n"+
"\n"+
"You're good at that kind of thing.");


// after map 20

public final static String  P3TEXT =(
"You've bashed and battered your way into\n"+
"the heart of the devil-hive.  Time for a\n"+
"Search-and-Destroy mission, aimed at the\n"+
"Gatekeeper, whose foul offspring is\n"+
"cascading to Earth.  Yeah, he's bad. But\n"+
"you know who's worse!\n"+
"\n"+
"Grinning evilly, you check your gear, and\n"+
"get ready to give the bastard a little Hell\n"+
"of your own making!");

// after map 30

public final static String  P4TEXT =(
"The Gatekeeper's evil face is splattered\n"+
"all over the place.  As its tattered corpse\n"+
"collapses, an inverted Gate forms and\n"+
"sucks down the shards of the last\n"+
"prototype Accelerator, not to mention the\n"+
"few remaining demons.  You're done. Hell\n"+
"has gone back to pounding bad dead folks \n"+
"instead of good live ones.  Remember to\n"+
"tell your grandkids to put a rocket\n"+
"launcher in your coffin. If you go to Hell\n"+
"when you die, you'll need it for some\n"+
"final cleaning-up ...");

// before map 31

public final static String  P5TEXT =(
"You've found the second-hardest level we\n"+
"got. Hope you have a saved game a level or\n"+
"two previous.  If not, be prepared to die\n"+
"aplenty. For master marines only.");

// before map 32

public final static String  P6TEXT =(
"Betcha wondered just what WAS the hardest\n"+
"level we had ready for ya?  Now you know.\n"+
"No one gets out alive.");


public final static String  T1TEXT =(
"You've fought your way out of the infested\n"+
"experimental labs.   It seems that UAC has\n"+
"once again gulped it down.  With their\n"+
"high turnover, it must be hard for poor\n"+
"old UAC to buy corporate health insurance\n"+
"nowadays..\n"+
"\n"+
"Ahead lies the military complex, now\n"+
"swarming with diseased horrors hot to get\n"+
"their teeth into you. With luck, the\n"+
"complex still has some warlike ordnance\n"+
"laying around.");


public final static String  T2TEXT =(
"You hear the grinding of heavy machinery\n"+
"ahead.  You sure hope they're not stamping\n"+
"out new hellspawn, but you're ready to\n"+
"ream out a whole herd if you have to.\n"+
"They might be planning a blood feast, but\n"+
"you feel about as mean as two thousand\n"+
"maniacs packed into one mad killer.\n"+
"\n"+
"You don't plan to go down easy.");


public final static String  T3TEXT =(
"The vista opening ahead looks real damn\n"+
"familiar. Smells familiar, too -- like\n"+
"fried excrement. You didn't like this\n"+
"place before, and you sure as hell ain't\n"+
"planning to like it now. The more you\n"+
"brood on it, the madder you get.\n"+
"Hefting your gun, an evil grin trickles\n"+
"onto your face. Time to take some names.");

public final static String  T4TEXT =(
"Suddenly, all is silent, from one horizon\n"+
"to the other. The agonizing echo of Hell\n"+
"fades away, the nightmare sky turns to\n"+
"blue, the heaps of monster corpses start \n"+
"to evaporate along with the evil stench \n"+
"that filled the air. Jeeze, maybe you've\n"+
"done it. Have you really won?\n"+
"\n"+
"Something rumbles in the distance.\n"+
"A blue light begins to glow inside the\n"+
"ruined skull of the demon-spitter.");


public final static String  T5TEXT =(
"What now? Looks totally different. Kind\n"+
"of like King Tut's condo. Well,\n"+
"whatever's here can't be any worse\n"+
"than usual. Can it?  Or maybe it's best\n"+
"to let sleeping gods lie..");


public final static String  T6TEXT =(
"Time for a vacation. You've burst the\n"+
"bowels of hell and by golly you're ready\n"+
"for a break. You mutter to yourself,\n"+
"Maybe someone else can kick Hell's ass\n"+
"next time around. Ahead lies a quiet town,\n"+
"with peaceful flowing water, quaint\n"+
"buildings, and presumably no Hellspawn.\n"+
"\n"+
"As you step off the transport, you hear\n"+
"the stomp of a cyberdemon's iron shoe.");



//
// Character cast strings F_FINALE.C
//
public final static String  CC_ZOMBIE="ZOMBIEMAN";
public final static String  CC_SHOTGUN="SHOTGUN GUY";
public final static String  CC_HEAVY="HEAVY WEAPON DUDE";
public final static String  CC_IMP="IMP";
public final static String  CC_DEMON="DEMON";
public final static String  CC_LOST="LOST SOUL";
public final static String  CC_CACO="CACODEMON";
public final static String  CC_HELL="HELL KNIGHT";
public final static String  CC_BARON="BARON OF HELL";
public final static String  CC_ARACH="ARACHNOTRON";
public final static String  CC_PAIN="PAIN ELEMENTAL";
public final static String  CC_REVEN="REVENANT";
public final static String  CC_MANCU="MANCUBUS";
public final static String  CC_ARCH="ARCH-VILE";
public final static String  CC_SPIDER="THE SPIDER MASTERMIND";
public final static String  CC_CYBER="THE CYBERDEMON";
public final static String  CC_NAZI="WAFFEN SS. SIEG HEIL!";
public final static String  CC_KEEN="COMMANDER KEEN";
public final static String  CC_BARREL="EXPLODING BARREL";
public final static String  CC_HERO="OUR HERO";

}
package doom;

import java.io.DataInputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

import w.CacheableDoomObject;
import w.IReadableDoomObject;

public class ticcmd_t implements IDatagramSerializable, IReadableDoomObject,CacheableDoomObject{
    
    // The length datagrams are supposed to have, for full compatibility.
    
    public static final int TICCMDLEN=8;
    
    // Initializes ticcmd buffer, too.
    
    public ticcmd_t(){
        this.buffer=new byte[TICCMDLEN];
    }
    
    /** *2048 for move */
    public byte forwardmove;

    /** *2048 for move */
    public byte sidemove;

    /** <<16 for angle delta */
    public short angleturn;

    /** checks for net game */
    public short consistancy;
    
    /** MAES: these are unsigned bytes :-( 
     *  However over networks, if we wish for vanilla compatibility,
     *  these must be reduced to 8-bit "chars"
     * */

    public char chatchar, buttons; 
    
    /** HERETIC look/fly up/down/centering */    
    public char		lookfly; 
    
    // TODO: will be ignored during vanilla demos. Consider using specialized
    // per-demotype readers instead of Cacheable/Unpackage.

    /** replaces G_CmdChecksum (ticcmd_t cmd) */

    /////////////////////////////////////////////
    
    // For datagram serialization
    private byte[] buffer;
    
    public int getChecksum(ticcmd_t cmd) {
        int sum = 0;
        sum += forwardmove;
        sum += sidemove;
        sum += angleturn;
        sum += consistancy;
        sum += chatchar;
        sum += buttons;
        return sum;
        }

    /** because Cloneable is bullshit */
    
     public void copyTo(ticcmd_t dest){
         dest.forwardmove = forwardmove;
         dest.sidemove = sidemove;
         dest.angleturn= angleturn;
         dest.consistancy= consistancy;
         dest.chatchar= chatchar;
         dest.buttons= buttons;
         dest.lookfly=lookfly;
     }
     
     private static StringBuilder sb=new StringBuilder();
     
     public String toString(){
         sb.setLength(0);
         sb.append(" forwardmove ");
         sb.append(Integer.toHexString(this.forwardmove)); 
         sb.append(" sidemove ");
         sb.append(Integer.toHexString(this.sidemove)); 
         sb.append(" angleturn ");
         sb.append(Integer.toHexString(this.angleturn)); 
         sb.append(" consistancy ");
         sb.append(Integer.toHexString(this.consistancy));      
         sb.append(" chatchar ");
         sb.append(chatchar);         
        sb.append(" buttons ");
         sb.append(Integer.toHexString(this.buttons));
         return sb.toString();
     }

    @Override
    public byte[] pack() {
        buffer[0]=forwardmove;
        buffer[1]=sidemove;        
        buffer[2]=(byte) (angleturn>>>8);
        buffer[3]=(byte) (angleturn&0x00FF);
        buffer[4]=(byte) (consistancy>>>8);
        buffer[5]=(byte) (consistancy&0x00FF);

        // We only send 8 bytes because the original length was 8 bytes.
        buffer[6]=(byte) (chatchar&0x00FF);
        buffer[7]=(byte) (buttons&0x00FF);
        
        return buffer;
    }

    @Override
    public void pack(byte[] buf, int offset) {
        buf[0+offset]=forwardmove;
        buf[1+offset]=sidemove;        
        buf[2+offset]=(byte) (angleturn>>>8);
        buf[3+offset]=(byte) (angleturn&0x00FF);
        buf[4+offset]=(byte) (consistancy>>>8);
        buf[5+offset]=(byte) (consistancy&0x00FF);

        // We only send 8 bytes because the original length was 8 bytes.
        buf[6+offset]=(byte) (chatchar&0x00FF);
        buf[7+offset]=(byte) (buttons&0x00FF);
        
    }

    @Override
    public void unpack(byte[] buf) {
        unpack(buf,0);        
        }

    @Override
    public void unpack(byte[] buf, int offset) {
        forwardmove=buf[0+offset];
        sidemove=   buf[1+offset];        
        angleturn=(short)(buf[2+offset]<<8|buf[3+offset]);
        consistancy=(short)(buf[4+offset]<<8|buf[5+offset]);
        // We blow these up to full chars.
        chatchar=(char)(0x00FF& buf[6+offset]);
        buttons=(char) (0x00FF& buf[7+offset]);
        
    }

    @Override
    public byte[] cached() {
        return this.buffer;
    }

    @Override
    public void read(DataInputStream f)
            throws IOException {
    	iobuffer.position(0);
    	iobuffer.order(ByteOrder.LITTLE_ENDIAN);
    	f.read(iobuffer.array());
    	unpack(iobuffer);
    }
    
    /** This is useful only when loading/saving players from savegames.
     *  It's NOT interchangeable with datagram methods, because it
     *  does not use the network byte order.
     */

    @Override
    public void unpack(ByteBuffer f)
            throws IOException {
        f.order(ByteOrder.LITTLE_ENDIAN);
        forwardmove=f.get();
        sidemove=   f.get();        
        // Even if they use the "unsigned char" syntax, angleturn is signed.
        angleturn=f.getShort();
        consistancy=f.getShort();
        // We blow these up to full chars.
        chatchar=(char) f.get();
        buttons=(char) f.get();
        
    }
    
    /** Ditto, we only pack some of the fields.
     * 
     * @param f
     * @throws IOException
     */
    public void pack(ByteBuffer f)
            throws IOException {
        f.order(ByteOrder.LITTLE_ENDIAN);
        f.put(forwardmove);
        f.put(sidemove);        
        // LE order on disk for vanilla compatibility.
        f.putShort(angleturn);
        f.putShort(consistancy);
        // We crimp these to bytes :-(
        f.put((byte) chatchar);
        f.put((byte) buttons);     
    }
    
    private static ByteBuffer iobuffer=ByteBuffer.allocate(8);
    
};

package doom;


//
// INTERMISSION
// Structure passed e.g. to WI_Start(wb)
//

public class wbplayerstruct_t implements Cloneable{
    
    public wbplayerstruct_t(){
        frags=new int[4];
    }
     public boolean in; // whether the player is in game
     
     /** Player stats, kills, collected items etc. */
     public int     skills;
     public int     sitems;
     public int     ssecret;
     public int     stime; 
     public int[]   frags;
     /** current score on entry, modified on return */
     public int     score;
   
     public wbplayerstruct_t clone(){
         wbplayerstruct_t r=null;
        try {
            r = (wbplayerstruct_t)super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
         /*r.in=this.in;
         r.skills=this.skills;
         r.sitems=this.sitems;
         r.ssecret=this.ssecret;
         r.stime=this.stime; */
         System.arraycopy(this.frags, 0, r.frags,0,r.frags.length);
         // r.score=this.score;
         
         
        return r;
         
     }
     
 }

package doom;

import static data.Limits.MAXPLAYERS;
import utils.C2JUtils;

public class wbstartstruct_t implements Cloneable{

        public wbstartstruct_t(){
            plyr=new wbplayerstruct_t[MAXPLAYERS];
            C2JUtils.initArrayOfObjects(plyr, wbplayerstruct_t.class);
        }
    
        public int      epsd;   // episode # (0-2)

        // if true, splash the secret level
        public boolean  didsecret;
        
        // previous and next levels, origin 0
        public int      last;
        public int      next;   
        
        public int      maxkills;
        public int      maxitems;
        public int      maxsecret;
        public int      maxfrags;

        /** the par time */
        public int      partime;
        
        /** index of this player in game */
        public int      pnum;   
        /** meant to be treated as a "struct", therefore assignments should be deep copies */
        public wbplayerstruct_t[]   plyr;
        
        public wbstartstruct_t clone(){
            wbstartstruct_t cl=null;
            try {
                cl=(wbstartstruct_t)super.clone();
            } catch (CloneNotSupportedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            /*cl.epsd=this.epsd;
            cl.didsecret=this.didsecret;
            cl.last=this.last;
            cl.next=this.next;
            cl.maxfrags=this.maxfrags;
            cl.maxitems=this.maxitems;
            cl.maxsecret=this.maxsecret;
            cl.maxkills=this.maxkills;
            cl.partime=this.partime;
            cl.pnum=this.pnum;*/
            for (int i=0;i<cl.plyr.length;i++){
                cl.plyr[i]=this.plyr[i].clone();    
            }
            //cl.plyr=this.plyr.clone();
            
            return cl;
            
        }
        
    } 

package doom;

import java.io.IOException;

/** Stuff that the "main" is supposed to do. DoomMain implements those.
 * 
 * @author Maes
 *
 */


public interface IDoom {


	/** Called by IO functions when input is detected. */
	void PostEvent (event_t ev);
	void PageTicker ();
	void PageDrawer ();
	void AdvanceDemo ();
	void StartTitle ();
    void QuitNetGame() throws IOException; 

}
package doom;

 // Player states.
 //
 public enum  playerstate_t
 {
     // Playing or camping.
     PST_LIVE,
     // Dead on the ground, view follows killer.
     PST_DEAD,
     // Ready to restart/respawn???
     PST_REBORN      

 };
package n;

import i.DoomStatusAware;
import i.IDoomSystem;
import doom.DoomMain;
import doom.DoomStatus;
import doom.IDoomGameNetworking;
import doom.NetConsts;
import doom.doomcom_t;

/** Does nothing.
 *  Allows running single-player games without an actual network.
 *  Hopefully, it will be replaced by a real UDP-based driver one day.
 *  
 * @author Velktron
 *
 */

public class DummyNetworkDriver implements NetConsts,DoomSystemNetworking, DoomStatusAware{

	////////////// STATUS ///////////

	IDoomSystem I;
	DoomMain DM;
	IDoomGameNetworking DGN;

	public DummyNetworkDriver(DoomStatus DC){
		updateStatus(DC);
	}

	@Override
	public void InitNetwork() {
		doomcom_t doomcom =new doomcom_t();
		doomcom.id=DOOMCOM_ID;
		doomcom.ticdup=1;

		// single player game
		DM.netgame = false;
		doomcom.id = DOOMCOM_ID;
		doomcom.numplayers = doomcom.numnodes = 1;
		doomcom.deathmatch = 0;
		doomcom.consoleplayer = 0;
		DGN.setDoomCom(doomcom);
	}

	@Override
	public void NetCmd() {
		// TODO Auto-generated method stub

	}

	@Override
	public void updateStatus(DoomStatus DC) {
		this.DM=DC.DM;
		this.DGN=DC.DM;
	}

}

package n;

// Emacs style mode select   -*- C++ -*- 
//-----------------------------------------------------------------------------
//
// $Id: DoomSystemNetworking.java,v 1.1 2010/11/17 23:55:06 velktron Exp $
//
// Copyright (C) 1993-1996 by id Software, Inc.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// DESCRIPTION:
//  System specific network interface stuff.
//
//-----------------------------------------------------------------------------

public interface DoomSystemNetworking{


// Called by D_DoomMain.

public void InitNetwork ();
public void NetCmd ();


}

//-----------------------------------------------------------------------------
//
// $Log: DoomSystemNetworking.java,v $
// Revision 1.1  2010/11/17 23:55:06  velktron
// Kind of playable/controllable.
//
// Revision 1.1  2010/10/22 16:22:43  velktron
// Renderer works stably enough but a ton of bleeding. Started working on netcode.
//
//
//-----------------------------------------------------------------------------


package n;

import doom.IDoomGameNetworking;
import doom.doomcom_t;

public class DummyNetworkHandler implements IDoomGameNetworking{

    @Override
    public void NetUpdate() {
        // TODO Auto-generated method stub
        
    }

    @Override
    public void TryRunTics() {
        // TODO Auto-generated method stub
        
    }

    @Override
    public doomcom_t getDoomCom() {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public void setDoomCom(doomcom_t doomcom) {
        // TODO Auto-generated method stub
        
    }

    @Override
    public int getTicdup() {
        // TODO Auto-generated method stub
        return 0;
    }

    @Override
    public void setTicdup(int ticdup) {
        // TODO Auto-generated method stub
        
    }

}

package n;

import static data.Limits.MAXNETNODES;
import static doom.NetConsts.CMD_GET;
import static doom.NetConsts.CMD_SEND;
import static doom.NetConsts.DOOMCOM_ID;
import i.DoomStatusAware;
import i.IDoomSystem;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketException;
import java.net.SocketTimeoutException;

import w.DoomBuffer;
import doom.DoomContext;
import doom.DoomMain;
import doom.DoomStatus;
import doom.doomcom_t;
import doom.doomdata_t;

// Emacs style mode select   -*- C++ -*- 
//-----------------------------------------------------------------------------
//
// $Id: BasicNetworkInterface.java,v 1.5 2011/05/26 13:39:06 velktron Exp $
//
// Copyright (C) 1993-1996 by id Software, Inc.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// $Log: BasicNetworkInterface.java,v $
// Revision 1.5  2011/05/26 13:39:06  velktron
// Now using ICommandLineManager
//
// Revision 1.4  2011/05/18 16:54:31  velktron
// Changed to DoomStatus
//
// Revision 1.3  2011/05/17 16:53:42  velktron
// _D_'s version.
//
// Revision 1.2  2010/12/20 17:15:08  velktron
// Made the renderer more OO -> TextureManager and other changes as well.
//
// Revision 1.1  2010/11/17 23:55:06  velktron
// Kind of playable/controllable.
//
// Revision 1.2  2010/11/11 15:31:28  velktron
// Fixed "warped floor" error.
//
// Revision 1.1  2010/10/22 16:22:43  velktron
// Renderer works stably enough but a ton of bleeding. Started working on netcode.
//
//
// DESCRIPTION:
//
//-----------------------------------------------------------------------------

public class BasicNetworkInterface
        implements DoomSystemNetworking, DoomStatusAware {


  public static final String rcsid = "$Id: BasicNetworkInterface.java,v 1.5 2011/05/26 13:39:06 velktron Exp $";

  ////////////// STATUS ///////////
  
  IDoomSystem I;
  protected DoomMain DM;
  
  public BasicNetworkInterface(DoomContext DC){
      this.DM=DC.DM;
      //this.myargv=DM.myargv;
      //this.myargc=DM.myargc;
      sendData=new doomdata_t();
      recvData=new doomdata_t();
      // We can do that since the buffer is reused.
      // Note: this will effectively tie doomdata and the datapacket.
      recvPacket=new DatagramPacket(recvData.cached(),recvData.cached().length);
      sendPacket=new DatagramPacket(sendData.cached(),sendData.cached().length);
      
      
  }
  
  // Bind it to the ones inside DN and DM;  
  //doomdata_t netbuffer;
  doomcom_t doomcom;
  
  
  // For some odd reason...
  
  /** Changes endianness of a number */
  
  public static int ntohl(int x) {      
   return       ((((x & 0x000000ff) << 24) | 
                               ((x & 0x0000ff00) <<  8) | 
                               ((x & 0x00ff0000) >>>  8) | 
                               ((x & 0xff000000) >>> 24)));
      }

  public static short ntohs(short x) {
      return (short) (((x & 0x00ff) << 8) | ((x & 0xff00) >>> 8));
      }
        
  public static int htonl(int x) {
      return ntohl(x);
      }
  
  public static short htons(short x){
      return ntohs(x);
  }

  //void    NetSend ();
  //boolean NetListen ();


  //
  // NETWORKING
  //

  // Maes: come on, we all know it's 666.
  int DOOMPORT =  666;//(IPPORT_USERRESERVED +0x1d );
  
  //_D_: for testing purposes. If testing on the same machine, we can't have two UDP servers on the same port
  int RECVPORT = DOOMPORT;
  int SENDPORT = DOOMPORT;

  //DatagramSocket         sendsocket;
  DatagramSocket         insocket;

    
  // MAES: closest java equivalent
  DatagramSocket /*InetAddress*/ sendaddress[]=new DatagramSocket/*InetAddress*/[MAXNETNODES];

  
  interface NetFunction {
      public void invoke();
  }
  

  // To use inside packetsend. Declare once and reuse to save on heap costs.
  private doomdata_t  sendData;
  private doomdata_t  recvData;
  
  // We also reuse always the same DatagramPacket, "peged" to sw's byte buffer.
  private DatagramPacket recvPacket;
  private DatagramPacket sendPacket;
  
  public void sendSocketPacket(DatagramSocket ds, DatagramPacket dp) throws IOException {
      ds.send(dp);
  }

  public PacketSend packetSend = new PacketSend();

  public class PacketSend implements NetFunction {

      @Override
      public void invoke() {
          int     c;

          doomdata_t netbuffer = DM.netbuffer;

          // byte swap: so this is transferred as little endian? Ugh
          /*sendData.checksum = htonl(netbuffer.checksum);
          sendData.player = netbuffer.player;
          sendData.retransmitfrom = netbuffer.retransmitfrom;
          sendData.starttic = netbuffer.starttic;
          sendData.numtics = netbuffer.numtics;
          for (c=0 ; c< netbuffer.numtics ; c++)
          {
              sendData.cmds[c].forwardmove = netbuffer.cmds[c].forwardmove;
              sendData.cmds[c].sidemove = netbuffer.cmds[c].sidemove;
              sendData.cmds[c].angleturn = htons(netbuffer.cmds[c].angleturn);
              sendData.cmds[c].consistancy = htons(netbuffer.cmds[c].consistancy);
              sendData.cmds[c].chatchar = netbuffer.cmds[c].chatchar;
              sendData.cmds[c].buttons = netbuffer.cmds[c].buttons;
          }
*/
          //printf ("sending %i\n",gametic);      
          
          sendData.copyFrom(netbuffer);
          // MAES: This will force the buffer to be refreshed.
          byte[] bytes = sendData.pack();

          /*System.out.print("SEND >> Thisplayer: "+DM.consoleplayer+" numtics: "+sendData.numtics+" consistency: ");
          for (doom.ticcmd_t t: sendData.cmds)
              System.out.print(t.consistancy+",");
          System.out.println();*/

          // The socket already contains the address it needs,
          // and the packet's buffer is already modified. Send away.
          sendPacket.setData(bytes, 0, doomcom.datalength);
          DatagramSocket sendsocket;
          try {
              sendsocket = sendaddress[doomcom.remotenode];
              sendPacket.setSocketAddress(sendsocket.getRemoteSocketAddress());
              sendSocketPacket(sendsocket, sendPacket);
          } catch (Exception e) {
              e.printStackTrace();
              I.Error ("SendPacket error: %s",e.getMessage());
          }

          //  if (c == -1)
          //      I_Error ("SendPacket error: %s",strerror(errno));
      }

  }


  public void socketGetPacket(DatagramSocket ds, DatagramPacket dp) throws IOException {
      ds.receive(dp);
  }
  
  // Used inside PacketGet
  private boolean first=true;

  public PacketGet packetGet = new PacketGet();
  public class PacketGet implements NetFunction {

      @Override
      public void invoke() {
          int         i;
          int         c;

          // Receive back into swp.
          try {
              //recvPacket.setSocketAddress(insocket.getLocalSocketAddress());
              socketGetPacket(insocket, recvPacket);
          } 
          catch (SocketTimeoutException e) {
              doomcom.remotenode = -1;       // no packet
              return;
          }
          catch (Exception e)
          { if (e.getClass()!=java.nio.channels.IllegalBlockingModeException.class){
              I.Error ("GetPacket: %s",e.getStackTrace());
          }
          }

          recvData.unpack(recvPacket.getData());
          InetAddress fromaddress = recvPacket.getAddress();

          /*System.out.print("RECV << Thisplayer: "+DM.consoleplayer+" numtics: "+recvData.numtics+" consistency: ");
          for (doom.ticcmd_t t: recvData.cmds)
              System.out.print(t.consistancy+",");
          System.out.println();*/
          
          {
              //static int first=1;
              if (first){
                  sb.setLength(0);
                  sb.append("("+DM.consoleplayer+") PacketRECV len=");
                  sb.append(recvPacket.getLength());
                  sb.append(":p=[0x");
                  sb.append(Integer.toHexString(recvData.checksum));
                  sb.append(" 0x");
                  sb.append(DoomBuffer.getBEInt(recvData.retransmitfrom,recvData.starttic,recvData.player,recvData.numtics));
                  sb.append("numtics: "+recvData.numtics);
                  System.out.println(sb.toString());
                  first = false;
              }
          }

          // find remote node number
          for (i=0 ; i<doomcom.numnodes ; i++) {
              if (sendaddress[i] != null) {
                  if (fromaddress.equals(sendaddress[i].getInetAddress()))
                      break;
              }
          }

          if (i == doomcom.numnodes)
          {
              // packet is not from one of the players (new game broadcast)
              doomcom.remotenode = -1;       // no packet
              return;
          }

          doomcom.remotenode = (short) i;            // good packet from a game player
          doomcom.datalength = (short) recvPacket.getLength();

          //_D_: temporary hack to test two player on single machine
          //doomcom.remotenode = (short)(RECVPORT-DOOMPORT);
          

          
          // byte swap
          /*doomdata_t netbuffer = DM.netbuffer;
          netbuffer.checksum = ntohl(recvData.checksum);
          netbuffer.player = recvData.player;
          netbuffer.retransmitfrom = recvData.retransmitfrom;
          netbuffer.starttic = recvData.starttic;
          netbuffer.numtics = recvData.numtics;

          for (c=0 ; c< netbuffer.numtics ; c++)
          {
              netbuffer.cmds[c].forwardmove = recvData.cmds[c].forwardmove;
              netbuffer.cmds[c].sidemove = recvData.cmds[c].sidemove;
              netbuffer.cmds[c].angleturn = ntohs(recvData.cmds[c].angleturn);
              netbuffer.cmds[c].consistancy = ntohs(recvData.cmds[c].consistancy);
              netbuffer.cmds[c].chatchar = recvData.cmds[c].chatchar;
              netbuffer.cmds[c].buttons = recvData.cmds[c].buttons;
          } */
          DM.netbuffer.copyFrom(recvData);

      }

  };

  // Maes: oh great. More function pointer "fun".
  NetFunction netget = packetGet;
  NetFunction netsend = packetSend;


  //
  // I_InitNetwork
  //
    
  @Override
  public void InitNetwork() {
      boolean     trueval = true;
      int         i;
      int         p;
      //struct hostent* hostentry;  // host information entry

      doomcom = new doomcom_t();
      //netbuffer = new doomdata_t();
      DM.setDoomCom(doomcom);
      //DM.netbuffer = netbuffer;

      // set up for network
      i = DM.CM.CheckParm ("-dup");
      if ((i!=0) && i< DM.CM.getArgc()-1)
      {
          doomcom.ticdup = (short) (DM.CM.getArgv(i+1).charAt(0)-'0');
          if (doomcom.ticdup < 1)
              doomcom.ticdup = 1;
          if (doomcom.ticdup > 9)
              doomcom.ticdup = 9;
      }
      else
          doomcom. ticdup = 1;

      if (DM.CM.CheckParm ("-extratic")!=0)
          doomcom. extratics = 1;
      else
          doomcom. extratics = 0;

      p = DM.CM.CheckParm ("-port");
      if ((p!=0) && (p<DM.CM.getArgc()-1))
      {
          DOOMPORT = Integer.parseInt(DM.CM.getArgv(p+1));
          System.out.println ("using alternate port "+DOOMPORT);
      }

      // parse network game options,
      //  -net <consoleplayer> <host> <host> ...
      i = DM.CM.CheckParm ("-net");
      if (i==0)
      {
          // single player game
          DM.netgame = false;
          doomcom.id = DOOMCOM_ID;
          doomcom.numplayers = doomcom.numnodes = 1;
          doomcom.deathmatch = 0; // false
          doomcom.consoleplayer = 0;
          return;
      }

      DM.netgame = true;

      // parse player number and host list
      doomcom.consoleplayer = (short) (DM.CM.getArgv(i+1).charAt(0)-'1');
      
      RECVPORT = SENDPORT = DOOMPORT;
      if (doomcom.consoleplayer == 0)
          SENDPORT++;
      else
          RECVPORT++;

      doomcom.numnodes = 1;  // this node for sure

      i++;
      while (++i < DM.CM.getArgc() && DM.CM.getArgv(i).charAt(0) != '-')
      {
          try {
          InetAddress addr = InetAddress.getByName(DM.CM.getArgv(i));
          DatagramSocket ds = new DatagramSocket(null);
          ds.setReuseAddress(true);
          ds.connect(addr, SENDPORT);

          sendaddress[doomcom.numnodes] = ds;
          }catch (Exception e) {
              e.printStackTrace();
          }
          
          doomcom.numnodes++;
      }

      doomcom.id = DOOMCOM_ID;
      doomcom.numplayers = doomcom.numnodes;

      // build message to receive
      try {
          insocket = new DatagramSocket(null);
          insocket.setReuseAddress(true);
          insocket.setSoTimeout(1);
          insocket.bind(new InetSocketAddress(RECVPORT));
      } catch (SocketException e1) {
          // TODO Auto-generated catch block
          e1.printStackTrace();
      }
  }

    @Override
    public void NetCmd() {
          if (insocket == null) //HACK in case "netgame" is due to "addbot"
              return;

        if (DM.doomcom.command == CMD_SEND)
        {
        netsend.invoke ();
        }
        else if (doomcom.command == CMD_GET)
        {
        netget.invoke ();
        }
        else
        I.Error ("Bad net cmd: %i\n",doomcom.command);

    }

    // Instance StringBuilder
    private StringBuilder sb=new StringBuilder();

    @Override
    public void updateStatus(DoomStatus DC) {
        // TODO Auto-generated method stub
        
    }
    
}

package n;

public interface IDoomNet {
    public void NetUpdate();
}

package f;

/* Emacs style mode select   -*- C++ -*- 
//-----------------------------------------------------------------------------
//
// $Id: EndLevel.java,v 1.12 2016/07/04 07:52:26 velktron Exp $
//
// Copyright (C) 1993-1996 by id Software, Inc.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// $Log: EndLevel.java,v $
// Revision 1.12  2016/07/04 07:52:26  velktron
// GPL license header, and cleanup for files that are not in direct correspondence with original ones.
//
// Revision 1.11  2012/09/24 17:16:23  velktron
// Massive merge between HiColor and HEAD. There's no difference from now on, and development continues on HEAD.
//
// Revision 1.8.2.2  2012/09/24 16:57:43  velktron
// Addressed generics warnings.
//
// Revision 1.8.2.1  2011/11/27 18:18:34  velktron
// Use cacheClear() on deactivation.
//
// Revision 1.8  2011/11/01 19:02:57  velktron
// Using screen number constants
//
// Revision 1.7  2011/10/23 18:11:32  velktron
// Generic compliance for DoomVideoInterface
//
// Revision 1.6  2011/08/23 16:13:53  velktron
// Got rid of Z remnants.
//
// Revision 1.5  2011/07/31 21:49:38  velktron
// Changed endlevel drawer's behavior to be closer to prBoom+'s. Allows using 1994TU.WAD while backwards compatible.
//
// Revision 1.4  2011/06/02 14:56:48  velktron
// imports
//
// Revision 1.3  2011/06/02 14:53:21  velktron
// Moved Endlevel constants to AbstractEndLevel
//
// Revision 1.2  2011/06/02 14:14:28  velktron
// Implemented endlevel unloading of graphics, changed state enum.
//
// Revision 1.1  2011/06/02 14:00:48  velktron
// Moved Endlevel stuff  to f, where it makes more sense.
//
// Revision 1.18  2011/05/31 12:25:14  velktron
// Endlevel -mostly- scaled correctly.
//
// Revision 1.17  2011/05/29 22:15:32  velktron
// Introduced IRandom interface.
//
// Revision 1.16  2011/05/24 17:54:02  velktron
// Defaults tester
//
// Revision 1.15  2011/05/23 17:00:39  velktron
// Got rid of verbosity
//
// Revision 1.14  2011/05/21 16:53:24  velktron
// Adapted to use new gamemode system.
//
// Revision 1.13  2011/05/18 16:58:04  velktron
// Changed to DoomStatus
//
// Revision 1.12  2011/05/17 16:52:19  velktron
// Switched to DoomStatus
//
// Revision 1.11  2011/05/11 14:12:08  velktron
// Interfaced with DoomGame
//
// Revision 1.10  2011/05/10 10:39:18  velktron
// Semi-playable Techdemo v1.3 milestone
//
// Revision 1.9  2011/05/06 14:00:54  velktron
// More of _D_'s changes committed.
//
// Revision 1.8  2011/02/11 00:11:13  velktron
// A MUCH needed update to v1.3.
//
// Revision 1.7  2010/12/20 17:15:08  velktron
// Made the renderer more OO -> TextureManager and other changes as well.
//
// Revision 1.6  2010/11/12 13:37:25  velktron
// Rationalized the LUT system - now it's 100% procedurally generated.
//
// Revision 1.5  2010/09/23 07:31:11  velktron
// fuck
//
// Revision 1.4  2010/09/02 15:56:54  velktron
// Bulk of unified renderer copyediting done.
//
// Some changes like e.g. global separate limits class and instance methods for seg_t and node_t introduced.
//
// Revision 1.3  2010/08/23 14:36:08  velktron
// Menu mostly working, implemented Killough's fast hash-based GetNumForName, although it can probably be finetuned even more.
//
// Revision 1.2  2010/08/13 14:06:36  velktron
// Endlevel screen fully functional!
//
// Revision 1.1  2010/07/06 16:32:38  velktron
// Threw some work in WI, now EndLevel. YEAH THERE'S GONNA BE A SEPARATE EndLevel OBJECT THAT'S HOW PIMP THE PROJECT IS!!!!11!!!
//
// Revision 1.1  2010/06/30 08:58:51  velktron
// Let's see if this stuff will finally commit....
//
//
// Most stuff is still  being worked on. For a good place to start and get an idea of what is being done, I suggest checking out the "testers" package.
//
// Revision 1.1  2010/06/29 11:07:34  velktron
// Release often, release early they say...
//
// Commiting ALL stuff done so far. A lot of stuff is still broken/incomplete, and there's still mixed C code in there. I suggest you load everything up in Eclpise and see what gives from there.
//
// A good place to start is the testers/ directory, where you  can get an idea of how a few of the implemented stuff works.
//
//
// DESCRIPTION:
//	Intermission screens.
//
//-----------------------------------------------------------------------------*/


import static data.Defines.*;
import static data.Limits.*;
import static v.DoomVideoRenderer.*;
import defines.*;
import data.sounds.musicenum_t;
import data.sounds.sfxenum_t;
import doom.DoomMain;
import doom.DoomStatus;
import doom.IDoomGame;
import doom.event_t;
import doom.player_t;
import doom.wbplayerstruct_t;
import doom.wbstartstruct_t;
import m.IRandom;
import rr.*;
import s.IDoomSound;
import st.IDoomStatusBar;
import v.DoomVideoRenderer;
import v.IVideoScale;
import w.IWadLoader;
import w.animenum_t;

/** This class (stuff.c) seems to implement the endlevel screens.
 * 
 * @author Maes
 *
 */

public class EndLevel<T,V> extends AbstractEndLevel {

    ////////////////// STATUS ///////////////////
    private DoomMain<?,?> DS;
    private IDoomGame DG;
    private DoomVideoRenderer<?,?> V;
    private IDoomSound S;
    private IWadLoader W;
    private IDoomStatusBar ST;
    private IRandom RND;
    
    private static int COUNT_KILLS=2;
    private static int COUNT_ITEMS=4;
    private static int COUNT_SECRETS=6;
    private static int COUNT_TIME=8;
    private static int COUNT_DONE=10;
    
    static enum endlevel_state{
    	NoState,
    	StatCount,
    	ShowNextLoc, 
    	JustShutOff
    }
    
  //GLOBAL LOCATIONS
    private static final int WI_TITLEY       =2;
    private static final int WI_SPACINGY         =3;
    
    
//
// GENERAL DATA
//

//
// Locally used stuff.
//
public static final int FB= 0;
private static final boolean RANGECHECKING = true;

// Where to draw some stuff. To be scaled up, so they
// are not final.

public static int SP_STATSX;
public static int SP_STATSY;

public static int SP_TIMEX;
public static int SP_TIMEY;

// States for single-player
protected static int SP_KILLS	=	0;
protected static int SP_ITEMS	=	2;
protected static int SP_SECRET	=	4;
protected static int SP_FRAGS	=	6 ;
protected static int SP_TIME		=	8 ;
protected static int SP_PAR		=	SP_TIME;

protected int SP_PAUSE	=	1;

// in seconds
protected  int SHOWNEXTLOCDELAY	=4;
protected  int SHOWLASTLOCDELAY	=SHOWNEXTLOCDELAY;

// used to accelerate or skip a stage
int		acceleratestage;

// wbs->pnum
int		me;

 // specifies current state )
endlevel_state	state;

// contains information passed into intermission
public wbstartstruct_t	wbs;

wbplayerstruct_t[] plrs;  // wbs->plyr[]

// used for general timing
int 		cnt;  

// used for timing of background animation
int 		bcnt;

// signals to refresh everything for one frame
int 		firstrefresh; 

int[]		cnt_kills=new int[MAXPLAYERS];
int[]		cnt_items=new int[MAXPLAYERS];
int[]		cnt_secret=new int[MAXPLAYERS];
int		cnt_time;
int		cnt_par;
int		cnt_pause;

// # of commercial levels
int		NUMCMAPS; 


//
//	GRAPHICS
//

// background (map of levels).
patch_t		bg;

// You Are Here graphic
patch_t[]		yah=new patch_t[2]; 

// splat
patch_t		splat;

/** %, : graphics */
patch_t		percent,colon;

/** 0-9 graphic */
patch_t[]		num=new patch_t[10];

/** minus sign */
 patch_t	wiminus;

// "Finished!" graphics
 patch_t		finished;

// "Entering" graphic
 patch_t		entering; 

// "secret"
 patch_t		sp_secret;

 /** "Kills", "Scrt", "Items", "Frags" */
 patch_t		kills,	secret,items,frags;

/** Time sucks. */
 patch_t		time,	par,sucks;

/** "killers", "victims" */
 patch_t		killers,victims; 

/** "Total", your face, your dead face */
 patch_t		total,	star, bstar;

/** "red P[1..MAXPLAYERS]"*/
 patch_t[]		p=new patch_t[MAXPLAYERS];

/** "gray P[1..MAXPLAYERS]" */
 patch_t[] bp=new patch_t[MAXPLAYERS];

 /** Name graphics of each level (centered) */
 patch_t[]	lnames;

//
// CODE
//

// slam background
// UNUSED  unsigned char *background=0;


public EndLevel(DoomStatus<T,V> DC) {
    this.updateStatus(DC);
   
    // _D_: commented this, otherwise something didn't work
    //this.Start(DS.wminfo);
}

protected void slamBackground()
{
    //    memcpy(screens[0], screens[1], SCREENWIDTH * SCREENHEIGHT);
    // Remember, the second arg is the source!
    System.arraycopy(V.getScreen(SCREEN_BG), 0 ,V.getScreen(SCREEN_FG),0, SCREENWIDTH * SCREENHEIGHT);
    V.MarkRect (0, 0, SCREENWIDTH, SCREENHEIGHT);
}

// The ticker is used to detect keys
//  because of timing issues in netgames.
public boolean Responder(event_t ev)
{
    return false;
}


/** Draws "<Levelname> Finished!" */	
protected void drawLF()
{
    int y = WI_TITLEY;

    // draw <LevelName> 
    V.DrawScaledPatch((320 - lnames[wbs.last].width)/2,
        y, FB,vs, lnames[wbs.last]);

    // draw "Finished!"
    y += (5*lnames[wbs.last].height)/4;
    
    V.DrawScaledPatch((320 - finished.width)/2,
        y, FB,vs, finished);
}



/** Draws "Entering <LevelName>" */
protected void drawEL()
{
    int y = WI_TITLEY; // This is in 320 x 200 coords!

    // draw "Entering"
    V.DrawScaledPatch((320 - entering.width)/2, y, FB,vs, entering);
    
    // HACK: if lnames[wbs.next] DOES have a defined nonzero topoffset, use it.
    // implicitly in DrawScaledPatch, and trump the normal behavior.
    // FIXME: this is only useful in a handful of prBoom+ maps, which use
    // a modified endlevel screen. The reason it works there is the behavior of the 
    // unified patch drawing function, which is approximated with this hack.
    
    
    	if (lnames[wbs.next].topoffset==0) 
    	y += (5*lnames[wbs.next].height)/4;
    // draw level.
        
    V.DrawScaledPatch((320 - lnames[wbs.next].width)/2,
        y, FB,vs, lnames[wbs.next]);
 
}

/** For whatever fucked-up reason, it expects c to be an array of patches, 
 *  and may choose to draw from alternative ones...which however are never loaded,
 *  or are supposed to be "next" in memory or whatever. I kept this behavior, however 
 *  in Java it will NOT work as intended, if ever.
 * 
 * @param n
 * @param c
 */

protected void
drawOnLnode
( int		n,
  patch_t[]	c )
{

    int		i;
    int		left;
    int		top;
    int		right;
    int		bottom;
    boolean	fits = false;

    i = 0;
    do
    {
	left = lnodes[wbs.epsd][n].x - c[i].leftoffset;
	top = lnodes[wbs.epsd][n].y - c[i].topoffset;
	right = left + c[i].width;
	bottom = top + c[i].height;

	if (left >= 0
	    && right < SCREENWIDTH
	    && top >= 0
	    && bottom < SCREENHEIGHT)
	{
	    fits = true;
	}
	else
	{
	    i++;
	}
    } while (!fits && i!=2);

    if (fits && i<2)
    {
	//V.DrawPatch(lnodes[wbs.epsd][n].x, lnodes[wbs.epsd][n].y,
	//	    FB, c[i]);
        V./*DrawPatchDirect*/DrawScaledPatch(lnodes[wbs.epsd][n].x, lnodes[wbs.epsd][n].y,
            FB, vs,c[i]);
    }
    else
    {
	// DEBUG
	System.out.println("Could not place patch on level "+ n+1); 
    }
}



protected void initAnimatedBack()
{
    int		i;
    anim_t	a;

    if (DS.isCommercial())
	return;

    if (wbs.epsd > 2)
	return;

    for (i=0;i<NUMANIMS[wbs.epsd];i++)
    {
	a = anims[wbs.epsd][i];

	// init variables
	a.ctr = -1;

	// specify the next time to draw it
	if (a.type == animenum_t.ANIM_ALWAYS)
	    a.nexttic = bcnt + 1 + (RND.M_Random()%a.period);
	else if (a.type == animenum_t.ANIM_RANDOM)
	    a.nexttic = bcnt + 1 + a.data2+(RND.M_Random()%a.data1);
	else if (a.type == animenum_t.ANIM_LEVEL)
	    a.nexttic = bcnt + 1;
    }

}

protected void updateAnimatedBack()
{
    int		i;
    anim_t	a;

    if (DS.isCommercial())
	return;

    if (wbs.epsd > 2)
	return;

    int aaptr=wbs.epsd;
    
    for (i=0;i<NUMANIMS[wbs.epsd];i++)
    {
	a = anims[aaptr][i];
	
	if (bcnt == a.nexttic)
	{
	    switch (a.type)
	    {
	      case ANIM_ALWAYS:
		if (++anims[aaptr][i].ctr >= a.nanims) a.ctr = 0;
		a.nexttic = bcnt + a.period;
		break;

	      case ANIM_RANDOM:
		a.ctr++;
		if (a.ctr == a.nanims)
		{
		    a.ctr = -1;
		    a.nexttic = bcnt+a.data2+(RND.M_Random()%a.data1);
		}
		else a.nexttic = bcnt + a.period;
		break;
		
	      case ANIM_LEVEL:
		// gawd-awful hack for level anims
		if (!(state == endlevel_state.StatCount && i == 7)
		    && wbs.next == a.data1)
		{
		    a.ctr++;
		    if (a.ctr == a.nanims) a.ctr--;
		    a.nexttic = bcnt + a.period;
		}
		break;
	    }
	}

    }

}

protected void drawAnimatedBack()
{
    int			i;
    anim_t		a;

    if (DS.isCommercial())
	return;

    if (wbs.epsd > 2)
	return;

    for (i=0 ; i<NUMANIMS[wbs.epsd] ; i++)
    {
	a = anims[wbs.epsd][i];

	if (a.ctr >= 0)
	    V.DrawScaledPatch(a.loc.x, a.loc.y, FB,vs, a.p[a.ctr]);
    }

}

/**
 * Draws a number.
 * If digits > 0, then use that many digits minimum,
 *  otherwise only use as many as necessary.
 * Returns new x position.
 */

protected int
drawNum
( int		x,
  int		y,
  int		n,
  int		digits )
{

    int		fontwidth = num[0].width;
    boolean		neg;
    int		temp;

    if (digits < 0)
    {
	if (n==0)
	{
	    // make variable-length zeros 1 digit long
	    digits = 1;
	}
	else
	{
	    // figure out # of digits in #
	    digits = 0;
	    temp = n;

	    while (temp!=0)
	    {
		temp /= 10;
		digits++;
	    }
	}
    }

    neg = (n < 0);
    if (neg)
	n = -n;

    // if non-number, do not draw it
    if (n == 1994)
	return 0;

    // draw the new number
    while ((digits--)!=0)
    {
	x -= fontwidth*BEST_X_SCALE;
	V.DrawScaledPatch(x, y, V_NOSCALESTART|FB,vs, num[ n % 10 ]);
	n /= 10;
    }

    // draw a minus sign if necessary
    if (neg)
	V.DrawScaledPatch(x-=8*BEST_X_SCALE, y, V_NOSCALESTART|FB,vs, wiminus);

    return x;

}

protected 

void
drawPercent
( int		x,
  int		y,
  int		p )
{
    if (p < 0)
	return;

    V.DrawScaledPatch(x, y, V_NOSCALESTART|FB,vs, percent);
    drawNum(x, y, p, -1);
}



//
// Display level completion time and par,
//  or "sucks" message if overflow.
//
protected void drawTime
( int		x,
  int		y,
  int		t )
{

    int		div;
    int		n;

    if (t<0)
	return;

    if (t <= 61*59)
    {
	div = 1;

	do
	{
	    n = (t / div) % 60;
	    x = drawNum(x, y, n, 2) - colon.width*BEST_X_SCALE;
	    div *= 60;

	    // draw
	    if ((div==60) || (t / div)>0)
		V.DrawScaledPatch(x, y, V_NOSCALESTART|FB, vs,colon);
	    
	} while ((t / div)>0);
    }
    else
    {
	// "sucks"
	V.DrawScaledPatch(x - sucks.width*BEST_X_SCALE, y, V_NOSCALESTART|FB, vs,sucks); 
    }
}


protected void End()
{
	state=endlevel_state.JustShutOff;
	V.clearCaches();
    unloadData();
}

protected void unloadData()
{
  int   i;
  int   j;

  W.UnlockLumpNum(wiminus); wiminus=null;

  for (i=0 ; i<10 ; i++) {
    W.UnlockLumpNum(num[i]); num[i]=null;
  	}
    
  if (DS.isCommercial())
    {
      for (i=0 ; i<NUMCMAPS ; i++){
        W.UnlockLumpNum(lnames[i]); lnames[i]=null;
      }
    }
  else
    {
      W.UnlockLumpNum(yah[0]); yah[0]=null;
      W.UnlockLumpNum(yah[1]); yah[1]=null;

      W.UnlockLumpNum(splat); splat=null;

      for (i=0 ; i<NUMMAPS ; i++) {
        W.UnlockLumpNum(lnames[i]); lnames[i]=null;
  
      }
      if (wbs.epsd < 3)
        {
          for (j=0;j<NUMANIMS[wbs.epsd];j++)
            {
              if (wbs.epsd != 1 || j != 8)
                for (i=0;i<anims[wbs.epsd][j].nanims;i++){
                  W.UnlockLumpNum(anims[wbs.epsd][j].p[i]);
                  anims[wbs.epsd][j].p[i]=null;
                }
            }
        }
    }
  W.UnlockLumpNum(percent); percent=null;
  W.UnlockLumpNum(colon); colon=null;
  W.UnlockLumpNum(finished); finished=null;
  W.UnlockLumpNum(entering); entering=null;
  W.UnlockLumpNum(kills); kills=null;
  W.UnlockLumpNum(secret); secret=null;
  W.UnlockLumpNum(sp_secret); sp_secret=null;
  W.UnlockLumpNum(items); items=null;
  W.UnlockLumpNum(frags); frags=null;
  W.UnlockLumpNum(time); time=null;
  W.UnlockLumpNum(sucks); sucks=null;
  W.UnlockLumpNum(par); par=null;
  W.UnlockLumpNum(victims); victims=null;
  W.UnlockLumpNum(killers); killers=null;
  W.UnlockLumpNum(total); total=null;
  for (i=0 ; i<MAXPLAYERS ; i++) {
    W.UnlockLumpNum(p[i]);
    W.UnlockLumpNum(bp[i]);
    p[i]=null;
    bp[i]=null;
  }
}


protected void initNoState()
{
    state = endlevel_state.NoState;
    acceleratestage = 0;
    cnt = 10;
}

protected void updateNoState() {

    updateAnimatedBack();

    if (--cnt==00)
    {
	End();
	DG.WorldDone();
    }

}

boolean		snl_pointeron = false;


protected void initShowNextLoc()
{
    state = endlevel_state.ShowNextLoc;
    acceleratestage = 0;
    cnt = SHOWNEXTLOCDELAY * TICRATE;

    initAnimatedBack();
}

protected void updateShowNextLoc()
{
    updateAnimatedBack();

    if ((--cnt==0) || (acceleratestage!=0))
	initNoState();
    else
	snl_pointeron = (cnt & 31) < 20;
}

protected void drawShowNextLoc()
{

    int		i;
    int		last;

    slamBackground();

    // draw animated background
    drawAnimatedBack(); 

    if ( !DS.isCommercial())
    {
  	if (wbs.epsd > 2)
	{
	    drawEL();
	    return;
	}
	
	last = (wbs.last == 8) ? wbs.next - 1 : wbs.last;

	// draw a splat on taken cities.
	for (i=0 ; i<=last ; i++)
	    drawOnLnode(i, new patch_t[]{splat});

	// splat the secret level?
	if (wbs.didsecret)
	    drawOnLnode(8, new patch_t[]{splat});

	// draw flashing ptr
	if (snl_pointeron)
	    drawOnLnode(wbs.next, yah); 
    }

    // draws which level you are entering..
    if ( (!DS.isCommercial())
	 || wbs.next != 30)
	drawEL();  

}

protected void drawNoState()
{
    snl_pointeron = true;
    drawShowNextLoc();
}

protected int fragSum(int playernum)
{
    int		i;
    int		frags = 0;
    
    for (i=0 ; i<MAXPLAYERS ; i++)
    {
	if (DS.playeringame[i]
	    && i!=playernum)
	{
	    frags += plrs[playernum].frags[i];
	}
    }

	
    // JDC hack - negative frags.
    frags -= plrs[playernum].frags[playernum];
    // UNUSED if (frags < 0)
    // 	frags = 0;

    return frags;
}



int		dm_state;
int[][]		dm_frags=new int[MAXPLAYERS][MAXPLAYERS];
int	[]	dm_totals=new int[MAXPLAYERS];



protected void initDeathmatchStats()
{

    int		i;
    int		j;

    state = endlevel_state.StatCount;
    acceleratestage = 0;
    dm_state = 1;

    cnt_pause = TICRATE;

    for (i=0 ; i<MAXPLAYERS ; i++)
    {
	if (DS.playeringame[i])
	{
	    for (j=0 ; j<MAXPLAYERS ; j++)
		if (DS.playeringame[j])
		    dm_frags[i][j] = 0;

	    dm_totals[i] = 0;
	}
    }
    
    initAnimatedBack();
}



protected void updateDeathmatchStats()
{

    int		i;
    int		j;
    
    boolean	stillticking;

    updateAnimatedBack();

    if ((acceleratestage!=0) && (dm_state != 4))
    {
	acceleratestage = 0;

	for (i=0 ; i<MAXPLAYERS ; i++)
	{
	    if (DS.playeringame[i])
	    {
		for (j=0 ; j<MAXPLAYERS ; j++)
		    if (DS.playeringame[j])
			dm_frags[i][j] = plrs[i].frags[j];

		dm_totals[i] = fragSum(i);
	    }
	}
	

	S.StartSound(null, sfxenum_t.sfx_barexp);
	dm_state = 4;
    }

    
    if (dm_state == 2)
    {
	if ((bcnt&3)==0)
	     S.StartSound(null, sfxenum_t.sfx_pistol);
	
	stillticking = false;

	for (i=0 ; i<MAXPLAYERS ; i++)
	{
	    if (DS.playeringame[i])
	    {
		for (j=0 ; j<MAXPLAYERS ; j++)
		{
		    if (DS.playeringame[j]
			&& dm_frags[i][j] != plrs[i].frags[j])
		    {
			if (plrs[i].frags[j] < 0)
			    dm_frags[i][j]--;
			else
			    dm_frags[i][j]++;

			if (dm_frags[i][j] > 99)
			    dm_frags[i][j] = 99;

			if (dm_frags[i][j] < -99)
			    dm_frags[i][j] = -99;
			
			stillticking = true;
		    }
		}
		dm_totals[i] = fragSum(i);

		if (dm_totals[i] > 99)
		    dm_totals[i] = 99;
		
		if (dm_totals[i] < -99)
		    dm_totals[i] = -99;
	    }
	    
	}
	if (!stillticking)
	{
	    S.StartSound(null,sfxenum_t.sfx_barexp);
	    dm_state++;
	}

    }
    else if (dm_state == 4)
    {
	if (acceleratestage!=0)
	{
	    S.StartSound(null, sfxenum_t.sfx_slop);

	    if ( DS.isCommercial())
		initNoState();
	    else
		initShowNextLoc();
	}
    }
    else if ((dm_state & 1)!=0)
    {
	if (--cnt_pause==0)
	{
	    dm_state++;
	    cnt_pause = TICRATE;
	}
    }
}



protected void drawDeathmatchStats()
{

    int		i;
    int		j;
    int		x;
    int		y;
    int		w;
    
    int		lh = WI_SPACINGY; // line height

    slamBackground();
    
    // draw animated background
    drawAnimatedBack(); 
    drawLF();

    // draw stat titles (top line)
    V.DrawPatch(DM_TOTALSX-total.width/2,
		DM_MATRIXY-WI_SPACINGY+10,
		FB,
		total);
    
    V.DrawPatch(DM_KILLERSX, DM_KILLERSY, FB, killers);
    V.DrawPatch(DM_VICTIMSX, DM_VICTIMSY, FB, victims);

    // draw P?
    x = DM_MATRIXX + DM_SPACINGX;
    y = DM_MATRIXY;

    for (i=0 ; i<MAXPLAYERS ; i++)
    {
	if (DS.playeringame[i])
	{
	    V.DrawPatch(x-p[i].width/2,
			DM_MATRIXY - WI_SPACINGY,
			FB,
			p[i]);
	    
	    V.DrawPatch(DM_MATRIXX-p[i].width/2,
			y,
			FB,
			p[i]);

	    if (i == me)
	    {
		V.DrawPatch(x-p[i].width/2,
			    DM_MATRIXY - WI_SPACINGY,
			    FB,
			    bstar);

		V.DrawPatch(DM_MATRIXX-p[i].width/2,
			    y,
			    FB,
			    star);
	    }
	}
	else
	{
	    // V_DrawPatch(x-SHORT(bp[i].width)/2,
	    //   DM_MATRIXY - WI_SPACINGY, FB, bp[i]);
	    // V_DrawPatch(DM_MATRIXX-SHORT(bp[i].width)/2,
	    //   y, FB, bp[i]);
	}
	x += DM_SPACINGX;
	y += WI_SPACINGY;
    }

    // draw stats
    y = DM_MATRIXY+10;
    w = num[0].width;

    for (i=0 ; i<MAXPLAYERS ; i++)
    {
	x = DM_MATRIXX + DM_SPACINGX;

	if (DS.playeringame[i])
	{
	    for (j=0 ; j<MAXPLAYERS ; j++)
	    {
		if (DS.playeringame[j])
		    drawNum(x+w, y, dm_frags[i][j], 2);

		x += DM_SPACINGX;
	    }
	    drawNum(DM_TOTALSX+w, y, dm_totals[i], 2);
	}
	y += WI_SPACINGY;
    }
}

 int[]	cnt_frags=new int[MAXPLAYERS];
 int	dofrags;
 int	ng_state;

 protected void initNetgameStats()
{

    int i;

    state = endlevel_state.StatCount;
    acceleratestage = 0;
    ng_state = 1;

    cnt_pause = TICRATE;

    for (i=0 ; i<MAXPLAYERS ; i++)
    {
	if (!DS.playeringame[i])
	    continue;

	cnt_kills[i] = cnt_items[i] = cnt_secret[i] = cnt_frags[i] = 0;

	dofrags += fragSum(i);
    }

    dofrags = ~~dofrags;

    initAnimatedBack();
}



protected void updateNetgameStats()
{

    int		i;
    int		fsum;
    
    boolean	stillticking;

    updateAnimatedBack();

    if (acceleratestage!=0 && ng_state != 10)
    {
	acceleratestage = 0;

	for (i=0 ; i<MAXPLAYERS ; i++)
	{
	    if (!DS.playeringame[i])
		continue;

	    cnt_kills[i] = (plrs[i].skills * 100) / wbs.maxkills;
	    cnt_items[i] = (plrs[i].sitems * 100) / wbs.maxitems;
	    cnt_secret[i] = (plrs[i].ssecret * 100) / wbs.maxsecret;

	    if (dofrags!=0)
		cnt_frags[i] = fragSum(i);
	}
	S.StartSound(null, sfxenum_t.sfx_barexp);
	ng_state = 10;
    }

    if (ng_state == 2)
    {
	if ((bcnt&3)==0)
	    S.StartSound(null,  sfxenum_t.sfx_pistol);

	stillticking = false;

	for (i=0 ; i<MAXPLAYERS ; i++)
	{
	    if (!DS.playeringame[i])
		continue;

	    cnt_kills[i] += 2;

	    if (cnt_kills[i] >= (plrs[i].skills * 100) / wbs.maxkills)
		cnt_kills[i] = (plrs[i].skills * 100) / wbs.maxkills;
	    else
		stillticking = true;
	}
	
	if (!stillticking)
	{
	    S.StartSound(null,  sfxenum_t.sfx_barexp);
	    ng_state++;
	}
    }
    else if (ng_state == 4)
    {
	if ((bcnt&3)==0)
	   S.StartSound(null,  sfxenum_t.sfx_pistol);

	stillticking = false;

	for (i=0 ; i<MAXPLAYERS ; i++)
	{
	    if (!DS.playeringame[i])
		continue;

	    cnt_items[i] += 2;
	    if (cnt_items[i] >= (plrs[i].sitems * 100) / wbs.maxitems)
		cnt_items[i] = (plrs[i].sitems * 100) / wbs.maxitems;
	    else
		stillticking = true;
	}
	if (!stillticking)
	{
	   S.StartSound(null,  sfxenum_t.sfx_barexp);
	    ng_state++;
	}
    }
    else if (ng_state == 6)
    {
	if ((bcnt&3)==0)
	   S.StartSound(null,  sfxenum_t.sfx_pistol);

	stillticking = false;

	for (i=0 ; i<MAXPLAYERS ; i++)
	{
	    if (!DS.playeringame[i])
		continue;

	    cnt_secret[i] += 2;

	    if (cnt_secret[i] >= (plrs[i].ssecret * 100) / wbs.maxsecret)
		cnt_secret[i] = (plrs[i].ssecret * 100) / wbs.maxsecret;
	    else
		stillticking = true;
	}
	
	if (!stillticking)
	{
	    S.StartSound(null, sfxenum_t.sfx_barexp);
	    ng_state += 1 + 2*~dofrags;
	}
    }
    else if (ng_state == 8)
    {
	if ((bcnt&3)==0)
	  S.StartSound(null,  sfxenum_t.sfx_pistol);

	stillticking = false;

	for (i=0 ; i<MAXPLAYERS ; i++)
	{
	    if (!DS.playeringame[i])
		continue;

	    cnt_frags[i] += 1;

	    if (cnt_frags[i] >= (fsum = fragSum(i)))
		cnt_frags[i] = fsum;
	    else
		stillticking = true;
	}
	
	if (!stillticking)
	{
	    S.StartSound(null,  sfxenum_t.sfx_pldeth);
	    ng_state++;
	}
    }
    else if (ng_state == 10)
    {
	if (acceleratestage!=0)
	{
	   S.StartSound(null,  sfxenum_t.sfx_sgcock);
	    if ( DS.isCommercial() )
		initNoState();
	    else
		initShowNextLoc();
	}
    }
    else if ((ng_state & 1)!=0)
    {
	if (--cnt_pause==0)
	{
	    ng_state++;
	    cnt_pause = TICRATE;
	}
    }
}

protected void drawNetgameStats()
{
    int		i;
    int		x;
    int		y;
    int		pwidth = percent.width;

    slamBackground();
    
    // draw animated background
    drawAnimatedBack(); 

    drawLF();

    // draw stat titles (top line)
    V.DrawPatch(NG_STATSX()+NG_SPACINGX-kills.width,
		NG_STATSY, FB, kills);

    V.DrawPatch(NG_STATSX()+2*NG_SPACINGX-items.width,
		NG_STATSY, FB, items);

    V.DrawPatch(NG_STATSX()+3*NG_SPACINGX-secret.width,
		NG_STATSY, FB, secret);
    
    if (dofrags!=0)
	V.DrawPatch(NG_STATSX()+4*NG_SPACINGX-frags.width,
		    NG_STATSY, FB, frags);

    // draw stats
    y = NG_STATSY + kills.height;

    for (i=0 ; i<MAXPLAYERS ; i++)
    {
	if (!DS.playeringame[i])
	    continue;

	x = NG_STATSX();
	V.DrawPatch(x-p[i].width, y, FB, p[i]);

	if (i == me)
	    V.DrawPatch(x-p[i].width, y, FB, star);

	x += NG_SPACINGX;
	drawPercent(x-pwidth, y+10, cnt_kills[i]);	x += NG_SPACINGX;
	drawPercent(x-pwidth, y+10, cnt_items[i]);	x += NG_SPACINGX;
	drawPercent(x-pwidth, y+10, cnt_secret[i]);	x += NG_SPACINGX;

	if (dofrags!=0)
	    drawNum(x, y+10, cnt_frags[i], -1);

	y += WI_SPACINGY;
    }

}


 int	sp_state;

 protected void initStats()
{
    state = endlevel_state.StatCount;
    acceleratestage = 0;
    sp_state = 1;
    cnt_kills[0] = cnt_items[0] = cnt_secret[0] = -1;
    cnt_time = cnt_par = -1;
    cnt_pause = TICRATE;

    initAnimatedBack();
}

protected void updateStats()
{

    updateAnimatedBack();
    
    //System.out.println("SP_State "+sp_state);

    if ((acceleratestage !=0) && sp_state != COUNT_DONE)
    {
	acceleratestage = 0;
	cnt_kills[0] = (plrs[me].skills * 100) / wbs.maxkills;
	cnt_items[0] = (plrs[me].sitems * 100) / wbs.maxitems;
	cnt_secret[0] = (plrs[me].ssecret * 100) / wbs.maxsecret;
	cnt_time = plrs[me].stime / TICRATE;
	cnt_par = wbs.partime / TICRATE;
	S.StartSound(null,  sfxenum_t.sfx_barexp);
	sp_state = 10;
    }

    if (sp_state == COUNT_KILLS)
    {
	cnt_kills[0] += 2;

	if ((bcnt&3)==0)
	     S.StartSound(null,  sfxenum_t.sfx_pistol);

	if (cnt_kills[0] >= (plrs[me].skills * 100) / wbs.maxkills)
	{
	    cnt_kills[0] = (plrs[me].skills * 100) / wbs.maxkills;
	    S.StartSound(null, sfxenum_t.sfx_barexp);
	    sp_state++;
	}
    }
    else if (sp_state == COUNT_ITEMS)
    {
	cnt_items[0] += 2;

	if ((bcnt&3)==0)
	     S.StartSound(null, sfxenum_t.sfx_pistol);

	if (cnt_items[0] >= (plrs[me].sitems * 100) / wbs.maxitems)
	{
	    cnt_items[0] = (plrs[me].sitems * 100) / wbs.maxitems;
	     S.StartSound(null, sfxenum_t.sfx_barexp);
	    sp_state++;
	}
    }
    else if (sp_state == COUNT_SECRETS)
    {
	cnt_secret[0] += 2;

	if ((bcnt&3)==0)
	     S.StartSound(null, sfxenum_t.sfx_pistol);

	if (cnt_secret[0] >= (plrs[me].ssecret * 100) / wbs.maxsecret)
	{
	    cnt_secret[0] = (plrs[me].ssecret * 100) / wbs.maxsecret;
	     S.StartSound(null, sfxenum_t.sfx_barexp);
	    sp_state++;
	}
    }

    else if (sp_state == COUNT_TIME)
    {
	if ((bcnt&3)==0)
	    S.StartSound(null, sfxenum_t.sfx_pistol);

	cnt_time += 3;

	if (cnt_time >= plrs[me].stime / TICRATE)
	    cnt_time = plrs[me].stime / TICRATE;

	cnt_par += 3;

	if (cnt_par >= wbs.partime / TICRATE)
	{
	    cnt_par = wbs.partime / TICRATE;

	    if (cnt_time >= plrs[me].stime / TICRATE)
	    {
		 S.StartSound(null, sfxenum_t.sfx_barexp);
		sp_state++;
	    }
	}
    }
    else if (sp_state == COUNT_DONE)
    {
	if (acceleratestage!=0)
	{
	     S.StartSound(null, sfxenum_t.sfx_sgcock);

	    if (DS.isCommercial())
		initNoState();
	    else
		initShowNextLoc();
	}
    }
    // Non-drawing, pausing state. Any odd value introduces a 35 tic pause.
    else if ((sp_state & 1)>0)
    {
	if (--cnt_pause==0)
	{
	    sp_state++;
	    cnt_pause = TICRATE;
	}
    }

}

protected void drawStats()
{
    // line height
    int lh;	

    lh = (3*num[0].height*BEST_Y_SCALE)/2;

    slamBackground();

    // draw animated background
    drawAnimatedBack();
    
    drawLF();

    V.DrawScaledPatch(SP_STATSX, SP_STATSY, V_NOSCALESTART|FB,vs, kills);
    drawPercent(SCREENWIDTH - SP_STATSX, SP_STATSY, cnt_kills[0]);

    V.DrawScaledPatch(SP_STATSX, SP_STATSY+lh, V_NOSCALESTART|FB,vs, items);
    drawPercent(SCREENWIDTH - SP_STATSX, SP_STATSY+lh, cnt_items[0]);

    V.DrawScaledPatch(SP_STATSX, SP_STATSY+2*lh, V_NOSCALESTART|FB,vs, sp_secret);
    drawPercent(SCREENWIDTH - SP_STATSX, SP_STATSY+2*lh, cnt_secret[0]);

    V.DrawScaledPatch(SP_TIMEX, SP_TIMEY, V_NOSCALESTART|FB,vs, time);
    drawTime(SCREENWIDTH/2 - SP_TIMEX, SP_TIMEY, cnt_time);

    if (wbs.epsd < 3)
    {
	V.DrawScaledPatch(SCREENWIDTH/2 + SP_TIMEX, SP_TIMEY, V_NOSCALESTART|FB,vs, par);
	drawTime(SCREENWIDTH - SP_TIMEX, SP_TIMEY, cnt_par);
    }

}

protected void checkForAccelerate()
{

    // check for button presses to skip delays
    for (int i=0 ; i<MAXPLAYERS ; i++)
    {
        player_t player = DS.players[i];
	if (DS.playeringame[i])
	{
	    if ((player.cmd.buttons & BT_ATTACK)!=0)
	    {
		if (!player.attackdown)
		    acceleratestage = 1;
		player.attackdown = true;
	    }
	    else
		player.attackdown = false;
	    if ((player.cmd.buttons & BT_USE)!=0)
	    {
		if (!player.usedown)
		    acceleratestage = 1;
		player.usedown = true;
	    }
	    else
		player.usedown = false;
	}
    }
}



/** Updates stuff each tick */
public void Ticker()
{
    // counter for general background animation
    bcnt++;  

    if (bcnt == 1)
    {
	// intermission music
  	if ( DS.isCommercial())
  	
  	    S.ChangeMusic(musicenum_t.mus_dm2int.ordinal(), true);
	else
	    S.ChangeMusic(musicenum_t.mus_inter.ordinal(), true); 
    }

    checkForAccelerate();
//System.out.println("State "+state);
    
    switch (state)
    {
      case StatCount:
	if (DS.deathmatch) updateDeathmatchStats();
	else if (DS.netgame) updateNetgameStats();
	else updateStats();
	break;
	
      case ShowNextLoc:
	updateShowNextLoc();
	break;
	
      case NoState:
	updateNoState();
	 break;
      case JustShutOff:
    	  // We just finished, and graphics have been unloaded.
    	  // If we don't consume a tick in this way, Doom
    	  // will try to draw unloaded graphics.
    	  state=endlevel_state.NoState;
    	  break;
    }

}

protected void loadData()
{
    int		i;
    int		j;
    String	name;
    anim_t	a;

    if (DS.isCommercial())
	name= "INTERPIC";
    else 
	//sprintf(name, "WIMAP%d", wbs.epsd);
        name=("WIMAP"+Integer.toString(wbs.epsd));
    
    // MAES: For Ultimate Doom
    if ( DS.isRetail())
    {
      if (wbs.epsd == 3)
          name= "INTERPIC";
    }

    // background - draw it to screen 1 for quick redraw.
    bg = (patch_t) W.CacheLumpName(name, PU_CACHE,patch_t.class);    
    V.DrawPatchSolidScaled(0, 0, BEST_X_SCALE, BEST_Y_SCALE,1, bg);


    // UNUSED unsigned char *pic = screens[1];
    // if (gamemode == commercial)
    // {
    // darken the background image
    // while (pic != screens[1] + SCREENHEIGHT*SCREENWIDTH)
    // {
    //   *pic = colormaps[256*25 + *pic];
    //   pic++;
    // }
    //}

    if (DS.isCommercial())
    {
	NUMCMAPS = 32;								
//	lnames = (patch_t **) Z_Malloc(sizeof(patch_t*) * NUMCMAPS,
//				       PU_, 0);
	
	lnames=new patch_t[NUMCMAPS];
    String xxx=new String("CWILV%02d");
    //String buffer;
	for (i=0 ; i<NUMCMAPS ; i++)
	{								
	    name=String.format(xxx,i);
	    lnames[i] = (patch_t) W.CacheLumpName(name, PU_STATIC,patch_t.class);
	}					
    }
    else
    {
	lnames = new patch_t[NUMMAPS];
	String xxx=new String("WILV%d%d");

	for (i=0 ; i<NUMMAPS ; i++)
	{
	    name=String.format(xxx,wbs.epsd, i);
	    lnames[i] = (patch_t) W.CacheLumpName(name, PU_STATIC,patch_t.class);
	}

	// you are here
	yah[0] = (patch_t) W.CacheLumpName("WIURH0", PU_STATIC,patch_t.class);

	// you are here (alt.)
	yah[1] = (patch_t) W.CacheLumpName("WIURH1", PU_STATIC,patch_t.class);

	// splat
	splat = (patch_t) W.CacheLumpName("WISPLAT", PU_STATIC,patch_t.class); 
	
	if (wbs.epsd < 3)
	{
	    xxx=new String("WIA%d%02d%02d");
	    //xxx=new PrintfFormat("WIA%d%.2d%.2d");
	    for (j=0;j<NUMANIMS[wbs.epsd];j++)
	    {
		a = anims[wbs.epsd][j];
		for (i=0;i<a.nanims;i++)
		{
		    // MONDO HACK!
		    if (wbs.epsd != 1 || j != 8) 
		    {
			// animations
			name=String.format(xxx,wbs.epsd, j, i);  
			a.p[i] = (patch_t)W.CacheLumpName(name, PU_STATIC,patch_t.class);
		    }
		    else
		    {
			// HACK ALERT!
			a.p[i] = anims[1][4].p[i]; 
		    }
		}
	    }
	}
    }

    // More hacks on minus sign.
    wiminus = (patch_t)W.CacheLumpName("WIMINUS", PU_STATIC,patch_t.class); 

    String xxx=new String("WINUM%d");
    for (i=0;i<10;i++)
    {
	 // numbers 0-9
	name=String.format(xxx,i);     
	num[i] = (patch_t)W.CacheLumpName(name, PU_STATIC,patch_t.class);
    }

    // percent sign
    percent = (patch_t)W.CacheLumpName("WIPCNT", PU_STATIC,patch_t.class);

    // "finished"
    finished = (patch_t)W.CacheLumpName("WIF", PU_STATIC,patch_t.class);

    // "entering"
    entering = (patch_t)W.CacheLumpName("WIENTER", PU_STATIC,patch_t.class);

    // "kills"
    kills = (patch_t)W.CacheLumpName("WIOSTK", PU_STATIC,patch_t.class);   

    // "scrt"
    secret = (patch_t)W.CacheLumpName("WIOSTS", PU_STATIC,patch_t.class);

     // "secret"
    sp_secret =(patch_t)W.CacheLumpName("WISCRT2", PU_STATIC,patch_t.class);

    // Yuck. 
    if (DS.language==Language_t.french)
    {
	// "items"
	if (DS.netgame && !DS.deathmatch)
	    items = (patch_t)W.CacheLumpName("WIOBJ", PU_STATIC,patch_t.class);    
  	else
	    items = (patch_t)W.CacheLumpName("WIOSTI", PU_STATIC,patch_t.class);
    } else
	items = (patch_t)W.CacheLumpName("WIOSTI", PU_STATIC,patch_t.class);

    // "frgs"
    frags = (patch_t)W.CacheLumpName("WIFRGS", PU_STATIC,patch_t.class);    

    // ":"
    colon = (patch_t)W.CacheLumpName("WICOLON", PU_STATIC,patch_t.class); 

    // "time"
    time = (patch_t)W.CacheLumpName("WITIME", PU_STATIC,patch_t.class);  

    // "sucks"
    sucks = (patch_t)W.CacheLumpName("WISUCKS", PU_STATIC,patch_t.class);  

    // "par"
    par = (patch_t)W.CacheLumpName("WIPAR", PU_STATIC,patch_t.class);   

    // "killers" (vertical)
    killers = (patch_t)W.CacheLumpName("WIKILRS", PU_STATIC,patch_t.class);

    // "victims" (horiz)
    victims = (patch_t)W.CacheLumpName("WIVCTMS", PU_STATIC,patch_t.class);

    // "total"
    total = (patch_t)W.CacheLumpName("WIMSTT", PU_STATIC,patch_t.class);   

    // your face
    star = (patch_t)W.CacheLumpName("STFST01", PU_STATIC,patch_t.class);

    // dead face
    bstar = (patch_t)W.CacheLumpName("STFDEAD0", PU_STATIC,patch_t.class);    

    String xx1=new String("STPB%d");
    String xx2=new String("WIBP%d");
    for (i=0 ; i<MAXPLAYERS ; i++)
    {
	// "1,2,3,4"
    name= String.format(xx1,i);
	p[i] = (patch_t)W.CacheLumpName(name, PU_STATIC,patch_t.class);;

	// "1,2,3,4"
	name= String.format(xx2,i+1);
	bp[i] = (patch_t)W.CacheLumpName(name, PU_STATIC,patch_t.class);;
    }

}

/*

public void WI_unloadData()
{
    int		i;
    int		j;

    W.UnlockLumpNum(wiminus, PU_CACHE);

    for (i=0 ; i<10 ; i++)
	W.UnlockLumpNum(num[i], PU_CACHE);
    
    if (gamemode == commercial)
    {
  	for (i=0 ; i<NUMCMAPS ; i++)
	    W.UnlockLumpNum(lnames[i], PU_CACHE);
    }
    else
    {
	W.UnlockLumpNum(yah[0], PU_CACHE);
	W.UnlockLumpNum(yah[1], PU_CACHE);

	W.UnlockLumpNum(splat, PU_CACHE);

	for (i=0 ; i<NUMMAPS ; i++)
	    W.UnlockLumpNum(lnames[i], PU_CACHE);
	
	if (wbs.epsd < 3)
	{
	    for (j=0;j<NUMANIMS[wbs.epsd];j++)
	    {
		if (wbs.epsd != 1 || j != 8)
		    for (i=0;i<anims[wbs.epsd][j].nanims;i++)
			W.UnlockLumpNum(anims[wbs.epsd][j].p[i], PU_CACHE);
	    }
	}
    }
    
    Z_Free(lnames);

    W.UnlockLumpNum(percent, PU_CACHE);
    W.UnlockLumpNum(colon, PU_CACHE);
    W.UnlockLumpNum(finished, PU_CACHE);
    W.UnlockLumpNum(entering, PU_CACHE);
    W.UnlockLumpNum(kills, PU_CACHE);
    W.UnlockLumpNum(secret, PU_CACHE);
    W.UnlockLumpNum(sp_secret, PU_CACHE);
    W.UnlockLumpNum(items, PU_CACHE);
    W.UnlockLumpNum(frags, PU_CACHE);
    W.UnlockLumpNum(time, PU_CACHE);
    W.UnlockLumpNum(sucks, PU_CACHE);
    W.UnlockLumpNum(par, PU_CACHE);

    W.UnlockLumpNum(victims, PU_CACHE);
    W.UnlockLumpNum(killers, PU_CACHE);
    W.UnlockLumpNum(total, PU_CACHE);
    //  W.UnlockLumpNum(star, PU_CACHE);
    //  W.UnlockLumpNum(bstar, PU_CACHE);
    
    for (i=0 ; i<MAXPLAYERS ; i++)
	W.UnlockLumpNum(p[i], PU_CACHE);

    for (i=0 ; i<MAXPLAYERS ; i++)
	W.UnlockLumpNum(bp[i], PU_CACHE);
}
*/


public void Drawer ()
{
    switch (state)
    {
      case StatCount:
	if (DS.deathmatch)
	    drawDeathmatchStats();
	else if (DS.netgame)
	    drawNetgameStats();
	else
	    drawStats();
	break;
	
      case ShowNextLoc:
	drawShowNextLoc();
	break;
	
      case NoState:
	drawNoState();
	break;
    }
}


protected void initVariables(wbstartstruct_t wbstartstruct)
{

    wbs = wbstartstruct.clone();

if (RANGECHECKING){
    if (!DS.isCommercial())
    {
      if ( DS.isRetail())
	RNGCHECK(wbs.epsd, 0, 3);
      else
	RNGCHECK(wbs.epsd, 0, 2);
    }
    else
    {
	RNGCHECK(wbs.last, 0, 8);
	RNGCHECK(wbs.next, 0, 8);
    }
    RNGCHECK(wbs.pnum, 0, MAXPLAYERS);
    RNGCHECK(wbs.pnum, 0, MAXPLAYERS);
}

    acceleratestage = 0;
    cnt = bcnt = 0;
    firstrefresh = 1;
    me = wbs.pnum;
    plrs = wbs.plyr.clone();

    if (wbs.maxkills==0)
	wbs.maxkills = 1;

    if (wbs.maxitems==0)
	wbs.maxitems = 1;

    if (wbs.maxsecret==0)
	wbs.maxsecret = 1;

    // Sanity check for Ultimate.
    if ( !DS.isRetail())
      if (wbs.epsd > 2)
	wbs.epsd -= 3;
}

public void Start(wbstartstruct_t wbstartstruct)
{

    initVariables(wbstartstruct);
    loadData();

    if (DS.deathmatch)
	initDeathmatchStats();
    else if (DS.netgame)
	initNetgameStats();
    else
	initStats();
}


protected int NG_STATSX(){
    return 32 + star.width/2 + 32*~dofrags;
}

protected static boolean RNGCHECK(int what, int min, int max){
    return (what>=min && what <=max)?true:false;
}

@Override
public void updateStatus(DoomStatus DS) {
    this.DG=DS.DG;
    this.DS=DS.DM;
    this.V=DS.V;
    this.W=DS.W;
    this.RND=DS.RND;
    this.S=DS.S;
    this.ST=DS.ST;

    
}


//////////////////////////// VIDEO SCALE STUFF ////////////////////////////////

protected int SCREENWIDTH;
protected int SCREENHEIGHT;
protected IVideoScale vs;
protected int BEST_X_SCALE;
protected int BEST_Y_SCALE;


@Override
public void setVideoScale(IVideoScale vs) {
    this.vs=vs;
}

@Override
public void initScaling() {
    this.SCREENHEIGHT=vs.getScreenHeight();
    this.SCREENWIDTH=vs.getScreenWidth();
    this.BEST_X_SCALE=vs.getScalingX();
    this.BEST_Y_SCALE=vs.getScalingY();
    
    // Pre-scale stuff.
    SP_STATSX       =50*vs.getSafeScaling();
    SP_STATSY      = 50*vs.getSafeScaling();;

    SP_TIMEX      =  16*vs.getSafeScaling();
    SP_TIMEY      =  (SCREENHEIGHT-ST.getHeight());   
}

}
package f;

import static data.Defines.HU_FONTSIZE;
import static data.Defines.HU_FONTSTART;
import static data.Defines.PU_CACHE;
import static data.Defines.PU_LEVEL;
import static data.Defines.FF_FRAMEMASK;
import static data.Limits.MAXPLAYERS;
import static data.info.mobjinfo;
import static data.info.states;
import static doom.englsh.*;
import static utils.C2JUtils.*;
import hu.HU;
import i.DoomStatusAware;

import java.io.IOException;
import java.util.ArrayList;

import rr.ISpriteManager;
import rr.flat_t;
import rr.patch_t;
import rr.spritedef_t;
import rr.spriteframe_t;
import s.IDoomSound;
import utils.C2JUtils;
import v.DoomVideoRenderer;
import v.IVideoScale;
import v.IVideoScaleAware;
import w.IWadLoader;
import data.mobjtype_t;
import data.sounds.musicenum_t;
import data.state_t;
import defines.*;
import data.sounds.sfxenum_t;
import doom.DoomStatus;
import doom.IDoomGame;
import doom.event_t;
import doom.evtype_t;
import doom.gameaction_t;

// Emacs style mode select   -*- C++ -*- 
//-----------------------------------------------------------------------------
//
// $Id: Finale.java,v 1.28 2012/09/24 17:16:23 velktron Exp $
//
// Copyright (C) 1993-1996 by id Software, Inc.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// DESCRIPTION:
//  Game completion, final screen animation.
//
//-----------------------------------------------------------------------------

public class Finale<T> implements DoomStatusAware, IVideoScaleAware {

	public static final String rcsid = "$Id: Finale.java,v 1.28 2012/09/24 17:16:23 velktron Exp $";

	IDoomGame DG;
	DoomStatus<?,?> DS;
	DoomVideoRenderer<T,?> V;
	IDoomSound S;
	HU HU;
	IWadLoader W;
	ISpriteManager SM;

	int finalestage;

	int finalecount;

	private static int TEXTSPEED = 3;
	private static int TEXTWAIT = 250;

	String[] doom_text = { E1TEXT, E2TEXT, E3TEXT, E4TEXT };

	String[] doom2_text = { C1TEXT, C2TEXT, C3TEXT, C4TEXT, C5TEXT, C6TEXT };

	String[] plut_text = { P1TEXT, P2TEXT, P3TEXT, P4TEXT, P5TEXT, P6TEXT };
	String[] tnt_text = { T1TEXT, T2TEXT, T3TEXT, T4TEXT, T5TEXT, T6TEXT };

	String finaletext;
	String finaleflat;

	/**
	 * F_StartFinale
	 */
	public void StartFinale() {
		DG.setGameAction(gameaction_t.ga_nothing);
		DS.gamestate = gamestate_t.GS_FINALE;
		DS.viewactive = false;
		DS.automapactive = false;
		String[] texts = null;

		// Pick proper text.
		switch (DS.getGameMode()) {
		case commercial:
		case pack_xbla:
			texts = doom2_text;
			break;
		case pack_tnt:
			texts = tnt_text;
			break;
		case pack_plut:
			texts = plut_text;
			break;
		case shareware:
		case registered:
		case retail:
			texts = doom_text;
			break;
		}

		// Okay - IWAD dependend stuff.
		// This has been changed severly, and
		// some stuff might have changed in the process.
		switch (DS.getGameMode()) {

		// DOOM 1 - E1, E3 or E4, but each nine missions
		case shareware:
		case registered:
		case retail: {
			S.ChangeMusic(musicenum_t.mus_victor, true);

			switch (DS.gameepisode) {
			case 1:
				finaleflat = "FLOOR4_8";
				finaletext = texts[0];
				break;
			case 2:
				finaleflat = "SFLR6_1";
				finaletext = texts[1];
				break;
			case 3:
				finaleflat = "MFLR8_4";
				finaletext = texts[2];
				break;
			case 4:
				finaleflat = "MFLR8_3";
				finaletext = texts[3];
				break;
			default:
				// Ouch.
				break;
			}
			break;
		}

			// DOOM II and missions packs with E1, M34
		case commercial:
		case pack_xbla:
		case pack_tnt:
		case pack_plut: {
			S.ChangeMusic(musicenum_t.mus_read_m, true);

			switch (DS.gamemap) {
			case 6:
				finaleflat = "SLIME16";
				finaletext = texts[0];
				break;
			case 11:
				finaleflat = "RROCK14";
				finaletext = texts[1];
				break;
			case 20:
				finaleflat = "RROCK07";
				finaleflat = texts[2];
				break;
			case 30:
				finaleflat = "RROCK17";
				finaletext = texts[3];
				break;
			case 15:
				finaleflat = "RROCK13";
				finaletext = texts[4];
				break;
			case 31:
				finaleflat = "RROCK19";
				finaletext = texts[5];
				break;
			default:
				// Ouch.
				break;
			}
			break;
		}

			// Indeterminate.
		default:
			S.ChangeMusic(musicenum_t.mus_read_m, true);
			finaleflat = "F_SKY1"; // Not used anywhere else.
			finaletext = doom2_text[1];
			; // FIXME - other text, music?
			break;
		}

		finalestage = 0;
		finalecount = 0;

	}

	public boolean Responder(event_t event) {
		if (finalestage == 2)
			return CastResponder(event);

		return false;
	}

	/**
	 * F_Ticker
	 */

	public void Ticker() {
		int i;

		// check for skipping
		if ((DS.isCommercial()) && (finalecount > 50)) {
			// go on to the next level
			for (i = 0; i < MAXPLAYERS; i++)
				if (DS.players[i].cmd.buttons != 0)
					break;

			if (i < MAXPLAYERS) {
				if (DS.gamemap == 30)
					StartCast();
				else
					DG.setGameAction(gameaction_t.ga_worlddone);
			}
		}

		// advance animation
		finalecount++;

		if (finalestage == 2) {
			CastTicker();
			return;
		}

		if (DS.isCommercial())
			return;
		// MAES: this is when we can transition to bunny.
		if ((finalestage == 0)
				&& finalecount > finaletext.length() * TEXTSPEED + TEXTWAIT) {
			finalecount = 0;
			finalestage = 1;
			DS.wipegamestate = gamestate_t.GS_MINUS_ONE; // force a wipe
			if (DS.gameepisode == 3)

				S.StartMusic(musicenum_t.mus_bunny);
		}
	}

	//
	// F_TextWrite
	//

	// #include "hu_stuff.h"
	patch_t[] hu_font;

	@SuppressWarnings("unchecked")
    public void TextWrite() {
		T src;
		//V dest;
		int w;
		int count;
		char[] ch;
		int c;
		int cx;
		int cy;

		// erase the entire screen to a tiled background
		src = (T)((flat_t) W.CacheLumpName(finaleflat, PU_CACHE, flat_t.class)).data;
		//dest = V.getScreen(DoomVideoRenderer.SCREEN_FG);

		for (int y = 0; y < SCREENHEIGHT; y+=64) {
			
			int y_maxdraw=Math.min(SCREENHEIGHT-y, 64);
			
			// Draw whole blocks.
			for (int x = 0; x < SCREENWIDTH; x+=64) {
				int x_maxdraw=Math.min(SCREENWIDTH-x, 64);
				V.DrawBlock(x, y, DoomVideoRenderer.SCREEN_FG, x_maxdraw,y_maxdraw,
						src);
			}
		}

		V.MarkRect(0, 0, SCREENWIDTH, SCREENHEIGHT);

		// draw some of the text onto the screen
		cx = 10;
		cy = 10;
		ch = finaletext.toCharArray();

		count = (finalecount - 10) / TEXTSPEED;
		if (count < 0)
			count = 0;

		// _D_: added min between count and ch.length, so that the text is not
		// written all at once
		for (int i = 0; i < Math.min(ch.length, count); i++)

		{
			c = ch[i];
			if (c == 0)
				break;
			if (c == '\n') {
				cx = 10;
				cy += 11;
				continue;
			}

			c = Character.toUpperCase(c) - HU_FONTSTART;
			if (c < 0 || c > HU_FONTSIZE) {
				cx += 4;
				continue;
			}

			w = hu_font[c].width;
			if (cx + w > SCREENWIDTH)
				break;
			V.DrawScaledPatch(cx, cy, 0, vs, hu_font[c]);
			cx += w;
		}

	}

	private final castinfo_t[] castorder;

	int castnum;
	int casttics;
	state_t caststate;
	boolean castdeath;
	int castframes;
	int castonmelee;
	boolean castattacking;

	//
	// F_StartCast
	//
	// extern gamestate_t wipegamestate;

	public void StartCast() {
		DS.wipegamestate = gamestate_t.GS_MINUS_ONE; // force a screen wipe
		castnum = 0;
		caststate = states[mobjinfo[castorder[castnum].type.ordinal()].seestate
				.ordinal()];
		casttics = (int) caststate.tics;
		castdeath = false;
		finalestage = 2;
		castframes = 0;
		castonmelee = 0;
		castattacking = false;
		S.ChangeMusic(musicenum_t.mus_evil, true);
	}

	//
	// F_CastTicker
	//
	public void CastTicker() {
		statenum_t st;
		sfxenum_t sfx;

		if (--casttics > 0)
			return; // not time to change state yet

		if (caststate.tics == -1 || caststate.nextstate == statenum_t.S_NULL
				|| caststate.nextstate == null) {
			// switch from deathstate to next monster
			castnum++;
			castdeath = false;
			if (castorder[castnum].name == null)
				castnum = 0;
			if (mobjinfo[castorder[castnum].type.ordinal()].seesound.ordinal() != 0)
				;
			S.StartSound(null,
					mobjinfo[castorder[castnum].type.ordinal()].seesound);
			caststate = states[mobjinfo[castorder[castnum].type.ordinal()].seestate
					.ordinal()];
			castframes = 0;
		} else {
			// just advance to next state in animation
			if (caststate == states[statenum_t.S_PLAY_ATK1.ordinal()]) {
				stopattack(); // Oh, gross hack!
				afterstopattack();
				return; // bye ...
			}

			st = caststate.nextstate;
			caststate = states[st.ordinal()];
			castframes++;

			// sound hacks....
			switch (st) {
			case S_PLAY_ATK1:
				sfx = sfxenum_t.sfx_dshtgn;
				break;
			case S_POSS_ATK2:
				sfx = sfxenum_t.sfx_pistol;
				break;
			case S_SPOS_ATK2:
				sfx = sfxenum_t.sfx_shotgn;
				break;
			case S_VILE_ATK2:
				sfx = sfxenum_t.sfx_vilatk;
				break;
			case S_SKEL_FIST2:
				sfx = sfxenum_t.sfx_skeswg;
				break;
			case S_SKEL_FIST4:
				sfx = sfxenum_t.sfx_skepch;
				break;
			case S_SKEL_MISS2:
				sfx = sfxenum_t.sfx_skeatk;
				break;
			case S_FATT_ATK8:
			case S_FATT_ATK5:
			case S_FATT_ATK2:
				sfx = sfxenum_t.sfx_firsht;
				break;
			case S_CPOS_ATK2:
			case S_CPOS_ATK3:
			case S_CPOS_ATK4:
				sfx = sfxenum_t.sfx_shotgn;
				break;
			case S_TROO_ATK3:
				sfx = sfxenum_t.sfx_claw;
				break;
			case S_SARG_ATK2:
				sfx = sfxenum_t.sfx_sgtatk;
				break;
			case S_BOSS_ATK2:
			case S_BOS2_ATK2:
			case S_HEAD_ATK2:
				sfx = sfxenum_t.sfx_firsht;
				break;
			case S_SKULL_ATK2:
				sfx = sfxenum_t.sfx_sklatk;
				break;
			case S_SPID_ATK2:
			case S_SPID_ATK3:
				sfx = sfxenum_t.sfx_shotgn;
				break;
			case S_BSPI_ATK2:
				sfx = sfxenum_t.sfx_plasma;
				break;
			case S_CYBER_ATK2:
			case S_CYBER_ATK4:
			case S_CYBER_ATK6:
				sfx = sfxenum_t.sfx_rlaunc;
				break;
			case S_PAIN_ATK3:
				sfx = sfxenum_t.sfx_sklatk;
				break;
			default:
				sfx = null;
				break;
			}

			if (sfx != null) // Fixed mute thanks to _D_ 8/6/2011
				S.StartSound(null, sfx);
		}

		if (castframes == 12) {
			// go into attack frame
			castattacking = true;
			if (castonmelee != 0)
				caststate = states[mobjinfo[castorder[castnum].type.ordinal()].meleestate
						.ordinal()];
			else
				caststate = states[mobjinfo[castorder[castnum].type.ordinal()].missilestate
						.ordinal()];
			castonmelee ^= 1;
			if (caststate == states[statenum_t.S_NULL.ordinal()]) {
				if (castonmelee != 0)
					caststate = states[mobjinfo[castorder[castnum].type
							.ordinal()].meleestate.ordinal()];
				else
					caststate = states[mobjinfo[castorder[castnum].type
							.ordinal()].missilestate.ordinal()];
			}
		}

		if (castattacking) {
			if (castframes == 24
					|| caststate == states[mobjinfo[castorder[castnum].type
							.ordinal()].seestate.ordinal()])

				stopattack();
		}

		afterstopattack();
	}

	protected void stopattack() {
		castattacking = false;
		castframes = 0;
		caststate = states[mobjinfo[castorder[castnum].type.ordinal()].seestate
				.ordinal()];
	}

	protected void afterstopattack() {
		casttics = (int) caststate.tics;
		if (casttics == -1)
			casttics = 15;
	}

	/**
	 * CastResponder
	 */

	public boolean CastResponder(event_t ev) {
		if (ev.type != evtype_t.ev_keydown)
			return false;

		if (castdeath)
			return true; // already in dying frames

		// go into death frame
		castdeath = true;
		caststate = states[mobjinfo[castorder[castnum].type.ordinal()].deathstate
				.ordinal()];
		casttics = (int) caststate.tics;
		castframes = 0;
		castattacking = false;
		if (mobjinfo[castorder[castnum].type.ordinal()].deathsound != null)
			S.StartSound(null,
					mobjinfo[castorder[castnum].type.ordinal()].deathsound);

		return true;
	}

	public void CastPrint(String text) {
		char[] ch;
		int c;
		int cx;
		int w;
		int width;

		// find width
		ch = text.toCharArray();
		width = 0;

		for (int i = 0; i < ch.length; i++) {
			c = ch[i];
			if (c == 0)
				break;
			c = Character.toUpperCase(c) - HU_FONTSTART;
			if (c < 0 || c > HU_FONTSIZE) {
				width += 4;
				continue;
			}

			w = hu_font[c].width;
			width += w;
		}

		// draw it
		cx = 160 - width / 2;
		// ch = text;
		for (int i = 0; i < ch.length; i++) {
			c = ch[i];
			if (c == 0)
				break;
			c = Character.toUpperCase(c) - HU_FONTSTART;
			if (c < 0 || c > HU_FONTSIZE) {
				cx += 4;
				continue;
			}

			w = hu_font[c].width;
			V.DrawScaledPatch(cx, 180, 0, vs, hu_font[c]);
			cx += w;
		}

	}

	/**
	 * F_CastDrawer
	 * 
	 * @throws IOException
	 */

	// public void V_DrawPatchFlipped (int x, int y, int scrn, patch_t patch);

	public void CastDrawer() {
		spritedef_t sprdef;
		spriteframe_t sprframe;
		int lump;
		boolean flip;
		patch_t patch = null;

		// erase the entire screen to a background
		V.DrawPatchSolidScaled(0, 0, SAFE_SCALE, SAFE_SCALE, 0,
				W.CachePatchName("BOSSBACK", PU_CACHE));

		this.CastPrint(castorder[castnum].name);

		// draw the current frame in the middle of the screen
		sprdef = SM.getSprite(caststate.sprite.ordinal());
		sprframe = sprdef.spriteframes[caststate.frame & FF_FRAMEMASK];
		lump = sprframe.lump[0];
		flip = eval(sprframe.flip[0]);
		// flip=false;
		// lump=0;

		patch = W.CachePatchNum(lump + SM.getFirstSpriteLump());

		if (flip)
			V.DrawScaledPatch(160, 170, 0 | DoomVideoRenderer.V_FLIPPEDPATCH,
					vs, patch);
		else
			V.DrawScaledPatch(160, 170, 0, vs, patch);
	}

	protected int laststage;

	/**
	 * F_BunnyScroll
	 */
	public void BunnyScroll() {
		int scrolled;
		int x;
		patch_t p1;
		patch_t p2;
		String name;
		int stage;

		p1 = W.CachePatchName("PFUB2", PU_LEVEL);
		p2 = W.CachePatchName("PFUB1", PU_LEVEL);

		V.MarkRect(0, 0, SCREENWIDTH, SCREENHEIGHT);

		scrolled = 320 - (finalecount - 230) / 2;
		if (scrolled > 320)
			scrolled = 320;
		if (scrolled < 0)
			scrolled = 0;

		for (x = 0; x < 320; x++) {
			if (x + scrolled < 320)
				V.DrawPatchColScaled(x, p1, x + scrolled, vs, 0);
			else
				V.DrawPatchColScaled(x, p2, x + scrolled - 320, vs, 0);
		}

		if (finalecount < 1130)
			return;
		if (finalecount < 1180) {
			V.DrawScaledPatch((320 - 13 * 8) / 2, (320 - 8 * 8) / 2, 0, vs,
					W.CachePatchName("END0", PU_CACHE));
			laststage = 0;
			return;
		}

		stage = (finalecount - 1180) / 5;
		if (stage > 6)
			stage = 6;
		if (stage > laststage) {
			S.StartSound(null, sfxenum_t.sfx_pistol);
			laststage = stage;
		}

		name = ("END" + stage);
		V.DrawScaledPatch((320 - 13 * 8) / 2, (320 - 8 * 8) / 2, 0, vs,
				W.CachePatchName(name, PU_CACHE));
	}

	//
	// F_Drawer
	//
	public void Drawer() {
		if (finalestage == 2) {
			CastDrawer();
			return;
		}

		if (finalestage == 0)
			TextWrite();
		else {
			switch (DS.gameepisode) {
			case 1:
				if (DS.isCommercial() || DS.isRegistered())
					V.DrawPatchSolidScaled(0, 0, this.SAFE_SCALE,
							this.SAFE_SCALE, 0,
							W.CachePatchName("CREDIT", PU_CACHE));
				else
					// Fun fact: Registered/Ultimate Doom has no "HELP2" lump.
					V.DrawPatchSolidScaled(0, 0, this.SAFE_SCALE,
							this.SAFE_SCALE, 0,
							W.CachePatchName("HELP2", PU_CACHE));
				break;
			case 2:
				V.DrawPatchSolidScaled(0, 0, this.SAFE_SCALE, this.SAFE_SCALE,
						0, W.CachePatchName("VICTORY2", PU_CACHE));
				break;
			case 3:
				BunnyScroll();
				break;
			case 4:
				V.DrawPatchSolidScaled(0, 0, this.SAFE_SCALE, this.SAFE_SCALE,
						0, W.CachePatchName("ENDPIC", PU_CACHE));
				break;
			}
		}

	}

	@SuppressWarnings("unchecked")
	public Finale(DoomStatus DC) {
		this.updateStatus(DC);
		hu_font = HU.getHUFonts();

		castinfo_t shit = new castinfo_t(CC_ZOMBIE, mobjtype_t.MT_POSSESSED);

		ArrayList<castinfo_t> crap = new ArrayList<castinfo_t>();
		crap.add(new castinfo_t(CC_ZOMBIE, mobjtype_t.MT_POSSESSED));
		crap.add(new castinfo_t(CC_SHOTGUN, mobjtype_t.MT_SHOTGUY));
		crap.add(new castinfo_t(CC_HEAVY, mobjtype_t.MT_CHAINGUY));
		crap.add(new castinfo_t(CC_IMP, mobjtype_t.MT_TROOP));
		crap.add(new castinfo_t(CC_DEMON, mobjtype_t.MT_SERGEANT));
		crap.add(new castinfo_t(CC_LOST, mobjtype_t.MT_SKULL));
		crap.add(new castinfo_t(CC_CACO, mobjtype_t.MT_HEAD));
		crap.add(new castinfo_t(CC_HELL, mobjtype_t.MT_KNIGHT));
		crap.add(new castinfo_t(CC_BARON, mobjtype_t.MT_BRUISER));
		crap.add(new castinfo_t(CC_ARACH, mobjtype_t.MT_BABY));
		crap.add(new castinfo_t(CC_PAIN, mobjtype_t.MT_PAIN));
		crap.add(new castinfo_t(CC_REVEN, mobjtype_t.MT_UNDEAD));
		crap.add(new castinfo_t(CC_MANCU, mobjtype_t.MT_FATSO));
		crap.add(new castinfo_t(CC_ARCH, mobjtype_t.MT_VILE));
		crap.add(new castinfo_t(CC_SPIDER, mobjtype_t.MT_SPIDER));
		crap.add(new castinfo_t(CC_CYBER, mobjtype_t.MT_CYBORG));
		crap.add(new castinfo_t(CC_HERO, mobjtype_t.MT_PLAYER));
		crap.add(new castinfo_t(null, null));

		castorder = C2JUtils
				.createArrayOfObjects(castinfo_t.class, crap.size());
		crap.toArray(castorder);

	}

	@Override
	public void updateStatus(DoomStatus<?,?> DC) {
		this.DG = DC.DG;
		this.DS = DC.DM;
		V = (DoomVideoRenderer<T,?>) DC.V;
		S = DC.S;
		HU = DC.HU;
		W = DC.W;
		SM = DC.SM;
	}

	// //////////////////////////VIDEO SCALE STUFF
	// ////////////////////////////////

	protected int SCREENWIDTH;
	protected int SCREENHEIGHT;
	protected int SAFE_SCALE;
	protected IVideoScale vs;

	@Override
	public void setVideoScale(IVideoScale vs) {
		this.vs = vs;
	}

	@Override
	public void initScaling() {
		this.SCREENHEIGHT = vs.getScreenHeight();
		this.SCREENWIDTH = vs.getScreenWidth();
		this.SAFE_SCALE = vs.getSafeScaling();

		// Pre-scale stuff.

	}

}

// /$Log
package f;

import data.mobjtype_t;

/**
 * Final DOOM 2 animation Casting by id Software. in order of appearance
 */

public class castinfo_t {
	String name;
	mobjtype_t type;

	public castinfo_t() {

	}

	public castinfo_t(String name, mobjtype_t type) {
		this.name = name;
		this.type = type;
	}

}

package f;

public interface IWiper {

    boolean ScreenWipe(int wipeno, int x, int y, int width, int height,
            int ticks);

    boolean EndScreen(int x, int y, int width, int height);

    boolean StartScreen(int x, int y, int width, int height);

}

package f;

public class point_t {
     public point_t(int x, int y) {
        this.x=x;
        this.y=y;
    }
     public   int     x;
     public  int     y;
        
    }

package f;

import doom.DoomStatus;
import m.IRandom;
import v.DoomVideoRenderer;
import v.IVideoScale;
import v.IVideoScaleAware;
import i.DoomStatusAware;
import i.DoomVideoInterface;

public abstract class AbstractWiper<T,V> implements IWiper, DoomStatusAware, IVideoScaleAware{
    
    //
    //                           SCREEN WIPE PACKAGE
    //

    /** These don't seem to be used anywhere */
    
    public static enum wipe
    {
        // simple gradual pixel change for 8-bit only
        // MAES: this transition isn't guaranteed to always terminate
        // see Chocolate Strife develpment. Unused in Doom anyway.
        ColorXForm,
        
        // weird screen melt
        Melt,  

        NUMWIPES
    };
    
    /** when false, stop the wipe */
    protected volatile boolean  go = false;

    protected V    wipe_scr_start;
    protected V    wipe_scr_end;
    protected V    wipe_scr;
    
////////////////////////////VIDEO SCALE STUFF ////////////////////////////////

    protected int SCREENWIDTH;
    protected int SCREENHEIGHT;
    protected int Y_SCALE;
    protected IVideoScale vs;


    @Override
    public void setVideoScale(IVideoScale vs) {
        this.vs=vs;
    }

    @Override
    public void initScaling() {
        this.SCREENHEIGHT=vs.getScreenHeight();
        this.SCREENWIDTH=vs.getScreenWidth();
        this.Y_SCALE=vs.getScalingY();

        // Pre-scale stuff.
    }

    ///////////////////// STATUS /////////////////////

    @SuppressWarnings("unchecked")
    @Override
    public void updateStatus(DoomStatus<?,?> DS){
    this.RND=DS.RND;
    this.V=(DoomVideoRenderer<T, V>) DS.V;
    this.VI=(DoomVideoInterface<V>) DS.VI;
    }
    
    IRandom RND;
    DoomVideoRenderer<T,V> V;
    DoomVideoInterface<V> VI;

    
}

package f;

import w.animenum_t;
import rr.patch_t;

//
//Animation.
//There is another anim_t used in p_spec.
//

public class anim_t {

        public anim_t(animenum_t type, int period, int nanims, point_t loc,
            int data1, int data2, patch_t[] p, int nexttic, int lastdrawn,
            int ctr, int state) {
        this.type = type;
        this.period = period;
        this.nanims = nanims;
        this.loc = loc;
        this.data1 = data1;
        this.data2 = data2;
        this.p = p;
        this.nexttic = nexttic;
        this.lastdrawn = lastdrawn;
        this.ctr = ctr;
        this.state = state;
    }
        // Partial constructor, only 4 first fields.
        public anim_t(animenum_t animAlways, int period, int nanims, point_t loc
               ) {
            this.type = animAlways;
            this.period = period;
            this.nanims = nanims;
            this.loc = loc;
        }
        
        // Partial constructor, only 5 first fields.
        public anim_t(animenum_t type, int period, int nanims, point_t loc, int data1
               ) {
            this.type = type;
            this.period = period;
            this.nanims = nanims;
            this.loc = loc;
            this.data1=data1;
        }
        
        public animenum_t  type;

        // period in tics between animations
        public int     period;

        // number of animation frames
        public int     nanims;

        // location of animation
        point_t loc;

        // ALWAYS: n/a,
        // RANDOM: period deviation (<256),
        // LEVEL: level
        public int     data1;

        // ALWAYS: n/a,
        // RANDOM: random base period,
        // LEVEL: n/a
        public int     data2; 

        // actual graphics for frames of animations
        //Maes: was pointer to array
        public patch_t[] p= new patch_t[3];

        // following must be initialized to zero before use!

        // next value of bcnt (used in conjunction with period)
        public int     nexttic;

        // last drawn animation frame
        public int     lastdrawn;

        // next frame number to animate
        public int     ctr;
        
        // used by RANDOM and LEVEL when animating
        public int     state;  

    } 

package f;

import i.DoomStatusAware;
import v.IVideoScaleAware;
import w.animenum_t;
import static data.Defines.TICRATE;

public abstract class AbstractEndLevel implements DoomStatusAware, IVideoScaleAware{
	
	//NET GAME STUFF

	public static final int NG_STATSY       =50;
	public static final int NG_SPACINGX        = 64;

	//DEATHMATCH STUFF
	public static final int DM_MATRIXX = 42;
	public static final int DM_MATRIXY =     68;

	public static final int DM_SPACINGX =    40;

	public static final int DM_TOTALSX =     269;

	public static final int DM_KILLERSX  =   10;
	public static final int DM_KILLERSY   =  100;
	public static final int DM_VICTIMSX    =     5;
	public static final int DM_VICTIMSY    = 50;

	// static point_t lnodes[NUMEPISODES][NUMMAPS] 
	final static public point_t[][] lnodes =
	{
	    // Episode 0 World Map
	    {
	    new point_t( 185, 164 ),   // location of level 0 (CJ)
	    new point_t( 148, 143 ),   // location of level 1 new point_t(CJ)
	    new point_t( 69, 122 ),    // location of level 2 new point_t(CJ)
	    new point_t( 209, 102 ),   // location of level 3 new point_t(CJ)
	    new point_t( 116, 89 ),    // location of level 4 new point_t(CJ)
	    new point_t( 166, 55 ),    // location of level 5 new point_t(CJ)
	    new point_t( 71, 56 ), // location of level 6 new point_t(CJ)
	    new point_t( 135, 29 ),    // location of level 7 new point_t(CJ)
	    new point_t( 71, 24 )  // location of level 8 new point_t(CJ)
	    },

	    // Episode 1 World Map should go here
	    {
	    new point_t( 254, 25 ),    // location of level 0 new point_t(CJ)
	    new point_t( 97, 50 ), // location of level 1 new point_t(CJ)
	    new point_t( 188, 64 ),    // location of level 2 new point_t(CJ)
	    new point_t( 128, 78 ),    // location of level 3 new point_t(CJ)
	    new point_t( 214, 92 ),    // location of level 4 new point_t(CJ)
	    new point_t( 133, 130 ),   // location of level 5 new point_t(CJ)
	    new point_t( 208, 136 ),   // location of level 6 new point_t(CJ)
	    new point_t( 148, 140 ),   // location of level 7 new point_t(CJ)
	    new point_t( 235, 158 )    // location of level 8 new point_t(CJ)
	    },

	    // Episode 2 World Map should go here
	    {
	    new point_t( 156, 168 ),   // location of level 0 new point_t(CJ)
	    new point_t( 48, 154 ),    // location of level 1 new point_t(CJ)
	    new point_t( 174, 95 ),    // location of level 2 new point_t(CJ)
	    new point_t( 265, 75 ),    // location of level 3 new point_t(CJ)
	    new point_t( 130, 48 ),    // location of level 4 new point_t(CJ)
	    new point_t( 279, 23 ),    // location of level 5 new point_t(CJ)
	    new point_t( 198, 48 ),    // location of level 6 new point_t(CJ)
	    new point_t( 140, 25 ),    // location of level 7 new point_t(CJ)
	    new point_t( 281, 136 )    // location of level 8 new point_t(CJ)
	    }

	};

	//
	//Animation locations for episode 0 (1).
	//Using patches saves a lot of space,
	//as they replace 320x200 full screen frames.
	//
	
	public static  final anim_t[] epsd0animinfo =
	{
	 new anim_t(animenum_t.ANIM_ALWAYS, TICRATE/3, 3, new point_t( 224, 104 ) ),
	  new anim_t( animenum_t.ANIM_ALWAYS, TICRATE/3, 3,  new point_t( 184, 160 ) ),
	  new anim_t( animenum_t.ANIM_ALWAYS, TICRATE/3, 3,  new point_t( 112, 136 ) ),
	  new anim_t( animenum_t.ANIM_ALWAYS, TICRATE/3, 3,  new point_t( 72, 112 ) ),
	  new anim_t( animenum_t.ANIM_ALWAYS, TICRATE/3, 3, new point_t( 88, 96 ) ),
	  new anim_t( animenum_t.ANIM_ALWAYS, TICRATE/3, 3,  new point_t( 64, 48 ) ),
	  new anim_t( animenum_t.ANIM_ALWAYS, TICRATE/3, 3,  new point_t( 192, 40 ) ),
	  new anim_t(animenum_t.ANIM_ALWAYS, TICRATE/3, 3,  new point_t( 136, 16 ) ),
	  new anim_t(animenum_t. ANIM_ALWAYS, TICRATE/3, 3,  new point_t( 80, 16 ) ),
	  new anim_t( animenum_t.ANIM_ALWAYS, TICRATE/3, 3,  new point_t( 64, 24 ) )
	};

	public static final anim_t[] epsd1animinfo =
	{
	  new anim_t( animenum_t.ANIM_LEVEL, TICRATE/3, 1,  new point_t( 128, 136 ), 1 ),
	  new anim_t( animenum_t.ANIM_LEVEL, TICRATE/3, 1,  new point_t( 128, 136 ), 2 ),
	  new anim_t( animenum_t.ANIM_LEVEL, TICRATE/3, 1,  new point_t( 128, 136 ), 3 ),
	  new anim_t( animenum_t.ANIM_LEVEL, TICRATE/3, 1, new point_t( 128, 136 ), 4 ),
	  new anim_t( animenum_t.ANIM_LEVEL, TICRATE/3, 1,  new point_t(128, 136 ), 5 ),
	  new anim_t( animenum_t.ANIM_LEVEL, TICRATE/3, 1,  new point_t( 128, 136 ), 6 ),
	  new anim_t( animenum_t.ANIM_LEVEL, TICRATE/3, 1,  new point_t( 128, 136 ), 7 ),
	  new anim_t( animenum_t.ANIM_LEVEL, TICRATE/3, 3, new point_t( 192, 144 ), 8 ),
	  new anim_t( animenum_t.ANIM_LEVEL, TICRATE/3, 1,  new point_t( 128, 136 ), 8 )
	};

	public static final anim_t[] epsd2animinfo =
	{
	  new anim_t( animenum_t.ANIM_ALWAYS, TICRATE/3, 3,  new point_t( 104, 168 ) ),
	  new anim_t( animenum_t.ANIM_ALWAYS, TICRATE/3, 3,  new point_t( 40, 136 ) ),
	  new anim_t( animenum_t.ANIM_ALWAYS, TICRATE/3, 3,  new point_t( 160, 96 ) ),
	  new anim_t( animenum_t.ANIM_ALWAYS, TICRATE/3, 3,  new point_t(104, 80 ) ),
	  new anim_t( animenum_t.ANIM_ALWAYS, TICRATE/3, 3, new point_t( 120, 32 ) ),
	  new anim_t( animenum_t.ANIM_ALWAYS, TICRATE/4, 3,  new point_t( 40, 0 ) )
	};

	/*static int NUMANIMS[NUMEPISODES] =
	{
	 sizeof(epsd0animinfo)/sizeof(anim_t),
	 sizeof(epsd1animinfo)/sizeof(anim_t),
	 sizeof(epsd2animinfo)/sizeof(anim_t)
	};*/

	// MAES: cute, but we can do it in a more Java-friendly way :-p

	public static final int[] NUMANIMS={epsd0animinfo.length,epsd1animinfo.length,epsd2animinfo.length};

	/** ATTENTION: there's a difference between these "anims" and those used in p_spec.c */

	public static final anim_t[][] anims =
	{
	 epsd0animinfo,
	 epsd1animinfo,
	 epsd2animinfo
	};

}

package f;

import v.DoomVideoRenderer;
import doom.DoomMain;

public abstract class Wiper<T,V> extends AbstractWiper<T,V> {

    static final String rcsid = "$Id: Wiper.java,v 1.18 2012/09/24 17:16:23 velktron Exp $";
    
    protected wipefun[] wipes;
    
    public Wiper(DoomMain<T,V> DC){
        this.updateStatus(DC);
    }
    
    public static final class HiColor extends Wiper<byte[],short[]>{

        public HiColor(DoomMain<byte[], short[]> DC) {
            super(DC);
            wipes=new wipefun[]{
                    new wipe_initColorXForm(), new wipe_doColorXForm(), new wipe_exitColorXForm(),
                    new wipe_initMelt(), new wipe_doMelt(), new wipe_exitMelt()
                    };
        }
        
        /** Those guys sure have an obsession with shit...this is supposed to do some
         * lame-ass transpose.
         * 
         * @param array
         * @param width
         * @param height
         */
        
        protected final void
        shittyColMajorXform
        ( short[]    array,
          int       width,
          int       height )
        {
            int     x;
            int     y;
            short[]  dest;

            dest = new short[width*height];

            for(y=0;y<height;y++)
            for(x=0;x<width;x++){
                dest[x*height+y] = array[y*width+x];
                //dest[(1+x)*height+y] = array[y*width+(1+x)];
            }
            System.arraycopy(dest, 0, array, 0, width*height);

            //Z_Free(dest);

        }
        
        class wipe_doColorXForm implements wipefun{
            
            public boolean
        invoke
        ( int   width,
          int   height,
          int   ticks )
        {
            boolean changed;
            short[]   w=wipe_scr;
            short[]   e=wipe_scr_end;
            int     newval;

            changed = false;
            int pw =0;// wipe_scr;
            int pe = 0; //wipe_scr_end;
            
            while (pw!=width*height)
            {
            if (w[pw] != e[pe])
            {
                if (w[pw] > e[pe])
                {
                newval = w[pw] - ticks;
                if (newval < e[pe])
                    w[pw] = e[pe];
                else
                    w[pw] = (byte) newval;
                changed = true;
                }
                else if (w[pw] < e[pe])
                {
                newval = w[pw] + ticks;
                if (newval > e[pe])
                    w[pw] = e[pe];
                else
                    w[pw] = (byte) newval;
                changed = true;
                }
            }
            pw++;
            pe++;
            }

            return !changed;

        }
        }
    
        class wipe_doMelt implements wipefun{
            public boolean
            invoke
        ( int   width,
          int   height,
          int   ticks )
        {
                
            //   int w2=2*width;
            //    int w3=3*width;
            //    int w4=4*width;

            int     dy;
            int     idx;
            
            // Indexex to short* ?! WTF... 
            int  ps;
            int  pd;
            
            short[] s;//=wipe_scr_end;
            short[] d=wipe_scr;
            
            boolean done = true;

            //width=2;

            while (ticks-->0)
            {
            for (int i=0;i<width;i++)
            {
                // Column won't start yet.
                if (y[i]<0)
                {
                y[i]++; done = false;
                }
                else if (y[i] < height)
                {
                    
                    
                dy = (y[i] < 16*Y_SCALE) ? y[i]+Y_SCALE : 8*Y_SCALE;
                if (y[i]+dy >= height) dy = height - y[i];
                ps = i*height+y[i];// &((short *)wipe_scr_end)[i*height+y[i]];
                pd = y[i]*width+i;//&((short *)wipe_scr)[y[i]*width+i];
                idx = 0;

                s=wipe_scr_end;
                

                
                // MAES: this part should draw the END SCREEN "behind" the melt.
                for (int j=dy;j>0;j--)
                {
                    d[pd+idx] = s[ps++];
                    idx += width;
                }
                y[i] += dy;
                s=wipe_scr_start;
                ps = i*height; //&((short *)wipe_scr_start)[i*height];
                pd = y[i]*width+i; //&((short *)wipe_scr)[y[i]*width+i];
                idx = 0;

                // This draws a column shifted by y[i]
                
                for (int j=height-y[i];j>0;j--)
                {
                    d[pd+idx] = s[ps++];
                    idx+=width;
                }
                done = false;
                }
            }
            }

            return done;

        }
        }
    }
    
    public static final class Indexed extends Wiper<byte[],byte[]>{

        public Indexed(DoomMain<byte[], byte[]> DC) {
            super(DC);
            wipes=new wipefun[]{
                    new wipe_initColorXForm(), new wipe_doColorXForm(), new wipe_exitColorXForm(),
                    new wipe_initMelt(), new wipe_doMelt(), new wipe_exitMelt()
                    };
        }
        
        /** Those guys sure have an obsession with shit...this is supposed to do some
         * lame-ass transpose.
         * 
         * @param array
         * @param width
         * @param height
         */
        
        protected final void
        shittyColMajorXform
        ( byte[]    array,
          int       width,
          int       height )
        {
            int     x;
            int     y;
            byte[]  dest;

            dest = new byte[width*height];

            for(y=0;y<height;y++)
            for(x=0;x<width;x++){
                dest[x*height+y] = array[y*width+x];
                //dest[(1+x)*height+y] = array[y*width+(1+x)];
            }
            System.arraycopy(dest, 0, array, 0, width*height);

            //Z_Free(dest);

        }
        
        class wipe_doColorXForm implements wipefun{
            
            public boolean
        invoke
        ( int   width,
          int   height,
          int   ticks )
        {
            boolean changed;
            byte[]   w=wipe_scr;
            byte[]   e=wipe_scr_end;
            int     newval;

            changed = false;
            int pw =0;// wipe_scr;
            int pe = 0; //wipe_scr_end;
            
            while (pw!=width*height)
            {
            if (w[pw] != e[pe])
            {
                if (w[pw] > e[pe])
                {
                newval = w[pw] - ticks;
                if (newval < e[pe])
                    w[pw] = e[pe];
                else
                    w[pw] = (byte) newval;
                changed = true;
                }
                else if (w[pw] < e[pe])
                {
                newval = w[pw] + ticks;
                if (newval > e[pe])
                    w[pw] = e[pe];
                else
                    w[pw] = (byte) newval;
                changed = true;
                }
            }
            pw++;
            pe++;
            }

            return !changed;

        }
        }
    
        class wipe_doMelt implements wipefun{
            public boolean
            invoke
        ( int   width,
          int   height,
          int   ticks )
        {
                
            //   int w2=2*width;
            //    int w3=3*width;
            //    int w4=4*width;

            int     dy;
            int     idx;
            
            // Indexex to short* ?! WTF... 
            int  ps;
            int  pd;
            
            byte[] s;//=wipe_scr_end;
            byte[] d=wipe_scr;
            
            boolean done = true;

            //width=2;

            while (ticks-->0)
            {
            for (int i=0;i<width;i++)
            {
                // Column won't start yet.
                if (y[i]<0)
                {
                y[i]++; done = false;
                }
                else if (y[i] < height)
                {
                    
                    
                dy = (y[i] < 16*Y_SCALE) ? y[i]+Y_SCALE : 8*Y_SCALE;
                if (y[i]+dy >= height) dy = height - y[i];
                ps = i*height+y[i];// &((short *)wipe_scr_end)[i*height+y[i]];
                pd = y[i]*width+i;//&((short *)wipe_scr)[y[i]*width+i];
                idx = 0;

                s=wipe_scr_end;
                

                
                // MAES: this part should draw the END SCREEN "behind" the melt.
                for (int j=dy;j>0;j--)
                {
                    d[pd+idx] = s[ps++];
                    idx += width;
                }
                y[i] += dy;
                s=wipe_scr_start;
                ps = i*height; //&((short *)wipe_scr_start)[i*height];
                pd = y[i]*width+i; //&((short *)wipe_scr)[y[i]*width+i];
                idx = 0;

                // This draws a column shifted by y[i]
                
                for (int j=height-y[i];j>0;j--)
                {
                    d[pd+idx] = s[ps++];
                    idx+=width;
                }
                done = false;
                }
            }
            }

            return done;

        }
        }
    }
    
    public static final class TrueColor extends Wiper<byte[],int[]>{

        public TrueColor(DoomMain<byte[], int[]> DC) {
            super(DC);
            wipes=new wipefun[]{
                    new wipe_initColorXForm(), new wipe_doColorXForm(), new wipe_exitColorXForm(),
                    new wipe_initMelt(), new wipe_doMelt(), new wipe_exitMelt()
                    };
        }
        
        /** Those guys sure have an obsession with shit...this is supposed to do some
         * lame-ass transpose.
         * 
         * @param array
         * @param width
         * @param height
         */
        
        protected final void
        shittyColMajorXform
        ( int[]    array,
          int       width,
          int       height )
        {
            int     x;
            int     y;
            int[]  dest;

            dest = new int[width*height];

            for(y=0;y<height;y++)
            for(x=0;x<width;x++){
                dest[x*height+y] = array[y*width+x];
                //dest[(1+x)*height+y] = array[y*width+(1+x)];
            }
            System.arraycopy(dest, 0, array, 0, width*height);

            //Z_Free(dest);

        }
        
        class wipe_doColorXForm implements wipefun{
            
            public boolean
        invoke
        ( int   width,
          int   height,
          int   ticks )
        {
            boolean changed;
            int[]   w=wipe_scr;
            int[]   e=wipe_scr_end;
            int     newval;

            changed = false;
            int pw =0;// wipe_scr;
            int pe = 0; //wipe_scr_end;
            
            while (pw!=width*height)
            {
            if (w[pw] != e[pe])
            {
                if (w[pw] > e[pe])
                {
                newval = w[pw] - ticks;
                if (newval < e[pe])
                    w[pw] = e[pe];
                else
                    w[pw] = (byte) newval;
                changed = true;
                }
                else if (w[pw] < e[pe])
                {
                newval = w[pw] + ticks;
                if (newval > e[pe])
                    w[pw] = e[pe];
                else
                    w[pw] = (byte) newval;
                changed = true;
                }
            }
            pw++;
            pe++;
            }

            return !changed;

        }
        }
    
        class wipe_doMelt implements wipefun{
            public boolean
            invoke
        ( int   width,
          int   height,
          int   ticks )
        {
                
            //   int w2=2*width;
            //    int w3=3*width;
            //    int w4=4*width;

            int     dy;
            int     idx;
            
            // Indexex to short* ?! WTF... 
            int  ps;
            int  pd;
            
            int[] s;//=wipe_scr_end;
            int[] d=wipe_scr;
            
            boolean done = true;

            //width=2;

            while (ticks-->0)
            {
            for (int i=0;i<width;i++)
            {
                // Column won't start yet.
                if (y[i]<0)
                {
                y[i]++; done = false;
                }
                else if (y[i] < height)
                {
                    
                    
                dy = (y[i] < 16*Y_SCALE) ? y[i]+Y_SCALE : 8*Y_SCALE;
                if (y[i]+dy >= height) dy = height - y[i];
                ps = i*height+y[i];// &((short *)wipe_scr_end)[i*height+y[i]];
                pd = y[i]*width+i;//&((short *)wipe_scr)[y[i]*width+i];
                idx = 0;

                s=wipe_scr_end;
                

                
                // MAES: this part should draw the END SCREEN "behind" the melt.
                for (int j=dy;j>0;j--)
                {
                    d[pd+idx] = s[ps++];
                    idx += width;
                }
                y[i] += dy;
                s=wipe_scr_start;
                ps = i*height; //&((short *)wipe_scr_start)[i*height];
                pd = y[i]*width+i; //&((short *)wipe_scr)[y[i]*width+i];
                idx = 0;

                // This draws a column shifted by y[i]
                
                for (int j=height-y[i];j>0;j--)
                {
                    d[pd+idx] = s[ps++];
                    idx+=width;
                }
                done = false;
                }
            }
            }

            return done;

        }
        }
    }
    
    protected abstract void shittyColMajorXform( V array, int       width,  int       height );
    
    class wipe_initColorXForm implements wipefun{
        public boolean
        invoke
    ( int   width,
      int   height,
      int   ticks )
    {
        System.arraycopy(wipe_scr_start,0 ,wipe_scr, 0,width*height);
        return false;
    }
    
    }
   
    
    
    class wipe_exitColorXForm implements wipefun{
    public boolean
    invoke
    ( int   width,
      int   height,
      int   ticks )
    {
        return false;
    }
    }


    protected int[] y;

    class wipe_initMelt implements wipefun{
        public boolean
        invoke
    ( int   width,
      int   height,
      int   ticks )
    {
        int i, r;
        
        // copy start screen to main screen
        System.arraycopy(wipe_scr_start, 0,wipe_scr, 0,width*height);
        
        // makes this wipe faster (in theory)
        // to have stuff in column-major format
        shittyColMajorXform(wipe_scr_start, width, height);
        shittyColMajorXform(wipe_scr_end, width, height);
        
        // setup initial column positions
        // (y<0 => not ready to scroll yet)
        y = new int[width];
        
        y[0] = -(RND.M_Random()%16);
        
        for (int j=1;j<Y_SCALE;j++){
        	y[j]=y[j-1];
        }
        
        for (i=Y_SCALE;i<width;i+=Y_SCALE)
        {
            r = (RND.M_Random()%3) - 1;
            y[i] = y[i-1] + r;
            if (y[i] > 0) y[i] = 0;
            else if (y[i] == -16) y[i] = -15;
            
        	for (int j=1;j<Y_SCALE;j++){
        		y[i+j]=y[i];
        	}
        
        }

        return false;
    }
    }

    
    
    class wipe_exitMelt implements wipefun{
        public boolean
        invoke
    ( int   width,
      int   height,
      int   ticks )

    {
        y=null; //Z_Free(y);
        return false;
    }
    }

    /** Sets "from" screen and stores it in "screen 2"*/
    
    @Override
    public boolean
    StartScreen
    ( int   x,
      int   y,
      int   width,
      int   height )
    {
        wipe_scr_start = V.getScreen(DoomVideoRenderer.SCREEN_WS);
        //  byte[] screen_zero=V.getScreen(0);
        VI.ReadScreen(wipe_scr_start);
        
        //System.arraycopy(screen_zero,0,wipe_scr_start, 0, SCREENWIDTH*SCREENHEIGHT);
        return false;
    }

    /** Sets "to" screen and stores it to "screen 3" */

    @Override
    public boolean
    EndScreen
    ( int   x,
      int   y,
      int   width,
      int   height )
    {
        // Set end screen to "screen 3" and copy visible screen to it.
        wipe_scr_end = V.getScreen(DoomVideoRenderer.SCREEN_WE);
        VI.ReadScreen(wipe_scr_end);
        
        // Restore starting screen.
        V screen_zero= V.getScreen(DoomVideoRenderer.SCREEN_FG);        
        System.arraycopy(wipe_scr_start,0,screen_zero, 0, SCREENWIDTH*SCREENHEIGHT);
        return false;
    }
    @Override
    public boolean
    ScreenWipe
    ( int   wipeno,
      int   x,
      int   y,
      int   width,
      int   height,
      int   ticks )
    {
        boolean rc;
  
        //System.out.println("Ticks do "+ticks);

        // initial stuff
        if (!go)
        {
        go = true;
        //wipe_scr = new byte[width*height]; // DEBUG
        wipe_scr = V.getScreen(DoomVideoRenderer.SCREEN_FG);
        // HOW'S THAT FOR A FUNCTION POINTER, BIATCH?!
        (wipes[wipeno*3]).invoke(width, height, ticks);
        }

        // do a piece of wipe-in
        V.MarkRect(0, 0, width, height);
        rc = (wipes[wipeno*3+1]).invoke(width, height, ticks);
        // V.DrawBlock(x, y, 0, width, height, wipe_scr); // DEBUG

        // final stuff
        if (rc)
        {
        go = false;
        (wipes[wipeno*3+2]).invoke(width, height, ticks);
        }

        return !go;

    }

    
    /** Interface for ASS-WIPING functions */
    
    interface wipefun{
        public boolean invoke(int   width,
                int   height,
                int   ticks );
    }
    
}

package data;
// Emacs style mode select   -*- C++ -*- 
//-----------------------------------------------------------------------------
//
// $Id: dstrings.java,v 1.4 2010/10/01 16:47:51 velktron Exp $
//
// Copyright (C) 1993-1996 by id Software, Inc.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// $Log: dstrings.java,v $
// Revision 1.4  2010/10/01 16:47:51  velktron
// Fixed tab interception.
//
// Revision 1.3  2010/09/22 16:40:02  velktron
// MASSIVE changes in the status passing model.
// DoomMain and DoomGame unified.
// Doomstat merged into DoomMain (now status and game functions are one).
//
// Most of DoomMain implemented. Possible to attempt a "classic type" start but will stop when reading sprites.
//
// Revision 1.2  2010/07/22 15:37:53  velktron
// MAJOR changes in Menu system.
//
// Revision 1.1  2010/06/30 08:58:51  velktron
// Let's see if this stuff will finally commit....
//
//
// Most stuff is still  being worked on. For a good place to start and get an idea of what is being done, I suggest checking out the "testers" package.
//
// Revision 1.1  2010/06/29 11:07:34  velktron
// Release often, release early they say...
//
// Commiting ALL stuff done so far. A lot of stuff is still broken/incomplete, and there's still mixed C code in there. I suggest you load everything up in Eclpise and see what gives from there.
//
// A good place to start is the testers/ directory, where you  can get an idea of how a few of the implemented stuff works.
//
//
// DESCRIPTION:
//	Globally defined strings.
// 
//-----------------------------------------------------------------------------

import static doom.englsh.*;

public class dstrings{

//public static const char rcsid[] = "$Id: dstrings.java,v 1.4 2010/10/01 16:47:51 velktron Exp $";


    // Misc. other strings.
    public static final String SAVEGAMENAME=    "doomsav";
    
    /** File locations,  relative to current position.
     * Path names are OS-sensitive. 
     * Only really used with the -shdev command line parameter.
     * 
     * MAES: shouldn't those need path separator characters? :-S
     */
    
    
    public static final String DEVMAPS= "devmaps", DEVDATA ="devdata";


    // Not done in french?

    // QuitDOOM messages
    public static final int NUM_QUITMESSAGES= 22;


public static final String[] endmsg=
{
  // DOOM1
  QUITMSG,
  "please don't leave, there's more\ndemons to toast!",
  "let's beat it -- this is turning\ninto a bloodbath!",
  "i wouldn't leave if i were you.\ndos is much worse.",
  "you're trying to say you like dos\nbetter than me, right?",
  "don't leave yet -- there's a\ndemon around that corner!",
  "ya know, next time you come in here\ni'm gonna toast ya.",
  "go ahead and leave. see if i care.",

  // QuitDOOM II messages
  "you want to quit?\nthen, thou hast lost an eighth!",
  "don't go now, there's a \ndimensional shambler waiting\nat the dos prompt!",
  "get outta here and go back\nto your boring programs.",
  "if i were your boss, i'd \n deathmatch ya in a minute!",
  "look, bud. you leave now\nand you forfeit your body count!",
  "just leave. when you come\nback, i'll be waiting with a bat.",
  "you're lucky i don't smack\nyou for thinking about leaving.",

  // FinalDOOM?
  "fuck you, pussy!\nget the fuck out!",
  "you quit and i'll jizz\nin your cystholes!",
  "if you leave, i'll make\nthe lord drink my jizz.",
  "hey, ron! can we say\n'fuck' in the game?",
  "i'd leave: this is just\nmore monsters and levels.\nwhat a load.",
  "suck it down, asshole!\nyou're a fucking wimp!",
  "don't quit now! we're \nstill spending your money!",

  // Internal debug. Different style, too.
  "THIS IS NO MESSAGE!\nPage intentionally left blank."
};
}


  



package data;

/**
 *   SoundFX struct.
 *    
 *   
 *
 */

public class sfxinfo_t {
	
		public sfxinfo_t(){
			
		}
	
        /** up to 6-character name */
        public String   name;

        /** Sfx singularity (only one at a time) */
        public boolean     singularity;

        /** Sfx priority */
        public int     priority;

        // referenced sound if a link
        // MAES: since in pure hackish C style, a "0" value would be used as a boolean, we'll need to distinguish more
        // unambiguously. So for querying, look at the "linked" boolean or a getter.
        public boolean linked;
        
        public sfxinfo_t  link;

        public sfxinfo_t getLink() {
            if (linked) return link;
            else return null;
        }

        public void setLink(sfxinfo_t link) {
            this.link=link;
        }
        
        // pitch if a link
        public int     pitch;

        // volume if a link
        public int     volume;

        /** sound data (used to be void*) */
        public byte[]  data;

        // this is checked every second to see if sound
        // can be thrown out (if 0, then decrement, if -1,
        // then throw out, if > 0, then it is in use)
        public int     usefulness;

        // lump number of sfx
        public int     lumpnum;

        public sfxinfo_t(String name, boolean singularity, int priority,
                sfxinfo_t link, int pitch, int volume, byte[] data,
                int usefulness, int lumpnum) {
            this.name = name;
            this.singularity = singularity;
            this.priority = priority;
            this.link = link;
            this.pitch = pitch;
            this.volume = volume;
            this.data = data;
            this.usefulness = usefulness;
            this.lumpnum = lumpnum;
        }
        
        /** MAES: Call this constructor if you don't want a cross-linked sound.
         * 
         * @param name
         * @param singularity
         * @param priority
         * @param pitch
         * @param volume
         * @param usefulness
         */
        
        public sfxinfo_t(String name, boolean singularity, int priority,
                int pitch, int volume, int usefulness) {
            this.name = name;
            this.singularity = singularity;
            this.priority = priority;
            this.linked = false;
            this.pitch = pitch;
            this.volume = volume;
            this.usefulness = usefulness;
        }
        
        public sfxinfo_t(String name, boolean singularity, int priority, boolean linked,
                int pitch, int volume, int usefulness) {
            this.name = name;
            this.singularity = singularity;
            this.priority = priority;
            this.linked = linked;
            this.pitch = pitch;
            this.volume = volume;
            this.usefulness = usefulness;
        }
        
        public int identify(sfxinfo_t[] array){
        	for (int i=0;i<array.length;i++){
        		if (array[i]==this){
        			return i;
        		}
        	}
        	// Duh
        	return 0;
        }
        
    };

package data;
// Emacs style mode select   -*- C++ -*- 
//-----------------------------------------------------------------------------
//
// $Id: doomtype.java,v 1.3 2011/02/11 00:11:13 velktron Exp $
//
// Copyright (C) 1993-1996 by id Software, Inc.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// DESCRIPTION:
//	Simple basic typedefs, isolated here to make it easier
//	 separating modules.
//    
//-----------------------------------------------------------------------------


public class doomtype {

// C's "chars" are actually Java signed bytes.
public static byte MAXCHAR =((byte)0x7f);
public static short MAXSHORT=	((short)0x7fff);

// Max pos 32-bit int.
public static int MAXINT=((int)0x7fffffff);	
public static long MAXLONG=((long)0x7fffffff);
public static byte MINCHAR=((byte)0x80);
public static short MINSHORT=((short)0x8000);

// Max negative 32-bit integer.
public static int MININT=((int)0x80000000);
public static long MINLONG=((long)0x80000000);
}
package data;

//import m.define;
import static data.Limits.MAXINT;
import static data.Limits.MININT;
import static m.fixed_t.FRACBITS;
import static m.fixed_t.FRACUNIT;
import static m.fixed_t.MAPFRACUNIT;
import g.Keys;
import defines.ammotype_t;
import defines.card_t;
import doom.weapontype_t;

// Emacs style mode select   -*- C++ -*- 
//-----------------------------------------------------------------------------
//
// $Id: Defines.java,v 1.48 2012/09/24 17:16:22 velktron Exp $
//
// Copyright (C) 1993-1996 by id Software, Inc.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// DESCRIPTION:
//  Internally used data structures for virtually everything,
//   key definitions, lots of other stuff.
//
//-----------------------------------------------------------------------------

//#ifndef __DOOMDEF__
//#define __DOOMDEF__

//#include <stdio.h>
//#include <string.h>

//
// Global parameters/defines.
//
// DOOM version
public final class Defines{
    
    /** Seems to be 109 for shareware 1.9, wtf is this*/
public static final int VERSION =  109 ;

/** Some parts of the code may actually be better used as if in a UNIX environment */

public static final boolean NORMALUNIX =false;


/** If rangecheck is undefined,  ost parameter validation debugging code will not be compiled */
public static final boolean RANGECHECK=false;

// Do or do not use external soundserver.
// The sndserver binary to be run separately
//  has been introduced by Dave Taylor.
// The integrated sound support is experimental,
//  and unfinished. Default is synchronous.
// Experimental asynchronous timer based is
//  handled by SNDINTR. 
//#define SNDSERV  1
//#define SNDINTR  1

// Defines suck. C sucks.
// C++ might sucks for OOP, but it sure is a better C.
// So there.

// MAES: moved static defines out of here and into VideoScaleInfo.

// State updates, number of tics / second.
public static final int BASETICRATE = 35;
public static final int TIC_MUL = 1;
public static final int TICRATE = BASETICRATE*TIC_MUL;

//
// Difficulty/skill settings/filters.
//

// Skill flags.
public static int MTF_EASY =       1;
public static int MTF_NORMAL =     2;
public static int MTF_HARD =       4;

// Deaf monsters/do not react to sound.
public static int MTF_AMBUSH =8;

//Maes: this makes it a bit less retarded.
public static final int NUMCARDS=card_t.NUMCARDS.ordinal();


//Maes: this makes it a bit less retarded.
public static final int NUMWEAPONS=weapontype_t.NUMWEAPONS.ordinal();

//Maes: this makes it a bit less retarded.
public static final int NUMAMMO=ammotype_t.NUMAMMO.ordinal();

// Power up artifacts.
public static final int pw_invulnerability=0;
public static final int    pw_strength=1;
public static final int    pw_invisibility=2;
public static final int    pw_ironfeet=3;
public static final int    pw_allmap=4;
public static final int    pw_infrared=5;
public static final int    NUMPOWERS=6;


/** Power up durations,
 *  how many seconds till expiration,
 *  assuming TICRATE is 35 ticks/second.
 */
    public static final int INVULNTICS=(30*TICRATE),
    INVISTICS=(60*TICRATE),
    INFRATICS=(120*TICRATE),
    IRONTICS =(60*TICRATE);

// Center command from Heretic
public final static int TOCENTER=-8;

// from r_defs.h:



//Silhouette, needed for clipping Segs (mainly)
//and sprites representing things.
public static final int SIL_NONE =0;
public static final int SIL_BOTTOM     =1;
public static final int SIL_TOP  =       2;
public static final int SIL_BOTH    =    3;

//SKY, store the number for name.
static public final String SKYFLATNAME  ="F_SKY1";

// The sky map is 256*128*4 maps.
public static final int ANGLETOSKYSHIFT   = 22;

// From r_draw.c

// status bar height at bottom of screen
public static final int  SBARHEIGHT     = 32;


//
//Different vetween registered DOOM (1994) and
//Ultimate DOOM - Final edition (retail, 1995?).
//This is supposedly ignored for commercial
//release (aka DOOM II), which had 34 maps
//in one episode. So there.
public static final int NUMEPISODES=4;
public static final int NUMMAPS     =9;


//in tics
//U #define PAUSELEN        (TICRATE*2) 
//U #define SCORESTEP       100
//U #define ANIMPERIOD      32
//pixel distance from "(YOU)" to "PLAYER N"
//U #define STARDIST        10 
//U #define WK 1


// MAES 23/5/2011: moved SP_... stuff to EndLevel

public static final int    BACKUPTICS     = 12;




// From Zone:

//
//ZONE MEMORY
//PU - purge tags.
//Tags < 100 are not overwritten until freed.
public static final int PU_STATIC   =    1;   // static entire execution time
public static final int PU_SOUND    =    2;   // static while playing
public static final int PU_MUSIC   =     3;   // static while playing
public static final int PU_DAVE   =  4;   // anything else Dave wants static
public static final int PU_LEVEL   =     50;  // static until level exited
public static final int PU_LEVSPEC  =    51;    // a special thinker in a level
//Tags >= 100 are purgable whenever needed.
public static final int PU_PURGELEVEL =  100;
public static final int PU_CACHE     =   101;


// From hu_lib.h:

//background and foreground screen numbers
//different from other modules.
public static final int BG     =     1;
public static final int FG     =     0;

//font stuff
static public final char HU_CHARERASE  =  Keys.KEY_BACKSPACE;

public static final int HU_MAXLINES  =   4;
public static final int HU_MAXLINELENGTH  =  80;

// From hu_stuff.h

//
//Globally visible constants.
//
static public final byte HU_FONTSTART  =  '!'; // the first font characters
static public final byte HU_FONTEND  ='_'; // the last font characters

//Calculate # of glyphs in font.
public static final int HU_FONTSIZE = (HU_FONTEND - HU_FONTSTART + 1); 

static public final char HU_BROADCAST   = 5;

static public final char HU_MSGREFRESH =  Keys.KEY_ENTER;
static public final char HU_MSGX     =0;
static public final char HU_MSGY     =0;
static public final char HU_MSGWIDTH =64;  // in characters
static public final char HU_MSGHEIGHT  =  1;   // in lines

public static final int HU_MSGTIMEOUT =  (4*TICRATE);

public static final int SAVESTRINGSIZE = 24;

//
// Button/action code definitions.
// From d_event.h

     // Press "Fire".
    public static final int BT_ATTACK       = 1;
     // Use button, to open doors, activate switches.
    public static final int BT_USE      = 2;

     // Flag: game events, not really buttons.
    public static final int BT_SPECIAL      = 128;
    public static final int BT_SPECIALMASK  = 3;
     
     // Flag, weapon change pending.
     // If true, the next 3 bits hold weapon num.
    public static final int  BT_CHANGE       = 4;
     // The 3bit weapon mask and shift, convenience.
    public static final int  BT_WEAPONMASK   = (8+16+32);
    public static final int   BT_WEAPONSHIFT  = 3;

     // Pause the game.
    public static final int  BTS_PAUSE       = 1;
     // Save the game at each console.
    public static final int  BTS_SAVEGAME    = 2;

     // Savegame slot numbers
     //  occupy the second byte of buttons.    
    public static final int BTS_SAVEMASK    = (4+8+16);
    public static final int BTS_SAVESHIFT   = 2;
   

    
    
    //==================== Stuff from r_local.c =========================================

    
    public static final int FLOATSPEED        =(FRACUNIT*4);

    public static final int VIEWHEIGHT    =   (41*FRACUNIT);

    // mapblocks are used to check movement
    // against lines and things
    public static final int MAPBLOCKUNITS=    128;
    public static final int MAPBLOCKSIZE  =(MAPBLOCKUNITS*FRACUNIT);
    public static final int MAPBLOCKSHIFT =(FRACBITS+7);
    public static final int MAPBMASK      =(MAPBLOCKSIZE-1);
    public static final int MAPBTOFRAC=       (MAPBLOCKSHIFT-FRACBITS);
    public static final int BLOCKMAPPADDING=       8*FRACUNIT;

    // player radius for movement checking
    public static final int PLAYERRADIUS  =16*FRACUNIT;
    public static final int GRAVITY   =   MAPFRACUNIT;
    public static int USERANGE      =(64*FRACUNIT);
    public static int MELEERANGE    =   (64*FRACUNIT);
    public static int MISSILERANGE=(32*64*FRACUNIT);

    // follow a player exlusively for 3 seconds
    public static int   BASETHRESHOLD=      100;


    
    public static int PT_ADDLINES     =1;
    public static int PT_ADDTHINGS    =2;
    public static int PT_EARLYOUT     =4;
    
 //
 // P_MOBJ
 //
 public static int ONFLOORZ  =   MININT;
 public static int ONCEILINGZ    =   MAXINT;

 // Time interval for item respawning.
 public static int ITEMQUESIZE       =128;
 

    /** Indicate a leaf. e6y: support for extended nodes */
    public static final int NF_SUBSECTOR = 0x80000000;

    /** This is the regular leaf indicator. Use for reference/conversions */
    public static final int NF_SUBSECTOR_CLASSIC = 0x8000;

    
    
    /** Player states. */
    
    public static final int   PST_LIVE=0,     // Playing or camping.    
        PST_DEAD=1,        // Dead on the ground, view follows killer.

        PST_REBORN=2;            // Ready to restart/respawn???
 
    public static final int  FF_FULLBRIGHT =  0x8000;  // flag in thing->frame
    public static final int  FF_FRAMEMASK =   0x7fff;


 
static final String
rcsid = "$Id: Defines.java,v 1.48 2012/09/24 17:16:22 velktron Exp $";
}


package data;

import defines.statenum_t;
import data.sounds.sfxenum_t;

public class mobjinfo_t {
   
        public mobjinfo_t(int doomednum, statenum_t spawnstate, int spawnhealth,
                statenum_t seestate, sfxenum_t seesound, int reactiontime,
                sfxenum_t attacksound, statenum_t painstate,
                int painchance, sfxenum_t painsound,
                statenum_t meleestate, statenum_t missilestate,
                statenum_t deathstate, statenum_t xdeathstate,
                sfxenum_t deathsound, int speed, int radius, int height,
                int mass, int damage, sfxenum_t activesound, long flags,
                statenum_t raisestate) {
            super();
            this.doomednum = doomednum;
            this.spawnstate = spawnstate;
            this.spawnhealth = spawnhealth;
            this.seestate = seestate;
            this.seesound = seesound;
            this.reactiontime = reactiontime;
            this.attacksound = attacksound;
            this.painstate = painstate;
            this.painchance = painchance;
            this.painsound = painsound;
            this.meleestate = meleestate;
            this.missilestate = missilestate;
            this.deathstate = deathstate;
            this.xdeathstate = xdeathstate;
            this.deathsound = deathsound;
            this.speed = speed;
            this.radius = radius;
            this.height = height;
            this.mass = mass;
            this.damage = damage;
            this.activesound = activesound;
            this.flags = flags;
            this.raisestate = raisestate;
        }
        
        public int doomednum;
        public statenum_t spawnstate;
        public int spawnhealth;
        public statenum_t seestate;
        public sfxenum_t seesound;
        public int reactiontime;
        public sfxenum_t attacksound;
        public statenum_t painstate;
        public int painchance;
        public sfxenum_t painsound;
        public statenum_t meleestate;
        public statenum_t missilestate;
        public statenum_t deathstate;
        public statenum_t xdeathstate;
        public sfxenum_t deathsound;
        public int speed;
        public int radius;
        public int height;
        public int mass;
        public int damage;
        public sfxenum_t activesound;
        public long flags;
        public statenum_t raisestate;
    }

package data;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import w.CacheableDoomObject;

/**
 *  LineSeg, generated by splitting LineDefs
 * using partition lines selected by BSP builder.
 * MAES: this is the ON-DISK structure. The corresponding memory structure, 
 * segs_t, has fixed_t members. 
 */

public class mapseg_t implements CacheableDoomObject{
    
    public mapseg_t(){
        
    }

   public char     v1;
   public char     v2;
   public char     angle;      
   public char     linedef;
   public char     side;
   public char     offset;
   
   public static int sizeOf(){
       return 12;
   }
   
   @Override
   public void unpack(ByteBuffer buf)
           throws IOException {
       buf.order(ByteOrder.LITTLE_ENDIAN);
       this.v1 = buf.getChar();
       this.v2 = buf.getChar();
       this.angle=buf.getChar();       
       this.linedef=buf.getChar();
       this.side=buf.getChar();
       this.offset=buf.getChar();
      
   }
   
   public String toString(){
       return String.format("mapseg_t v1,2: %d %d ang: %d ld: %d sd: %d off: %d",
           (int)v1,(int)v2,(int)angle,(int)linedef,(int)side,(int)offset);
   }
   
 };

package data;
// Emacs style mode select   -*- C++ -*- 
//-----------------------------------------------------------------------------
//
// $Id: doomdata.java,v 1.4 2011/02/11 00:11:13 velktron Exp $
//
// Copyright (C) 1993-1996 by id Software, Inc.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// DESCRIPTION:
//  all external data is defined here
//  most of the data is loaded into different structures at run time
//  some internal structures shared by many modules are here
//
//-----------------------------------------------------------------------------



    
// The most basic types we use, portability.
//#include "doomtype.h"

// Some global defines, that configure the game.


public class doomdata{
//
// Map level types.
// The following data structures define the persistent format
// used in the lumps of the WAD files.
//

// Lump order in a map WAD: each map needs a couple of lumps
// to provide a complete scene geometry description.
// Maes: this was defined as a typeless enum, probably intended to be used as cardinality?
// Turning it into "ML" enum or int consts.
public static enum ML
{
  ML_LABEL,		// A separator, name, ExMx or MAPxx
  ML_THINGS,		// Monsters, items..
  ML_LINEDEFS,		// LineDefs, from editing
  ML_SIDEDEFS,		// SideDefs, from editing
  ML_VERTEXES,		// Vertices, edited and BSP splits generated
  ML_SEGS,		// LineSegs, from LineDefs split by BSP
  ML_SSECTORS,		// SubSectors, list of LineSegs
  ML_NODES,		// BSP nodes
  ML_SECTORS,		// Sectors, from editing
  ML_REJECT,		// LUT, sector-sector visibility	
  ML_BLOCKMAP		// LUT, motion clipping, walls/grid element
};


// A single Vertex.
static class mapvertex_t
{
  short		x;
  short		y;
};


// A SideDef, defining the visual appearance of a wall,
// by setting textures and offsets.
static class  mapsidedef_t
{
  short		textureoffset;
  short		rowoffset;
  char[]		toptexture=new char[8];
  char[]		bottomtexture=new char[8];
  char[]		midtexture=new char[8];
  // Front sector, towards viewer.
  short		sector;
}



// A LineDef, as used for editing, and as input
// to the BSP builder.
static class maplinedef_t
{
  short		v1;
  short		v2;
  short		flags;
  short		special;
  short		tag;
  // sidenum[1] will be -1 if one sided
  int[]		sidenum=new int[2];		
} 


//
// LineDef attributes.
//

// Solid, is an obstacle.
public static final int ML_BLOCKING		=1;

// Blocks monsters only.
public static final int ML_BLOCKMONSTERS=	2;

// Backside will not be present at all
//  if not two sided.
public static final int ML_TWOSIDED		=4;

// If a texture is pegged, the texture will have
// the end exposed to air held constant at the
// top or bottom of the texture (stairs or pulled
// down things) and will move with a height change
// of one of the neighbor sectors.
// Unpegged textures allways have the first row of
// the texture at the top pixel of the line for both
// top and bottom textures (use next to windows).

// upper texture unpegged
public static final int ML_DONTPEGTOP=8;

// lower texture unpegged
public static final int ML_DONTPEGBOTTOM=	16	;

// In AutoMap: don't map as two sided: IT'S A SECRET!
public static final int ML_SECRET=		32;

// Sound rendering: don't let sound cross two of these.
public static final int ML_SOUNDBLOCK=		64;

// Don't draw on the automap at all.
public static final int ML_DONTDRAW=		128;

// Set if already seen, thus drawn in automap.
public static final int ML_MAPPED		=256;




// Sector definition, from editing.
class mapsector_t
{
  short		floorheight;
  short		ceilingheight;
  char[] floorpic=new char[8];
  char[]		ceilingpic=new char[8];
  short		lightlevel;
  short		special;
  short		tag;
};

// SubSector, as generated by BSP.
class mapsubsector_t
{
  short		numsegs;
  // Index of first one, segs are stored sequentially.
  short		firstseg;	
};


// LineSeg, generated by splitting LineDefs
// using partition lines selected by BSP builder.
class mapseg_t
{
  short		v1;
  short		v2;
  short		angle;		
  short		linedef;
  short		side;
  short		offset;
};



// BSP node structure.

// Indicate a leaf.
public static int	NF_SUBSECTOR	=0x8000;

class mapnode_t
{
  // Partition line from (x,y) to x+dx,y+dy)
  short		x;
  short		y;
  short		dx;
  short		dy;

  // Bounding box for each child,
  // clip against view frustum.
  short[][]		bbox=new short[2][4];

  // If NF_SUBSECTOR its a subsector,
  // else it's a node of another subtree.
  //Maes: used to be unsigned short
  int[] children=new int[2];

} ;

}
package data;

import static data.sounds.*;
import static m.fixed_t.*;
import static p.MobjFlags.*;
import defines.statenum_t;
import doom.think_t;

// Emacs style mode select   -*- C++ -*- 
//-----------------------------------------------------------------------------
//
// $Id: info.java,v 1.13 2013/06/03 12:36:42 velktron Exp $
//
// Copyright (C) 1993-1996 by id Software, Inc.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

//
//-----------------------------------------------------------------------------

/** 
 * DESCRIPTION:
 *  Thing frame/state LUT, generated by multigen utilitiy.
 *  This one is the original DOOM version, preserved.
 * 
 * @author velktron
 *
 */

public class info{

public static final state_t[]	states = {
    // MAES: ATTENTION! The "null" state looks just like an imp. If you see it, then something's
    // wrong. When you check for "null" state, you should actually check whether something 
    // is set to S_NULL!
    new state_t(spritenum_t.SPR_TROO,0,-1,null,statenum_t.S_NULL,0,0),	// S_NULL
    // _D_: replaced "null" by "think_t.A_Light0" (see linuxdoom source)
    // I was trying to make the red fire around the gun apprear when we shoot
    // but I didn't succeed. This modification below is minor as I didn't see
    // any change in the game
    new state_t(spritenum_t.SPR_SHTG,4,0,think_t.A_Light0,statenum_t.S_NULL,0,0),	// S_LIGHTDONE
    new state_t(spritenum_t.SPR_PUNG,0,1,think_t.A_WeaponReady,statenum_t.S_PUNCH,0,0),	// S_PUNCH
    new state_t(spritenum_t.SPR_PUNG,0,1,think_t.A_Lower,statenum_t.S_PUNCHDOWN,0,0),	// S_PUNCHDOWN
    new state_t(spritenum_t.SPR_PUNG,0,1,think_t.A_Raise,statenum_t.S_PUNCHUP,0,0),	// S_PUNCHUP
    new state_t(spritenum_t.SPR_PUNG,1,4,null,statenum_t.S_PUNCH2,0,0),		// S_PUNCH1
    new state_t(spritenum_t.SPR_PUNG,2,4,think_t.A_Punch,statenum_t.S_PUNCH3,0,0),	// S_PUNCH2
    new state_t(spritenum_t.SPR_PUNG,3,5,null,statenum_t.S_PUNCH4,0,0),		// S_PUNCH3
    new state_t(spritenum_t.SPR_PUNG,2,4,null,statenum_t.S_PUNCH5,0,0),		// S_PUNCH4
    new state_t(spritenum_t.SPR_PUNG,1,5,think_t.A_ReFire,statenum_t.S_PUNCH,0,0),	// S_PUNCH5
    new state_t(spritenum_t.SPR_PISG,0,1,think_t.A_WeaponReady,statenum_t.S_PISTOL,0,0),// S_PISTOL
    new state_t(spritenum_t.SPR_PISG,0,1,think_t.A_Lower,statenum_t.S_PISTOLDOWN,0,0),	// S_PISTOLDOWN
    new state_t(spritenum_t.SPR_PISG,0,1,think_t.A_Raise,statenum_t.S_PISTOLUP,0,0),	// S_PISTOLUP
    new state_t(spritenum_t.SPR_PISG,0,4,null,statenum_t.S_PISTOL2,0,0),	// S_PISTOL1
    new state_t(spritenum_t.SPR_PISG,1,6,think_t.A_FirePistol,statenum_t.S_PISTOL3,0,0),// S_PISTOL2
    new state_t(spritenum_t.SPR_PISG,2,4,null,statenum_t.S_PISTOL4,0,0),	// S_PISTOL3
    new state_t(spritenum_t.SPR_PISG,1,5,think_t.A_ReFire,statenum_t.S_PISTOL,0,0),	// S_PISTOL4
    new state_t(spritenum_t.SPR_PISF,32768,7,think_t.A_Light1,statenum_t.S_LIGHTDONE,0,0),	// S_PISTOLFLASH
    new state_t(spritenum_t.SPR_SHTG,0,1,think_t.A_WeaponReady,statenum_t.S_SGUN,0,0),	// S_SGUN
    new state_t(spritenum_t.SPR_SHTG,0,1,think_t.A_Lower,statenum_t.S_SGUNDOWN,0,0),	// S_SGUNDOWN
    new state_t(spritenum_t.SPR_SHTG,0,1,think_t.A_Raise,statenum_t.S_SGUNUP,0,0),	// S_SGUNUP
    new state_t(spritenum_t.SPR_SHTG,0,3,null,statenum_t.S_SGUN2,0,0),	// S_SGUN1
    new state_t(spritenum_t.SPR_SHTG,0,7,think_t.A_FireShotgun,statenum_t.S_SGUN3,0,0),	// S_SGUN2
    new state_t(spritenum_t.SPR_SHTG,1,5,null,statenum_t.S_SGUN4,0,0),	// S_SGUN3
    new state_t(spritenum_t.SPR_SHTG,2,5,null,statenum_t.S_SGUN5,0,0),	// S_SGUN4
    new state_t(spritenum_t.SPR_SHTG,3,4,null,statenum_t.S_SGUN6,0,0),	// S_SGUN5
    new state_t(spritenum_t.SPR_SHTG,2,5,null,statenum_t.S_SGUN7,0,0),	// S_SGUN6
    new state_t(spritenum_t.SPR_SHTG,1,5,null,statenum_t.S_SGUN8,0,0),	// S_SGUN7
    new state_t(spritenum_t.SPR_SHTG,0,3,null,statenum_t.S_SGUN9,0,0),	// S_SGUN8
    new state_t(spritenum_t.SPR_SHTG,0,7,think_t.A_ReFire,statenum_t.S_SGUN,0,0),	// S_SGUN9
    new state_t(spritenum_t.SPR_SHTF,32768,4,think_t.A_Light1,statenum_t.S_SGUNFLASH2,0,0),	// S_SGUNFLASH1
    new state_t(spritenum_t.SPR_SHTF,32769,3,think_t.A_Light2,statenum_t.S_LIGHTDONE,0,0),	// S_SGUNFLASH2
    new state_t(spritenum_t.SPR_SHT2,0,1,think_t.A_WeaponReady,statenum_t.S_DSGUN,0,0),	// S_DSGUN
    new state_t(spritenum_t.SPR_SHT2,0,1,think_t.A_Lower,statenum_t.S_DSGUNDOWN,0,0),	// S_DSGUNDOWN
    new state_t(spritenum_t.SPR_SHT2,0,1,think_t.A_Raise,statenum_t.S_DSGUNUP,0,0),	// S_DSGUNUP
    new state_t(spritenum_t.SPR_SHT2,0,3,null,statenum_t.S_DSGUN2,0,0),	// S_DSGUN1
    new state_t(spritenum_t.SPR_SHT2,0,7,think_t.A_FireShotgun2,statenum_t.S_DSGUN3,0,0),	// S_DSGUN2
    new state_t(spritenum_t.SPR_SHT2,1,7,null,statenum_t.S_DSGUN4,0,0),	// S_DSGUN3
    new state_t(spritenum_t.SPR_SHT2,2,7,think_t.A_CheckReload,statenum_t.S_DSGUN5,0,0),	// S_DSGUN4
    new state_t(spritenum_t.SPR_SHT2,3,7,think_t.A_OpenShotgun2,statenum_t.S_DSGUN6,0,0),	// S_DSGUN5
    new state_t(spritenum_t.SPR_SHT2,4,7,null,statenum_t.S_DSGUN7,0,0),	// S_DSGUN6
    new state_t(spritenum_t.SPR_SHT2,5,7,think_t.A_LoadShotgun2,statenum_t.S_DSGUN8,0,0),	// S_DSGUN7
    new state_t(spritenum_t.SPR_SHT2,6,6,null,statenum_t.S_DSGUN9,0,0),	// S_DSGUN8
    new state_t(spritenum_t.SPR_SHT2,7,6,think_t.A_CloseShotgun2,statenum_t.S_DSGUN10,0,0),	// S_DSGUN9
    new state_t(spritenum_t.SPR_SHT2,0,5,think_t.A_ReFire,statenum_t.S_DSGUN,0,0),	// S_DSGUN10
    new state_t(spritenum_t.SPR_SHT2,1,7,null,statenum_t.S_DSNR2,0,0),	// S_DSNR1
    new state_t(spritenum_t.SPR_SHT2,0,3,null,statenum_t.S_DSGUNDOWN,0,0),	// S_DSNR2
    new state_t(spritenum_t.SPR_SHT2,32776,5,think_t.A_Light1,statenum_t.S_DSGUNFLASH2,0,0),	// S_DSGUNFLASH1
    new state_t(spritenum_t.SPR_SHT2,32777,4,think_t.A_Light2,statenum_t.S_LIGHTDONE,0,0),	// S_DSGUNFLASH2
    new state_t(spritenum_t.SPR_CHGG,0,1,think_t.A_WeaponReady,statenum_t.S_CHAIN,0,0),	// S_CHAIN
    new state_t(spritenum_t.SPR_CHGG,0,1,think_t.A_Lower,statenum_t.S_CHAINDOWN,0,0),	// S_CHAINDOWN
    new state_t(spritenum_t.SPR_CHGG,0,1,think_t.A_Raise,statenum_t.S_CHAINUP,0,0),	// S_CHAINUP
    new state_t(spritenum_t.SPR_CHGG,0,4,think_t.A_FireCGun,statenum_t.S_CHAIN2,0,0),	// S_CHAIN1
    new state_t(spritenum_t.SPR_CHGG,1,4,think_t.A_FireCGun,statenum_t.S_CHAIN3,0,0),	// S_CHAIN2
    new state_t(spritenum_t.SPR_CHGG,1,0,think_t.A_ReFire,statenum_t.S_CHAIN,0,0),	// S_CHAIN3
    new state_t(spritenum_t.SPR_CHGF,32768,5,think_t.A_Light1,statenum_t.S_LIGHTDONE,0,0),	// S_CHAINFLASH1
    new state_t(spritenum_t.SPR_CHGF,32769,5,think_t.A_Light2,statenum_t.S_LIGHTDONE,0,0),	// S_CHAINFLASH2
    new state_t(spritenum_t.SPR_MISG,0,1,think_t.A_WeaponReady,statenum_t.S_MISSILE,0,0),	// S_MISSILE
    new state_t(spritenum_t.SPR_MISG,0,1,think_t.A_Lower,statenum_t.S_MISSILEDOWN,0,0),	// S_MISSILEDOWN
    new state_t(spritenum_t.SPR_MISG,0,1,think_t.A_Raise,statenum_t.S_MISSILEUP,0,0),	// S_MISSILEUP
    new state_t(spritenum_t.SPR_MISG,1,8,think_t.A_GunFlash,statenum_t.S_MISSILE2,0,0),	// S_MISSILE1
    new state_t(spritenum_t.SPR_MISG,1,12,think_t.A_FireMissile,statenum_t.S_MISSILE3,0,0),	// S_MISSILE2
    new state_t(spritenum_t.SPR_MISG,1,0,think_t.A_ReFire,statenum_t.S_MISSILE,0,0),	// S_MISSILE3
    new state_t(spritenum_t.SPR_MISF,32768,3,think_t.A_Light1,statenum_t.S_MISSILEFLASH2,0,0),	// S_MISSILEFLASH1
    new state_t(spritenum_t.SPR_MISF,32769,4,null,statenum_t.S_MISSILEFLASH3,0,0),	// S_MISSILEFLASH2
    new state_t(spritenum_t.SPR_MISF,32770,4,think_t.A_Light2,statenum_t.S_MISSILEFLASH4,0,0),	// S_MISSILEFLASH3
    new state_t(spritenum_t.SPR_MISF,32771,4,think_t.A_Light2,statenum_t.S_LIGHTDONE,0,0),	// S_MISSILEFLASH4
    new state_t(spritenum_t.SPR_SAWG,2,4,think_t.A_WeaponReady,statenum_t.S_SAWB,0,0),	// S_SAW
    new state_t(spritenum_t.SPR_SAWG,3,4,think_t.A_WeaponReady,statenum_t.S_SAW,0,0),	// S_SAWB
    new state_t(spritenum_t.SPR_SAWG,2,1,think_t.A_Lower,statenum_t.S_SAWDOWN,0,0),	// S_SAWDOWN
    new state_t(spritenum_t.SPR_SAWG,2,1,think_t.A_Raise,statenum_t.S_SAWUP,0,0),	// S_SAWUP
    new state_t(spritenum_t.SPR_SAWG,0,4,think_t.A_Saw,statenum_t.S_SAW2,0,0),	// S_SAW1
    new state_t(spritenum_t.SPR_SAWG,1,4,think_t.A_Saw,statenum_t.S_SAW3,0,0),	// S_SAW2
    new state_t(spritenum_t.SPR_SAWG,1,0,think_t.A_ReFire,statenum_t.S_SAW,0,0),	// S_SAW3
    new state_t(spritenum_t.SPR_PLSG,0,1,think_t.A_WeaponReady,statenum_t.S_PLASMA,0,0),	// S_PLASMA
    new state_t(spritenum_t.SPR_PLSG,0,1,think_t.A_Lower,statenum_t.S_PLASMADOWN,0,0),	// S_PLASMADOWN
    new state_t(spritenum_t.SPR_PLSG,0,1,think_t.A_Raise,statenum_t.S_PLASMAUP,0,0),	// S_PLASMAUP
    new state_t(spritenum_t.SPR_PLSG,0,3,think_t.A_FirePlasma,statenum_t.S_PLASMA2,0,0),	// S_PLASMA1
    new state_t(spritenum_t.SPR_PLSG,1,20,think_t.A_ReFire,statenum_t.S_PLASMA,0,0),	// S_PLASMA2
    new state_t(spritenum_t.SPR_PLSF,32768,4,think_t.A_Light1,statenum_t.S_LIGHTDONE,0,0),	// S_PLASMAFLASH1
    new state_t(spritenum_t.SPR_PLSF,32769,4,think_t.A_Light1,statenum_t.S_LIGHTDONE,0,0),	// S_PLASMAFLASH2
    new state_t(spritenum_t.SPR_BFGG,0,1,think_t.A_WeaponReady,statenum_t.S_BFG,0,0),	// S_BFG
    new state_t(spritenum_t.SPR_BFGG,0,1,think_t.A_Lower,statenum_t.S_BFGDOWN,0,0),	// S_BFGDOWN
    new state_t(spritenum_t.SPR_BFGG,0,1,think_t.A_Raise,statenum_t.S_BFGUP,0,0),	// S_BFGUP
    new state_t(spritenum_t.SPR_BFGG,0,20,think_t.A_BFGsound,statenum_t.S_BFG2,0,0),	// S_BFG1
    new state_t(spritenum_t.SPR_BFGG,1,10,think_t.A_GunFlash,statenum_t.S_BFG3,0,0),	// S_BFG2
    new state_t(spritenum_t.SPR_BFGG,1,10,think_t.A_FireBFG,statenum_t.S_BFG4,0,0),	// S_BFG3
    new state_t(spritenum_t.SPR_BFGG,1,20,think_t.A_ReFire,statenum_t.S_BFG,0,0),	// S_BFG4
    new state_t(spritenum_t.SPR_BFGF,32768,11,think_t.A_Light1,statenum_t.S_BFGFLASH2,0,0),	// S_BFGFLASH1
    new state_t(spritenum_t.SPR_BFGF,32769,6,think_t.A_Light2,statenum_t.S_LIGHTDONE,0,0),	// S_BFGFLASH2
    new state_t(spritenum_t.SPR_BLUD,2,8,null,statenum_t.S_BLOOD2,0,0),	// S_BLOOD1
    new state_t(spritenum_t.SPR_BLUD,1,8,null,statenum_t.S_BLOOD3,0,0),	// S_BLOOD2
    new state_t(spritenum_t.SPR_BLUD,0,8,null,statenum_t.S_NULL,0,0),	// S_BLOOD3
    new state_t(spritenum_t.SPR_PUFF,32768,4,null,statenum_t.S_PUFF2,0,0),	// S_PUFF1
    new state_t(spritenum_t.SPR_PUFF,1,4,null,statenum_t.S_PUFF3,0,0),	// S_PUFF2
    new state_t(spritenum_t.SPR_PUFF,2,4,null,statenum_t.S_PUFF4,0,0),	// S_PUFF3
    new state_t(spritenum_t.SPR_PUFF,3,4,null,statenum_t.S_NULL,0,0),	// S_PUFF4
    new state_t(spritenum_t.SPR_BAL1,32768,4,null,statenum_t.S_TBALL2,0,0),	// S_TBALL1
    new state_t(spritenum_t.SPR_BAL1,32769,4,null,statenum_t.S_TBALL1,0,0),	// S_TBALL2
    new state_t(spritenum_t.SPR_BAL1,32770,6,null,statenum_t.S_TBALLX2,0,0),	// S_TBALLX1
    new state_t(spritenum_t.SPR_BAL1,32771,6,null,statenum_t.S_TBALLX3,0,0),	// S_TBALLX2
    new state_t(spritenum_t.SPR_BAL1,32772,6,null,statenum_t.S_NULL,0,0),	// S_TBALLX3
    new state_t(spritenum_t.SPR_BAL2,32768,4,null,statenum_t.S_RBALL2,0,0),	// S_RBALL1
    new state_t(spritenum_t.SPR_BAL2,32769,4,null,statenum_t.S_RBALL1,0,0),	// S_RBALL2
    new state_t(spritenum_t.SPR_BAL2,32770,6,null,statenum_t.S_RBALLX2,0,0),	// S_RBALLX1
    new state_t(spritenum_t.SPR_BAL2,32771,6,null,statenum_t.S_RBALLX3,0,0),	// S_RBALLX2
    new state_t(spritenum_t.SPR_BAL2,32772,6,null,statenum_t.S_NULL,0,0),	// S_RBALLX3
    new state_t(spritenum_t.SPR_PLSS,32768,6,null,statenum_t.S_PLASBALL2,0,0),	// S_PLASBALL
    new state_t(spritenum_t.SPR_PLSS,32769,6,null,statenum_t.S_PLASBALL,0,0),	// S_PLASBALL2
    new state_t(spritenum_t.SPR_PLSE,32768,4,null,statenum_t.S_PLASEXP2,0,0),	// S_PLASEXP
    new state_t(spritenum_t.SPR_PLSE,32769,4,null,statenum_t.S_PLASEXP3,0,0),	// S_PLASEXP2
    new state_t(spritenum_t.SPR_PLSE,32770,4,null,statenum_t.S_PLASEXP4,0,0),	// S_PLASEXP3
    new state_t(spritenum_t.SPR_PLSE,32771,4,null,statenum_t.S_PLASEXP5,0,0),	// S_PLASEXP4
    new state_t(spritenum_t.SPR_PLSE,32772,4,null,statenum_t.S_NULL,0,0),	// S_PLASEXP5
    new state_t(spritenum_t.SPR_MISL,32768,1,null,statenum_t.S_ROCKET,0,0),	// S_ROCKET
    new state_t(spritenum_t.SPR_BFS1,32768,4,null,statenum_t.S_BFGSHOT2,0,0),	// S_BFGSHOT
    new state_t(spritenum_t.SPR_BFS1,32769,4,null,statenum_t.S_BFGSHOT,0,0),	// S_BFGSHOT2
    new state_t(spritenum_t.SPR_BFE1,32768,8,null,statenum_t.S_BFGLAND2,0,0),	// S_BFGLAND
    new state_t(spritenum_t.SPR_BFE1,32769,8,null,statenum_t.S_BFGLAND3,0,0),	// S_BFGLAND2
    new state_t(spritenum_t.SPR_BFE1,32770,8,think_t.A_BFGSpray,statenum_t.S_BFGLAND4,0,0),	// S_BFGLAND3
    new state_t(spritenum_t.SPR_BFE1,32771,8,null,statenum_t.S_BFGLAND5,0,0),	// S_BFGLAND4
    new state_t(spritenum_t.SPR_BFE1,32772,8,null,statenum_t.S_BFGLAND6,0,0),	// S_BFGLAND5
    new state_t(spritenum_t.SPR_BFE1,32773,8,null,statenum_t.S_NULL,0,0),	// S_BFGLAND6
    new state_t(spritenum_t.SPR_BFE2,32768,8,null,statenum_t.S_BFGEXP2,0,0),	// S_BFGEXP
    new state_t(spritenum_t.SPR_BFE2,32769,8,null,statenum_t.S_BFGEXP3,0,0),	// S_BFGEXP2
    new state_t(spritenum_t.SPR_BFE2,32770,8,null,statenum_t.S_BFGEXP4,0,0),	// S_BFGEXP3
    new state_t(spritenum_t.SPR_BFE2,32771,8,null,statenum_t.S_NULL,0,0),	// S_BFGEXP4
    new state_t(spritenum_t.SPR_MISL,32769,8,think_t.A_Explode,statenum_t.S_EXPLODE2,0,0),	// S_EXPLODE1
    new state_t(spritenum_t.SPR_MISL,32770,6,null,statenum_t.S_EXPLODE3,0,0),	// S_EXPLODE2
    new state_t(spritenum_t.SPR_MISL,32771,4,null,statenum_t.S_NULL,0,0),	// S_EXPLODE3
    new state_t(spritenum_t.SPR_TFOG,32768,6,null,statenum_t.S_TFOG01,0,0),	// S_TFOG
    new state_t(spritenum_t.SPR_TFOG,32769,6,null,statenum_t.S_TFOG02,0,0),	// S_TFOG01
    new state_t(spritenum_t.SPR_TFOG,32768,6,null,statenum_t.S_TFOG2,0,0),	// S_TFOG02
    new state_t(spritenum_t.SPR_TFOG,32769,6,null,statenum_t.S_TFOG3,0,0),	// S_TFOG2
    new state_t(spritenum_t.SPR_TFOG,32770,6,null,statenum_t.S_TFOG4,0,0),	// S_TFOG3
    new state_t(spritenum_t.SPR_TFOG,32771,6,null,statenum_t.S_TFOG5,0,0),	// S_TFOG4
    new state_t(spritenum_t.SPR_TFOG,32772,6,null,statenum_t.S_TFOG6,0,0),	// S_TFOG5
    new state_t(spritenum_t.SPR_TFOG,32773,6,null,statenum_t.S_TFOG7,0,0),	// S_TFOG6
    new state_t(spritenum_t.SPR_TFOG,32774,6,null,statenum_t.S_TFOG8,0,0),	// S_TFOG7
    new state_t(spritenum_t.SPR_TFOG,32775,6,null,statenum_t.S_TFOG9,0,0),	// S_TFOG8
    new state_t(spritenum_t.SPR_TFOG,32776,6,null,statenum_t.S_TFOG10,0,0),	// S_TFOG9
    new state_t(spritenum_t.SPR_TFOG,32777,6,null,statenum_t.S_NULL,0,0),	// S_TFOG10
    new state_t(spritenum_t.SPR_IFOG,32768,6,null,statenum_t.S_IFOG01,0,0),	// S_IFOG
    new state_t(spritenum_t.SPR_IFOG,32769,6,null,statenum_t.S_IFOG02,0,0),	// S_IFOG01
    new state_t(spritenum_t.SPR_IFOG,32768,6,null,statenum_t.S_IFOG2,0,0),	// S_IFOG02
    new state_t(spritenum_t.SPR_IFOG,32769,6,null,statenum_t.S_IFOG3,0,0),	// S_IFOG2
    new state_t(spritenum_t.SPR_IFOG,32770,6,null,statenum_t.S_IFOG4,0,0),	// S_IFOG3
    new state_t(spritenum_t.SPR_IFOG,32771,6,null,statenum_t.S_IFOG5,0,0),	// S_IFOG4
    new state_t(spritenum_t.SPR_IFOG,32772,6,null,statenum_t.S_NULL,0,0),	// S_IFOG5
    new state_t(spritenum_t.SPR_PLAY,0,-1,null,statenum_t.S_NULL,0,0),	// S_PLAY
    new state_t(spritenum_t.SPR_PLAY,0,4,null,statenum_t.S_PLAY_RUN2,0,0),	// S_PLAY_RUN1
    new state_t(spritenum_t.SPR_PLAY,1,4,null,statenum_t.S_PLAY_RUN3,0,0),	// S_PLAY_RUN2
    new state_t(spritenum_t.SPR_PLAY,2,4,null,statenum_t.S_PLAY_RUN4,0,0),	// S_PLAY_RUN3
    new state_t(spritenum_t.SPR_PLAY,3,4,null,statenum_t.S_PLAY_RUN1,0,0),	// S_PLAY_RUN4
    new state_t(spritenum_t.SPR_PLAY,4,12,null,statenum_t.S_PLAY,0,0),	// S_PLAY_ATK1
    new state_t(spritenum_t.SPR_PLAY,32773,6,null,statenum_t.S_PLAY_ATK1,0,0),	// S_PLAY_ATK2
    new state_t(spritenum_t.SPR_PLAY,6,4,null,statenum_t.S_PLAY_PAIN2,0,0),	// S_PLAY_PAIN
    new state_t(spritenum_t.SPR_PLAY,6,4,think_t.A_Pain,statenum_t.S_PLAY,0,0),	// S_PLAY_PAIN2
    new state_t(spritenum_t.SPR_PLAY,7,10,null,statenum_t.S_PLAY_DIE2,0,0),	// S_PLAY_DIE1
    new state_t(spritenum_t.SPR_PLAY,8,10,think_t.A_PlayerScream,statenum_t.S_PLAY_DIE3,0,0),	// S_PLAY_DIE2
    new state_t(spritenum_t.SPR_PLAY,9,10,think_t.A_Fall,statenum_t.S_PLAY_DIE4,0,0),	// S_PLAY_DIE3
    new state_t(spritenum_t.SPR_PLAY,10,10,null,statenum_t.S_PLAY_DIE5,0,0),	// S_PLAY_DIE4
    new state_t(spritenum_t.SPR_PLAY,11,10,null,statenum_t.S_PLAY_DIE6,0,0),	// S_PLAY_DIE5
    new state_t(spritenum_t.SPR_PLAY,12,10,null,statenum_t.S_PLAY_DIE7,0,0),	// S_PLAY_DIE6
    new state_t(spritenum_t.SPR_PLAY,13,-1,null,statenum_t.S_NULL,0,0),	// S_PLAY_DIE7
    new state_t(spritenum_t.SPR_PLAY,14,5,null,statenum_t.S_PLAY_XDIE2,0,0),	// S_PLAY_XDIE1
    new state_t(spritenum_t.SPR_PLAY,15,5,think_t.A_XScream,statenum_t.S_PLAY_XDIE3,0,0),	// S_PLAY_XDIE2
    new state_t(spritenum_t.SPR_PLAY,16,5,think_t.A_Fall,statenum_t.S_PLAY_XDIE4,0,0),	// S_PLAY_XDIE3
    new state_t(spritenum_t.SPR_PLAY,17,5,null,statenum_t.S_PLAY_XDIE5,0,0),	// S_PLAY_XDIE4
    new state_t(spritenum_t.SPR_PLAY,18,5,null,statenum_t.S_PLAY_XDIE6,0,0),	// S_PLAY_XDIE5
    new state_t(spritenum_t.SPR_PLAY,19,5,null,statenum_t.S_PLAY_XDIE7,0,0),	// S_PLAY_XDIE6
    new state_t(spritenum_t.SPR_PLAY,20,5,null,statenum_t.S_PLAY_XDIE8,0,0),	// S_PLAY_XDIE7
    new state_t(spritenum_t.SPR_PLAY,21,5,null,statenum_t.S_PLAY_XDIE9,0,0),	// S_PLAY_XDIE8
    new state_t(spritenum_t.SPR_PLAY,22,-1,null,statenum_t.S_NULL,0,0),	// S_PLAY_XDIE9
    new state_t(spritenum_t.SPR_POSS,0,10,think_t.A_Look,statenum_t.S_POSS_STND2,0,0),	// S_POSS_STND
    new state_t(spritenum_t.SPR_POSS,1,10,think_t.A_Look,statenum_t.S_POSS_STND,0,0),	// S_POSS_STND2
    new state_t(spritenum_t.SPR_POSS,0,4,think_t.A_Chase,statenum_t.S_POSS_RUN2,0,0),	// S_POSS_RUN1
    new state_t(spritenum_t.SPR_POSS,0,4,think_t.A_Chase,statenum_t.S_POSS_RUN3,0,0),	// S_POSS_RUN2
    new state_t(spritenum_t.SPR_POSS,1,4,think_t.A_Chase,statenum_t.S_POSS_RUN4,0,0),	// S_POSS_RUN3
    new state_t(spritenum_t.SPR_POSS,1,4,think_t.A_Chase,statenum_t.S_POSS_RUN5,0,0),	// S_POSS_RUN4
    new state_t(spritenum_t.SPR_POSS,2,4,think_t.A_Chase,statenum_t.S_POSS_RUN6,0,0),	// S_POSS_RUN5
    new state_t(spritenum_t.SPR_POSS,2,4,think_t.A_Chase,statenum_t.S_POSS_RUN7,0,0),	// S_POSS_RUN6
    new state_t(spritenum_t.SPR_POSS,3,4,think_t.A_Chase,statenum_t.S_POSS_RUN8,0,0),	// S_POSS_RUN7
    new state_t(spritenum_t.SPR_POSS,3,4,think_t.A_Chase,statenum_t.S_POSS_RUN1,0,0),	// S_POSS_RUN8
    new state_t(spritenum_t.SPR_POSS,4,10,think_t.A_FaceTarget,statenum_t.S_POSS_ATK2,0,0),	// S_POSS_ATK1
    new state_t(spritenum_t.SPR_POSS,5,8,think_t.A_PosAttack,statenum_t.S_POSS_ATK3,0,0),	// S_POSS_ATK2
    new state_t(spritenum_t.SPR_POSS,4,8,null,statenum_t.S_POSS_RUN1,0,0),	// S_POSS_ATK3
    new state_t(spritenum_t.SPR_POSS,6,3,null,statenum_t.S_POSS_PAIN2,0,0),	// S_POSS_PAIN
    new state_t(spritenum_t.SPR_POSS,6,3,think_t.A_Pain,statenum_t.S_POSS_RUN1,0,0),	// S_POSS_PAIN2
    new state_t(spritenum_t.SPR_POSS,7,5,null,statenum_t.S_POSS_DIE2,0,0),	// S_POSS_DIE1
    new state_t(spritenum_t.SPR_POSS,8,5,think_t.A_Scream,statenum_t.S_POSS_DIE3,0,0),	// S_POSS_DIE2
    new state_t(spritenum_t.SPR_POSS,9,5,think_t.A_Fall,statenum_t.S_POSS_DIE4,0,0),	// S_POSS_DIE3
    new state_t(spritenum_t.SPR_POSS,10,5,null,statenum_t.S_POSS_DIE5,0,0),	// S_POSS_DIE4
    new state_t(spritenum_t.SPR_POSS,11,-1,null,statenum_t.S_NULL,0,0),	// S_POSS_DIE5
    new state_t(spritenum_t.SPR_POSS,12,5,null,statenum_t.S_POSS_XDIE2,0,0),	// S_POSS_XDIE1
    new state_t(spritenum_t.SPR_POSS,13,5,think_t.A_XScream,statenum_t.S_POSS_XDIE3,0,0),	// S_POSS_XDIE2
    new state_t(spritenum_t.SPR_POSS,14,5,think_t.A_Fall,statenum_t.S_POSS_XDIE4,0,0),	// S_POSS_XDIE3
    new state_t(spritenum_t.SPR_POSS,15,5,null,statenum_t.S_POSS_XDIE5,0,0),	// S_POSS_XDIE4
    new state_t(spritenum_t.SPR_POSS,16,5,null,statenum_t.S_POSS_XDIE6,0,0),	// S_POSS_XDIE5
    new state_t(spritenum_t.SPR_POSS,17,5,null,statenum_t.S_POSS_XDIE7,0,0),	// S_POSS_XDIE6
    new state_t(spritenum_t.SPR_POSS,18,5,null,statenum_t.S_POSS_XDIE8,0,0),	// S_POSS_XDIE7
    new state_t(spritenum_t.SPR_POSS,19,5,null,statenum_t.S_POSS_XDIE9,0,0),	// S_POSS_XDIE8
    new state_t(spritenum_t.SPR_POSS,20,-1,null,statenum_t.S_NULL,0,0),	// S_POSS_XDIE9
    new state_t(spritenum_t.SPR_POSS,10,5,null,statenum_t.S_POSS_RAISE2,0,0),	// S_POSS_RAISE1
    new state_t(spritenum_t.SPR_POSS,9,5,null,statenum_t.S_POSS_RAISE3,0,0),	// S_POSS_RAISE2
    new state_t(spritenum_t.SPR_POSS,8,5,null,statenum_t.S_POSS_RAISE4,0,0),	// S_POSS_RAISE3
    new state_t(spritenum_t.SPR_POSS,7,5,null,statenum_t.S_POSS_RUN1,0,0),	// S_POSS_RAISE4
    new state_t(spritenum_t.SPR_SPOS,0,10,think_t.A_Look,statenum_t.S_SPOS_STND2,0,0),	// S_SPOS_STND
    new state_t(spritenum_t.SPR_SPOS,1,10,think_t.A_Look,statenum_t.S_SPOS_STND,0,0),	// S_SPOS_STND2
    new state_t(spritenum_t.SPR_SPOS,0,3,think_t.A_Chase,statenum_t.S_SPOS_RUN2,0,0),	// S_SPOS_RUN1
    new state_t(spritenum_t.SPR_SPOS,0,3,think_t.A_Chase,statenum_t.S_SPOS_RUN3,0,0),	// S_SPOS_RUN2
    new state_t(spritenum_t.SPR_SPOS,1,3,think_t.A_Chase,statenum_t.S_SPOS_RUN4,0,0),	// S_SPOS_RUN3
    new state_t(spritenum_t.SPR_SPOS,1,3,think_t.A_Chase,statenum_t.S_SPOS_RUN5,0,0),	// S_SPOS_RUN4
    new state_t(spritenum_t.SPR_SPOS,2,3,think_t.A_Chase,statenum_t.S_SPOS_RUN6,0,0),	// S_SPOS_RUN5
    new state_t(spritenum_t.SPR_SPOS,2,3,think_t.A_Chase,statenum_t.S_SPOS_RUN7,0,0),	// S_SPOS_RUN6
    new state_t(spritenum_t.SPR_SPOS,3,3,think_t.A_Chase,statenum_t.S_SPOS_RUN8,0,0),	// S_SPOS_RUN7
    new state_t(spritenum_t.SPR_SPOS,3,3,think_t.A_Chase,statenum_t.S_SPOS_RUN1,0,0),	// S_SPOS_RUN8
    new state_t(spritenum_t.SPR_SPOS,4,10,think_t.A_FaceTarget,statenum_t.S_SPOS_ATK2,0,0),	// S_SPOS_ATK1
    new state_t(spritenum_t.SPR_SPOS,32773,10,think_t.A_SPosAttack,statenum_t.S_SPOS_ATK3,0,0),	// S_SPOS_ATK2
    new state_t(spritenum_t.SPR_SPOS,4,10,null,statenum_t.S_SPOS_RUN1,0,0),	// S_SPOS_ATK3
    new state_t(spritenum_t.SPR_SPOS,6,3,null,statenum_t.S_SPOS_PAIN2,0,0),	// S_SPOS_PAIN
    new state_t(spritenum_t.SPR_SPOS,6,3,think_t.A_Pain,statenum_t.S_SPOS_RUN1,0,0),	// S_SPOS_PAIN2
    new state_t(spritenum_t.SPR_SPOS,7,5,null,statenum_t.S_SPOS_DIE2,0,0),	// S_SPOS_DIE1
    new state_t(spritenum_t.SPR_SPOS,8,5,think_t.A_Scream,statenum_t.S_SPOS_DIE3,0,0),	// S_SPOS_DIE2
    new state_t(spritenum_t.SPR_SPOS,9,5,think_t.A_Fall,statenum_t.S_SPOS_DIE4,0,0),	// S_SPOS_DIE3
    new state_t(spritenum_t.SPR_SPOS,10,5,null,statenum_t.S_SPOS_DIE5,0,0),	// S_SPOS_DIE4
    new state_t(spritenum_t.SPR_SPOS,11,-1,null,statenum_t.S_NULL,0,0),	// S_SPOS_DIE5
    new state_t(spritenum_t.SPR_SPOS,12,5,null,statenum_t.S_SPOS_XDIE2,0,0),	// S_SPOS_XDIE1
    new state_t(spritenum_t.SPR_SPOS,13,5,think_t.A_XScream,statenum_t.S_SPOS_XDIE3,0,0),	// S_SPOS_XDIE2
    new state_t(spritenum_t.SPR_SPOS,14,5,think_t.A_Fall,statenum_t.S_SPOS_XDIE4,0,0),	// S_SPOS_XDIE3
    new state_t(spritenum_t.SPR_SPOS,15,5,null,statenum_t.S_SPOS_XDIE5,0,0),	// S_SPOS_XDIE4
    new state_t(spritenum_t.SPR_SPOS,16,5,null,statenum_t.S_SPOS_XDIE6,0,0),	// S_SPOS_XDIE5
    new state_t(spritenum_t.SPR_SPOS,17,5,null,statenum_t.S_SPOS_XDIE7,0,0),	// S_SPOS_XDIE6
    new state_t(spritenum_t.SPR_SPOS,18,5,null,statenum_t.S_SPOS_XDIE8,0,0),	// S_SPOS_XDIE7
    new state_t(spritenum_t.SPR_SPOS,19,5,null,statenum_t.S_SPOS_XDIE9,0,0),	// S_SPOS_XDIE8
    new state_t(spritenum_t.SPR_SPOS,20,-1,null,statenum_t.S_NULL,0,0),	// S_SPOS_XDIE9
    new state_t(spritenum_t.SPR_SPOS,11,5,null,statenum_t.S_SPOS_RAISE2,0,0),	// S_SPOS_RAISE1
    new state_t(spritenum_t.SPR_SPOS,10,5,null,statenum_t.S_SPOS_RAISE3,0,0),	// S_SPOS_RAISE2
    new state_t(spritenum_t.SPR_SPOS,9,5,null,statenum_t.S_SPOS_RAISE4,0,0),	// S_SPOS_RAISE3
    new state_t(spritenum_t.SPR_SPOS,8,5,null,statenum_t.S_SPOS_RAISE5,0,0),	// S_SPOS_RAISE4
    new state_t(spritenum_t.SPR_SPOS,7,5,null,statenum_t.S_SPOS_RUN1,0,0),	// S_SPOS_RAISE5
    new state_t(spritenum_t.SPR_VILE,0,10,think_t.A_Look,statenum_t.S_VILE_STND2,0,0),	// S_VILE_STND
    new state_t(spritenum_t.SPR_VILE,1,10,think_t.A_Look,statenum_t.S_VILE_STND,0,0),	// S_VILE_STND2
    new state_t(spritenum_t.SPR_VILE,0,2,think_t.A_VileChase,statenum_t.S_VILE_RUN2,0,0),	// S_VILE_RUN1
    new state_t(spritenum_t.SPR_VILE,0,2,think_t.A_VileChase,statenum_t.S_VILE_RUN3,0,0),	// S_VILE_RUN2
    new state_t(spritenum_t.SPR_VILE,1,2,think_t.A_VileChase,statenum_t.S_VILE_RUN4,0,0),	// S_VILE_RUN3
    new state_t(spritenum_t.SPR_VILE,1,2,think_t.A_VileChase,statenum_t.S_VILE_RUN5,0,0),	// S_VILE_RUN4
    new state_t(spritenum_t.SPR_VILE,2,2,think_t.A_VileChase,statenum_t.S_VILE_RUN6,0,0),	// S_VILE_RUN5
    new state_t(spritenum_t.SPR_VILE,2,2,think_t.A_VileChase,statenum_t.S_VILE_RUN7,0,0),	// S_VILE_RUN6
    new state_t(spritenum_t.SPR_VILE,3,2,think_t.A_VileChase,statenum_t.S_VILE_RUN8,0,0),	// S_VILE_RUN7
    new state_t(spritenum_t.SPR_VILE,3,2,think_t.A_VileChase,statenum_t.S_VILE_RUN9,0,0),	// S_VILE_RUN8
    new state_t(spritenum_t.SPR_VILE,4,2,think_t.A_VileChase,statenum_t.S_VILE_RUN10,0,0),	// S_VILE_RUN9
    new state_t(spritenum_t.SPR_VILE,4,2,think_t.A_VileChase,statenum_t.S_VILE_RUN11,0,0),	// S_VILE_RUN10
    new state_t(spritenum_t.SPR_VILE,5,2,think_t.A_VileChase,statenum_t.S_VILE_RUN12,0,0),	// S_VILE_RUN11
    new state_t(spritenum_t.SPR_VILE,5,2,think_t.A_VileChase,statenum_t.S_VILE_RUN1,0,0),	// S_VILE_RUN12
    new state_t(spritenum_t.SPR_VILE,32774,0,think_t.A_VileStart,statenum_t.S_VILE_ATK2,0,0),	// S_VILE_ATK1
    new state_t(spritenum_t.SPR_VILE,32774,10,think_t.A_FaceTarget,statenum_t.S_VILE_ATK3,0,0),	// S_VILE_ATK2
    new state_t(spritenum_t.SPR_VILE,32775,8,think_t.A_VileTarget,statenum_t.S_VILE_ATK4,0,0),	// S_VILE_ATK3
    new state_t(spritenum_t.SPR_VILE,32776,8,think_t.A_FaceTarget,statenum_t.S_VILE_ATK5,0,0),	// S_VILE_ATK4
    new state_t(spritenum_t.SPR_VILE,32777,8,think_t.A_FaceTarget,statenum_t.S_VILE_ATK6,0,0),	// S_VILE_ATK5
    new state_t(spritenum_t.SPR_VILE,32778,8,think_t.A_FaceTarget,statenum_t.S_VILE_ATK7,0,0),	// S_VILE_ATK6
    new state_t(spritenum_t.SPR_VILE,32779,8,think_t.A_FaceTarget,statenum_t.S_VILE_ATK8,0,0),	// S_VILE_ATK7
    new state_t(spritenum_t.SPR_VILE,32780,8,think_t.A_FaceTarget,statenum_t.S_VILE_ATK9,0,0),	// S_VILE_ATK8
    new state_t(spritenum_t.SPR_VILE,32781,8,think_t.A_FaceTarget,statenum_t.S_VILE_ATK10,0,0),	// S_VILE_ATK9
    new state_t(spritenum_t.SPR_VILE,32782,8,think_t.A_VileAttack,statenum_t.S_VILE_ATK11,0,0),	// S_VILE_ATK10
    new state_t(spritenum_t.SPR_VILE,32783,20,null,statenum_t.S_VILE_RUN1,0,0),	// S_VILE_ATK11
    new state_t(spritenum_t.SPR_VILE,32794,10,null,statenum_t.S_VILE_HEAL2,0,0),	// S_VILE_HEAL1
    new state_t(spritenum_t.SPR_VILE,32795,10,null,statenum_t.S_VILE_HEAL3,0,0),	// S_VILE_HEAL2
    new state_t(spritenum_t.SPR_VILE,32796,10,null,statenum_t.S_VILE_RUN1,0,0),	// S_VILE_HEAL3
    new state_t(spritenum_t.SPR_VILE,16,5,null,statenum_t.S_VILE_PAIN2,0,0),	// S_VILE_PAIN
    new state_t(spritenum_t.SPR_VILE,16,5,think_t.A_Pain,statenum_t.S_VILE_RUN1,0,0),	// S_VILE_PAIN2
    new state_t(spritenum_t.SPR_VILE,16,7,null,statenum_t.S_VILE_DIE2,0,0),	// S_VILE_DIE1
    new state_t(spritenum_t.SPR_VILE,17,7,think_t.A_Scream,statenum_t.S_VILE_DIE3,0,0),	// S_VILE_DIE2
    new state_t(spritenum_t.SPR_VILE,18,7,think_t.A_Fall,statenum_t.S_VILE_DIE4,0,0),	// S_VILE_DIE3
    new state_t(spritenum_t.SPR_VILE,19,7,null,statenum_t.S_VILE_DIE5,0,0),	// S_VILE_DIE4
    new state_t(spritenum_t.SPR_VILE,20,7,null,statenum_t.S_VILE_DIE6,0,0),	// S_VILE_DIE5
    new state_t(spritenum_t.SPR_VILE,21,7,null,statenum_t.S_VILE_DIE7,0,0),	// S_VILE_DIE6
    new state_t(spritenum_t.SPR_VILE,22,7,null,statenum_t.S_VILE_DIE8,0,0),	// S_VILE_DIE7
    new state_t(spritenum_t.SPR_VILE,23,5,null,statenum_t.S_VILE_DIE9,0,0),	// S_VILE_DIE8
    new state_t(spritenum_t.SPR_VILE,24,5,null,statenum_t.S_VILE_DIE10,0,0),	// S_VILE_DIE9
    new state_t(spritenum_t.SPR_VILE,25,-1,null,statenum_t.S_NULL,0,0),	// S_VILE_DIE10
    new state_t(spritenum_t.SPR_FIRE,32768,2,think_t.A_StartFire,statenum_t.S_FIRE2,0,0),	// S_FIRE1
    new state_t(spritenum_t.SPR_FIRE,32769,2,think_t.A_Fire,statenum_t.S_FIRE3,0,0),	// S_FIRE2
    new state_t(spritenum_t.SPR_FIRE,32768,2,think_t.A_Fire,statenum_t.S_FIRE4,0,0),	// S_FIRE3
    new state_t(spritenum_t.SPR_FIRE,32769,2,think_t.A_Fire,statenum_t.S_FIRE5,0,0),	// S_FIRE4
    new state_t(spritenum_t.SPR_FIRE,32770,2,think_t.A_FireCrackle,statenum_t.S_FIRE6,0,0),	// S_FIRE5
    new state_t(spritenum_t.SPR_FIRE,32769,2,think_t.A_Fire,statenum_t.S_FIRE7,0,0),	// S_FIRE6
    new state_t(spritenum_t.SPR_FIRE,32770,2,think_t.A_Fire,statenum_t.S_FIRE8,0,0),	// S_FIRE7
    new state_t(spritenum_t.SPR_FIRE,32769,2,think_t.A_Fire,statenum_t.S_FIRE9,0,0),	// S_FIRE8
    new state_t(spritenum_t.SPR_FIRE,32770,2,think_t.A_Fire,statenum_t.S_FIRE10,0,0),	// S_FIRE9
    new state_t(spritenum_t.SPR_FIRE,32771,2,think_t.A_Fire,statenum_t.S_FIRE11,0,0),	// S_FIRE10
    new state_t(spritenum_t.SPR_FIRE,32770,2,think_t.A_Fire,statenum_t.S_FIRE12,0,0),	// S_FIRE11
    new state_t(spritenum_t.SPR_FIRE,32771,2,think_t.A_Fire,statenum_t.S_FIRE13,0,0),	// S_FIRE12
    new state_t(spritenum_t.SPR_FIRE,32770,2,think_t.A_Fire,statenum_t.S_FIRE14,0,0),	// S_FIRE13
    new state_t(spritenum_t.SPR_FIRE,32771,2,think_t.A_Fire,statenum_t.S_FIRE15,0,0),	// S_FIRE14
    new state_t(spritenum_t.SPR_FIRE,32772,2,think_t.A_Fire,statenum_t.S_FIRE16,0,0),	// S_FIRE15
    new state_t(spritenum_t.SPR_FIRE,32771,2,think_t.A_Fire,statenum_t.S_FIRE17,0,0),	// S_FIRE16
    new state_t(spritenum_t.SPR_FIRE,32772,2,think_t.A_Fire,statenum_t.S_FIRE18,0,0),	// S_FIRE17
    new state_t(spritenum_t.SPR_FIRE,32771,2,think_t.A_Fire,statenum_t.S_FIRE19,0,0),	// S_FIRE18
    new state_t(spritenum_t.SPR_FIRE,32772,2,think_t.A_FireCrackle,statenum_t.S_FIRE20,0,0),	// S_FIRE19
    new state_t(spritenum_t.SPR_FIRE,32773,2,think_t.A_Fire,statenum_t.S_FIRE21,0,0),	// S_FIRE20
    new state_t(spritenum_t.SPR_FIRE,32772,2,think_t.A_Fire,statenum_t.S_FIRE22,0,0),	// S_FIRE21
    new state_t(spritenum_t.SPR_FIRE,32773,2,think_t.A_Fire,statenum_t.S_FIRE23,0,0),	// S_FIRE22
    new state_t(spritenum_t.SPR_FIRE,32772,2,think_t.A_Fire,statenum_t.S_FIRE24,0,0),	// S_FIRE23
    new state_t(spritenum_t.SPR_FIRE,32773,2,think_t.A_Fire,statenum_t.S_FIRE25,0,0),	// S_FIRE24
    new state_t(spritenum_t.SPR_FIRE,32774,2,think_t.A_Fire,statenum_t.S_FIRE26,0,0),	// S_FIRE25
    new state_t(spritenum_t.SPR_FIRE,32775,2,think_t.A_Fire,statenum_t.S_FIRE27,0,0),	// S_FIRE26
    new state_t(spritenum_t.SPR_FIRE,32774,2,think_t.A_Fire,statenum_t.S_FIRE28,0,0),	// S_FIRE27
    new state_t(spritenum_t.SPR_FIRE,32775,2,think_t.A_Fire,statenum_t.S_FIRE29,0,0),	// S_FIRE28
    new state_t(spritenum_t.SPR_FIRE,32774,2,think_t.A_Fire,statenum_t.S_FIRE30,0,0),	// S_FIRE29
    new state_t(spritenum_t.SPR_FIRE,32775,2,think_t.A_Fire,statenum_t.S_NULL,0,0),	// S_FIRE30
    new state_t(spritenum_t.SPR_PUFF,1,4,null,statenum_t.S_SMOKE2,0,0),	// S_SMOKE1
    new state_t(spritenum_t.SPR_PUFF,2,4,null,statenum_t.S_SMOKE3,0,0),	// S_SMOKE2
    new state_t(spritenum_t.SPR_PUFF,1,4,null,statenum_t.S_SMOKE4,0,0),	// S_SMOKE3
    new state_t(spritenum_t.SPR_PUFF,2,4,null,statenum_t.S_SMOKE5,0,0),	// S_SMOKE4
    new state_t(spritenum_t.SPR_PUFF,3,4,null,statenum_t.S_NULL,0,0),	// S_SMOKE5
    new state_t(spritenum_t.SPR_FATB,32768,2,think_t.A_Tracer,statenum_t.S_TRACER2,0,0),	// S_TRACER
    new state_t(spritenum_t.SPR_FATB,32769,2,think_t.A_Tracer,statenum_t.S_TRACER,0,0),	// S_TRACER2
    new state_t(spritenum_t.SPR_FBXP,32768,8,null,statenum_t.S_TRACEEXP2,0,0),	// S_TRACEEXP1
    new state_t(spritenum_t.SPR_FBXP,32769,6,null,statenum_t.S_TRACEEXP3,0,0),	// S_TRACEEXP2
    new state_t(spritenum_t.SPR_FBXP,32770,4,null,statenum_t.S_NULL,0,0),	// S_TRACEEXP3
    new state_t(spritenum_t.SPR_SKEL,0,10,think_t.A_Look,statenum_t.S_SKEL_STND2,0,0),	// S_SKEL_STND
    new state_t(spritenum_t.SPR_SKEL,1,10,think_t.A_Look,statenum_t.S_SKEL_STND,0,0),	// S_SKEL_STND2
    new state_t(spritenum_t.SPR_SKEL,0,2,think_t.A_Chase,statenum_t.S_SKEL_RUN2,0,0),	// S_SKEL_RUN1
    new state_t(spritenum_t.SPR_SKEL,0,2,think_t.A_Chase,statenum_t.S_SKEL_RUN3,0,0),	// S_SKEL_RUN2
    new state_t(spritenum_t.SPR_SKEL,1,2,think_t.A_Chase,statenum_t.S_SKEL_RUN4,0,0),	// S_SKEL_RUN3
    new state_t(spritenum_t.SPR_SKEL,1,2,think_t.A_Chase,statenum_t.S_SKEL_RUN5,0,0),	// S_SKEL_RUN4
    new state_t(spritenum_t.SPR_SKEL,2,2,think_t.A_Chase,statenum_t.S_SKEL_RUN6,0,0),	// S_SKEL_RUN5
    new state_t(spritenum_t.SPR_SKEL,2,2,think_t.A_Chase,statenum_t.S_SKEL_RUN7,0,0),	// S_SKEL_RUN6
    new state_t(spritenum_t.SPR_SKEL,3,2,think_t.A_Chase,statenum_t.S_SKEL_RUN8,0,0),	// S_SKEL_RUN7
    new state_t(spritenum_t.SPR_SKEL,3,2,think_t.A_Chase,statenum_t.S_SKEL_RUN9,0,0),	// S_SKEL_RUN8
    new state_t(spritenum_t.SPR_SKEL,4,2,think_t.A_Chase,statenum_t.S_SKEL_RUN10,0,0),	// S_SKEL_RUN9
    new state_t(spritenum_t.SPR_SKEL,4,2,think_t.A_Chase,statenum_t.S_SKEL_RUN11,0,0),	// S_SKEL_RUN10
    new state_t(spritenum_t.SPR_SKEL,5,2,think_t.A_Chase,statenum_t.S_SKEL_RUN12,0,0),	// S_SKEL_RUN11
    new state_t(spritenum_t.SPR_SKEL,5,2,think_t.A_Chase,statenum_t.S_SKEL_RUN1,0,0),	// S_SKEL_RUN12
    new state_t(spritenum_t.SPR_SKEL,6,0,think_t.A_FaceTarget,statenum_t.S_SKEL_FIST2,0,0),	// S_SKEL_FIST1
    new state_t(spritenum_t.SPR_SKEL,6,6,think_t.A_SkelWhoosh,statenum_t.S_SKEL_FIST3,0,0),	// S_SKEL_FIST2
    new state_t(spritenum_t.SPR_SKEL,7,6,think_t.A_FaceTarget,statenum_t.S_SKEL_FIST4,0,0),	// S_SKEL_FIST3
    new state_t(spritenum_t.SPR_SKEL,8,6,think_t.A_SkelFist,statenum_t.S_SKEL_RUN1,0,0),	// S_SKEL_FIST4
    new state_t(spritenum_t.SPR_SKEL,32777,0,think_t.A_FaceTarget,statenum_t.S_SKEL_MISS2,0,0),	// S_SKEL_MISS1
    new state_t(spritenum_t.SPR_SKEL,32777,10,think_t.A_FaceTarget,statenum_t.S_SKEL_MISS3,0,0),	// S_SKEL_MISS2
    new state_t(spritenum_t.SPR_SKEL,10,10,think_t.A_SkelMissile,statenum_t.S_SKEL_MISS4,0,0),	// S_SKEL_MISS3
    new state_t(spritenum_t.SPR_SKEL,10,10,think_t.A_FaceTarget,statenum_t.S_SKEL_RUN1,0,0),	// S_SKEL_MISS4
    new state_t(spritenum_t.SPR_SKEL,11,5,null,statenum_t.S_SKEL_PAIN2,0,0),	// S_SKEL_PAIN
    new state_t(spritenum_t.SPR_SKEL,11,5,think_t.A_Pain,statenum_t.S_SKEL_RUN1,0,0),	// S_SKEL_PAIN2
    new state_t(spritenum_t.SPR_SKEL,11,7,null,statenum_t.S_SKEL_DIE2,0,0),	// S_SKEL_DIE1
    new state_t(spritenum_t.SPR_SKEL,12,7,null,statenum_t.S_SKEL_DIE3,0,0),	// S_SKEL_DIE2
    new state_t(spritenum_t.SPR_SKEL,13,7,think_t.A_Scream,statenum_t.S_SKEL_DIE4,0,0),	// S_SKEL_DIE3
    new state_t(spritenum_t.SPR_SKEL,14,7,think_t.A_Fall,statenum_t.S_SKEL_DIE5,0,0),	// S_SKEL_DIE4
    new state_t(spritenum_t.SPR_SKEL,15,7,null,statenum_t.S_SKEL_DIE6,0,0),	// S_SKEL_DIE5
    new state_t(spritenum_t.SPR_SKEL,16,-1,null,statenum_t.S_NULL,0,0),	// S_SKEL_DIE6
    new state_t(spritenum_t.SPR_SKEL,16,5,null,statenum_t.S_SKEL_RAISE2,0,0),	// S_SKEL_RAISE1
    new state_t(spritenum_t.SPR_SKEL,15,5,null,statenum_t.S_SKEL_RAISE3,0,0),	// S_SKEL_RAISE2
    new state_t(spritenum_t.SPR_SKEL,14,5,null,statenum_t.S_SKEL_RAISE4,0,0),	// S_SKEL_RAISE3
    new state_t(spritenum_t.SPR_SKEL,13,5,null,statenum_t.S_SKEL_RAISE5,0,0),	// S_SKEL_RAISE4
    new state_t(spritenum_t.SPR_SKEL,12,5,null,statenum_t.S_SKEL_RAISE6,0,0),	// S_SKEL_RAISE5
    new state_t(spritenum_t.SPR_SKEL,11,5,null,statenum_t.S_SKEL_RUN1,0,0),	// S_SKEL_RAISE6
    new state_t(spritenum_t.SPR_MANF,32768,4,null,statenum_t.S_FATSHOT2,0,0),	// S_FATSHOT1
    new state_t(spritenum_t.SPR_MANF,32769,4,null,statenum_t.S_FATSHOT1,0,0),	// S_FATSHOT2
    new state_t(spritenum_t.SPR_MISL,32769,8,null,statenum_t.S_FATSHOTX2,0,0),	// S_FATSHOTX1
    new state_t(spritenum_t.SPR_MISL,32770,6,null,statenum_t.S_FATSHOTX3,0,0),	// S_FATSHOTX2
    new state_t(spritenum_t.SPR_MISL,32771,4,null,statenum_t.S_NULL,0,0),	// S_FATSHOTX3
    new state_t(spritenum_t.SPR_FATT,0,15,think_t.A_Look,statenum_t.S_FATT_STND2,0,0),	// S_FATT_STND
    new state_t(spritenum_t.SPR_FATT,1,15,think_t.A_Look,statenum_t.S_FATT_STND,0,0),	// S_FATT_STND2
    new state_t(spritenum_t.SPR_FATT,0,4,think_t.A_Chase,statenum_t.S_FATT_RUN2,0,0),	// S_FATT_RUN1
    new state_t(spritenum_t.SPR_FATT,0,4,think_t.A_Chase,statenum_t.S_FATT_RUN3,0,0),	// S_FATT_RUN2
    new state_t(spritenum_t.SPR_FATT,1,4,think_t.A_Chase,statenum_t.S_FATT_RUN4,0,0),	// S_FATT_RUN3
    new state_t(spritenum_t.SPR_FATT,1,4,think_t.A_Chase,statenum_t.S_FATT_RUN5,0,0),	// S_FATT_RUN4
    new state_t(spritenum_t.SPR_FATT,2,4,think_t.A_Chase,statenum_t.S_FATT_RUN6,0,0),	// S_FATT_RUN5
    new state_t(spritenum_t.SPR_FATT,2,4,think_t.A_Chase,statenum_t.S_FATT_RUN7,0,0),	// S_FATT_RUN6
    new state_t(spritenum_t.SPR_FATT,3,4,think_t.A_Chase,statenum_t.S_FATT_RUN8,0,0),	// S_FATT_RUN7
    new state_t(spritenum_t.SPR_FATT,3,4,think_t.A_Chase,statenum_t.S_FATT_RUN9,0,0),	// S_FATT_RUN8
    new state_t(spritenum_t.SPR_FATT,4,4,think_t.A_Chase,statenum_t.S_FATT_RUN10,0,0),	// S_FATT_RUN9
    new state_t(spritenum_t.SPR_FATT,4,4,think_t.A_Chase,statenum_t.S_FATT_RUN11,0,0),	// S_FATT_RUN10
    new state_t(spritenum_t.SPR_FATT,5,4,think_t.A_Chase,statenum_t.S_FATT_RUN12,0,0),	// S_FATT_RUN11
    new state_t(spritenum_t.SPR_FATT,5,4,think_t.A_Chase,statenum_t.S_FATT_RUN1,0,0),	// S_FATT_RUN12
    new state_t(spritenum_t.SPR_FATT,6,20,think_t.A_FatRaise,statenum_t.S_FATT_ATK2,0,0),	// S_FATT_ATK1
    new state_t(spritenum_t.SPR_FATT,32775,10,think_t.A_FatAttack1,statenum_t.S_FATT_ATK3,0,0),	// S_FATT_ATK2
    new state_t(spritenum_t.SPR_FATT,8,5,think_t.A_FaceTarget,statenum_t.S_FATT_ATK4,0,0),	// S_FATT_ATK3
    new state_t(spritenum_t.SPR_FATT,6,5,think_t.A_FaceTarget,statenum_t.S_FATT_ATK5,0,0),	// S_FATT_ATK4
    new state_t(spritenum_t.SPR_FATT,32775,10,think_t.A_FatAttack2,statenum_t.S_FATT_ATK6,0,0),	// S_FATT_ATK5
    new state_t(spritenum_t.SPR_FATT,8,5,think_t.A_FaceTarget,statenum_t.S_FATT_ATK7,0,0),	// S_FATT_ATK6
    new state_t(spritenum_t.SPR_FATT,6,5,think_t.A_FaceTarget,statenum_t.S_FATT_ATK8,0,0),	// S_FATT_ATK7
    new state_t(spritenum_t.SPR_FATT,32775,10,think_t.A_FatAttack3,statenum_t.S_FATT_ATK9,0,0),	// S_FATT_ATK8
    new state_t(spritenum_t.SPR_FATT,8,5,think_t.A_FaceTarget,statenum_t.S_FATT_ATK10,0,0),	// S_FATT_ATK9
    new state_t(spritenum_t.SPR_FATT,6,5,think_t.A_FaceTarget,statenum_t.S_FATT_RUN1,0,0),	// S_FATT_ATK10
    new state_t(spritenum_t.SPR_FATT,9,3,null,statenum_t.S_FATT_PAIN2,0,0),	// S_FATT_PAIN
    new state_t(spritenum_t.SPR_FATT,9,3,think_t.A_Pain,statenum_t.S_FATT_RUN1,0,0),	// S_FATT_PAIN2
    new state_t(spritenum_t.SPR_FATT,10,6,null,statenum_t.S_FATT_DIE2,0,0),	// S_FATT_DIE1
    new state_t(spritenum_t.SPR_FATT,11,6,think_t.A_Scream,statenum_t.S_FATT_DIE3,0,0),	// S_FATT_DIE2
    new state_t(spritenum_t.SPR_FATT,12,6,think_t.A_Fall,statenum_t.S_FATT_DIE4,0,0),	// S_FATT_DIE3
    new state_t(spritenum_t.SPR_FATT,13,6,null,statenum_t.S_FATT_DIE5,0,0),	// S_FATT_DIE4
    new state_t(spritenum_t.SPR_FATT,14,6,null,statenum_t.S_FATT_DIE6,0,0),	// S_FATT_DIE5
    new state_t(spritenum_t.SPR_FATT,15,6,null,statenum_t.S_FATT_DIE7,0,0),	// S_FATT_DIE6
    new state_t(spritenum_t.SPR_FATT,16,6,null,statenum_t.S_FATT_DIE8,0,0),	// S_FATT_DIE7
    new state_t(spritenum_t.SPR_FATT,17,6,null,statenum_t.S_FATT_DIE9,0,0),	// S_FATT_DIE8
    new state_t(spritenum_t.SPR_FATT,18,6,null,statenum_t.S_FATT_DIE10,0,0),	// S_FATT_DIE9
    new state_t(spritenum_t.SPR_FATT,19,-1,think_t.A_BossDeath,statenum_t.S_NULL,0,0),	// S_FATT_DIE10
    new state_t(spritenum_t.SPR_FATT,17,5,null,statenum_t.S_FATT_RAISE2,0,0),	// S_FATT_RAISE1
    new state_t(spritenum_t.SPR_FATT,16,5,null,statenum_t.S_FATT_RAISE3,0,0),	// S_FATT_RAISE2
    new state_t(spritenum_t.SPR_FATT,15,5,null,statenum_t.S_FATT_RAISE4,0,0),	// S_FATT_RAISE3
    new state_t(spritenum_t.SPR_FATT,14,5,null,statenum_t.S_FATT_RAISE5,0,0),	// S_FATT_RAISE4
    new state_t(spritenum_t.SPR_FATT,13,5,null,statenum_t.S_FATT_RAISE6,0,0),	// S_FATT_RAISE5
    new state_t(spritenum_t.SPR_FATT,12,5,null,statenum_t.S_FATT_RAISE7,0,0),	// S_FATT_RAISE6
    new state_t(spritenum_t.SPR_FATT,11,5,null,statenum_t.S_FATT_RAISE8,0,0),	// S_FATT_RAISE7
    new state_t(spritenum_t.SPR_FATT,10,5,null,statenum_t.S_FATT_RUN1,0,0),	// S_FATT_RAISE8
    new state_t(spritenum_t.SPR_CPOS,0,10,think_t.A_Look,statenum_t.S_CPOS_STND2,0,0),	// S_CPOS_STND
    new state_t(spritenum_t.SPR_CPOS,1,10,think_t.A_Look,statenum_t.S_CPOS_STND,0,0),	// S_CPOS_STND2
    new state_t(spritenum_t.SPR_CPOS,0,3,think_t.A_Chase,statenum_t.S_CPOS_RUN2,0,0),	// S_CPOS_RUN1
    new state_t(spritenum_t.SPR_CPOS,0,3,think_t.A_Chase,statenum_t.S_CPOS_RUN3,0,0),	// S_CPOS_RUN2
    new state_t(spritenum_t.SPR_CPOS,1,3,think_t.A_Chase,statenum_t.S_CPOS_RUN4,0,0),	// S_CPOS_RUN3
    new state_t(spritenum_t.SPR_CPOS,1,3,think_t.A_Chase,statenum_t.S_CPOS_RUN5,0,0),	// S_CPOS_RUN4
    new state_t(spritenum_t.SPR_CPOS,2,3,think_t.A_Chase,statenum_t.S_CPOS_RUN6,0,0),	// S_CPOS_RUN5
    new state_t(spritenum_t.SPR_CPOS,2,3,think_t.A_Chase,statenum_t.S_CPOS_RUN7,0,0),	// S_CPOS_RUN6
    new state_t(spritenum_t.SPR_CPOS,3,3,think_t.A_Chase,statenum_t.S_CPOS_RUN8,0,0),	// S_CPOS_RUN7
    new state_t(spritenum_t.SPR_CPOS,3,3,think_t.A_Chase,statenum_t.S_CPOS_RUN1,0,0),	// S_CPOS_RUN8
    new state_t(spritenum_t.SPR_CPOS,4,10,think_t.A_FaceTarget,statenum_t.S_CPOS_ATK2,0,0),	// S_CPOS_ATK1
    new state_t(spritenum_t.SPR_CPOS,32773,4,think_t.A_CPosAttack,statenum_t.S_CPOS_ATK3,0,0),	// S_CPOS_ATK2
    new state_t(spritenum_t.SPR_CPOS,32772,4,think_t.A_CPosAttack,statenum_t.S_CPOS_ATK4,0,0),	// S_CPOS_ATK3
    new state_t(spritenum_t.SPR_CPOS,5,1,think_t.A_CPosRefire,statenum_t.S_CPOS_ATK2,0,0),	// S_CPOS_ATK4
    new state_t(spritenum_t.SPR_CPOS,6,3,null,statenum_t.S_CPOS_PAIN2,0,0),	// S_CPOS_PAIN
    new state_t(spritenum_t.SPR_CPOS,6,3,think_t.A_Pain,statenum_t.S_CPOS_RUN1,0,0),	// S_CPOS_PAIN2
    new state_t(spritenum_t.SPR_CPOS,7,5,null,statenum_t.S_CPOS_DIE2,0,0),	// S_CPOS_DIE1
    new state_t(spritenum_t.SPR_CPOS,8,5,think_t.A_Scream,statenum_t.S_CPOS_DIE3,0,0),	// S_CPOS_DIE2
    new state_t(spritenum_t.SPR_CPOS,9,5,think_t.A_Fall,statenum_t.S_CPOS_DIE4,0,0),	// S_CPOS_DIE3
    new state_t(spritenum_t.SPR_CPOS,10,5,null,statenum_t.S_CPOS_DIE5,0,0),	// S_CPOS_DIE4
    new state_t(spritenum_t.SPR_CPOS,11,5,null,statenum_t.S_CPOS_DIE6,0,0),	// S_CPOS_DIE5
    new state_t(spritenum_t.SPR_CPOS,12,5,null,statenum_t.S_CPOS_DIE7,0,0),	// S_CPOS_DIE6
    new state_t(spritenum_t.SPR_CPOS,13,-1,null,statenum_t.S_NULL,0,0),	// S_CPOS_DIE7
    new state_t(spritenum_t.SPR_CPOS,14,5,null,statenum_t.S_CPOS_XDIE2,0,0),	// S_CPOS_XDIE1
    new state_t(spritenum_t.SPR_CPOS,15,5,think_t.A_XScream,statenum_t.S_CPOS_XDIE3,0,0),	// S_CPOS_XDIE2
    new state_t(spritenum_t.SPR_CPOS,16,5,think_t.A_Fall,statenum_t.S_CPOS_XDIE4,0,0),	// S_CPOS_XDIE3
    new state_t(spritenum_t.SPR_CPOS,17,5,null,statenum_t.S_CPOS_XDIE5,0,0),	// S_CPOS_XDIE4
    new state_t(spritenum_t.SPR_CPOS,18,5,null,statenum_t.S_CPOS_XDIE6,0,0),	// S_CPOS_XDIE5
    new state_t(spritenum_t.SPR_CPOS,19,-1,null,statenum_t.S_NULL,0,0),	// S_CPOS_XDIE6
    new state_t(spritenum_t.SPR_CPOS,13,5,null,statenum_t.S_CPOS_RAISE2,0,0),	// S_CPOS_RAISE1
    new state_t(spritenum_t.SPR_CPOS,12,5,null,statenum_t.S_CPOS_RAISE3,0,0),	// S_CPOS_RAISE2
    new state_t(spritenum_t.SPR_CPOS,11,5,null,statenum_t.S_CPOS_RAISE4,0,0),	// S_CPOS_RAISE3
    new state_t(spritenum_t.SPR_CPOS,10,5,null,statenum_t.S_CPOS_RAISE5,0,0),	// S_CPOS_RAISE4
    new state_t(spritenum_t.SPR_CPOS,9,5,null,statenum_t.S_CPOS_RAISE6,0,0),	// S_CPOS_RAISE5
    new state_t(spritenum_t.SPR_CPOS,8,5,null,statenum_t.S_CPOS_RAISE7,0,0),	// S_CPOS_RAISE6
    new state_t(spritenum_t.SPR_CPOS,7,5,null,statenum_t.S_CPOS_RUN1,0,0),	// S_CPOS_RAISE7
    new state_t(spritenum_t.SPR_TROO,0,10,think_t.A_Look,statenum_t.S_TROO_STND2,0,0),	// S_TROO_STND
    new state_t(spritenum_t.SPR_TROO,1,10,think_t.A_Look,statenum_t.S_TROO_STND,0,0),	// S_TROO_STND2
    new state_t(spritenum_t.SPR_TROO,0,3,think_t.A_Chase,statenum_t.S_TROO_RUN2,0,0),	// S_TROO_RUN1
    new state_t(spritenum_t.SPR_TROO,0,3,think_t.A_Chase,statenum_t.S_TROO_RUN3,0,0),	// S_TROO_RUN2
    new state_t(spritenum_t.SPR_TROO,1,3,think_t.A_Chase,statenum_t.S_TROO_RUN4,0,0),	// S_TROO_RUN3
    new state_t(spritenum_t.SPR_TROO,1,3,think_t.A_Chase,statenum_t.S_TROO_RUN5,0,0),	// S_TROO_RUN4
    new state_t(spritenum_t.SPR_TROO,2,3,think_t.A_Chase,statenum_t.S_TROO_RUN6,0,0),	// S_TROO_RUN5
    new state_t(spritenum_t.SPR_TROO,2,3,think_t.A_Chase,statenum_t.S_TROO_RUN7,0,0),	// S_TROO_RUN6
    new state_t(spritenum_t.SPR_TROO,3,3,think_t.A_Chase,statenum_t.S_TROO_RUN8,0,0),	// S_TROO_RUN7
    new state_t(spritenum_t.SPR_TROO,3,3,think_t.A_Chase,statenum_t.S_TROO_RUN1,0,0),	// S_TROO_RUN8
    new state_t(spritenum_t.SPR_TROO,4,8,think_t.A_FaceTarget,statenum_t.S_TROO_ATK2,0,0),	// S_TROO_ATK1
    new state_t(spritenum_t.SPR_TROO,5,8,think_t.A_FaceTarget,statenum_t.S_TROO_ATK3,0,0),	// S_TROO_ATK2
    new state_t(spritenum_t.SPR_TROO,6,6,think_t.A_TroopAttack,statenum_t.S_TROO_RUN1,0,0),	// S_TROO_ATK3
    new state_t(spritenum_t.SPR_TROO,7,2,null,statenum_t.S_TROO_PAIN2,0,0),	// S_TROO_PAIN
    new state_t(spritenum_t.SPR_TROO,7,2,think_t.A_Pain,statenum_t.S_TROO_RUN1,0,0),	// S_TROO_PAIN2
    new state_t(spritenum_t.SPR_TROO,8,8,null,statenum_t.S_TROO_DIE2,0,0),	// S_TROO_DIE1
    new state_t(spritenum_t.SPR_TROO,9,8,think_t.A_Scream,statenum_t.S_TROO_DIE3,0,0),	// S_TROO_DIE2
    new state_t(spritenum_t.SPR_TROO,10,6,null,statenum_t.S_TROO_DIE4,0,0),	// S_TROO_DIE3
    new state_t(spritenum_t.SPR_TROO,11,6,think_t.A_Fall,statenum_t.S_TROO_DIE5,0,0),	// S_TROO_DIE4
    new state_t(spritenum_t.SPR_TROO,12,-1,null,statenum_t.S_NULL,0,0),	// S_TROO_DIE5
    new state_t(spritenum_t.SPR_TROO,13,5,null,statenum_t.S_TROO_XDIE2,0,0),	// S_TROO_XDIE1
    new state_t(spritenum_t.SPR_TROO,14,5,think_t.A_XScream,statenum_t.S_TROO_XDIE3,0,0),	// S_TROO_XDIE2
    new state_t(spritenum_t.SPR_TROO,15,5,null,statenum_t.S_TROO_XDIE4,0,0),	// S_TROO_XDIE3
    new state_t(spritenum_t.SPR_TROO,16,5,think_t.A_Fall,statenum_t.S_TROO_XDIE5,0,0),	// S_TROO_XDIE4
    new state_t(spritenum_t.SPR_TROO,17,5,null,statenum_t.S_TROO_XDIE6,0,0),	// S_TROO_XDIE5
    new state_t(spritenum_t.SPR_TROO,18,5,null,statenum_t.S_TROO_XDIE7,0,0),	// S_TROO_XDIE6
    new state_t(spritenum_t.SPR_TROO,19,5,null,statenum_t.S_TROO_XDIE8,0,0),	// S_TROO_XDIE7
    new state_t(spritenum_t.SPR_TROO,20,-1,null,statenum_t.S_NULL,0,0),	// S_TROO_XDIE8
    new state_t(spritenum_t.SPR_TROO,12,8,null,statenum_t.S_TROO_RAISE2,0,0),	// S_TROO_RAISE1
    new state_t(spritenum_t.SPR_TROO,11,8,null,statenum_t.S_TROO_RAISE3,0,0),	// S_TROO_RAISE2
    new state_t(spritenum_t.SPR_TROO,10,6,null,statenum_t.S_TROO_RAISE4,0,0),	// S_TROO_RAISE3
    new state_t(spritenum_t.SPR_TROO,9,6,null,statenum_t.S_TROO_RAISE5,0,0),	// S_TROO_RAISE4
    new state_t(spritenum_t.SPR_TROO,8,6,null,statenum_t.S_TROO_RUN1,0,0),	// S_TROO_RAISE5
    new state_t(spritenum_t.SPR_SARG,0,10,think_t.A_Look,statenum_t.S_SARG_STND2,0,0),	// S_SARG_STND
    new state_t(spritenum_t.SPR_SARG,1,10,think_t.A_Look,statenum_t.S_SARG_STND,0,0),	// S_SARG_STND2
    new state_t(spritenum_t.SPR_SARG,0,2,think_t.A_Chase,statenum_t.S_SARG_RUN2,0,0),	// S_SARG_RUN1
    new state_t(spritenum_t.SPR_SARG,0,2,think_t.A_Chase,statenum_t.S_SARG_RUN3,0,0),	// S_SARG_RUN2
    new state_t(spritenum_t.SPR_SARG,1,2,think_t.A_Chase,statenum_t.S_SARG_RUN4,0,0),	// S_SARG_RUN3
    new state_t(spritenum_t.SPR_SARG,1,2,think_t.A_Chase,statenum_t.S_SARG_RUN5,0,0),	// S_SARG_RUN4
    new state_t(spritenum_t.SPR_SARG,2,2,think_t.A_Chase,statenum_t.S_SARG_RUN6,0,0),	// S_SARG_RUN5
    new state_t(spritenum_t.SPR_SARG,2,2,think_t.A_Chase,statenum_t.S_SARG_RUN7,0,0),	// S_SARG_RUN6
    new state_t(spritenum_t.SPR_SARG,3,2,think_t.A_Chase,statenum_t.S_SARG_RUN8,0,0),	// S_SARG_RUN7
    new state_t(spritenum_t.SPR_SARG,3,2,think_t.A_Chase,statenum_t.S_SARG_RUN1,0,0),	// S_SARG_RUN8
    new state_t(spritenum_t.SPR_SARG,4,8,think_t.A_FaceTarget,statenum_t.S_SARG_ATK2,0,0),	// S_SARG_ATK1
    new state_t(spritenum_t.SPR_SARG,5,8,think_t.A_FaceTarget,statenum_t.S_SARG_ATK3,0,0),	// S_SARG_ATK2
    new state_t(spritenum_t.SPR_SARG,6,8,think_t.A_SargAttack,statenum_t.S_SARG_RUN1,0,0),	// S_SARG_ATK3
    new state_t(spritenum_t.SPR_SARG,7,2,null,statenum_t.S_SARG_PAIN2,0,0),	// S_SARG_PAIN
    new state_t(spritenum_t.SPR_SARG,7,2,think_t.A_Pain,statenum_t.S_SARG_RUN1,0,0),	// S_SARG_PAIN2
    new state_t(spritenum_t.SPR_SARG,8,8,null,statenum_t.S_SARG_DIE2,0,0),	// S_SARG_DIE1
    new state_t(spritenum_t.SPR_SARG,9,8,think_t.A_Scream,statenum_t.S_SARG_DIE3,0,0),	// S_SARG_DIE2
    new state_t(spritenum_t.SPR_SARG,10,4,null,statenum_t.S_SARG_DIE4,0,0),	// S_SARG_DIE3
    new state_t(spritenum_t.SPR_SARG,11,4,think_t.A_Fall,statenum_t.S_SARG_DIE5,0,0),	// S_SARG_DIE4
    new state_t(spritenum_t.SPR_SARG,12,4,null,statenum_t.S_SARG_DIE6,0,0),	// S_SARG_DIE5
    new state_t(spritenum_t.SPR_SARG,13,-1,null,statenum_t.S_NULL,0,0),	// S_SARG_DIE6
    new state_t(spritenum_t.SPR_SARG,13,5,null,statenum_t.S_SARG_RAISE2,0,0),	// S_SARG_RAISE1
    new state_t(spritenum_t.SPR_SARG,12,5,null,statenum_t.S_SARG_RAISE3,0,0),	// S_SARG_RAISE2
    new state_t(spritenum_t.SPR_SARG,11,5,null,statenum_t.S_SARG_RAISE4,0,0),	// S_SARG_RAISE3
    new state_t(spritenum_t.SPR_SARG,10,5,null,statenum_t.S_SARG_RAISE5,0,0),	// S_SARG_RAISE4
    new state_t(spritenum_t.SPR_SARG,9,5,null,statenum_t.S_SARG_RAISE6,0,0),	// S_SARG_RAISE5
    new state_t(spritenum_t.SPR_SARG,8,5,null,statenum_t.S_SARG_RUN1,0,0),	// S_SARG_RAISE6
    new state_t(spritenum_t.SPR_HEAD,0,10,think_t.A_Look,statenum_t.S_HEAD_STND,0,0),	// S_HEAD_STND
    new state_t(spritenum_t.SPR_HEAD,0,3,think_t.A_Chase,statenum_t.S_HEAD_RUN1,0,0),	// S_HEAD_RUN1
    new state_t(spritenum_t.SPR_HEAD,1,5,think_t.A_FaceTarget,statenum_t.S_HEAD_ATK2,0,0),	// S_HEAD_ATK1
    new state_t(spritenum_t.SPR_HEAD,2,5,think_t.A_FaceTarget,statenum_t.S_HEAD_ATK3,0,0),	// S_HEAD_ATK2
    new state_t(spritenum_t.SPR_HEAD,32771,5,think_t.A_HeadAttack,statenum_t.S_HEAD_RUN1,0,0),	// S_HEAD_ATK3
    new state_t(spritenum_t.SPR_HEAD,4,3,null,statenum_t.S_HEAD_PAIN2,0,0),	// S_HEAD_PAIN
    new state_t(spritenum_t.SPR_HEAD,4,3,think_t.A_Pain,statenum_t.S_HEAD_PAIN3,0,0),	// S_HEAD_PAIN2
    new state_t(spritenum_t.SPR_HEAD,5,6,null,statenum_t.S_HEAD_RUN1,0,0),	// S_HEAD_PAIN3
    new state_t(spritenum_t.SPR_HEAD,6,8,null,statenum_t.S_HEAD_DIE2,0,0),	// S_HEAD_DIE1
    new state_t(spritenum_t.SPR_HEAD,7,8,think_t.A_Scream,statenum_t.S_HEAD_DIE3,0,0),	// S_HEAD_DIE2
    new state_t(spritenum_t.SPR_HEAD,8,8,null,statenum_t.S_HEAD_DIE4,0,0),	// S_HEAD_DIE3
    new state_t(spritenum_t.SPR_HEAD,9,8,null,statenum_t.S_HEAD_DIE5,0,0),	// S_HEAD_DIE4
    new state_t(spritenum_t.SPR_HEAD,10,8,think_t.A_Fall,statenum_t.S_HEAD_DIE6,0,0),	// S_HEAD_DIE5
    new state_t(spritenum_t.SPR_HEAD,11,-1,null,statenum_t.S_NULL,0,0),	// S_HEAD_DIE6
    new state_t(spritenum_t.SPR_HEAD,11,8,null,statenum_t.S_HEAD_RAISE2,0,0),	// S_HEAD_RAISE1
    new state_t(spritenum_t.SPR_HEAD,10,8,null,statenum_t.S_HEAD_RAISE3,0,0),	// S_HEAD_RAISE2
    new state_t(spritenum_t.SPR_HEAD,9,8,null,statenum_t.S_HEAD_RAISE4,0,0),	// S_HEAD_RAISE3
    new state_t(spritenum_t.SPR_HEAD,8,8,null,statenum_t.S_HEAD_RAISE5,0,0),	// S_HEAD_RAISE4
    new state_t(spritenum_t.SPR_HEAD,7,8,null,statenum_t.S_HEAD_RAISE6,0,0),	// S_HEAD_RAISE5
    new state_t(spritenum_t.SPR_HEAD,6,8,null,statenum_t.S_HEAD_RUN1,0,0),	// S_HEAD_RAISE6
    new state_t(spritenum_t.SPR_BAL7,32768,4,null,statenum_t.S_BRBALL2,0,0),	// S_BRBALL1
    new state_t(spritenum_t.SPR_BAL7,32769,4,null,statenum_t.S_BRBALL1,0,0),	// S_BRBALL2
    new state_t(spritenum_t.SPR_BAL7,32770,6,null,statenum_t.S_BRBALLX2,0,0),	// S_BRBALLX1
    new state_t(spritenum_t.SPR_BAL7,32771,6,null,statenum_t.S_BRBALLX3,0,0),	// S_BRBALLX2
    new state_t(spritenum_t.SPR_BAL7,32772,6,null,statenum_t.S_NULL,0,0),	// S_BRBALLX3
    new state_t(spritenum_t.SPR_BOSS,0,10,think_t.A_Look,statenum_t.S_BOSS_STND2,0,0),	// S_BOSS_STND
    new state_t(spritenum_t.SPR_BOSS,1,10,think_t.A_Look,statenum_t.S_BOSS_STND,0,0),	// S_BOSS_STND2
    new state_t(spritenum_t.SPR_BOSS,0,3,think_t.A_Chase,statenum_t.S_BOSS_RUN2,0,0),	// S_BOSS_RUN1
    new state_t(spritenum_t.SPR_BOSS,0,3,think_t.A_Chase,statenum_t.S_BOSS_RUN3,0,0),	// S_BOSS_RUN2
    new state_t(spritenum_t.SPR_BOSS,1,3,think_t.A_Chase,statenum_t.S_BOSS_RUN4,0,0),	// S_BOSS_RUN3
    new state_t(spritenum_t.SPR_BOSS,1,3,think_t.A_Chase,statenum_t.S_BOSS_RUN5,0,0),	// S_BOSS_RUN4
    new state_t(spritenum_t.SPR_BOSS,2,3,think_t.A_Chase,statenum_t.S_BOSS_RUN6,0,0),	// S_BOSS_RUN5
    new state_t(spritenum_t.SPR_BOSS,2,3,think_t.A_Chase,statenum_t.S_BOSS_RUN7,0,0),	// S_BOSS_RUN6
    new state_t(spritenum_t.SPR_BOSS,3,3,think_t.A_Chase,statenum_t.S_BOSS_RUN8,0,0),	// S_BOSS_RUN7
    new state_t(spritenum_t.SPR_BOSS,3,3,think_t.A_Chase,statenum_t.S_BOSS_RUN1,0,0),	// S_BOSS_RUN8
    new state_t(spritenum_t.SPR_BOSS,4,8,think_t.A_FaceTarget,statenum_t.S_BOSS_ATK2,0,0),	// S_BOSS_ATK1
    new state_t(spritenum_t.SPR_BOSS,5,8,think_t.A_FaceTarget,statenum_t.S_BOSS_ATK3,0,0),	// S_BOSS_ATK2
    new state_t(spritenum_t.SPR_BOSS,6,8,think_t.A_BruisAttack,statenum_t.S_BOSS_RUN1,0,0),	// S_BOSS_ATK3
    new state_t(spritenum_t.SPR_BOSS,7,2,null,statenum_t.S_BOSS_PAIN2,0,0),	// S_BOSS_PAIN
    new state_t(spritenum_t.SPR_BOSS,7,2,think_t.A_Pain,statenum_t.S_BOSS_RUN1,0,0),	// S_BOSS_PAIN2
    new state_t(spritenum_t.SPR_BOSS,8,8,null,statenum_t.S_BOSS_DIE2,0,0),	// S_BOSS_DIE1
    new state_t(spritenum_t.SPR_BOSS,9,8,think_t.A_Scream,statenum_t.S_BOSS_DIE3,0,0),	// S_BOSS_DIE2
    new state_t(spritenum_t.SPR_BOSS,10,8,null,statenum_t.S_BOSS_DIE4,0,0),	// S_BOSS_DIE3
    new state_t(spritenum_t.SPR_BOSS,11,8,think_t.A_Fall,statenum_t.S_BOSS_DIE5,0,0),	// S_BOSS_DIE4
    new state_t(spritenum_t.SPR_BOSS,12,8,null,statenum_t.S_BOSS_DIE6,0,0),	// S_BOSS_DIE5
    new state_t(spritenum_t.SPR_BOSS,13,8,null,statenum_t.S_BOSS_DIE7,0,0),	// S_BOSS_DIE6
    new state_t(spritenum_t.SPR_BOSS,14,-1,think_t.A_BossDeath,statenum_t.S_NULL,0,0),	// S_BOSS_DIE7
    new state_t(spritenum_t.SPR_BOSS,14,8,null,statenum_t.S_BOSS_RAISE2,0,0),	// S_BOSS_RAISE1
    new state_t(spritenum_t.SPR_BOSS,13,8,null,statenum_t.S_BOSS_RAISE3,0,0),	// S_BOSS_RAISE2
    new state_t(spritenum_t.SPR_BOSS,12,8,null,statenum_t.S_BOSS_RAISE4,0,0),	// S_BOSS_RAISE3
    new state_t(spritenum_t.SPR_BOSS,11,8,null,statenum_t.S_BOSS_RAISE5,0,0),	// S_BOSS_RAISE4
    new state_t(spritenum_t.SPR_BOSS,10,8,null,statenum_t.S_BOSS_RAISE6,0,0),	// S_BOSS_RAISE5
    new state_t(spritenum_t.SPR_BOSS,9,8,null,statenum_t.S_BOSS_RAISE7,0,0),	// S_BOSS_RAISE6
    new state_t(spritenum_t.SPR_BOSS,8,8,null,statenum_t.S_BOSS_RUN1,0,0),	// S_BOSS_RAISE7
    new state_t(spritenum_t.SPR_BOS2,0,10,think_t.A_Look,statenum_t.S_BOS2_STND2,0,0),	// S_BOS2_STND
    new state_t(spritenum_t.SPR_BOS2,1,10,think_t.A_Look,statenum_t.S_BOS2_STND,0,0),	// S_BOS2_STND2
    new state_t(spritenum_t.SPR_BOS2,0,3,think_t.A_Chase,statenum_t.S_BOS2_RUN2,0,0),	// S_BOS2_RUN1
    new state_t(spritenum_t.SPR_BOS2,0,3,think_t.A_Chase,statenum_t.S_BOS2_RUN3,0,0),	// S_BOS2_RUN2
    new state_t(spritenum_t.SPR_BOS2,1,3,think_t.A_Chase,statenum_t.S_BOS2_RUN4,0,0),	// S_BOS2_RUN3
    new state_t(spritenum_t.SPR_BOS2,1,3,think_t.A_Chase,statenum_t.S_BOS2_RUN5,0,0),	// S_BOS2_RUN4
    new state_t(spritenum_t.SPR_BOS2,2,3,think_t.A_Chase,statenum_t.S_BOS2_RUN6,0,0),	// S_BOS2_RUN5
    new state_t(spritenum_t.SPR_BOS2,2,3,think_t.A_Chase,statenum_t.S_BOS2_RUN7,0,0),	// S_BOS2_RUN6
    new state_t(spritenum_t.SPR_BOS2,3,3,think_t.A_Chase,statenum_t.S_BOS2_RUN8,0,0),	// S_BOS2_RUN7
    new state_t(spritenum_t.SPR_BOS2,3,3,think_t.A_Chase,statenum_t.S_BOS2_RUN1,0,0),	// S_BOS2_RUN8
    new state_t(spritenum_t.SPR_BOS2,4,8,think_t.A_FaceTarget,statenum_t.S_BOS2_ATK2,0,0),	// S_BOS2_ATK1
    new state_t(spritenum_t.SPR_BOS2,5,8,think_t.A_FaceTarget,statenum_t.S_BOS2_ATK3,0,0),	// S_BOS2_ATK2
    new state_t(spritenum_t.SPR_BOS2,6,8,think_t.A_BruisAttack,statenum_t.S_BOS2_RUN1,0,0),	// S_BOS2_ATK3
    new state_t(spritenum_t.SPR_BOS2,7,2,null,statenum_t.S_BOS2_PAIN2,0,0),	// S_BOS2_PAIN
    new state_t(spritenum_t.SPR_BOS2,7,2,think_t.A_Pain,statenum_t.S_BOS2_RUN1,0,0),	// S_BOS2_PAIN2
    new state_t(spritenum_t.SPR_BOS2,8,8,null,statenum_t.S_BOS2_DIE2,0,0),	// S_BOS2_DIE1
    new state_t(spritenum_t.SPR_BOS2,9,8,think_t.A_Scream,statenum_t.S_BOS2_DIE3,0,0),	// S_BOS2_DIE2
    new state_t(spritenum_t.SPR_BOS2,10,8,null,statenum_t.S_BOS2_DIE4,0,0),	// S_BOS2_DIE3
    new state_t(spritenum_t.SPR_BOS2,11,8,think_t.A_Fall,statenum_t.S_BOS2_DIE5,0,0),	// S_BOS2_DIE4
    new state_t(spritenum_t.SPR_BOS2,12,8,null,statenum_t.S_BOS2_DIE6,0,0),	// S_BOS2_DIE5
    new state_t(spritenum_t.SPR_BOS2,13,8,null,statenum_t.S_BOS2_DIE7,0,0),	// S_BOS2_DIE6
    new state_t(spritenum_t.SPR_BOS2,14,-1,null,statenum_t.S_NULL,0,0),	// S_BOS2_DIE7
    new state_t(spritenum_t.SPR_BOS2,14,8,null,statenum_t.S_BOS2_RAISE2,0,0),	// S_BOS2_RAISE1
    new state_t(spritenum_t.SPR_BOS2,13,8,null,statenum_t.S_BOS2_RAISE3,0,0),	// S_BOS2_RAISE2
    new state_t(spritenum_t.SPR_BOS2,12,8,null,statenum_t.S_BOS2_RAISE4,0,0),	// S_BOS2_RAISE3
    new state_t(spritenum_t.SPR_BOS2,11,8,null,statenum_t.S_BOS2_RAISE5,0,0),	// S_BOS2_RAISE4
    new state_t(spritenum_t.SPR_BOS2,10,8,null,statenum_t.S_BOS2_RAISE6,0,0),	// S_BOS2_RAISE5
    new state_t(spritenum_t.SPR_BOS2,9,8,null,statenum_t.S_BOS2_RAISE7,0,0),	// S_BOS2_RAISE6
    new state_t(spritenum_t.SPR_BOS2,8,8,null,statenum_t.S_BOS2_RUN1,0,0),	// S_BOS2_RAISE7
    new state_t(spritenum_t.SPR_SKUL,32768,10,think_t.A_Look,statenum_t.S_SKULL_STND2,0,0),	// S_SKULL_STND
    new state_t(spritenum_t.SPR_SKUL,32769,10,think_t.A_Look,statenum_t.S_SKULL_STND,0,0),	// S_SKULL_STND2
    new state_t(spritenum_t.SPR_SKUL,32768,6,think_t.A_Chase,statenum_t.S_SKULL_RUN2,0,0),	// S_SKULL_RUN1
    new state_t(spritenum_t.SPR_SKUL,32769,6,think_t.A_Chase,statenum_t.S_SKULL_RUN1,0,0),	// S_SKULL_RUN2
    new state_t(spritenum_t.SPR_SKUL,32770,10,think_t.A_FaceTarget,statenum_t.S_SKULL_ATK2,0,0),	// S_SKULL_ATK1
    new state_t(spritenum_t.SPR_SKUL,32771,4,think_t.A_SkullAttack,statenum_t.S_SKULL_ATK3,0,0),	// S_SKULL_ATK2
    new state_t(spritenum_t.SPR_SKUL,32770,4,null,statenum_t.S_SKULL_ATK4,0,0),	// S_SKULL_ATK3
    new state_t(spritenum_t.SPR_SKUL,32771,4,null,statenum_t.S_SKULL_ATK3,0,0),	// S_SKULL_ATK4
    new state_t(spritenum_t.SPR_SKUL,32772,3,null,statenum_t.S_SKULL_PAIN2,0,0),	// S_SKULL_PAIN
    new state_t(spritenum_t.SPR_SKUL,32772,3,think_t.A_Pain,statenum_t.S_SKULL_RUN1,0,0),	// S_SKULL_PAIN2
    new state_t(spritenum_t.SPR_SKUL,32773,6,null,statenum_t.S_SKULL_DIE2,0,0),	// S_SKULL_DIE1
    new state_t(spritenum_t.SPR_SKUL,32774,6,think_t.A_Scream,statenum_t.S_SKULL_DIE3,0,0),	// S_SKULL_DIE2
    new state_t(spritenum_t.SPR_SKUL,32775,6,null,statenum_t.S_SKULL_DIE4,0,0),	// S_SKULL_DIE3
    new state_t(spritenum_t.SPR_SKUL,32776,6,think_t.A_Fall,statenum_t.S_SKULL_DIE5,0,0),	// S_SKULL_DIE4
    new state_t(spritenum_t.SPR_SKUL,9,6,null,statenum_t.S_SKULL_DIE6,0,0),	// S_SKULL_DIE5
    new state_t(spritenum_t.SPR_SKUL,10,6,null,statenum_t.S_NULL,0,0),	// S_SKULL_DIE6
    new state_t(spritenum_t.SPR_SPID,0,10,think_t.A_Look,statenum_t.S_SPID_STND2,0,0),	// S_SPID_STND
    new state_t(spritenum_t.SPR_SPID,1,10,think_t.A_Look,statenum_t.S_SPID_STND,0,0),	// S_SPID_STND2
    new state_t(spritenum_t.SPR_SPID,0,3,think_t.A_Metal,statenum_t.S_SPID_RUN2,0,0),	// S_SPID_RUN1
    new state_t(spritenum_t.SPR_SPID,0,3,think_t.A_Chase,statenum_t.S_SPID_RUN3,0,0),	// S_SPID_RUN2
    new state_t(spritenum_t.SPR_SPID,1,3,think_t.A_Chase,statenum_t.S_SPID_RUN4,0,0),	// S_SPID_RUN3
    new state_t(spritenum_t.SPR_SPID,1,3,think_t.A_Chase,statenum_t.S_SPID_RUN5,0,0),	// S_SPID_RUN4
    new state_t(spritenum_t.SPR_SPID,2,3,think_t.A_Metal,statenum_t.S_SPID_RUN6,0,0),	// S_SPID_RUN5
    new state_t(spritenum_t.SPR_SPID,2,3,think_t.A_Chase,statenum_t.S_SPID_RUN7,0,0),	// S_SPID_RUN6
    new state_t(spritenum_t.SPR_SPID,3,3,think_t.A_Chase,statenum_t.S_SPID_RUN8,0,0),	// S_SPID_RUN7
    new state_t(spritenum_t.SPR_SPID,3,3,think_t.A_Chase,statenum_t.S_SPID_RUN9,0,0),	// S_SPID_RUN8
    new state_t(spritenum_t.SPR_SPID,4,3,think_t.A_Metal,statenum_t.S_SPID_RUN10,0,0),	// S_SPID_RUN9
    new state_t(spritenum_t.SPR_SPID,4,3,think_t.A_Chase,statenum_t.S_SPID_RUN11,0,0),	// S_SPID_RUN10
    new state_t(spritenum_t.SPR_SPID,5,3,think_t.A_Chase,statenum_t.S_SPID_RUN12,0,0),	// S_SPID_RUN11
    new state_t(spritenum_t.SPR_SPID,5,3,think_t.A_Chase,statenum_t.S_SPID_RUN1,0,0),	// S_SPID_RUN12
    new state_t(spritenum_t.SPR_SPID,32768,20,think_t.A_FaceTarget,statenum_t.S_SPID_ATK2,0,0),	// S_SPID_ATK1
    new state_t(spritenum_t.SPR_SPID,32774,4,think_t.A_SPosAttack,statenum_t.S_SPID_ATK3,0,0),	// S_SPID_ATK2
    new state_t(spritenum_t.SPR_SPID,32775,4,think_t.A_SPosAttack,statenum_t.S_SPID_ATK4,0,0),	// S_SPID_ATK3
    new state_t(spritenum_t.SPR_SPID,32775,1,think_t.A_SpidRefire,statenum_t.S_SPID_ATK2,0,0),	// S_SPID_ATK4
    new state_t(spritenum_t.SPR_SPID,8,3,null,statenum_t.S_SPID_PAIN2,0,0),	// S_SPID_PAIN
    new state_t(spritenum_t.SPR_SPID,8,3,think_t.A_Pain,statenum_t.S_SPID_RUN1,0,0),	// S_SPID_PAIN2
    new state_t(spritenum_t.SPR_SPID,9,20,think_t.A_Scream,statenum_t.S_SPID_DIE2,0,0),	// S_SPID_DIE1
    new state_t(spritenum_t.SPR_SPID,10,10,think_t.A_Fall,statenum_t.S_SPID_DIE3,0,0),	// S_SPID_DIE2
    new state_t(spritenum_t.SPR_SPID,11,10,null,statenum_t.S_SPID_DIE4,0,0),	// S_SPID_DIE3
    new state_t(spritenum_t.SPR_SPID,12,10,null,statenum_t.S_SPID_DIE5,0,0),	// S_SPID_DIE4
    new state_t(spritenum_t.SPR_SPID,13,10,null,statenum_t.S_SPID_DIE6,0,0),	// S_SPID_DIE5
    new state_t(spritenum_t.SPR_SPID,14,10,null,statenum_t.S_SPID_DIE7,0,0),	// S_SPID_DIE6
    new state_t(spritenum_t.SPR_SPID,15,10,null,statenum_t.S_SPID_DIE8,0,0),	// S_SPID_DIE7
    new state_t(spritenum_t.SPR_SPID,16,10,null,statenum_t.S_SPID_DIE9,0,0),	// S_SPID_DIE8
    new state_t(spritenum_t.SPR_SPID,17,10,null,statenum_t.S_SPID_DIE10,0,0),	// S_SPID_DIE9
    new state_t(spritenum_t.SPR_SPID,18,30,null,statenum_t.S_SPID_DIE11,0,0),	// S_SPID_DIE10
    new state_t(spritenum_t.SPR_SPID,18,-1,think_t.A_BossDeath,statenum_t.S_NULL,0,0),	// S_SPID_DIE11
    new state_t(spritenum_t.SPR_BSPI,0,10,think_t.A_Look,statenum_t.S_BSPI_STND2,0,0),	// S_BSPI_STND
    new state_t(spritenum_t.SPR_BSPI,1,10,think_t.A_Look,statenum_t.S_BSPI_STND,0,0),	// S_BSPI_STND2
    new state_t(spritenum_t.SPR_BSPI,0,20,null,statenum_t.S_BSPI_RUN1,0,0),	// S_BSPI_SIGHT
    new state_t(spritenum_t.SPR_BSPI,0,3,think_t.A_BabyMetal,statenum_t.S_BSPI_RUN2,0,0),	// S_BSPI_RUN1
    new state_t(spritenum_t.SPR_BSPI,0,3,think_t.A_Chase,statenum_t.S_BSPI_RUN3,0,0),	// S_BSPI_RUN2
    new state_t(spritenum_t.SPR_BSPI,1,3,think_t.A_Chase,statenum_t.S_BSPI_RUN4,0,0),	// S_BSPI_RUN3
    new state_t(spritenum_t.SPR_BSPI,1,3,think_t.A_Chase,statenum_t.S_BSPI_RUN5,0,0),	// S_BSPI_RUN4
    new state_t(spritenum_t.SPR_BSPI,2,3,think_t.A_Chase,statenum_t.S_BSPI_RUN6,0,0),	// S_BSPI_RUN5
    new state_t(spritenum_t.SPR_BSPI,2,3,think_t.A_Chase,statenum_t.S_BSPI_RUN7,0,0),	// S_BSPI_RUN6
    new state_t(spritenum_t.SPR_BSPI,3,3,think_t.A_BabyMetal,statenum_t.S_BSPI_RUN8,0,0),	// S_BSPI_RUN7
    new state_t(spritenum_t.SPR_BSPI,3,3,think_t.A_Chase,statenum_t.S_BSPI_RUN9,0,0),	// S_BSPI_RUN8
    new state_t(spritenum_t.SPR_BSPI,4,3,think_t.A_Chase,statenum_t.S_BSPI_RUN10,0,0),	// S_BSPI_RUN9
    new state_t(spritenum_t.SPR_BSPI,4,3,think_t.A_Chase,statenum_t.S_BSPI_RUN11,0,0),	// S_BSPI_RUN10
    new state_t(spritenum_t.SPR_BSPI,5,3,think_t.A_Chase,statenum_t.S_BSPI_RUN12,0,0),	// S_BSPI_RUN11
    new state_t(spritenum_t.SPR_BSPI,5,3,think_t.A_Chase,statenum_t.S_BSPI_RUN1,0,0),	// S_BSPI_RUN12
    new state_t(spritenum_t.SPR_BSPI,32768,20,think_t.A_FaceTarget,statenum_t.S_BSPI_ATK2,0,0),	// S_BSPI_ATK1
    new state_t(spritenum_t.SPR_BSPI,32774,4,think_t.A_BspiAttack,statenum_t.S_BSPI_ATK3,0,0),	// S_BSPI_ATK2
    new state_t(spritenum_t.SPR_BSPI,32775,4,null,statenum_t.S_BSPI_ATK4,0,0),	// S_BSPI_ATK3
    new state_t(spritenum_t.SPR_BSPI,32775,1,think_t.A_SpidRefire,statenum_t.S_BSPI_ATK2,0,0),	// S_BSPI_ATK4
    new state_t(spritenum_t.SPR_BSPI,8,3,null,statenum_t.S_BSPI_PAIN2,0,0),	// S_BSPI_PAIN
    new state_t(spritenum_t.SPR_BSPI,8,3,think_t.A_Pain,statenum_t.S_BSPI_RUN1,0,0),	// S_BSPI_PAIN2
    new state_t(spritenum_t.SPR_BSPI,9,20,think_t.A_Scream,statenum_t.S_BSPI_DIE2,0,0),	// S_BSPI_DIE1
    new state_t(spritenum_t.SPR_BSPI,10,7,think_t.A_Fall,statenum_t.S_BSPI_DIE3,0,0),	// S_BSPI_DIE2
    new state_t(spritenum_t.SPR_BSPI,11,7,null,statenum_t.S_BSPI_DIE4,0,0),	// S_BSPI_DIE3
    new state_t(spritenum_t.SPR_BSPI,12,7,null,statenum_t.S_BSPI_DIE5,0,0),	// S_BSPI_DIE4
    new state_t(spritenum_t.SPR_BSPI,13,7,null,statenum_t.S_BSPI_DIE6,0,0),	// S_BSPI_DIE5
    new state_t(spritenum_t.SPR_BSPI,14,7,null,statenum_t.S_BSPI_DIE7,0,0),	// S_BSPI_DIE6
    new state_t(spritenum_t.SPR_BSPI,15,-1,think_t.A_BossDeath,statenum_t.S_NULL,0,0),	// S_BSPI_DIE7
    new state_t(spritenum_t.SPR_BSPI,15,5,null,statenum_t.S_BSPI_RAISE2,0,0),	// S_BSPI_RAISE1
    new state_t(spritenum_t.SPR_BSPI,14,5,null,statenum_t.S_BSPI_RAISE3,0,0),	// S_BSPI_RAISE2
    new state_t(spritenum_t.SPR_BSPI,13,5,null,statenum_t.S_BSPI_RAISE4,0,0),	// S_BSPI_RAISE3
    new state_t(spritenum_t.SPR_BSPI,12,5,null,statenum_t.S_BSPI_RAISE5,0,0),	// S_BSPI_RAISE4
    new state_t(spritenum_t.SPR_BSPI,11,5,null,statenum_t.S_BSPI_RAISE6,0,0),	// S_BSPI_RAISE5
    new state_t(spritenum_t.SPR_BSPI,10,5,null,statenum_t.S_BSPI_RAISE7,0,0),	// S_BSPI_RAISE6
    new state_t(spritenum_t.SPR_BSPI,9,5,null,statenum_t.S_BSPI_RUN1,0,0),	// S_BSPI_RAISE7
    new state_t(spritenum_t.SPR_APLS,32768,5,null,statenum_t.S_ARACH_PLAZ2,0,0),	// S_ARACH_PLAZ
    new state_t(spritenum_t.SPR_APLS,32769,5,null,statenum_t.S_ARACH_PLAZ,0,0),	// S_ARACH_PLAZ2
    new state_t(spritenum_t.SPR_APBX,32768,5,null,statenum_t.S_ARACH_PLEX2,0,0),	// S_ARACH_PLEX
    new state_t(spritenum_t.SPR_APBX,32769,5,null,statenum_t.S_ARACH_PLEX3,0,0),	// S_ARACH_PLEX2
    new state_t(spritenum_t.SPR_APBX,32770,5,null,statenum_t.S_ARACH_PLEX4,0,0),	// S_ARACH_PLEX3
    new state_t(spritenum_t.SPR_APBX,32771,5,null,statenum_t.S_ARACH_PLEX5,0,0),	// S_ARACH_PLEX4
    new state_t(spritenum_t.SPR_APBX,32772,5,null,statenum_t.S_NULL,0,0),	// S_ARACH_PLEX5
    new state_t(spritenum_t.SPR_CYBR,0,10,think_t.A_Look,statenum_t.S_CYBER_STND2,0,0),	// S_CYBER_STND
    new state_t(spritenum_t.SPR_CYBR,1,10,think_t.A_Look,statenum_t.S_CYBER_STND,0,0),	// S_CYBER_STND2
    new state_t(spritenum_t.SPR_CYBR,0,3,think_t.A_Hoof,statenum_t.S_CYBER_RUN2,0,0),	// S_CYBER_RUN1
    new state_t(spritenum_t.SPR_CYBR,0,3,think_t.A_Chase,statenum_t.S_CYBER_RUN3,0,0),	// S_CYBER_RUN2
    new state_t(spritenum_t.SPR_CYBR,1,3,think_t.A_Chase,statenum_t.S_CYBER_RUN4,0,0),	// S_CYBER_RUN3
    new state_t(spritenum_t.SPR_CYBR,1,3,think_t.A_Chase,statenum_t.S_CYBER_RUN5,0,0),	// S_CYBER_RUN4
    new state_t(spritenum_t.SPR_CYBR,2,3,think_t.A_Chase,statenum_t.S_CYBER_RUN6,0,0),	// S_CYBER_RUN5
    new state_t(spritenum_t.SPR_CYBR,2,3,think_t.A_Chase,statenum_t.S_CYBER_RUN7,0,0),	// S_CYBER_RUN6
    new state_t(spritenum_t.SPR_CYBR,3,3,think_t.A_Metal,statenum_t.S_CYBER_RUN8,0,0),	// S_CYBER_RUN7
    new state_t(spritenum_t.SPR_CYBR,3,3,think_t.A_Chase,statenum_t.S_CYBER_RUN1,0,0),	// S_CYBER_RUN8
    new state_t(spritenum_t.SPR_CYBR,4,6,think_t.A_FaceTarget,statenum_t.S_CYBER_ATK2,0,0),	// S_CYBER_ATK1
    new state_t(spritenum_t.SPR_CYBR,5,12,think_t.A_CyberAttack,statenum_t.S_CYBER_ATK3,0,0),	// S_CYBER_ATK2
    new state_t(spritenum_t.SPR_CYBR,4,12,think_t.A_FaceTarget,statenum_t.S_CYBER_ATK4,0,0),	// S_CYBER_ATK3
    new state_t(spritenum_t.SPR_CYBR,5,12,think_t.A_CyberAttack,statenum_t.S_CYBER_ATK5,0,0),	// S_CYBER_ATK4
    new state_t(spritenum_t.SPR_CYBR,4,12,think_t.A_FaceTarget,statenum_t.S_CYBER_ATK6,0,0),	// S_CYBER_ATK5
    new state_t(spritenum_t.SPR_CYBR,5,12,think_t.A_CyberAttack,statenum_t.S_CYBER_RUN1,0,0),	// S_CYBER_ATK6
    new state_t(spritenum_t.SPR_CYBR,6,10,think_t.A_Pain,statenum_t.S_CYBER_RUN1,0,0),	// S_CYBER_PAIN
    new state_t(spritenum_t.SPR_CYBR,7,10,null,statenum_t.S_CYBER_DIE2,0,0),	// S_CYBER_DIE1
    new state_t(spritenum_t.SPR_CYBR,8,10,think_t.A_Scream,statenum_t.S_CYBER_DIE3,0,0),	// S_CYBER_DIE2
    new state_t(spritenum_t.SPR_CYBR,9,10,null,statenum_t.S_CYBER_DIE4,0,0),	// S_CYBER_DIE3
    new state_t(spritenum_t.SPR_CYBR,10,10,null,statenum_t.S_CYBER_DIE5,0,0),	// S_CYBER_DIE4
    new state_t(spritenum_t.SPR_CYBR,11,10,null,statenum_t.S_CYBER_DIE6,0,0),	// S_CYBER_DIE5
    new state_t(spritenum_t.SPR_CYBR,12,10,think_t.A_Fall,statenum_t.S_CYBER_DIE7,0,0),	// S_CYBER_DIE6
    new state_t(spritenum_t.SPR_CYBR,13,10,null,statenum_t.S_CYBER_DIE8,0,0),	// S_CYBER_DIE7
    new state_t(spritenum_t.SPR_CYBR,14,10,null,statenum_t.S_CYBER_DIE9,0,0),	// S_CYBER_DIE8
    new state_t(spritenum_t.SPR_CYBR,15,30,null,statenum_t.S_CYBER_DIE10,0,0),	// S_CYBER_DIE9
    new state_t(spritenum_t.SPR_CYBR,15,-1,think_t.A_BossDeath,statenum_t.S_NULL,0,0),	// S_CYBER_DIE10
    new state_t(spritenum_t.SPR_PAIN,0,10,think_t.A_Look,statenum_t.S_PAIN_STND,0,0),	// S_PAIN_STND
    new state_t(spritenum_t.SPR_PAIN,0,3,think_t.A_Chase,statenum_t.S_PAIN_RUN2,0,0),	// S_PAIN_RUN1
    new state_t(spritenum_t.SPR_PAIN,0,3,think_t.A_Chase,statenum_t.S_PAIN_RUN3,0,0),	// S_PAIN_RUN2
    new state_t(spritenum_t.SPR_PAIN,1,3,think_t.A_Chase,statenum_t.S_PAIN_RUN4,0,0),	// S_PAIN_RUN3
    new state_t(spritenum_t.SPR_PAIN,1,3,think_t.A_Chase,statenum_t.S_PAIN_RUN5,0,0),	// S_PAIN_RUN4
    new state_t(spritenum_t.SPR_PAIN,2,3,think_t.A_Chase,statenum_t.S_PAIN_RUN6,0,0),	// S_PAIN_RUN5
    new state_t(spritenum_t.SPR_PAIN,2,3,think_t.A_Chase,statenum_t.S_PAIN_RUN1,0,0),	// S_PAIN_RUN6
    new state_t(spritenum_t.SPR_PAIN,3,5,think_t.A_FaceTarget,statenum_t.S_PAIN_ATK2,0,0),	// S_PAIN_ATK1
    new state_t(spritenum_t.SPR_PAIN,4,5,think_t.A_FaceTarget,statenum_t.S_PAIN_ATK3,0,0),	// S_PAIN_ATK2
    new state_t(spritenum_t.SPR_PAIN,32773,5,think_t.A_FaceTarget,statenum_t.S_PAIN_ATK4,0,0),	// S_PAIN_ATK3
    new state_t(spritenum_t.SPR_PAIN,32773,0,think_t.A_PainAttack,statenum_t.S_PAIN_RUN1,0,0),	// S_PAIN_ATK4
    new state_t(spritenum_t.SPR_PAIN,6,6,null,statenum_t.S_PAIN_PAIN2,0,0),	// S_PAIN_PAIN
    new state_t(spritenum_t.SPR_PAIN,6,6,think_t.A_Pain,statenum_t.S_PAIN_RUN1,0,0),	// S_PAIN_PAIN2
    new state_t(spritenum_t.SPR_PAIN,32775,8,null,statenum_t.S_PAIN_DIE2,0,0),	// S_PAIN_DIE1
    new state_t(spritenum_t.SPR_PAIN,32776,8,think_t.A_Scream,statenum_t.S_PAIN_DIE3,0,0),	// S_PAIN_DIE2
    new state_t(spritenum_t.SPR_PAIN,32777,8,null,statenum_t.S_PAIN_DIE4,0,0),	// S_PAIN_DIE3
    new state_t(spritenum_t.SPR_PAIN,32778,8,null,statenum_t.S_PAIN_DIE5,0,0),	// S_PAIN_DIE4
    new state_t(spritenum_t.SPR_PAIN,32779,8,think_t.A_PainDie,statenum_t.S_PAIN_DIE6,0,0),	// S_PAIN_DIE5
    new state_t(spritenum_t.SPR_PAIN,32780,8,null,statenum_t.S_NULL,0,0),	// S_PAIN_DIE6
    new state_t(spritenum_t.SPR_PAIN,12,8,null,statenum_t.S_PAIN_RAISE2,0,0),	// S_PAIN_RAISE1
    new state_t(spritenum_t.SPR_PAIN,11,8,null,statenum_t.S_PAIN_RAISE3,0,0),	// S_PAIN_RAISE2
    new state_t(spritenum_t.SPR_PAIN,10,8,null,statenum_t.S_PAIN_RAISE4,0,0),	// S_PAIN_RAISE3
    new state_t(spritenum_t.SPR_PAIN,9,8,null,statenum_t.S_PAIN_RAISE5,0,0),	// S_PAIN_RAISE4
    new state_t(spritenum_t.SPR_PAIN,8,8,null,statenum_t.S_PAIN_RAISE6,0,0),	// S_PAIN_RAISE5
    new state_t(spritenum_t.SPR_PAIN,7,8,null,statenum_t.S_PAIN_RUN1,0,0),	// S_PAIN_RAISE6
    new state_t(spritenum_t.SPR_SSWV,0,10,think_t.A_Look,statenum_t.S_SSWV_STND2,0,0),	// S_SSWV_STND
    new state_t(spritenum_t.SPR_SSWV,1,10,think_t.A_Look,statenum_t.S_SSWV_STND,0,0),	// S_SSWV_STND2
    new state_t(spritenum_t.SPR_SSWV,0,3,think_t.A_Chase,statenum_t.S_SSWV_RUN2,0,0),	// S_SSWV_RUN1
    new state_t(spritenum_t.SPR_SSWV,0,3,think_t.A_Chase,statenum_t.S_SSWV_RUN3,0,0),	// S_SSWV_RUN2
    new state_t(spritenum_t.SPR_SSWV,1,3,think_t.A_Chase,statenum_t.S_SSWV_RUN4,0,0),	// S_SSWV_RUN3
    new state_t(spritenum_t.SPR_SSWV,1,3,think_t.A_Chase,statenum_t.S_SSWV_RUN5,0,0),	// S_SSWV_RUN4
    new state_t(spritenum_t.SPR_SSWV,2,3,think_t.A_Chase,statenum_t.S_SSWV_RUN6,0,0),	// S_SSWV_RUN5
    new state_t(spritenum_t.SPR_SSWV,2,3,think_t.A_Chase,statenum_t.S_SSWV_RUN7,0,0),	// S_SSWV_RUN6
    new state_t(spritenum_t.SPR_SSWV,3,3,think_t.A_Chase,statenum_t.S_SSWV_RUN8,0,0),	// S_SSWV_RUN7
    new state_t(spritenum_t.SPR_SSWV,3,3,think_t.A_Chase,statenum_t.S_SSWV_RUN1,0,0),	// S_SSWV_RUN8
    new state_t(spritenum_t.SPR_SSWV,4,10,think_t.A_FaceTarget,statenum_t.S_SSWV_ATK2,0,0),	// S_SSWV_ATK1
    new state_t(spritenum_t.SPR_SSWV,5,10,think_t.A_FaceTarget,statenum_t.S_SSWV_ATK3,0,0),	// S_SSWV_ATK2
    new state_t(spritenum_t.SPR_SSWV,32774,4,think_t.A_CPosAttack,statenum_t.S_SSWV_ATK4,0,0),	// S_SSWV_ATK3
    new state_t(spritenum_t.SPR_SSWV,5,6,think_t.A_FaceTarget,statenum_t.S_SSWV_ATK5,0,0),	// S_SSWV_ATK4
    new state_t(spritenum_t.SPR_SSWV,32774,4,think_t.A_CPosAttack,statenum_t.S_SSWV_ATK6,0,0),	// S_SSWV_ATK5
    new state_t(spritenum_t.SPR_SSWV,5,1,think_t.A_CPosRefire,statenum_t.S_SSWV_ATK2,0,0),	// S_SSWV_ATK6
    new state_t(spritenum_t.SPR_SSWV,7,3,null,statenum_t.S_SSWV_PAIN2,0,0),	// S_SSWV_PAIN
    new state_t(spritenum_t.SPR_SSWV,7,3,think_t.A_Pain,statenum_t.S_SSWV_RUN1,0,0),	// S_SSWV_PAIN2
    new state_t(spritenum_t.SPR_SSWV,8,5,null,statenum_t.S_SSWV_DIE2,0,0),	// S_SSWV_DIE1
    new state_t(spritenum_t.SPR_SSWV,9,5,think_t.A_Scream,statenum_t.S_SSWV_DIE3,0,0),	// S_SSWV_DIE2
    new state_t(spritenum_t.SPR_SSWV,10,5,think_t.A_Fall,statenum_t.S_SSWV_DIE4,0,0),	// S_SSWV_DIE3
    new state_t(spritenum_t.SPR_SSWV,11,5,null,statenum_t.S_SSWV_DIE5,0,0),	// S_SSWV_DIE4
    new state_t(spritenum_t.SPR_SSWV,12,-1,null,statenum_t.S_NULL,0,0),	// S_SSWV_DIE5
    new state_t(spritenum_t.SPR_SSWV,13,5,null,statenum_t.S_SSWV_XDIE2,0,0),	// S_SSWV_XDIE1
    new state_t(spritenum_t.SPR_SSWV,14,5,think_t.A_XScream,statenum_t.S_SSWV_XDIE3,0,0),	// S_SSWV_XDIE2
    new state_t(spritenum_t.SPR_SSWV,15,5,think_t.A_Fall,statenum_t.S_SSWV_XDIE4,0,0),	// S_SSWV_XDIE3
    new state_t(spritenum_t.SPR_SSWV,16,5,null,statenum_t.S_SSWV_XDIE5,0,0),	// S_SSWV_XDIE4
    new state_t(spritenum_t.SPR_SSWV,17,5,null,statenum_t.S_SSWV_XDIE6,0,0),	// S_SSWV_XDIE5
    new state_t(spritenum_t.SPR_SSWV,18,5,null,statenum_t.S_SSWV_XDIE7,0,0),	// S_SSWV_XDIE6
    new state_t(spritenum_t.SPR_SSWV,19,5,null,statenum_t.S_SSWV_XDIE8,0,0),	// S_SSWV_XDIE7
    new state_t(spritenum_t.SPR_SSWV,20,5,null,statenum_t.S_SSWV_XDIE9,0,0),	// S_SSWV_XDIE8
    new state_t(spritenum_t.SPR_SSWV,21,-1,null,statenum_t.S_NULL,0,0),	// S_SSWV_XDIE9
    new state_t(spritenum_t.SPR_SSWV,12,5,null,statenum_t.S_SSWV_RAISE2,0,0),	// S_SSWV_RAISE1
    new state_t(spritenum_t.SPR_SSWV,11,5,null,statenum_t.S_SSWV_RAISE3,0,0),	// S_SSWV_RAISE2
    new state_t(spritenum_t.SPR_SSWV,10,5,null,statenum_t.S_SSWV_RAISE4,0,0),	// S_SSWV_RAISE3
    new state_t(spritenum_t.SPR_SSWV,9,5,null,statenum_t.S_SSWV_RAISE5,0,0),	// S_SSWV_RAISE4
    new state_t(spritenum_t.SPR_SSWV,8,5,null,statenum_t.S_SSWV_RUN1,0,0),	// S_SSWV_RAISE5
    new state_t(spritenum_t.SPR_KEEN,0,-1,null,statenum_t.S_KEENSTND,0,0),	// S_KEENSTND
    new state_t(spritenum_t.SPR_KEEN,0,6,null,statenum_t.S_COMMKEEN2,0,0),	// S_COMMKEEN
    new state_t(spritenum_t.SPR_KEEN,1,6,null,statenum_t.S_COMMKEEN3,0,0),	// S_COMMKEEN2
    new state_t(spritenum_t.SPR_KEEN,2,6,think_t.A_Scream,statenum_t.S_COMMKEEN4,0,0),	// S_COMMKEEN3
    new state_t(spritenum_t.SPR_KEEN,3,6,null,statenum_t.S_COMMKEEN5,0,0),	// S_COMMKEEN4
    new state_t(spritenum_t.SPR_KEEN,4,6,null,statenum_t.S_COMMKEEN6,0,0),	// S_COMMKEEN5
    new state_t(spritenum_t.SPR_KEEN,5,6,null,statenum_t.S_COMMKEEN7,0,0),	// S_COMMKEEN6
    new state_t(spritenum_t.SPR_KEEN,6,6,null,statenum_t.S_COMMKEEN8,0,0),	// S_COMMKEEN7
    new state_t(spritenum_t.SPR_KEEN,7,6,null,statenum_t.S_COMMKEEN9,0,0),	// S_COMMKEEN8
    new state_t(spritenum_t.SPR_KEEN,8,6,null,statenum_t.S_COMMKEEN10,0,0),	// S_COMMKEEN9
    new state_t(spritenum_t.SPR_KEEN,9,6,null,statenum_t.S_COMMKEEN11,0,0),	// S_COMMKEEN10
    new state_t(spritenum_t.SPR_KEEN,10,6,think_t.A_KeenDie,statenum_t.S_COMMKEEN12,0,0),// S_COMMKEEN11
    new state_t(spritenum_t.SPR_KEEN,11,-1,null,statenum_t.S_NULL,0,0),		// S_COMMKEEN12
    new state_t(spritenum_t.SPR_KEEN,12,4,null,statenum_t.S_KEENPAIN2,0,0),	// S_KEENPAIN
    new state_t(spritenum_t.SPR_KEEN,12,8,think_t.A_Pain,statenum_t.S_KEENSTND,0,0),	// S_KEENPAIN2
    new state_t(spritenum_t.SPR_BBRN,0,-1,null,statenum_t.S_NULL,0,0),		// S_BRAIN
    new state_t(spritenum_t.SPR_BBRN,1,36,think_t.A_BrainPain,statenum_t.S_BRAIN,0,0),	// S_BRAIN_PAIN
    new state_t(spritenum_t.SPR_BBRN,0,100,think_t.A_BrainScream,statenum_t.S_BRAIN_DIE2,0,0),	// S_BRAIN_DIE1
    new state_t(spritenum_t.SPR_BBRN,0,10,null,statenum_t.S_BRAIN_DIE3,0,0),	// S_BRAIN_DIE2
    new state_t(spritenum_t.SPR_BBRN,0,10,null,statenum_t.S_BRAIN_DIE4,0,0),	// S_BRAIN_DIE3
    new state_t(spritenum_t.SPR_BBRN,0,-1,think_t.A_BrainDie,statenum_t.S_NULL,0,0),	// S_BRAIN_DIE4
    new state_t(spritenum_t.SPR_SSWV,0,10,think_t.A_Look,statenum_t.S_BRAINEYE,0,0),	// S_BRAINEYE
    new state_t(spritenum_t.SPR_SSWV,0,181,think_t.A_BrainAwake,statenum_t.S_BRAINEYE1,0,0),	// S_BRAINEYESEE
    new state_t(spritenum_t.SPR_SSWV,0,150,think_t.A_BrainSpit,statenum_t.S_BRAINEYE1,0,0),	// S_BRAINEYE1
    new state_t(spritenum_t.SPR_BOSF,32768,3,think_t.A_SpawnSound,statenum_t.S_SPAWN2,0,0),	// S_SPAWN1
    new state_t(spritenum_t.SPR_BOSF,32769,3,think_t.A_SpawnFly,statenum_t.S_SPAWN3,0,0),	// S_SPAWN2
    new state_t(spritenum_t.SPR_BOSF,32770,3,think_t.A_SpawnFly,statenum_t.S_SPAWN4,0,0),	// S_SPAWN3
    new state_t(spritenum_t.SPR_BOSF,32771,3,think_t.A_SpawnFly,statenum_t.S_SPAWN1,0,0),	// S_SPAWN4
    new state_t(spritenum_t.SPR_FIRE,32768,4,think_t.A_Fire,statenum_t.S_SPAWNFIRE2,0,0),	// S_SPAWNFIRE1
    new state_t(spritenum_t.SPR_FIRE,32769,4,think_t.A_Fire,statenum_t.S_SPAWNFIRE3,0,0),	// S_SPAWNFIRE2
    new state_t(spritenum_t.SPR_FIRE,32770,4,think_t.A_Fire,statenum_t.S_SPAWNFIRE4,0,0),	// S_SPAWNFIRE3
    new state_t(spritenum_t.SPR_FIRE,32771,4,think_t.A_Fire,statenum_t.S_SPAWNFIRE5,0,0),	// S_SPAWNFIRE4
    new state_t(spritenum_t.SPR_FIRE,32772,4,think_t.A_Fire,statenum_t.S_SPAWNFIRE6,0,0),	// S_SPAWNFIRE5
    new state_t(spritenum_t.SPR_FIRE,32773,4,think_t.A_Fire,statenum_t.S_SPAWNFIRE7,0,0),	// S_SPAWNFIRE6
    new state_t(spritenum_t.SPR_FIRE,32774,4,think_t.A_Fire,statenum_t.S_SPAWNFIRE8,0,0),	// S_SPAWNFIRE7
    new state_t(spritenum_t.SPR_FIRE,32775,4,think_t.A_Fire,statenum_t.S_NULL,0,0),		// S_SPAWNFIRE8
    new state_t(spritenum_t.SPR_MISL,32769,10,null,statenum_t.S_BRAINEXPLODE2,0,0),	// S_BRAINEXPLODE1
    new state_t(spritenum_t.SPR_MISL,32770,10,null,statenum_t.S_BRAINEXPLODE3,0,0),	// S_BRAINEXPLODE2
    new state_t(spritenum_t.SPR_MISL,32771,10,think_t.A_BrainExplode,statenum_t.S_NULL,0,0),	// S_BRAINEXPLODE3
    new state_t(spritenum_t.SPR_ARM1,0,6,null,statenum_t.S_ARM1A,0,0),	// S_ARM1
    new state_t(spritenum_t.SPR_ARM1,32769,7,null,statenum_t.S_ARM1,0,0),	// S_ARM1A
    new state_t(spritenum_t.SPR_ARM2,0,6,null,statenum_t.S_ARM2A,0,0),	// S_ARM2
    new state_t(spritenum_t.SPR_ARM2,32769,6,null,statenum_t.S_ARM2,0,0),	// S_ARM2A
    new state_t(spritenum_t.SPR_BAR1,0,6,null,statenum_t.S_BAR2,0,0),	// S_BAR1
    new state_t(spritenum_t.SPR_BAR1,1,6,null,statenum_t.S_BAR1,0,0),	// S_BAR2
    new state_t(spritenum_t.SPR_BEXP,32768,5,null,statenum_t.S_BEXP2,0,0),	// S_BEXP
    new state_t(spritenum_t.SPR_BEXP,32769,5,think_t.A_Scream,statenum_t.S_BEXP3,0,0),	// S_BEXP2
    new state_t(spritenum_t.SPR_BEXP,32770,5,null,statenum_t.S_BEXP4,0,0),	// S_BEXP3
    new state_t(spritenum_t.SPR_BEXP,32771,10,think_t.A_Explode,statenum_t.S_BEXP5,0,0),	// S_BEXP4
    new state_t(spritenum_t.SPR_BEXP,32772,10,null,statenum_t.S_NULL,0,0),	// S_BEXP5
    new state_t(spritenum_t.SPR_FCAN,32768,4,null,statenum_t.S_BBAR2,0,0),	// S_BBAR1
    new state_t(spritenum_t.SPR_FCAN,32769,4,null,statenum_t.S_BBAR3,0,0),	// S_BBAR2
    new state_t(spritenum_t.SPR_FCAN,32770,4,null,statenum_t.S_BBAR1,0,0),	// S_BBAR3
    new state_t(spritenum_t.SPR_BON1,0,6,null,statenum_t.S_BON1A,0,0),	// S_BON1
    new state_t(spritenum_t.SPR_BON1,1,6,null,statenum_t.S_BON1B,0,0),	// S_BON1A
    new state_t(spritenum_t.SPR_BON1,2,6,null,statenum_t.S_BON1C,0,0),	// S_BON1B
    new state_t(spritenum_t.SPR_BON1,3,6,null,statenum_t.S_BON1D,0,0),	// S_BON1C
    new state_t(spritenum_t.SPR_BON1,2,6,null,statenum_t.S_BON1E,0,0),	// S_BON1D
    new state_t(spritenum_t.SPR_BON1,1,6,null,statenum_t.S_BON1,0,0),	// S_BON1E
    new state_t(spritenum_t.SPR_BON2,0,6,null,statenum_t.S_BON2A,0,0),	// S_BON2
    new state_t(spritenum_t.SPR_BON2,1,6,null,statenum_t.S_BON2B,0,0),	// S_BON2A
    new state_t(spritenum_t.SPR_BON2,2,6,null,statenum_t.S_BON2C,0,0),	// S_BON2B
    new state_t(spritenum_t.SPR_BON2,3,6,null,statenum_t.S_BON2D,0,0),	// S_BON2C
    new state_t(spritenum_t.SPR_BON2,2,6,null,statenum_t.S_BON2E,0,0),	// S_BON2D
    new state_t(spritenum_t.SPR_BON2,1,6,null,statenum_t.S_BON2,0,0),	// S_BON2E
    new state_t(spritenum_t.SPR_BKEY,0,10,null,statenum_t.S_BKEY2,0,0),	// S_BKEY
    new state_t(spritenum_t.SPR_BKEY,32769,10,null,statenum_t.S_BKEY,0,0),	// S_BKEY2
    new state_t(spritenum_t.SPR_RKEY,0,10,null,statenum_t.S_RKEY2,0,0),	// S_RKEY
    new state_t(spritenum_t.SPR_RKEY,32769,10,null,statenum_t.S_RKEY,0,0),	// S_RKEY2
    new state_t(spritenum_t.SPR_YKEY,0,10,null,statenum_t.S_YKEY2,0,0),	// S_YKEY
    new state_t(spritenum_t.SPR_YKEY,32769,10,null,statenum_t.S_YKEY,0,0),	// S_YKEY2
    new state_t(spritenum_t.SPR_BSKU,0,10,null,statenum_t.S_BSKULL2,0,0),	// S_BSKULL
    new state_t(spritenum_t.SPR_BSKU,32769,10,null,statenum_t.S_BSKULL,0,0),	// S_BSKULL2
    new state_t(spritenum_t.SPR_RSKU,0,10,null,statenum_t.S_RSKULL2,0,0),	// S_RSKULL
    new state_t(spritenum_t.SPR_RSKU,32769,10,null,statenum_t.S_RSKULL,0,0),	// S_RSKULL2
    new state_t(spritenum_t.SPR_YSKU,0,10,null,statenum_t.S_YSKULL2,0,0),	// S_YSKULL
    new state_t(spritenum_t.SPR_YSKU,32769,10,null,statenum_t.S_YSKULL,0,0),	// S_YSKULL2
    new state_t(spritenum_t.SPR_STIM,0,-1,null,statenum_t.S_NULL,0,0),	// S_STIM
    new state_t(spritenum_t.SPR_MEDI,0,-1,null,statenum_t.S_NULL,0,0),	// S_MEDI
    new state_t(spritenum_t.SPR_SOUL,32768,6,null,statenum_t.S_SOUL2,0,0),	// S_SOUL
    new state_t(spritenum_t.SPR_SOUL,32769,6,null,statenum_t.S_SOUL3,0,0),	// S_SOUL2
    new state_t(spritenum_t.SPR_SOUL,32770,6,null,statenum_t.S_SOUL4,0,0),	// S_SOUL3
    new state_t(spritenum_t.SPR_SOUL,32771,6,null,statenum_t.S_SOUL5,0,0),	// S_SOUL4
    new state_t(spritenum_t.SPR_SOUL,32770,6,null,statenum_t.S_SOUL6,0,0),	// S_SOUL5
    new state_t(spritenum_t.SPR_SOUL,32769,6,null,statenum_t.S_SOUL,0,0),	// S_SOUL6
    new state_t(spritenum_t.SPR_PINV,32768,6,null,statenum_t.S_PINV2,0,0),	// S_PINV
    new state_t(spritenum_t.SPR_PINV,32769,6,null,statenum_t.S_PINV3,0,0),	// S_PINV2
    new state_t(spritenum_t.SPR_PINV,32770,6,null,statenum_t.S_PINV4,0,0),	// S_PINV3
    new state_t(spritenum_t.SPR_PINV,32771,6,null,statenum_t.S_PINV,0,0),	// S_PINV4
    new state_t(spritenum_t.SPR_PSTR,32768,-1,null,statenum_t.S_NULL,0,0),	// S_PSTR
    new state_t(spritenum_t.SPR_PINS,32768,6,null,statenum_t.S_PINS2,0,0),	// S_PINS
    new state_t(spritenum_t.SPR_PINS,32769,6,null,statenum_t.S_PINS3,0,0),	// S_PINS2
    new state_t(spritenum_t.SPR_PINS,32770,6,null,statenum_t.S_PINS4,0,0),	// S_PINS3
    new state_t(spritenum_t.SPR_PINS,32771,6,null,statenum_t.S_PINS,0,0),	// S_PINS4
    new state_t(spritenum_t.SPR_MEGA,32768,6,null,statenum_t.S_MEGA2,0,0),	// S_MEGA
    new state_t(spritenum_t.SPR_MEGA,32769,6,null,statenum_t.S_MEGA3,0,0),	// S_MEGA2
    new state_t(spritenum_t.SPR_MEGA,32770,6,null,statenum_t.S_MEGA4,0,0),	// S_MEGA3
    new state_t(spritenum_t.SPR_MEGA,32771,6,null,statenum_t.S_MEGA,0,0),	// S_MEGA4
    new state_t(spritenum_t.SPR_SUIT,32768,-1,null,statenum_t.S_NULL,0,0),	// S_SUIT
    new state_t(spritenum_t.SPR_PMAP,32768,6,null,statenum_t.S_PMAP2,0,0),	// S_PMAP
    new state_t(spritenum_t.SPR_PMAP,32769,6,null,statenum_t.S_PMAP3,0,0),	// S_PMAP2
    new state_t(spritenum_t.SPR_PMAP,32770,6,null,statenum_t.S_PMAP4,0,0),	// S_PMAP3
    new state_t(spritenum_t.SPR_PMAP,32771,6,null,statenum_t.S_PMAP5,0,0),	// S_PMAP4
    new state_t(spritenum_t.SPR_PMAP,32770,6,null,statenum_t.S_PMAP6,0,0),	// S_PMAP5
    new state_t(spritenum_t.SPR_PMAP,32769,6,null,statenum_t.S_PMAP,0,0),	// S_PMAP6
    new state_t(spritenum_t.SPR_PVIS,32768,6,null,statenum_t.S_PVIS2,0,0),	// S_PVIS
    new state_t(spritenum_t.SPR_PVIS,1,6,null,statenum_t.S_PVIS,0,0),	// S_PVIS2
    new state_t(spritenum_t.SPR_CLIP,0,-1,null,statenum_t.S_NULL,0,0),	// S_CLIP
    new state_t(spritenum_t.SPR_AMMO,0,-1,null,statenum_t.S_NULL,0,0),	// S_AMMO
    new state_t(spritenum_t.SPR_ROCK,0,-1,null,statenum_t.S_NULL,0,0),	// S_ROCK
    new state_t(spritenum_t.SPR_BROK,0,-1,null,statenum_t.S_NULL,0,0),	// S_BROK
    new state_t(spritenum_t.SPR_CELL,0,-1,null,statenum_t.S_NULL,0,0),	// S_CELL
    new state_t(spritenum_t.SPR_CELP,0,-1,null,statenum_t.S_NULL,0,0),	// S_CELP
    new state_t(spritenum_t.SPR_SHEL,0,-1,null,statenum_t.S_NULL,0,0),	// S_SHEL
    new state_t(spritenum_t.SPR_SBOX,0,-1,null,statenum_t.S_NULL,0,0),	// S_SBOX
    new state_t(spritenum_t.SPR_BPAK,0,-1,null,statenum_t.S_NULL,0,0),	// S_BPAK
    new state_t(spritenum_t.SPR_BFUG,0,-1,null,statenum_t.S_NULL,0,0),	// S_BFUG
    new state_t(spritenum_t.SPR_MGUN,0,-1,null,statenum_t.S_NULL,0,0),	// S_MGUN
    new state_t(spritenum_t.SPR_CSAW,0,-1,null,statenum_t.S_NULL,0,0),	// S_CSAW
    new state_t(spritenum_t.SPR_LAUN,0,-1,null,statenum_t.S_NULL,0,0),	// S_LAUN
    new state_t(spritenum_t.SPR_PLAS,0,-1,null,statenum_t.S_NULL,0,0),	// S_PLAS
    new state_t(spritenum_t.SPR_SHOT,0,-1,null,statenum_t.S_NULL,0,0),	// S_SHOT
    new state_t(spritenum_t.SPR_SGN2,0,-1,null,statenum_t.S_NULL,0,0),	// S_SHOT2
    new state_t(spritenum_t.SPR_COLU,32768,-1,null,statenum_t.S_NULL,0,0),	// S_COLU
    new state_t(spritenum_t.SPR_SMT2,0,-1,null,statenum_t.S_NULL,0,0),	// S_STALAG
    new state_t(spritenum_t.SPR_GOR1,0,10,null,statenum_t.S_BLOODYTWITCH2,0,0),	// S_BLOODYTWITCH
    new state_t(spritenum_t.SPR_GOR1,1,15,null,statenum_t.S_BLOODYTWITCH3,0,0),	// S_BLOODYTWITCH2
    new state_t(spritenum_t.SPR_GOR1,2,8,null,statenum_t.S_BLOODYTWITCH4,0,0),	// S_BLOODYTWITCH3
    new state_t(spritenum_t.SPR_GOR1,1,6,null,statenum_t.S_BLOODYTWITCH,0,0),	// S_BLOODYTWITCH4
    new state_t(spritenum_t.SPR_PLAY,13,-1,null,statenum_t.S_NULL,0,0),	// S_DEADTORSO
    new state_t(spritenum_t.SPR_PLAY,18,-1,null,statenum_t.S_NULL,0,0),	// S_DEADBOTTOM
    new state_t(spritenum_t.SPR_POL2,0,-1,null,statenum_t.S_NULL,0,0),	// S_HEADSONSTICK
    new state_t(spritenum_t.SPR_POL5,0,-1,null,statenum_t.S_NULL,0,0),	// S_GIBS
    new state_t(spritenum_t.SPR_POL4,0,-1,null,statenum_t.S_NULL,0,0),	// S_HEADONASTICK
    new state_t(spritenum_t.SPR_POL3,32768,6,null,statenum_t.S_HEADCANDLES2,0,0),	// S_HEADCANDLES
    new state_t(spritenum_t.SPR_POL3,32769,6,null,statenum_t.S_HEADCANDLES,0,0),	// S_HEADCANDLES2
    new state_t(spritenum_t.SPR_POL1,0,-1,null,statenum_t.S_NULL,0,0),	// S_DEADSTICK
    new state_t(spritenum_t.SPR_POL6,0,6,null,statenum_t.S_LIVESTICK2,0,0),	// S_LIVESTICK
    new state_t(spritenum_t.SPR_POL6,1,8,null,statenum_t.S_LIVESTICK,0,0),	// S_LIVESTICK2
    new state_t(spritenum_t.SPR_GOR2,0,-1,null,statenum_t.S_NULL,0,0),	// S_MEAT2
    new state_t(spritenum_t.SPR_GOR3,0,-1,null,statenum_t.S_NULL,0,0),	// S_MEAT3
    new state_t(spritenum_t.SPR_GOR4,0,-1,null,statenum_t.S_NULL,0,0),	// S_MEAT4
    new state_t(spritenum_t.SPR_GOR5,0,-1,null,statenum_t.S_NULL,0,0),	// S_MEAT5
    new state_t(spritenum_t.SPR_SMIT,0,-1,null,statenum_t.S_NULL,0,0),	// S_STALAGTITE
    new state_t(spritenum_t.SPR_COL1,0,-1,null,statenum_t.S_NULL,0,0),	// S_TALLGRNCOL
    new state_t(spritenum_t.SPR_COL2,0,-1,null,statenum_t.S_NULL,0,0),	// S_SHRTGRNCOL
    new state_t(spritenum_t.SPR_COL3,0,-1,null,statenum_t.S_NULL,0,0),	// S_TALLREDCOL
    new state_t(spritenum_t.SPR_COL4,0,-1,null,statenum_t.S_NULL,0,0),	// S_SHRTREDCOL
    new state_t(spritenum_t.SPR_CAND,32768,-1,null,statenum_t.S_NULL,0,0),	// S_CANDLESTIK
    new state_t(spritenum_t.SPR_CBRA,32768,-1,null,statenum_t.S_NULL,0,0),	// S_CANDELABRA
    new state_t(spritenum_t.SPR_COL6,0,-1,null,statenum_t.S_NULL,0,0),	// S_SKULLCOL
    new state_t(spritenum_t.SPR_TRE1,0,-1,null,statenum_t.S_NULL,0,0),	// S_TORCHTREE
    new state_t(spritenum_t.SPR_TRE2,0,-1,null,statenum_t.S_NULL,0,0),	// S_BIGTREE
    new state_t(spritenum_t.SPR_ELEC,0,-1,null,statenum_t.S_NULL,0,0),	// S_TECHPILLAR
    new state_t(spritenum_t.SPR_CEYE,32768,6,null,statenum_t.S_EVILEYE2,0,0),	// S_EVILEYE
    new state_t(spritenum_t.SPR_CEYE,32769,6,null,statenum_t.S_EVILEYE3,0,0),	// S_EVILEYE2
    new state_t(spritenum_t.SPR_CEYE,32770,6,null,statenum_t.S_EVILEYE4,0,0),	// S_EVILEYE3
    new state_t(spritenum_t.SPR_CEYE,32769,6,null,statenum_t.S_EVILEYE,0,0),	// S_EVILEYE4
    new state_t(spritenum_t.SPR_FSKU,32768,6,null,statenum_t.S_FLOATSKULL2,0,0),	// S_FLOATSKULL
    new state_t(spritenum_t.SPR_FSKU,32769,6,null,statenum_t.S_FLOATSKULL3,0,0),	// S_FLOATSKULL2
    new state_t(spritenum_t.SPR_FSKU,32770,6,null,statenum_t.S_FLOATSKULL,0,0),	// S_FLOATSKULL3
    new state_t(spritenum_t.SPR_COL5,0,14,null,statenum_t.S_HEARTCOL2,0,0),	// S_HEARTCOL
    new state_t(spritenum_t.SPR_COL5,1,14,null,statenum_t.S_HEARTCOL,0,0),	// S_HEARTCOL2
    new state_t(spritenum_t.SPR_TBLU,32768,4,null,statenum_t.S_BLUETORCH2,0,0),	// S_BLUETORCH
    new state_t(spritenum_t.SPR_TBLU,32769,4,null,statenum_t.S_BLUETORCH3,0,0),	// S_BLUETORCH2
    new state_t(spritenum_t.SPR_TBLU,32770,4,null,statenum_t.S_BLUETORCH4,0,0),	// S_BLUETORCH3
    new state_t(spritenum_t.SPR_TBLU,32771,4,null,statenum_t.S_BLUETORCH,0,0),	// S_BLUETORCH4
    new state_t(spritenum_t.SPR_TGRN,32768,4,null,statenum_t.S_GREENTORCH2,0,0),	// S_GREENTORCH
    new state_t(spritenum_t.SPR_TGRN,32769,4,null,statenum_t.S_GREENTORCH3,0,0),	// S_GREENTORCH2
    new state_t(spritenum_t.SPR_TGRN,32770,4,null,statenum_t.S_GREENTORCH4,0,0),	// S_GREENTORCH3
    new state_t(spritenum_t.SPR_TGRN,32771,4,null,statenum_t.S_GREENTORCH,0,0),	// S_GREENTORCH4
    new state_t(spritenum_t.SPR_TRED,32768,4,null,statenum_t.S_REDTORCH2,0,0),	// S_REDTORCH
    new state_t(spritenum_t.SPR_TRED,32769,4,null,statenum_t.S_REDTORCH3,0,0),	// S_REDTORCH2
    new state_t(spritenum_t.SPR_TRED,32770,4,null,statenum_t.S_REDTORCH4,0,0),	// S_REDTORCH3
    new state_t(spritenum_t.SPR_TRED,32771,4,null,statenum_t.S_REDTORCH,0,0),	// S_REDTORCH4
    new state_t(spritenum_t.SPR_SMBT,32768,4,null,statenum_t.S_BTORCHSHRT2,0,0),	// S_BTORCHSHRT
    new state_t(spritenum_t.SPR_SMBT,32769,4,null,statenum_t.S_BTORCHSHRT3,0,0),	// S_BTORCHSHRT2
    new state_t(spritenum_t.SPR_SMBT,32770,4,null,statenum_t.S_BTORCHSHRT4,0,0),	// S_BTORCHSHRT3
    new state_t(spritenum_t.SPR_SMBT,32771,4,null,statenum_t.S_BTORCHSHRT,0,0),	// S_BTORCHSHRT4
    new state_t(spritenum_t.SPR_SMGT,32768,4,null,statenum_t.S_GTORCHSHRT2,0,0),	// S_GTORCHSHRT
    new state_t(spritenum_t.SPR_SMGT,32769,4,null,statenum_t.S_GTORCHSHRT3,0,0),	// S_GTORCHSHRT2
    new state_t(spritenum_t.SPR_SMGT,32770,4,null,statenum_t.S_GTORCHSHRT4,0,0),	// S_GTORCHSHRT3
    new state_t(spritenum_t.SPR_SMGT,32771,4,null,statenum_t.S_GTORCHSHRT,0,0),	// S_GTORCHSHRT4
    new state_t(spritenum_t.SPR_SMRT,32768,4,null,statenum_t.S_RTORCHSHRT2,0,0),	// S_RTORCHSHRT
    new state_t(spritenum_t.SPR_SMRT,32769,4,null,statenum_t.S_RTORCHSHRT3,0,0),	// S_RTORCHSHRT2
    new state_t(spritenum_t.SPR_SMRT,32770,4,null,statenum_t.S_RTORCHSHRT4,0,0),	// S_RTORCHSHRT3
    new state_t(spritenum_t.SPR_SMRT,32771,4,null,statenum_t.S_RTORCHSHRT,0,0),	// S_RTORCHSHRT4
    new state_t(spritenum_t.SPR_HDB1,0,-1,null,statenum_t.S_NULL,0,0),	// S_HANGNOGUTS
    new state_t(spritenum_t.SPR_HDB2,0,-1,null,statenum_t.S_NULL,0,0),	// S_HANGBNOBRAIN
    new state_t(spritenum_t.SPR_HDB3,0,-1,null,statenum_t.S_NULL,0,0),	// S_HANGTLOOKDN
    new state_t(spritenum_t.SPR_HDB4,0,-1,null,statenum_t.S_NULL,0,0),	// S_HANGTSKULL
    new state_t(spritenum_t.SPR_HDB5,0,-1,null,statenum_t.S_NULL,0,0),	// S_HANGTLOOKUP
    new state_t(spritenum_t.SPR_HDB6,0,-1,null,statenum_t.S_NULL,0,0),	// S_HANGTNOBRAIN
    new state_t(spritenum_t.SPR_POB1,0,-1,null,statenum_t.S_NULL,0,0),	// S_COLONGIBS
    new state_t(spritenum_t.SPR_POB2,0,-1,null,statenum_t.S_NULL,0,0),	// S_SMALLPOOL
    new state_t(spritenum_t.SPR_BRS1,0,-1,null,statenum_t.S_NULL,0,0),		// S_BRAINSTEM
    new state_t(spritenum_t.SPR_TLMP,32768,4,null,statenum_t.S_TECHLAMP2,0,0),	// S_TECHLAMP
    new state_t(spritenum_t.SPR_TLMP,32769,4,null,statenum_t.S_TECHLAMP3,0,0),	// S_TECHLAMP2
    new state_t(spritenum_t.SPR_TLMP,32770,4,null,statenum_t.S_TECHLAMP4,0,0),	// S_TECHLAMP3
    new state_t(spritenum_t.SPR_TLMP,32771,4,null,statenum_t.S_TECHLAMP,0,0),	// S_TECHLAMP4
    new state_t(spritenum_t.SPR_TLP2,32768,4,null,statenum_t.S_TECH2LAMP2,0,0),	// S_TECH2LAMP
    new state_t(spritenum_t.SPR_TLP2,32769,4,null,statenum_t.S_TECH2LAMP3,0,0),	// S_TECH2LAMP2
    new state_t(spritenum_t.SPR_TLP2,32770,4,null,statenum_t.S_TECH2LAMP4,0,0),	// S_TECH2LAMP3
    new state_t(spritenum_t.SPR_TLP2,32771,4,null,statenum_t.S_TECH2LAMP,0,0)	// S_TECH2LAMP4
};

public static mobjinfo_t[] mobjinfo = {
    new mobjinfo_t(		// MT_PLAYER
	-1,		// doomednum
	statenum_t.S_PLAY,		// spawnstate
	100,		// spawnhealth
	statenum_t.S_PLAY_RUN1,		// seestate
	sfxenum_t.sfx_None,		// seesound
	0,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_PLAY_PAIN,		// painstate
	255,		// painchance
	sfxenum_t.sfx_plpain,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_PLAY_ATK1,		// missilestate
	statenum_t.S_PLAY_DIE1,		// deathstate
	statenum_t.S_PLAY_XDIE1,		// xdeathstate
	sfxenum_t.sfx_pldeth,		// deathsound
	0,		// speed
	16*FRACUNIT,		// radius
	56*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_SOLID|MF_SHOOTABLE|MF_DROPOFF|MF_PICKUP|MF_NOTDMATCH,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_POSSESSED
	3004,		// doomednum
	statenum_t.S_POSS_STND,		// spawnstate
	20,		// spawnhealth
	statenum_t.S_POSS_RUN1,		// seestate
	sfxenum_t.sfx_posit1,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_pistol,		// attacksound
	statenum_t.S_POSS_PAIN,		// painstate
	200,		// painchance
	sfxenum_t.sfx_popain,		// painsound
	statenum_t.S_NULL,		// meleestate MAES: BE careful with "0 - null" states!
	statenum_t.S_POSS_ATK1,		// missilestate
	statenum_t.S_POSS_DIE1,		// deathstate
	statenum_t.S_POSS_XDIE1,		// xdeathstate
	sfxenum_t.sfx_podth1,		// deathsound
	8,		// speed
	20*FRACUNIT,		// radius
	56*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_posact,		// activesound
	MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL,		// flags
	statenum_t.S_POSS_RAISE1		// raisestate
    ),

    new mobjinfo_t(		// MT_SHOTGUY
	9,		// doomednum
	statenum_t.S_SPOS_STND,		// spawnstate
	30,		// spawnhealth
	statenum_t.S_SPOS_RUN1,		// seestate
	sfxenum_t.sfx_posit2,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_shotgn,		// attacksound MAES: Weird, this was 0 :-S
	statenum_t.S_SPOS_PAIN,		// painstate
	170,		// painchance
	sfxenum_t.sfx_popain,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_SPOS_ATK1,		// missilestate
	statenum_t.S_SPOS_DIE1,		// deathstate
	statenum_t.S_SPOS_XDIE1,		// xdeathstate
	sfxenum_t.sfx_podth2,		// deathsound
	8,		// speed
	20*FRACUNIT,		// radius
	56*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_posact,		// activesound
	MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL,		// flags
	statenum_t.S_SPOS_RAISE1		// raisestate
    ),

    new mobjinfo_t(		// MT_VILE
	64,		// doomednum
	statenum_t.S_VILE_STND,		// spawnstate
	700,		// spawnhealth
	statenum_t.S_VILE_RUN1,		// seestate
	sfxenum_t.sfx_vilsit,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_VILE_PAIN,		// painstate
	10,		// painchance
	sfxenum_t.sfx_vipain,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_VILE_ATK1,		// missilestate
	statenum_t.S_VILE_DIE1,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_vildth,		// deathsound
	15,		// speed
	20*FRACUNIT,		// radius
	56*FRACUNIT,		// height
	500,		// mass
	0,		// damage
	sfxenum_t.sfx_vilact,		// activesound
	MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_FIRE
	-1,		// doomednum
	statenum_t.S_FIRE1,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_NULL,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_None,		// deathsound
	0,		// speed
	20*FRACUNIT,		// radius
	16*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_NOBLOCKMAP|MF_NOGRAVITY,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_UNDEAD
	66,		// doomednum
	statenum_t.S_SKEL_STND,		// spawnstate
	300,		// spawnhealth
	statenum_t.S_SKEL_RUN1,		// seestate
	sfxenum_t.sfx_skesit,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_SKEL_PAIN,		// painstate
	100,		// painchance
	sfxenum_t.sfx_popain,		// painsound
	statenum_t.S_SKEL_FIST1,		// meleestate
	statenum_t.S_SKEL_MISS1,		// missilestate
	statenum_t.S_SKEL_DIE1,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_skedth,		// deathsound
	10,		// speed
	20*FRACUNIT,		// radius
	56*FRACUNIT,		// height
	500,		// mass
	0,		// damage
	sfxenum_t.sfx_skeact,		// activesound
	MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL,		// flags
	statenum_t.S_SKEL_RAISE1		// raisestate
    ),

    new mobjinfo_t(		// MT_TRACER
	-1,		// doomednum
	statenum_t.S_TRACER,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_skeatk,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_TRACEEXP1,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_barexp,		// deathsound
	10*FRACUNIT,		// speed
	11*FRACUNIT,		// radius
	8*FRACUNIT,		// height
	100,		// mass
	10,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF|MF_NOGRAVITY,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_SMOKE
	-1,		// doomednum
	statenum_t.S_SMOKE1,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_NULL,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_None,		// deathsound
	0,		// speed
	20*FRACUNIT,		// radius
	16*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_NOBLOCKMAP|MF_NOGRAVITY,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_FATSO
	67,		// doomednum
	statenum_t.S_FATT_STND,		// spawnstate
	600,		// spawnhealth
	statenum_t.S_FATT_RUN1,		// seestate
	sfxenum_t.sfx_mansit,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_FATT_PAIN,		// painstate
	80,		// painchance
	sfxenum_t.sfx_mnpain,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_FATT_ATK1,		// missilestate
	statenum_t.S_FATT_DIE1,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_mandth,		// deathsound
	8,		// speed
	48*FRACUNIT,		// radius
	64*FRACUNIT,		// height
	1000,		// mass
	0,		// damage
	sfxenum_t.sfx_posact,		// activesound
	MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL,		// flags
	statenum_t.S_FATT_RAISE1		// raisestate
    ),

    new mobjinfo_t(		// MT_FATSHOT
	-1,		// doomednum
	statenum_t.S_FATSHOT1,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_firsht,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_FATSHOTX1,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_firxpl,		// deathsound
	20*FRACUNIT,		// speed
	6*FRACUNIT,		// radius
	8*FRACUNIT,		// height
	100,		// mass
	8,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF|MF_NOGRAVITY,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_CHAINGUY
	65,		// doomednum
	statenum_t.S_CPOS_STND,		// spawnstate
	70,		// spawnhealth
	statenum_t.S_CPOS_RUN1,		// seestate
	sfxenum_t.sfx_posit2,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_CPOS_PAIN,		// painstate
	170,		// painchance
	sfxenum_t.sfx_popain,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_CPOS_ATK1,		// missilestate
	statenum_t.S_CPOS_DIE1,		// deathstate
	statenum_t.S_CPOS_XDIE1,		// xdeathstate
	sfxenum_t.sfx_podth2,		// deathsound
	8,		// speed
	20*FRACUNIT,		// radius
	56*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_posact,		// activesound
	MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL,		// flags
	statenum_t.S_CPOS_RAISE1		// raisestate
    ),

    new mobjinfo_t(		// MT_TROOP
	3001,		// doomednum
	statenum_t.S_TROO_STND,		// spawnstate
	60,		// spawnhealth
	statenum_t.S_TROO_RUN1,		// seestate
	sfxenum_t.sfx_bgsit1,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_TROO_PAIN,		// painstate
	200,		// painchance
	sfxenum_t.sfx_popain,		// painsound
	statenum_t.S_TROO_ATK1,		// meleestate
	statenum_t.S_TROO_ATK1,		// missilestate
	statenum_t.S_TROO_DIE1,		// deathstate
	statenum_t.S_TROO_XDIE1,		// xdeathstate
	sfxenum_t.sfx_bgdth1,		// deathsound
	8,		// speed
	20*FRACUNIT,		// radius
	56*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_bgact,		// activesound
	MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL,		// flags
	statenum_t.S_TROO_RAISE1		// raisestate
    ),

    new mobjinfo_t(		// MT_SERGEANT
	3002,		// doomednum
	statenum_t.S_SARG_STND,		// spawnstate
	150,		// spawnhealth
	statenum_t.S_SARG_RUN1,		// seestate
	sfxenum_t.sfx_sgtsit,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_sgtatk,		// attacksound
	statenum_t.S_SARG_PAIN,		// painstate
	180,		// painchance
	sfxenum_t.sfx_dmpain,		// painsound
	statenum_t.S_SARG_ATK1,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_SARG_DIE1,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_sgtdth,		// deathsound
	10,		// speed
	30*FRACUNIT,		// radius
	56*FRACUNIT,		// height
	400,		// mass
	0,		// damage
	sfxenum_t.sfx_dmact,		// activesound
	MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL,		// flags
	statenum_t.S_SARG_RAISE1		// raisestate
    ),

    new mobjinfo_t(		// MT_SHADOWS
	58,		// doomednum
	statenum_t.S_SARG_STND,		// spawnstate
	150,		// spawnhealth
	statenum_t.S_SARG_RUN1,		// seestate
	sfxenum_t.sfx_sgtsit,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_sgtatk,		// attacksound
	statenum_t.S_SARG_PAIN,		// painstate
	180,		// painchance
	sfxenum_t.sfx_dmpain,		// painsound
	statenum_t.S_SARG_ATK1,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_SARG_DIE1,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_sgtdth,		// deathsound
	10,		// speed
	30*FRACUNIT,		// radius
	56*FRACUNIT,		// height
	400,		// mass
	0,		// damage
	sfxenum_t.sfx_dmact,		// activesound
	MF_SOLID|MF_SHOOTABLE|MF_SHADOW|MF_COUNTKILL,		// flags
	statenum_t.S_SARG_RAISE1		// raisestate
    ),

    new mobjinfo_t(		// MT_HEAD
	3005,		// doomednum
	statenum_t.S_HEAD_STND,		// spawnstate
	400,		// spawnhealth
	statenum_t.S_HEAD_RUN1,		// seestate
	sfxenum_t.sfx_cacsit,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_HEAD_PAIN,		// painstate
	128,		// painchance
	sfxenum_t.sfx_dmpain,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_HEAD_ATK1,		// missilestate
	statenum_t.S_HEAD_DIE1,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_cacdth,		// deathsound
	8,		// speed
	31*FRACUNIT,		// radius
	56*FRACUNIT,		// height
	400,		// mass
	0,		// damage
	sfxenum_t.sfx_dmact,		// activesound
	MF_SOLID|MF_SHOOTABLE|MF_FLOAT|MF_NOGRAVITY|MF_COUNTKILL,		// flags
	statenum_t.S_HEAD_RAISE1		// raisestate
    ),

    new mobjinfo_t(		// MT_BRUISER
	3003,		// doomednum
	statenum_t.S_BOSS_STND,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_BOSS_RUN1,		// seestate
	sfxenum_t.sfx_brssit,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_BOSS_PAIN,		// painstate
	50,		// painchance
	sfxenum_t.sfx_dmpain,		// painsound
	statenum_t.S_BOSS_ATK1,		// meleestate
	statenum_t.S_BOSS_ATK1,		// missilestate
	statenum_t.S_BOSS_DIE1,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_brsdth,		// deathsound
	8,		// speed
	24*FRACUNIT,		// radius
	64*FRACUNIT,		// height
	1000,		// mass
	0,		// damage
	sfxenum_t.sfx_dmact,		// activesound
	MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL,		// flags
	statenum_t.S_BOSS_RAISE1		// raisestate
    ),

    new mobjinfo_t(		// MT_BRUISERSHOT
	-1,		// doomednum
	statenum_t.S_BRBALL1,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_firsht,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_BRBALLX1,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_firxpl,		// deathsound
	15*FRACUNIT,		// speed
	6*FRACUNIT,		// radius
	8*FRACUNIT,		// height
	100,		// mass
	8,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF|MF_NOGRAVITY,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_KNIGHT
	69,		// doomednum
	statenum_t.S_BOS2_STND,		// spawnstate
	500,		// spawnhealth
	statenum_t.S_BOS2_RUN1,		// seestate
	sfxenum_t.sfx_kntsit,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_BOS2_PAIN,		// painstate
	50,		// painchance
	sfxenum_t.sfx_dmpain,		// painsound
	statenum_t.S_BOS2_ATK1,		// meleestate
	statenum_t.S_BOS2_ATK1,		// missilestate
	statenum_t.S_BOS2_DIE1,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_kntdth,		// deathsound
	8,		// speed
	24*FRACUNIT,		// radius
	64*FRACUNIT,		// height
	1000,		// mass
	0,		// damage
	sfxenum_t.sfx_dmact,		// activesound
	MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL,		// flags
	statenum_t.S_BOS2_RAISE1		// raisestate
    ),

    new mobjinfo_t(		// MT_SKULL
	3006,		// doomednum
	statenum_t.S_SKULL_STND,		// spawnstate
	100,		// spawnhealth
	statenum_t.S_SKULL_RUN1,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_sklatk,		// attacksound
	statenum_t.S_SKULL_PAIN,		// painstate
	256,		// painchance
	sfxenum_t.sfx_dmpain,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_SKULL_ATK1,		// missilestate
	statenum_t.S_SKULL_DIE1,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_firxpl,		// deathsound
	8,		// speed
	16*FRACUNIT,		// radius
	56*FRACUNIT,		// height
	50,		// mass
	3,		// damage
	sfxenum_t.sfx_dmact,		// activesound
	MF_SOLID|MF_SHOOTABLE|MF_FLOAT|MF_NOGRAVITY,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_SPIDER
	7,		// doomednum
	statenum_t.S_SPID_STND,		// spawnstate
	3000,		// spawnhealth
	statenum_t.S_SPID_RUN1,		// seestate
	sfxenum_t.sfx_spisit,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_shotgn,		// attacksound
	statenum_t.S_SPID_PAIN,		// painstate
	40,		// painchance
	sfxenum_t.sfx_dmpain,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_SPID_ATK1,		// missilestate
	statenum_t.S_SPID_DIE1,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_spidth,		// deathsound
	12,		// speed
	128*FRACUNIT,		// radius
	100*FRACUNIT,		// height
	1000,		// mass
	0,		// damage
	sfxenum_t.sfx_dmact,		// activesound
	MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_BABY
	68,		// doomednum
	statenum_t.S_BSPI_STND,		// spawnstate
	500,		// spawnhealth
	statenum_t.S_BSPI_SIGHT,		// seestate
	sfxenum_t.sfx_bspsit,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_BSPI_PAIN,		// painstate
	128,		// painchance
	sfxenum_t.sfx_dmpain,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_BSPI_ATK1,		// missilestate
	statenum_t.S_BSPI_DIE1,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_bspdth,		// deathsound
	12,		// speed
	64*FRACUNIT,		// radius
	64*FRACUNIT,		// height
	600,		// mass
	0,		// damage
	sfxenum_t.sfx_bspact,		// activesound
	MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL,		// flags
	statenum_t.S_BSPI_RAISE1		// raisestate
    ),

    new mobjinfo_t(		// MT_CYBORG
	16,		// doomednum
	statenum_t.S_CYBER_STND,		// spawnstate
	4000,		// spawnhealth
	statenum_t.S_CYBER_RUN1,		// seestate
	sfxenum_t.sfx_cybsit,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_CYBER_PAIN,		// painstate
	20,		// painchance
	sfxenum_t.sfx_dmpain,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_CYBER_ATK1,		// missilestate
	statenum_t.S_CYBER_DIE1,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_cybdth,		// deathsound
	16,		// speed
	40*FRACUNIT,		// radius
	110*FRACUNIT,		// height
	1000,		// mass
	0,		// damage
	sfxenum_t.sfx_dmact,		// activesound
	MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_PAIN
	71,		// doomednum
	statenum_t.S_PAIN_STND,		// spawnstate
	400,		// spawnhealth
	statenum_t.S_PAIN_RUN1,		// seestate
	sfxenum_t.sfx_pesit,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_PAIN_PAIN,		// painstate
	128,		// painchance
	sfxenum_t.sfx_pepain,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_PAIN_ATK1,		// missilestate
	statenum_t.S_PAIN_DIE1,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_pedth,		// deathsound
	8,		// speed
	31*FRACUNIT,		// radius
	56*FRACUNIT,		// height
	400,		// mass
	0,		// damage
	sfxenum_t.sfx_dmact,		// activesound
	MF_SOLID|MF_SHOOTABLE|MF_FLOAT|MF_NOGRAVITY|MF_COUNTKILL,		// flags
	statenum_t.S_PAIN_RAISE1		// raisestate
    ),

    new mobjinfo_t(		// MT_WOLFSS
	84,		// doomednum
	statenum_t.S_SSWV_STND,		// spawnstate
	50,		// spawnhealth
	statenum_t.S_SSWV_RUN1,		// seestate
	sfxenum_t.sfx_sssit,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_SSWV_PAIN,		// painstate
	170,		// painchance
	sfxenum_t.sfx_popain,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_SSWV_ATK1,		// missilestate
	statenum_t.S_SSWV_DIE1,		// deathstate
	statenum_t.S_SSWV_XDIE1,		// xdeathstate
	sfxenum_t.sfx_ssdth,		// deathsound
	8,		// speed
	20*FRACUNIT,		// radius
	56*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_posact,		// activesound
	MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL,		// flags
	statenum_t.S_SSWV_RAISE1		// raisestate
    ),

    new mobjinfo_t(		// MT_KEEN
	72,		// doomednum
	statenum_t.S_KEENSTND,		// spawnstate
	100,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_KEENPAIN,		// painstate
	256,		// painchance
	sfxenum_t.sfx_keenpn,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_COMMKEEN,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_keendt,		// deathsound
	0,		// speed
	16*FRACUNIT,		// radius
	72*FRACUNIT,		// height
	10000000,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_SOLID|MF_SPAWNCEILING|MF_NOGRAVITY|MF_SHOOTABLE|MF_COUNTKILL,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_BOSSBRAIN
	88,		// doomednum
	statenum_t.S_BRAIN,		// spawnstate
	250,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_BRAIN_PAIN,		// painstate
	255,		// painchance
	sfxenum_t.sfx_bospn,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_BRAIN_DIE1,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_bosdth,		// deathsound
	0,		// speed
	16*FRACUNIT,		// radius
	16*FRACUNIT,		// height
	10000000,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_SOLID|MF_SHOOTABLE,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_BOSSSPIT
	89,		// doomednum
	statenum_t.S_BRAINEYE,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_BRAINEYESEE,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_NULL,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_None,		// deathsound
	0,		// speed
	20*FRACUNIT,		// radius
	32*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_NOBLOCKMAP|MF_NOSECTOR,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_BOSSTARGET
	87,		// doomednum
	statenum_t.S_NULL,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_NULL,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_None,		// deathsound
	0,		// speed
	20*FRACUNIT,		// radius
	32*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_NOBLOCKMAP|MF_NOSECTOR,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_SPAWNSHOT
	-1,		// doomednum
	statenum_t.S_SPAWN1,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_bospit,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_NULL,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_firxpl,		// deathsound
	10*FRACUNIT,		// speed
	6*FRACUNIT,		// radius
	32*FRACUNIT,		// height
	100,		// mass
	3,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF|MF_NOGRAVITY|MF_NOCLIP,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_SPAWNFIRE
	-1,		// doomednum
	statenum_t.S_SPAWNFIRE1,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_NULL,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_None,		// deathsound
	0,		// speed
	20*FRACUNIT,		// radius
	16*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_NOBLOCKMAP|MF_NOGRAVITY,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_BARREL
	2035,		// doomednum
	statenum_t.S_BAR1,		// spawnstate
	20,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_BEXP,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_barexp,		// deathsound
	0,		// speed
	10*FRACUNIT,		// radius
	42*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_SOLID|MF_SHOOTABLE|MF_NOBLOOD,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_TROOPSHOT
	-1,		// doomednum
	statenum_t.S_TBALL1,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_firsht,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_TBALLX1,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_firxpl,		// deathsound
	10*FRACUNIT,		// speed
	6*FRACUNIT,		// radius
	8*FRACUNIT,		// height
	100,		// mass
	3,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF|MF_NOGRAVITY,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_HEADSHOT
	-1,		// doomednum
	statenum_t.S_RBALL1,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_firsht,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_RBALLX1,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_firxpl,		// deathsound
	10*FRACUNIT,		// speed
	6*FRACUNIT,		// radius
	8*FRACUNIT,		// height
	100,		// mass
	5,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF|MF_NOGRAVITY,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_ROCKET
	-1,		// doomednum
	statenum_t.S_ROCKET,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_rlaunc,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_EXPLODE1,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_barexp,		// deathsound
	20*FRACUNIT,		// speed
	11*FRACUNIT,		// radius
	8*FRACUNIT,		// height
	100,		// mass
	20,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF|MF_NOGRAVITY,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_PLASMA
	-1,		// doomednum
	statenum_t.S_PLASBALL,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_plasma,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_PLASEXP,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_firxpl,		// deathsound
	25*FRACUNIT,		// speed
	13*FRACUNIT,		// radius
	8*FRACUNIT,		// height
	100,		// mass
	5,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF|MF_NOGRAVITY,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_BFG
	-1,		// doomednum
	statenum_t.S_BFGSHOT,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_BFGLAND,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_rxplod,		// deathsound
	25*FRACUNIT,		// speed
	13*FRACUNIT,		// radius
	8*FRACUNIT,		// height
	100,		// mass
	100,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF|MF_NOGRAVITY,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_ARACHPLAZ
	-1,		// doomednum
	statenum_t.S_ARACH_PLAZ,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_plasma,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_ARACH_PLEX,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_firxpl,		// deathsound
	25*FRACUNIT,		// speed
	13*FRACUNIT,		// radius
	8*FRACUNIT,		// height
	100,		// mass
	5,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF|MF_NOGRAVITY,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_PUFF
	-1,		// doomednum
	statenum_t.S_PUFF1,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_NULL,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_None,		// deathsound
	0,		// speed
	20*FRACUNIT,		// radius
	16*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_NOBLOCKMAP|MF_NOGRAVITY,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_BLOOD
	-1,		// doomednum
	statenum_t.S_BLOOD1,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_NULL,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_None,		// deathsound
	0,		// speed
	20*FRACUNIT,		// radius
	16*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_NOBLOCKMAP,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_TFOG
	-1,		// doomednum
	statenum_t.S_TFOG,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_NULL,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_None,		// deathsound
	0,		// speed
	20*FRACUNIT,		// radius
	16*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_NOBLOCKMAP|MF_NOGRAVITY,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_IFOG
	-1,		// doomednum
	statenum_t.S_IFOG,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_NULL,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_None,		// deathsound
	0,		// speed
	20*FRACUNIT,		// radius
	16*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_NOBLOCKMAP|MF_NOGRAVITY,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_TELEPORTMAN
	14,		// doomednum
	statenum_t.S_NULL,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_NULL,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_None,		// deathsound
	0,		// speed
	20*FRACUNIT,		// radius
	16*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_NOBLOCKMAP|MF_NOSECTOR,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_EXTRABFG
	-1,		// doomednum
	statenum_t.S_BFGEXP,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_NULL,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_None,		// deathsound
	0,		// speed
	20*FRACUNIT,		// radius
	16*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_NOBLOCKMAP|MF_NOGRAVITY,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_MISC0
	2018,		// doomednum
	statenum_t.S_ARM1,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_NULL,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_None,		// deathsound
	0,		// speed
	20*FRACUNIT,		// radius
	16*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_SPECIAL,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_MISC1
	2019,		// doomednum
	statenum_t.S_ARM2,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_NULL,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_None,		// deathsound
	0,		// speed
	20*FRACUNIT,		// radius
	16*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_SPECIAL,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_MISC2
	2014,		// doomednum
	statenum_t.S_BON1,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_NULL,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_None,		// deathsound
	0,		// speed
	20*FRACUNIT,		// radius
	16*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_SPECIAL|MF_COUNTITEM,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_MISC3
	2015,		// doomednum
	statenum_t.S_BON2,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_NULL,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_None,		// deathsound
	0,		// speed
	20*FRACUNIT,		// radius
	16*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_SPECIAL|MF_COUNTITEM,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_MISC4
	5,		// doomednum
	statenum_t.S_BKEY,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_NULL,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_None,		// deathsound
	0,		// speed
	20*FRACUNIT,		// radius
	16*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_SPECIAL|MF_NOTDMATCH,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_MISC5
	13,		// doomednum
	statenum_t.S_RKEY,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_NULL,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_None,		// deathsound
	0,		// speed
	20*FRACUNIT,		// radius
	16*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_SPECIAL|MF_NOTDMATCH,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_MISC6
	6,		// doomednum
	statenum_t.S_YKEY,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_NULL,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_None,		// deathsound
	0,		// speed
	20*FRACUNIT,		// radius
	16*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_SPECIAL|MF_NOTDMATCH,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_MISC7
	39,		// doomednum
	statenum_t.S_YSKULL,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_NULL,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_None,		// deathsound
	0,		// speed
	20*FRACUNIT,		// radius
	16*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_SPECIAL|MF_NOTDMATCH,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_MISC8
	38,		// doomednum
	statenum_t.S_RSKULL,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_NULL,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_None,		// deathsound
	0,		// speed
	20*FRACUNIT,		// radius
	16*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_SPECIAL|MF_NOTDMATCH,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_MISC9
	40,		// doomednum
	statenum_t.S_BSKULL,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_NULL,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_None,		// deathsound
	0,		// speed
	20*FRACUNIT,		// radius
	16*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_SPECIAL|MF_NOTDMATCH,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_MISC10
	2011,		// doomednum
	statenum_t.S_STIM,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_NULL,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_None,		// deathsound
	0,		// speed
	20*FRACUNIT,		// radius
	16*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_SPECIAL,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_MISC11
	2012,		// doomednum
	statenum_t.S_MEDI,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_NULL,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_None,		// deathsound
	0,		// speed
	20*FRACUNIT,		// radius
	16*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_SPECIAL,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_MISC12
	2013,		// doomednum
	statenum_t.S_SOUL,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_NULL,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_None,		// deathsound
	0,		// speed
	20*FRACUNIT,		// radius
	16*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_SPECIAL|MF_COUNTITEM,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_INV
	2022,		// doomednum
	statenum_t.S_PINV,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_NULL,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_None,		// deathsound
	0,		// speed
	20*FRACUNIT,		// radius
	16*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_SPECIAL|MF_COUNTITEM,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_MISC13
	2023,		// doomednum
	statenum_t.S_PSTR,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_NULL,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_None,		// deathsound
	0,		// speed
	20*FRACUNIT,		// radius
	16*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_SPECIAL|MF_COUNTITEM,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_INS
	2024,		// doomednum
	statenum_t.S_PINS,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_NULL,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_None,		// deathsound
	0,		// speed
	20*FRACUNIT,		// radius
	16*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_SPECIAL|MF_COUNTITEM,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_MISC14
	2025,		// doomednum
	statenum_t.S_SUIT,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_NULL,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_None,		// deathsound
	0,		// speed
	20*FRACUNIT,		// radius
	16*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_SPECIAL,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_MISC15
	2026,		// doomednum
	statenum_t.S_PMAP,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_NULL,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_None,		// deathsound
	0,		// speed
	20*FRACUNIT,		// radius
	16*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_SPECIAL|MF_COUNTITEM,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_MISC16
	2045,		// doomednum
	statenum_t.S_PVIS,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_NULL,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_None,		// deathsound
	0,		// speed
	20*FRACUNIT,		// radius
	16*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_SPECIAL|MF_COUNTITEM,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_MEGA
	83,		// doomednum
	statenum_t.S_MEGA,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_NULL,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_None,		// deathsound
	0,		// speed
	20*FRACUNIT,		// radius
	16*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_SPECIAL|MF_COUNTITEM,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_CLIP
	2007,		// doomednum
	statenum_t.S_CLIP,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_NULL,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_None,		// deathsound
	0,		// speed
	20*FRACUNIT,		// radius
	16*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_SPECIAL,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_MISC17
	2048,		// doomednum
	statenum_t.S_AMMO,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_NULL,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_None,		// deathsound
	0,		// speed
	20*FRACUNIT,		// radius
	16*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_SPECIAL,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_MISC18
	2010,		// doomednum
	statenum_t.S_ROCK,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_NULL,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_None,		// deathsound
	0,		// speed
	20*FRACUNIT,		// radius
	16*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_SPECIAL,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_MISC19
	2046,		// doomednum
	statenum_t.S_BROK,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_NULL,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_None,		// deathsound
	0,		// speed
	20*FRACUNIT,		// radius
	16*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_SPECIAL,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_MISC20
	2047,		// doomednum
	statenum_t.S_CELL,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_NULL,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_None,		// deathsound
	0,		// speed
	20*FRACUNIT,		// radius
	16*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_SPECIAL,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_MISC21
	17,		// doomednum
	statenum_t.S_CELP,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_NULL,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_None,		// deathsound
	0,		// speed
	20*FRACUNIT,		// radius
	16*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_SPECIAL,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_MISC22
	2008,		// doomednum
	statenum_t.S_SHEL,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_NULL,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_None,		// deathsound
	0,		// speed
	20*FRACUNIT,		// radius
	16*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_SPECIAL,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_MISC23
	2049,		// doomednum
	statenum_t.S_SBOX,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_NULL,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_None,		// deathsound
	0,		// speed
	20*FRACUNIT,		// radius
	16*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_SPECIAL,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_MISC24
	8,		// doomednum
	statenum_t.S_BPAK,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_NULL,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_None,		// deathsound
	0,		// speed
	20*FRACUNIT,		// radius
	16*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_SPECIAL,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_MISC25
	2006,		// doomednum
	statenum_t.S_BFUG,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_NULL,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_None,		// deathsound
	0,		// speed
	20*FRACUNIT,		// radius
	16*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_SPECIAL,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_CHAINGUN
	2002,		// doomednum
	statenum_t.S_MGUN,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_NULL,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_None,		// deathsound
	0,		// speed
	20*FRACUNIT,		// radius
	16*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_SPECIAL,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_MISC26
	2005,		// doomednum
	statenum_t.S_CSAW,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_NULL,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_None,		// deathsound
	0,		// speed
	20*FRACUNIT,		// radius
	16*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_SPECIAL,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_MISC27
	2003,		// doomednum
	statenum_t.S_LAUN,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_NULL,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_None,		// deathsound
	0,		// speed
	20*FRACUNIT,		// radius
	16*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_SPECIAL,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_MISC28
	2004,		// doomednum
	statenum_t.S_PLAS,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_NULL,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_None,		// deathsound
	0,		// speed
	20*FRACUNIT,		// radius
	16*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_SPECIAL,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_SHOTGUN
	2001,		// doomednum
	statenum_t.S_SHOT,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_NULL,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_None,		// deathsound
	0,		// speed
	20*FRACUNIT,		// radius
	16*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_SPECIAL,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_SUPERSHOTGUN
	82,		// doomednum
	statenum_t.S_SHOT2,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_NULL,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_None,		// deathsound
	0,		// speed
	20*FRACUNIT,		// radius
	16*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_SPECIAL,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_MISC29
	85,		// doomednum
	statenum_t.S_TECHLAMP,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_NULL,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_None,		// deathsound
	0,		// speed
	16*FRACUNIT,		// radius
	16*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_SOLID,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_MISC30
	86,		// doomednum
	statenum_t.S_TECH2LAMP,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_NULL,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_None,		// deathsound
	0,		// speed
	16*FRACUNIT,		// radius
	16*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_SOLID,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_MISC31
	2028,		// doomednum
	statenum_t.S_COLU,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_NULL,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_None,		// deathsound
	0,		// speed
	16*FRACUNIT,		// radius
	16*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_SOLID,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_MISC32
	30,		// doomednum
	statenum_t.S_TALLGRNCOL,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_NULL,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_None,		// deathsound
	0,		// speed
	16*FRACUNIT,		// radius
	16*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_SOLID,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_MISC33
	31,		// doomednum
	statenum_t.S_SHRTGRNCOL,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_NULL,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_None,		// deathsound
	0,		// speed
	16*FRACUNIT,		// radius
	16*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_SOLID,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_MISC34
	32,		// doomednum
	statenum_t.S_TALLREDCOL,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_NULL,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_None,		// deathsound
	0,		// speed
	16*FRACUNIT,		// radius
	16*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_SOLID,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_MISC35
	33,		// doomednum
	statenum_t.S_SHRTREDCOL,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_NULL,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_None,		// deathsound
	0,		// speed
	16*FRACUNIT,		// radius
	16*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_SOLID,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_MISC36
	37,		// doomednum
	statenum_t.S_SKULLCOL,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_NULL,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_None,		// deathsound
	0,		// speed
	16*FRACUNIT,		// radius
	16*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_SOLID,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_MISC37
	36,		// doomednum
	statenum_t.S_HEARTCOL,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_NULL,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_None,		// deathsound
	0,		// speed
	16*FRACUNIT,		// radius
	16*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_SOLID,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_MISC38
	41,		// doomednum
	statenum_t.S_EVILEYE,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_NULL,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_None,		// deathsound
	0,		// speed
	16*FRACUNIT,		// radius
	16*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_SOLID,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_MISC39
	42,		// doomednum
	statenum_t.S_FLOATSKULL,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_NULL,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_None,		// deathsound
	0,		// speed
	16*FRACUNIT,		// radius
	16*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_SOLID,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_MISC40
	43,		// doomednum
	statenum_t.S_TORCHTREE,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_NULL,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_None,		// deathsound
	0,		// speed
	16*FRACUNIT,		// radius
	16*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_SOLID,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_MISC41
	44,		// doomednum
	statenum_t.S_BLUETORCH,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_NULL,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_None,		// deathsound
	0,		// speed
	16*FRACUNIT,		// radius
	16*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_SOLID,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_MISC42
	45,		// doomednum
	statenum_t.S_GREENTORCH,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_NULL,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_None,		// deathsound
	0,		// speed
	16*FRACUNIT,		// radius
	16*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_SOLID,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_MISC43
	46,		// doomednum
	statenum_t.S_REDTORCH,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_NULL,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_None,		// deathsound
	0,		// speed
	16*FRACUNIT,		// radius
	16*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_SOLID,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_MISC44
	55,		// doomednum
	statenum_t.S_BTORCHSHRT,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_NULL,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_None,		// deathsound
	0,		// speed
	16*FRACUNIT,		// radius
	16*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_SOLID,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_MISC45
	56,		// doomednum
	statenum_t.S_GTORCHSHRT,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_NULL,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_None,		// deathsound
	0,		// speed
	16*FRACUNIT,		// radius
	16*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_SOLID,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_MISC46
	57,		// doomednum
	statenum_t.S_RTORCHSHRT,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_NULL,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_None,		// deathsound
	0,		// speed
	16*FRACUNIT,		// radius
	16*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_SOLID,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_MISC47
	47,		// doomednum
	statenum_t.S_STALAGTITE,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_NULL,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_None,		// deathsound
	0,		// speed
	16*FRACUNIT,		// radius
	16*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_SOLID,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_MISC48
	48,		// doomednum
	statenum_t.S_TECHPILLAR,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_NULL,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_None,		// deathsound
	0,		// speed
	16*FRACUNIT,		// radius
	16*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_SOLID,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_MISC49
	34,		// doomednum
	statenum_t.S_CANDLESTIK,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_NULL,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_None,		// deathsound
	0,		// speed
	20*FRACUNIT,		// radius
	16*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	0,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_MISC50
	35,		// doomednum
	statenum_t.S_CANDELABRA,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_NULL,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_None,		// deathsound
	0,		// speed
	16*FRACUNIT,		// radius
	16*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_SOLID,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_MISC51
	49,		// doomednum
	statenum_t.S_BLOODYTWITCH,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_NULL,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_None,		// deathsound
	0,		// speed
	16*FRACUNIT,		// radius
	68*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_SOLID|MF_SPAWNCEILING|MF_NOGRAVITY,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_MISC52
	50,		// doomednum
	statenum_t.S_MEAT2,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_NULL,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_None,		// deathsound
	0,		// speed
	16*FRACUNIT,		// radius
	84*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_SOLID|MF_SPAWNCEILING|MF_NOGRAVITY,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_MISC53
	51,		// doomednum
	statenum_t.S_MEAT3,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_NULL,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_None,		// deathsound
	0,		// speed
	16*FRACUNIT,		// radius
	84*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_SOLID|MF_SPAWNCEILING|MF_NOGRAVITY,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_MISC54
	52,		// doomednum
	statenum_t.S_MEAT4,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_NULL,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_None,		// deathsound
	0,		// speed
	16*FRACUNIT,		// radius
	68*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_SOLID|MF_SPAWNCEILING|MF_NOGRAVITY,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_MISC55
	53,		// doomednum
	statenum_t.S_MEAT5,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_NULL,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_None,		// deathsound
	0,		// speed
	16*FRACUNIT,		// radius
	52*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_SOLID|MF_SPAWNCEILING|MF_NOGRAVITY,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_MISC56
	59,		// doomednum
	statenum_t.S_MEAT2,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_NULL,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_None,		// deathsound
	0,		// speed
	20*FRACUNIT,		// radius
	84*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_SPAWNCEILING|MF_NOGRAVITY,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_MISC57
	60,		// doomednum
	statenum_t.S_MEAT4,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_NULL,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_None,		// deathsound
	0,		// speed
	20*FRACUNIT,		// radius
	68*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_SPAWNCEILING|MF_NOGRAVITY,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_MISC58
	61,		// doomednum
	statenum_t.S_MEAT3,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_NULL,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_None,		// deathsound
	0,		// speed
	20*FRACUNIT,		// radius
	52*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_SPAWNCEILING|MF_NOGRAVITY,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_MISC59
	62,		// doomednum
	statenum_t.S_MEAT5,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_NULL,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_None,		// deathsound
	0,		// speed
	20*FRACUNIT,		// radius
	52*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_SPAWNCEILING|MF_NOGRAVITY,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_MISC60
	63,		// doomednum
	statenum_t.S_BLOODYTWITCH,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_NULL,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_None,		// deathsound
	0,		// speed
	20*FRACUNIT,		// radius
	68*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_SPAWNCEILING|MF_NOGRAVITY,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_MISC61
	22,		// doomednum
	statenum_t.S_HEAD_DIE6,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_NULL,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_None,		// deathsound
	0,		// speed
	20*FRACUNIT,		// radius
	16*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	0,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_MISC62
	15,		// doomednum
	statenum_t.S_PLAY_DIE7,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_NULL,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_None,		// deathsound
	0,		// speed
	20*FRACUNIT,		// radius
	16*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	0,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_MISC63
	18,		// doomednum
	statenum_t.S_POSS_DIE5,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_NULL,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_None,		// deathsound
	0,		// speed
	20*FRACUNIT,		// radius
	16*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	0,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_MISC64
	21,		// doomednum
	statenum_t.S_SARG_DIE6,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_NULL,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_None,		// deathsound
	0,		// speed
	20*FRACUNIT,		// radius
	16*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	0,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_MISC65
	23,		// doomednum
	statenum_t.S_SKULL_DIE6,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_NULL,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_None,		// deathsound
	0,		// speed
	20*FRACUNIT,		// radius
	16*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	0,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_MISC66
	20,		// doomednum
	statenum_t.S_TROO_DIE5,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_NULL,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_None,		// deathsound
	0,		// speed
	20*FRACUNIT,		// radius
	16*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	0,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_MISC67
	19,		// doomednum
	statenum_t.S_SPOS_DIE5,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_NULL,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_None,		// deathsound
	0,		// speed
	20*FRACUNIT,		// radius
	16*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	0,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_MISC68
	10,		// doomednum
	statenum_t.S_PLAY_XDIE9,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_NULL,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_None,		// deathsound
	0,		// speed
	20*FRACUNIT,		// radius
	16*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	0,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_MISC69
	12,		// doomednum
	statenum_t.S_PLAY_XDIE9,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_NULL,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_None,		// deathsound
	0,		// speed
	20*FRACUNIT,		// radius
	16*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	0,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_MISC70
	28,		// doomednum
	statenum_t.S_HEADSONSTICK,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_NULL,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_None,		// deathsound
	0,		// speed
	16*FRACUNIT,		// radius
	16*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_SOLID,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_MISC71
	24,		// doomednum
	statenum_t.S_GIBS,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_NULL,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_None,		// deathsound
	0,		// speed
	20*FRACUNIT,		// radius
	16*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	0,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_MISC72
	27,		// doomednum
	statenum_t.S_HEADONASTICK,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_NULL,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_None,		// deathsound
	0,		// speed
	16*FRACUNIT,		// radius
	16*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_SOLID,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_MISC73
	29,		// doomednum
	statenum_t.S_HEADCANDLES,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_NULL,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_None,		// deathsound
	0,		// speed
	16*FRACUNIT,		// radius
	16*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_SOLID,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_MISC74
	25,		// doomednum
	statenum_t.S_DEADSTICK,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_NULL,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_None,		// deathsound
	0,		// speed
	16*FRACUNIT,		// radius
	16*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_SOLID,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_MISC75
	26,		// doomednum
	statenum_t.S_LIVESTICK,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_NULL,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_None,		// deathsound
	0,		// speed
	16*FRACUNIT,		// radius
	16*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_SOLID,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_MISC76
	54,		// doomednum
	statenum_t.S_BIGTREE,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_NULL,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_None,		// deathsound
	0,		// speed
	32*FRACUNIT,		// radius
	16*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_SOLID,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_MISC77
	70,		// doomednum
	statenum_t.S_BBAR1,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_NULL,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_None,		// deathsound
	0,		// speed
	16*FRACUNIT,		// radius
	16*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_SOLID,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_MISC78
	73,		// doomednum
	statenum_t.S_HANGNOGUTS,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_NULL,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_None,		// deathsound
	0,		// speed
	16*FRACUNIT,		// radius
	88*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_SOLID|MF_SPAWNCEILING|MF_NOGRAVITY,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_MISC79
	74,		// doomednum
	statenum_t.S_HANGBNOBRAIN,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_NULL,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_None,		// deathsound
	0,		// speed
	16*FRACUNIT,		// radius
	88*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_SOLID|MF_SPAWNCEILING|MF_NOGRAVITY,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_MISC80
	75,		// doomednum
	statenum_t.S_HANGTLOOKDN,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_NULL,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_None,		// deathsound
	0,		// speed
	16*FRACUNIT,		// radius
	64*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_SOLID|MF_SPAWNCEILING|MF_NOGRAVITY,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_MISC81
	76,		// doomednum
	statenum_t.S_HANGTSKULL,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_NULL,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_None,		// deathsound
	0,		// speed
	16*FRACUNIT,		// radius
	64*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_SOLID|MF_SPAWNCEILING|MF_NOGRAVITY,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_MISC82
	77,		// doomednum
	statenum_t.S_HANGTLOOKUP,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_NULL,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_None,		// deathsound
	0,		// speed
	16*FRACUNIT,		// radius
	64*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_SOLID|MF_SPAWNCEILING|MF_NOGRAVITY,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_MISC83
	78,		// doomednum
	statenum_t.S_HANGTNOBRAIN,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_NULL,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_None,		// deathsound
	0,		// speed
	16*FRACUNIT,		// radius
	64*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_SOLID|MF_SPAWNCEILING|MF_NOGRAVITY,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_MISC84
	79,		// doomednum
	statenum_t.S_COLONGIBS,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_NULL,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_None,		// deathsound
	0,		// speed
	20*FRACUNIT,		// radius
	16*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_NOBLOCKMAP,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_MISC85
	80,		// doomednum
	statenum_t.S_SMALLPOOL,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_NULL,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_None,		// deathsound
	0,		// speed
	20*FRACUNIT,		// radius
	16*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_NOBLOCKMAP,		// flags
	statenum_t.S_NULL		// raisestate
    ),

    new mobjinfo_t(		// MT_MISC86
	81,		// doomednum
	statenum_t.S_BRAINSTEM,		// spawnstate
	1000,		// spawnhealth
	statenum_t.S_NULL,		// seestate
	sfxenum_t.sfx_None,		// seesound
	8,		// reactiontime
	sfxenum_t.sfx_None,		// attacksound
	statenum_t.S_NULL,		// painstate
	0,		// painchance
	sfxenum_t.sfx_None,		// painsound
	statenum_t.S_NULL,		// meleestate
	statenum_t.S_NULL,		// missilestate
	statenum_t.S_NULL,		// deathstate
	statenum_t.S_NULL,		// xdeathstate
	sfxenum_t.sfx_None,		// deathsound
	0,		// speed
	20*FRACUNIT,		// radius
	16*FRACUNIT,		// height
	100,		// mass
	0,		// damage
	sfxenum_t.sfx_None,		// activesound
	MF_NOBLOCKMAP,		// flags
	statenum_t.S_NULL		// raisestate
    )
};

static {
// Need to set them to simulate pointer-like operations.
	for (int i=0;i<states.length;i++){
		states[i].id=i;
		
		//states[0]=null;
}
}
}


package data;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

import w.CacheableDoomObject;
import w.DoomBuffer;

/** BSP Node structure on-disk */
public class mapnode_t
        implements CacheableDoomObject {

    public mapnode_t() {
        this.bbox = new short[2][4];
        this.children = new char[2];
    }

    /** Partition line from (x,y) to x+dx,y+dy) */
    public short x, y, dx, dy;

    /** Bounding box for each child, clip against view frustum. */
    public short[][] bbox;

    /** If NF_SUBSECTOR its a subsector, else it's a node of another subtree. 
     *  In simpler words: if the first bit is set, strip it and use the rest
     *  as a subtree index. Else it's a node index.
     * */
    public char[] children = new char[2]; // MAES: used to be unsigned short.

    public static int sizeOf() {
        return (8 + 16 + 4);
    }

    @Override
    public void unpack(ByteBuffer buf)
            throws IOException {
        buf.order(ByteOrder.LITTLE_ENDIAN);
        this.x = buf.getShort();
        this.y = buf.getShort();
        this.dx = buf.getShort();
        this.dy = buf.getShort();
        DoomBuffer.readShortArray(buf, this.bbox[0], 4);
        DoomBuffer.readShortArray(buf, this.bbox[1], 4);
        DoomBuffer.readCharArray(buf, this.children, 2);
    }

}

package data;

import java.io.DataOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

import w.CacheableDoomObject;
import w.DoomBuffer;
import w.IPackableDoomObject;
import w.IWritableDoomObject;

/**
 * A LineDef, as used for editing, and as input to the BSP builder.
 */
public class maplinedef_t implements CacheableDoomObject,IPackableDoomObject,IWritableDoomObject{

    public maplinedef_t() {
        this.sidenum = new char[2];
    }

    public char v1;

    public char v2;

    public short flags;

    public short special;

    public short tag;

    /** sidenum[1] will be 0xFFFF if one sided */
    public char[] sidenum;

    public static int sizeOf() {
        return 14;
    }

    @Override
    public void unpack(ByteBuffer buf)
            throws IOException {
    buf.order(ByteOrder.LITTLE_ENDIAN);
    this.v1 = buf.getChar();
    this.v2 = buf.getChar();
    this.flags = buf.getShort();
    this.special = buf.getShort();
    this.tag = buf.getShort();
    DoomBuffer.readCharArray(buf, this.sidenum, 2);
    }

	@Override
	public void write(DataOutputStream dos) throws IOException {
        // More efficient, avoids duplicating code and
        // handles little endian better.
        iobuffer.position(0);
        iobuffer.order(ByteOrder.LITTLE_ENDIAN);
        this.pack(iobuffer);
        dos.write(iobuffer.array());		
	}

	@Override
	public void pack(ByteBuffer buf) throws IOException {
	    buf.order(ByteOrder.LITTLE_ENDIAN);
	    buf.putChar(v1);
	    buf.putChar(v2);
	    buf.putShort(flags);
	    buf.putShort(special);
	    buf.putShort(tag);
	    buf.putChar(sidenum[0]);
	    buf.putChar(sidenum[1]);		
	}
	
	private static ByteBuffer iobuffer=ByteBuffer.allocate(maplinedef_t.sizeOf());
}

package data;

import java.io.DataOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

import w.CacheableDoomObject;
import w.IPackableDoomObject;
import w.IWritableDoomObject;

/**
 * This is the structure of a map vertex ON DISK: in memory it gets shifted and
 * expanded to fixed_t. Also, on disk it only exists as part of the VERTEXES
 * lump: it is not individually cacheable, even though it implements
 * CacheableDoomObject.
 */

public class mapvertex_t implements CacheableDoomObject,IWritableDoomObject,IPackableDoomObject {

    public mapvertex_t(short x, short y) {
        this.x = x;
        this.y = y;
    }

    public mapvertex_t() {
        this((short) 0, (short) 0);
    }

    public short x;

    public short y;

    public static int sizeOf() {
        return 4;
    }

    @Override
    public void unpack(ByteBuffer buf)
            throws IOException {
        buf.order(ByteOrder.LITTLE_ENDIAN);
        x = buf.getShort();
        y = buf.getShort();
    }

	@Override
	public void pack(ByteBuffer buf) throws IOException {
        buf.order(ByteOrder.LITTLE_ENDIAN);
        buf.putShort(x);
        buf.putShort(y);		
	}

	@Override
	public void write(DataOutputStream dos) throws IOException {
        // More efficient, avoids duplicating code and
        // handles little endian better.
        iobuffer.position(0);
        iobuffer.order(ByteOrder.LITTLE_ENDIAN);
        this.pack(iobuffer);
        dos.write(iobuffer.array());		
	}
	
	private static final ByteBuffer iobuffer=ByteBuffer.allocate(mapvertex_t.sizeOf());

}

package data;

import java.io.DataOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

import rr.sector_t;
import w.CacheableDoomObject;
import w.DoomBuffer;
import w.IPackableDoomObject;
import w.IWritableDoomObject;

/** Sector definition, from editing. */ 
public class mapsector_t implements CacheableDoomObject,IWritableDoomObject, IPackableDoomObject {
    
        public mapsector_t(){

        }
    
      public short     floorheight;
      public  short     ceilingheight;
      public  String floorpic;
      public  String        ceilingpic;
      public  short     lightlevel;
      public  short     special;
      public  short     tag;
      
    @Override
    public void unpack(ByteBuffer buf)
            throws IOException {
        buf.order(ByteOrder.LITTLE_ENDIAN);
        this.floorheight = buf.getShort();
        this.ceilingheight = buf.getShort();
        this.floorpic=DoomBuffer.getNullTerminatedString(buf,8).toUpperCase();
        this.ceilingpic=DoomBuffer.getNullTerminatedString(buf,8).toUpperCase();
        this.lightlevel= buf.getShort();
        this.special= buf.getShort();
        this.tag= buf.getShort();
    }
    
    public static int sizeOf() {
        return 26;
    }
    
    @Override
    public void write(DataOutputStream dos)
            throws IOException {    	
    	
        // More efficient, avoids duplicating code and
        // handles little endian better.
        iobuffer.position(0);
        iobuffer.order(ByteOrder.LITTLE_ENDIAN);
        this.pack(iobuffer);
        dos.write(iobuffer.array());
        
    }

    public void pack(ByteBuffer b) throws IOException {
        b.order(ByteOrder.LITTLE_ENDIAN);
        b.putShort(this.floorheight);
        b.putShort(this.ceilingheight);
        DoomBuffer.putNullTerminatedString(b, this.floorpic,8);
        DoomBuffer.putNullTerminatedString(b, this.ceilingpic,8);
        b.putShort(this.lightlevel);
        b.putShort(this.special);
        b.putShort(this.tag);
    }
    
    private static final ByteBuffer iobuffer=ByteBuffer.allocate(mapsector_t.sizeOf());
      
}

package data;

public class musicinfo_t {

    public musicinfo_t() {
    }

    
    public musicinfo_t(String name) {
        this.name = name;
    }
    
    public musicinfo_t(String name, int lumpnum) {
        this.name = name;
        this.lumpnum = lumpnum;
    }

    // up to 6-character name
    public String   name;

    // lump number of music
    public int     lumpnum;
    
    // music data
    public byte[] data;

    // music handle once registered
    public int handle;
}

package data;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

import w.CacheableDoomObject;

/** SubSector, as generated by BSP. */

public class mapsubsector_t implements CacheableDoomObject{
    
        public mapsubsector_t(){
            
        }
        
      public char     numsegs;
      /** Index of first one, segs are stored sequentially. */
      public char     firstseg;
    @Override
    public void unpack(ByteBuffer buf)
            throws IOException {
        buf.order(ByteOrder.LITTLE_ENDIAN);
        this.numsegs = buf.getChar();
        this.firstseg = buf.getChar();
        
    } 
    
    public static int sizeOf(){
        return 4;
    }
}

package data;

/** This is actually used as a data type */

public enum spritenum_t {
	    SPR_TROO,
	    SPR_SHTG,
	    SPR_PUNG,
	    SPR_PISG,
	    SPR_PISF,
	    SPR_SHTF,
	    SPR_SHT2,
	    SPR_CHGG,
	    SPR_CHGF,
	    SPR_MISG,
	    SPR_MISF,
	    SPR_SAWG,
	    SPR_PLSG,
	    SPR_PLSF,
	    SPR_BFGG,
	    SPR_BFGF,
	    SPR_BLUD,
	    SPR_PUFF,
	    SPR_BAL1,
	    SPR_BAL2,
	    SPR_PLSS,
	    SPR_PLSE,
	    SPR_MISL,
	    SPR_BFS1,
	    SPR_BFE1,
	    SPR_BFE2,
	    SPR_TFOG,
	    SPR_IFOG,
	    SPR_PLAY,
	    SPR_POSS,
	    SPR_SPOS,
	    SPR_VILE,
	    SPR_FIRE,
	    SPR_FATB,
	    SPR_FBXP,
	    SPR_SKEL,
	    SPR_MANF,
	    SPR_FATT,
	    SPR_CPOS,
	    SPR_SARG,
	    SPR_HEAD,
	    SPR_BAL7,
	    SPR_BOSS,
	    SPR_BOS2,
	    SPR_SKUL,
	    SPR_SPID,
	    SPR_BSPI,
	    SPR_APLS,
	    SPR_APBX,
	    SPR_CYBR,
	    SPR_PAIN,
	    SPR_SSWV,
	    SPR_KEEN,
	    SPR_BBRN,
	    SPR_BOSF,
	    SPR_ARM1,
	    SPR_ARM2,
	    SPR_BAR1,
	    SPR_BEXP,
	    SPR_FCAN,
	    SPR_BON1,
	    SPR_BON2,
	    SPR_BKEY,
	    SPR_RKEY,
	    SPR_YKEY,
	    SPR_BSKU,
	    SPR_RSKU,
	    SPR_YSKU,
	    SPR_STIM,
	    SPR_MEDI,
	    SPR_SOUL,
	    SPR_PINV,
	    SPR_PSTR,
	    SPR_PINS,
	    SPR_MEGA,
	    SPR_SUIT,
	    SPR_PMAP,
	    SPR_PVIS,
	    SPR_CLIP,
	    SPR_AMMO,
	    SPR_ROCK,
	    SPR_BROK,
	    SPR_CELL,
	    SPR_CELP,
	    SPR_SHEL,
	    SPR_SBOX,
	    SPR_BPAK,
	    SPR_BFUG,
	    SPR_MGUN,
	    SPR_CSAW,
	    SPR_LAUN,
	    SPR_PLAS,
	    SPR_SHOT,
	    SPR_SGN2,
	    SPR_COLU,
	    SPR_SMT2,
	    SPR_GOR1,
	    SPR_POL2,
	    SPR_POL5,
	    SPR_POL4,
	    SPR_POL3,
	    SPR_POL1,
	    SPR_POL6,
	    SPR_GOR2,
	    SPR_GOR3,
	    SPR_GOR4,
	    SPR_GOR5,
	    SPR_SMIT,
	    SPR_COL1,
	    SPR_COL2,
	    SPR_COL3,
	    SPR_COL4,
	    SPR_CAND,
	    SPR_CBRA,
	    SPR_COL6,
	    SPR_TRE1,
	    SPR_TRE2,
	    SPR_ELEC,
	    SPR_CEYE,
	    SPR_FSKU,
	    SPR_COL5,
	    SPR_TBLU,
	    SPR_TGRN,
	    SPR_TRED,
	    SPR_SMBT,
	    SPR_SMGT,
	    SPR_SMRT,
	    SPR_HDB1,
	    SPR_HDB2,
	    SPR_HDB3,
	    SPR_HDB4,
	    SPR_HDB5,
	    SPR_HDB6,
	    SPR_POB1,
	    SPR_POB2,
	    SPR_BRS1,
	    SPR_TLMP,
	    SPR_TLP2,
	    NUMSPRITES
	};


package data;

import java.io.DataOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

import w.CacheableDoomObject;
import w.IPackableDoomObject;
import w.IWritableDoomObject;

/** mapthing_t ... same on disk AND in memory, wow?! */

public class mapthing_t implements CacheableDoomObject,IPackableDoomObject,IWritableDoomObject,Cloneable{
    public short x;

    public short y;

    public short angle;

    public short type;

    public short options;

    public mapthing_t() {
    }

    public mapthing_t(mapthing_t source) {
        this.copyFrom(source);
    }

    public static int sizeOf() {
        return 10;
    }

    @Override
    public void unpack(ByteBuffer buf)
            throws IOException {
        buf.order(ByteOrder.LITTLE_ENDIAN);
        this.x = buf.getShort();
        this.y = buf.getShort();
        this.angle = buf.getShort();
        this.type = buf.getShort();
        this.options = buf.getShort();
        
    }
    
    public void copyFrom(mapthing_t source){

        this.x=source.x;
        this.y=source.y;
        this.angle=source.angle;
        this.options=source.options;
        this.type=source.type;
    }

    @Override
    public void write(DataOutputStream f)
            throws IOException {
        
        // More efficient, avoids duplicating code and
        // handles little endian better.
        iobuffer.position(0);
        iobuffer.order(ByteOrder.LITTLE_ENDIAN);
        this.pack(iobuffer);
        f.write(iobuffer.array());
        
    }

    public void pack(ByteBuffer b) {
        b.order(ByteOrder.LITTLE_ENDIAN);
        b.putShort(x);
        b.putShort(y);
        b.putShort(angle);
        b.putShort(type);
        b.putShort(options);
    }
    
    private static ByteBuffer iobuffer=ByteBuffer.allocate(mapthing_t.sizeOf());
}
package data;

import static m.fixed_t.FRACUNIT;
import static m.fixed_t.MAPFRACUNIT;

/** Everything that constitutes a removable limit should go here */

public final class Limits {

	// Obvious rendering limits
	public static final int MAXVISPLANES = 128;
	public static final int MAXSEGS = 32;
	public static final int MAXVISSPRITES = 128;
	public static final int MAXDRAWSEGS = 256;
	// MAES: Moved MAXOPENINGS to renderer state, it's scale dependant.
	public static final int CEILSPEED = MAPFRACUNIT;
	public static final int CEILWAIT = 150;
	public static final int MAXCEILINGS = 30;

	public static final int MAXANIMS = 32;

	/** Animating line specials */
	public static final int MAXLINEANIMS = 64;

	// These are only used in the renderer, effectively putting
	// a limit to the size of lookup tables for screen buffers.
	public static final int MAXWIDTH = 1600;
	public static final int MAXHEIGHT = 1200;

	// Command line/file limits
	public static final int MAXWADFILES = 20;
	public static final int MAXARGVS = 100;

	// The maximum number of players, multiplayer/networking.
	// Max computers/players in a game. AFFECTS SAVEGAMES.
	public static final int MAXPLAYERS = 4;
	public final static int MAXNETNODES = 8;

	/** Some quirky engine limits */
	public static final int MAXEVENTS = 64;
	
	/** max # of wall switch TYPES in a level */
	public static final int MAXSWITCHES = 50;

	/** 20 adjoining sectors max! */
	public static final int MAX_ADJOINING_SECTORS = 20;

	// 4 players, 4 buttons each at once, max.
	public static final int MAXBUTTONS = 16;

	// 1 second, in ticks.
	public static final int BUTTONTIME = 35;

	/**
	 * keep track of special lines as they are hit, but don't process them until
	 * the move is proven valid
	 */
	public static final int MAXSPECIALCROSS = 8;
	public static final int MAXHEALTH = 100;

	/**
	 * MAXRADIUS is for precalculated sector block boxes the spider demon is
	 * larger, but we do not have any moving sectors nearby
	 */
	public static final int MAXRADIUS = 32 * FRACUNIT;

	public static final int MAXINTERCEPTS = 128;
	public static final int MAXMOVE = (30 * MAPFRACUNIT);

	/** Player spawn spots for deathmatch. */
	public static final int MAX_DM_STARTS = 10;

	// C's "chars" are actually Java signed bytes.
	public static final byte MAXCHAR = ((byte) 0x7f);
	public static final byte MINCHAR = ((byte) 0x80);

	// 16-bit integers...
	public static final short MAXSHORT = ((short) 0x7fff);
	public static final short MINSHORT = ((short) 0x8000);

	// Max pos 32-bit int.
	public static final int MAXINT = ((int) 0x7fffffff);
	public static final long MAXLONG = ((long) 0x7fffffff);

	// Max negative 32-bit integer. These are considered to be the same.
	public static final int MININT = ((int) 0x80000000);
	public static final long MINLONG = ((long) 0x80000000);

	// Buffering/memory limits.
	public static final int SAVEGAMESIZE = 0x2c000;

	public static final int SAVESTRINGSIZE = 24;
	public static final int VERSIONSIZE = 16;

	public static final int PLATWAIT = 3;
	public static final int PLATSPEED = MAPFRACUNIT;
	public static final int MAXPLATS = 30;
	public static final int MAXSKULLS = 20;
	public static final int NUMBRAINTARGETS=32;
	public static final int NUMMOBJTYPES=mobjtype_t.NUMMOBJTYPES.ordinal();
	
}

package data;

import static data.Tables.FINEANGLES;

/** Sine and Cosine.
 *  Java can't have that mush initialization data in one file, so I had to separate it.
 * 
 * @author Maes
 *
 */

public class SineCosine {

 // MAES: original size was 10240, but includes 5PI/4 periods.
 // We can get away with ints on this one because of the small range.
 // MAES: WTF? -64 ~ 64K range... so 17-bit accuracy? heh.

    /**
     * Original size was 10240, but includes 5PI/4 periods.
     * We can get away with ints on this one because of the small range.
     * MAES: WTF? -64 ~ 64K range... so 17-bit accuracy? heh.
     */
    
    public static int[] finesine=
    {
        25,75,125,175,226,276,326,376,
        427,477,527,578,628,678,728,779,
        829,879,929,980,1030,1080,1130,1181,
        1231,1281,1331,1382,1432,1482,1532,1583,
        1633,1683,1733,1784,1834,1884,1934,1985,
        2035,2085,2135,2186,2236,2286,2336,2387,
        2437,2487,2537,2587,2638,2688,2738,2788,
        2839,2889,2939,2989,3039,3090,3140,3190,
        3240,3291,3341,3391,3441,3491,3541,3592,
        3642,3692,3742,3792,3843,3893,3943,3993,
        4043,4093,4144,4194,4244,4294,4344,4394,
        4445,4495,4545,4595,4645,4695,4745,4796,
        4846,4896,4946,4996,5046,5096,5146,5197,
        5247,5297,5347,5397,5447,5497,5547,5597,
        5647,5697,5748,5798,5848,5898,5948,5998,
        6048,6098,6148,6198,6248,6298,6348,6398,
        6448,6498,6548,6598,6648,6698,6748,6798,
        6848,6898,6948,6998,7048,7098,7148,7198,
        7248,7298,7348,7398,7448,7498,7548,7598,
        7648,7697,7747,7797,7847,7897,7947,7997,
        8047,8097,8147,8196,8246,8296,8346,8396,
        8446,8496,8545,8595,8645,8695,8745,8794,
        8844,8894,8944,8994,9043,9093,9143,9193,
        9243,9292,9342,9392,9442,9491,9541,9591,
        9640,9690,9740,9790,9839,9889,9939,9988,
        10038,10088,10137,10187,10237,10286,10336,10386,
        10435,10485,10534,10584,10634,10683,10733,10782,
        10832,10882,10931,10981,11030,11080,11129,11179,
        11228,11278,11327,11377,11426,11476,11525,11575,
        11624,11674,11723,11773,11822,11872,11921,11970,
        12020,12069,12119,12168,12218,12267,12316,12366,
        12415,12464,12514,12563,12612,12662,12711,12760,
        12810,12859,12908,12957,13007,13056,13105,13154,
        13204,13253,13302,13351,13401,13450,13499,13548,
        13597,13647,13696,13745,13794,13843,13892,13941,
        13990,14040,14089,14138,14187,14236,14285,14334,
        14383,14432,14481,14530,14579,14628,14677,14726,
        14775,14824,14873,14922,14971,15020,15069,15118,
        15167,15215,15264,15313,15362,15411,15460,15509,
        15557,15606,15655,15704,15753,15802,15850,15899,
        15948,15997,16045,16094,16143,16191,16240,16289,
        16338,16386,16435,16484,16532,16581,16629,16678,
        16727,16775,16824,16872,16921,16970,17018,17067,
        17115,17164,17212,17261,17309,17358,17406,17455,
        17503,17551,17600,17648,17697,17745,17793,17842,
        17890,17939,17987,18035,18084,18132,18180,18228,
        18277,18325,18373,18421,18470,18518,18566,18614,
        18663,18711,18759,18807,18855,18903,18951,19000,
        19048,19096,19144,19192,19240,19288,19336,19384,
        19432,19480,19528,19576,19624,19672,19720,19768,
        19816,19864,19912,19959,20007,20055,20103,20151,
        20199,20246,20294,20342,20390,20438,20485,20533,
        20581,20629,20676,20724,20772,20819,20867,20915,
        20962,21010,21057,21105,21153,21200,21248,21295,
        21343,21390,21438,21485,21533,21580,21628,21675,
        21723,21770,21817,21865,21912,21960,22007,22054,
        22102,22149,22196,22243,22291,22338,22385,22433,
        22480,22527,22574,22621,22668,22716,22763,22810,
        22857,22904,22951,22998,23045,23092,23139,23186,
        23233,23280,23327,23374,23421,23468,23515,23562,
        23609,23656,23703,23750,23796,23843,23890,23937,
        23984,24030,24077,24124,24171,24217,24264,24311,
        24357,24404,24451,24497,24544,24591,24637,24684,
        24730,24777,24823,24870,24916,24963,25009,25056,
        25102,25149,25195,25241,25288,25334,25381,25427,
        25473,25520,25566,25612,25658,25705,25751,25797,
        25843,25889,25936,25982,26028,26074,26120,26166,
        26212,26258,26304,26350,26396,26442,26488,26534,
        26580,26626,26672,26718,26764,26810,26856,26902,
        26947,26993,27039,27085,27131,27176,27222,27268,
        27313,27359,27405,27450,27496,27542,27587,27633,
        27678,27724,27770,27815,27861,27906,27952,27997,
        28042,28088,28133,28179,28224,28269,28315,28360,
        28405,28451,28496,28541,28586,28632,28677,28722,
        28767,28812,28858,28903,28948,28993,29038,29083,
        29128,29173,29218,29263,29308,29353,29398,29443,
        29488,29533,29577,29622,29667,29712,29757,29801,
        29846,29891,29936,29980,30025,30070,30114,30159,
        30204,30248,30293,30337,30382,30426,30471,30515,
        30560,30604,30649,30693,30738,30782,30826,30871,
        30915,30959,31004,31048,31092,31136,31181,31225,
        31269,31313,31357,31402,31446,31490,31534,31578,
        31622,31666,31710,31754,31798,31842,31886,31930,
        31974,32017,32061,32105,32149,32193,32236,32280,
        32324,32368,32411,32455,32499,32542,32586,32630,
        32673,32717,32760,32804,32847,32891,32934,32978,
        33021,33065,33108,33151,33195,33238,33281,33325,
        33368,33411,33454,33498,33541,33584,33627,33670,
        33713,33756,33799,33843,33886,33929,33972,34015,
        34057,34100,34143,34186,34229,34272,34315,34358,
        34400,34443,34486,34529,34571,34614,34657,34699,
        34742,34785,34827,34870,34912,34955,34997,35040,
        35082,35125,35167,35210,35252,35294,35337,35379,
        35421,35464,35506,35548,35590,35633,35675,35717,
        35759,35801,35843,35885,35927,35969,36011,36053,
        36095,36137,36179,36221,36263,36305,36347,36388,
        36430,36472,36514,36555,36597,36639,36681,36722,
        36764,36805,36847,36889,36930,36972,37013,37055,
        37096,37137,37179,37220,37262,37303,37344,37386,
        37427,37468,37509,37551,37592,37633,37674,37715,
        37756,37797,37838,37879,37920,37961,38002,38043,
        38084,38125,38166,38207,38248,38288,38329,38370,
        38411,38451,38492,38533,38573,38614,38655,38695,
        38736,38776,38817,38857,38898,38938,38979,39019,
        39059,39100,39140,39180,39221,39261,39301,39341,
        39382,39422,39462,39502,39542,39582,39622,39662,
        39702,39742,39782,39822,39862,39902,39942,39982,
        40021,40061,40101,40141,40180,40220,40260,40300,
        40339,40379,40418,40458,40497,40537,40576,40616,
        40655,40695,40734,40773,40813,40852,40891,40931,
        40970,41009,41048,41087,41127,41166,41205,41244,
        41283,41322,41361,41400,41439,41478,41517,41556,
        41595,41633,41672,41711,41750,41788,41827,41866,
        41904,41943,41982,42020,42059,42097,42136,42174,
        42213,42251,42290,42328,42366,42405,42443,42481,
        42520,42558,42596,42634,42672,42711,42749,42787,
        42825,42863,42901,42939,42977,43015,43053,43091,
        43128,43166,43204,43242,43280,43317,43355,43393,
        43430,43468,43506,43543,43581,43618,43656,43693,
        43731,43768,43806,43843,43880,43918,43955,43992,
        44029,44067,44104,44141,44178,44215,44252,44289,
        44326,44363,44400,44437,44474,44511,44548,44585,
        44622,44659,44695,44732,44769,44806,44842,44879,
        44915,44952,44989,45025,45062,45098,45135,45171,
        45207,45244,45280,45316,45353,45389,45425,45462,
        45498,45534,45570,45606,45642,45678,45714,45750,
        45786,45822,45858,45894,45930,45966,46002,46037,
        46073,46109,46145,46180,46216,46252,46287,46323,
        46358,46394,46429,46465,46500,46536,46571,46606,
        46642,46677,46712,46747,46783,46818,46853,46888,
        46923,46958,46993,47028,47063,47098,47133,47168,
        47203,47238,47273,47308,47342,47377,47412,47446,
        47481,47516,47550,47585,47619,47654,47688,47723,
        47757,47792,47826,47860,47895,47929,47963,47998,
        48032,48066,48100,48134,48168,48202,48237,48271,
        48305,48338,48372,48406,48440,48474,48508,48542,
        48575,48609,48643,48676,48710,48744,48777,48811,
        48844,48878,48911,48945,48978,49012,49045,49078,
        49112,49145,49178,49211,49244,49278,49311,49344,
        49377,49410,49443,49476,49509,49542,49575,49608,
        49640,49673,49706,49739,49771,49804,49837,49869,
        49902,49935,49967,50000,50032,50065,50097,50129,
        50162,50194,50226,50259,50291,50323,50355,50387,
        50420,50452,50484,50516,50548,50580,50612,50644,
        50675,50707,50739,50771,50803,50834,50866,50898,
        50929,50961,50993,51024,51056,51087,51119,51150,
        51182,51213,51244,51276,51307,51338,51369,51401,
        51432,51463,51494,51525,51556,51587,51618,51649,
        51680,51711,51742,51773,51803,51834,51865,51896,
        51926,51957,51988,52018,52049,52079,52110,52140,
        52171,52201,52231,52262,52292,52322,52353,52383,
        52413,52443,52473,52503,52534,52564,52594,52624,
        52653,52683,52713,52743,52773,52803,52832,52862,
        52892,52922,52951,52981,53010,53040,53069,53099,
        53128,53158,53187,53216,53246,53275,53304,53334,
        53363,53392,53421,53450,53479,53508,53537,53566,
        53595,53624,53653,53682,53711,53739,53768,53797,
        53826,53854,53883,53911,53940,53969,53997,54026,
        54054,54082,54111,54139,54167,54196,54224,54252,
        54280,54308,54337,54365,54393,54421,54449,54477,
        54505,54533,54560,54588,54616,54644,54672,54699,
        54727,54755,54782,54810,54837,54865,54892,54920,
        54947,54974,55002,55029,55056,55084,55111,55138,
        55165,55192,55219,55246,55274,55300,55327,55354,
        55381,55408,55435,55462,55489,55515,55542,55569,
        55595,55622,55648,55675,55701,55728,55754,55781,
        55807,55833,55860,55886,55912,55938,55965,55991,
        56017,56043,56069,56095,56121,56147,56173,56199,
        56225,56250,56276,56302,56328,56353,56379,56404,
        56430,56456,56481,56507,56532,56557,56583,56608,
        56633,56659,56684,56709,56734,56760,56785,56810,
        56835,56860,56885,56910,56935,56959,56984,57009,
        57034,57059,57083,57108,57133,57157,57182,57206,
        57231,57255,57280,57304,57329,57353,57377,57402,
        57426,57450,57474,57498,57522,57546,57570,57594,
        57618,57642,57666,57690,57714,57738,57762,57785,
        57809,57833,57856,57880,57903,57927,57950,57974,
        57997,58021,58044,58067,58091,58114,58137,58160,
        58183,58207,58230,58253,58276,58299,58322,58345,
        58367,58390,58413,58436,58459,58481,58504,58527,
        58549,58572,58594,58617,58639,58662,58684,58706,
        58729,58751,58773,58795,58818,58840,58862,58884,
        58906,58928,58950,58972,58994,59016,59038,59059,
        59081,59103,59125,59146,59168,59190,59211,59233,
        59254,59276,59297,59318,59340,59361,59382,59404,
        59425,59446,59467,59488,59509,59530,59551,59572,
        59593,59614,59635,59656,59677,59697,59718,59739,
        59759,59780,59801,59821,59842,59862,59883,59903,
        59923,59944,59964,59984,60004,60025,60045,60065,
        60085,60105,60125,60145,60165,60185,60205,60225,
        60244,60264,60284,60304,60323,60343,60363,60382,
        60402,60421,60441,60460,60479,60499,60518,60537,
        60556,60576,60595,60614,60633,60652,60671,60690,
        60709,60728,60747,60766,60785,60803,60822,60841,
        60859,60878,60897,60915,60934,60952,60971,60989,
        61007,61026,61044,61062,61081,61099,61117,61135,
        61153,61171,61189,61207,61225,61243,61261,61279,
        61297,61314,61332,61350,61367,61385,61403,61420,
        61438,61455,61473,61490,61507,61525,61542,61559,
        61577,61594,61611,61628,61645,61662,61679,61696,
        61713,61730,61747,61764,61780,61797,61814,61831,
        61847,61864,61880,61897,61913,61930,61946,61963,
        61979,61995,62012,62028,62044,62060,62076,62092,
        62108,62125,62141,62156,62172,62188,62204,62220,
        62236,62251,62267,62283,62298,62314,62329,62345,
        62360,62376,62391,62407,62422,62437,62453,62468,
        62483,62498,62513,62528,62543,62558,62573,62588,
        62603,62618,62633,62648,62662,62677,62692,62706,
        62721,62735,62750,62764,62779,62793,62808,62822,
        62836,62850,62865,62879,62893,62907,62921,62935,
        62949,62963,62977,62991,63005,63019,63032,63046,
        63060,63074,63087,63101,63114,63128,63141,63155,
        63168,63182,63195,63208,63221,63235,63248,63261,
        63274,63287,63300,63313,63326,63339,63352,63365,
        63378,63390,63403,63416,63429,63441,63454,63466,
        63479,63491,63504,63516,63528,63541,63553,63565,
        63578,63590,63602,63614,63626,63638,63650,63662,
        63674,63686,63698,63709,63721,63733,63745,63756,
        63768,63779,63791,63803,63814,63825,63837,63848,
        63859,63871,63882,63893,63904,63915,63927,63938,
        63949,63960,63971,63981,63992,64003,64014,64025,
        64035,64046,64057,64067,64078,64088,64099,64109,
        64120,64130,64140,64151,64161,64171,64181,64192,
        64202,64212,64222,64232,64242,64252,64261,64271,
        64281,64291,64301,64310,64320,64330,64339,64349,
        64358,64368,64377,64387,64396,64405,64414,64424,
        64433,64442,64451,64460,64469,64478,64487,64496,
        64505,64514,64523,64532,64540,64549,64558,64566,
        64575,64584,64592,64601,64609,64617,64626,64634,
        64642,64651,64659,64667,64675,64683,64691,64699,
        64707,64715,64723,64731,64739,64747,64754,64762,
        64770,64777,64785,64793,64800,64808,64815,64822,
        64830,64837,64844,64852,64859,64866,64873,64880,
        64887,64895,64902,64908,64915,64922,64929,64936,
        64943,64949,64956,64963,64969,64976,64982,64989,
        64995,65002,65008,65015,65021,65027,65033,65040,
        65046,65052,65058,65064,65070,65076,65082,65088,
        65094,65099,65105,65111,65117,65122,65128,65133,
        65139,65144,65150,65155,65161,65166,65171,65177,
        65182,65187,65192,65197,65202,65207,65212,65217,
        65222,65227,65232,65237,65242,65246,65251,65256,
        65260,65265,65270,65274,65279,65283,65287,65292,
        65296,65300,65305,65309,65313,65317,65321,65325,
        65329,65333,65337,65341,65345,65349,65352,65356,
        65360,65363,65367,65371,65374,65378,65381,65385,
        65388,65391,65395,65398,65401,65404,65408,65411,
        65414,65417,65420,65423,65426,65429,65431,65434,
        65437,65440,65442,65445,65448,65450,65453,65455,
        65458,65460,65463,65465,65467,65470,65472,65474,
        65476,65478,65480,65482,65484,65486,65488,65490,
        65492,65494,65496,65497,65499,65501,65502,65504,
        65505,65507,65508,65510,65511,65513,65514,65515,
        65516,65518,65519,65520,65521,65522,65523,65524,
        65525,65526,65527,65527,65528,65529,65530,65530,
        65531,65531,65532,65532,65533,65533,65534,65534,
        65534,65535,65535,65535,65535,65535,65535,65535,
        65535,65535,65535,65535,65535,65535,65535,65534,
        65534,65534,65533,65533,65532,65532,65531,65531,
        65530,65530,65529,65528,65527,65527,65526,65525,
        65524,65523,65522,65521,65520,65519,65518,65516,
        65515,65514,65513,65511,65510,65508,65507,65505,
        65504,65502,65501,65499,65497,65496,65494,65492,
        65490,65488,65486,65484,65482,65480,65478,65476,
        65474,65472,65470,65467,65465,65463,65460,65458,
        65455,65453,65450,65448,65445,65442,65440,65437,
        65434,65431,65429,65426,65423,65420,65417,65414,
        65411,65408,65404,65401,65398,65395,65391,65388,
        65385,65381,65378,65374,65371,65367,65363,65360,
        65356,65352,65349,65345,65341,65337,65333,65329,
        65325,65321,65317,65313,65309,65305,65300,65296,
        65292,65287,65283,65279,65274,65270,65265,65260,
        65256,65251,65246,65242,65237,65232,65227,65222,
        65217,65212,65207,65202,65197,65192,65187,65182,
        65177,65171,65166,65161,65155,65150,65144,65139,
        65133,65128,65122,65117,65111,65105,65099,65094,
        65088,65082,65076,65070,65064,65058,65052,65046,
        65040,65033,65027,65021,65015,65008,65002,64995,
        64989,64982,64976,64969,64963,64956,64949,64943,
        64936,64929,64922,64915,64908,64902,64895,64887,
        64880,64873,64866,64859,64852,64844,64837,64830,
        64822,64815,64808,64800,64793,64785,64777,64770,
        64762,64754,64747,64739,64731,64723,64715,64707,
        64699,64691,64683,64675,64667,64659,64651,64642,
        64634,64626,64617,64609,64600,64592,64584,64575,
        64566,64558,64549,64540,64532,64523,64514,64505,
        64496,64487,64478,64469,64460,64451,64442,64433,
        64424,64414,64405,64396,64387,64377,64368,64358,
        64349,64339,64330,64320,64310,64301,64291,64281,
        64271,64261,64252,64242,64232,64222,64212,64202,
        64192,64181,64171,64161,64151,64140,64130,64120,
        64109,64099,64088,64078,64067,64057,64046,64035,
        64025,64014,64003,63992,63981,63971,63960,63949,
        63938,63927,63915,63904,63893,63882,63871,63859,
        63848,63837,63825,63814,63803,63791,63779,63768,
        63756,63745,63733,63721,63709,63698,63686,63674,
        63662,63650,63638,63626,63614,63602,63590,63578,
        63565,63553,63541,63528,63516,63504,63491,63479,
        63466,63454,63441,63429,63416,63403,63390,63378,
        63365,63352,63339,63326,63313,63300,63287,63274,
        63261,63248,63235,63221,63208,63195,63182,63168,
        63155,63141,63128,63114,63101,63087,63074,63060,
        63046,63032,63019,63005,62991,62977,62963,62949,
        62935,62921,62907,62893,62879,62865,62850,62836,
        62822,62808,62793,62779,62764,62750,62735,62721,
        62706,62692,62677,62662,62648,62633,62618,62603,
        62588,62573,62558,62543,62528,62513,62498,62483,
        62468,62453,62437,62422,62407,62391,62376,62360,
        62345,62329,62314,62298,62283,62267,62251,62236,
        62220,62204,62188,62172,62156,62141,62125,62108,
        62092,62076,62060,62044,62028,62012,61995,61979,
        61963,61946,61930,61913,61897,61880,61864,61847,
        61831,61814,61797,61780,61764,61747,61730,61713,
        61696,61679,61662,61645,61628,61611,61594,61577,
        61559,61542,61525,61507,61490,61473,61455,61438,
        61420,61403,61385,61367,61350,61332,61314,61297,
        61279,61261,61243,61225,61207,61189,61171,61153,
        61135,61117,61099,61081,61062,61044,61026,61007,
        60989,60971,60952,60934,60915,60897,60878,60859,
        60841,60822,60803,60785,60766,60747,60728,60709,
        60690,60671,60652,60633,60614,60595,60576,60556,
        60537,60518,60499,60479,60460,60441,60421,60402,
        60382,60363,60343,60323,60304,60284,60264,60244,
        60225,60205,60185,60165,60145,60125,60105,60085,
        60065,60045,60025,60004,59984,59964,59944,59923,
        59903,59883,59862,59842,59821,59801,59780,59759,
        59739,59718,59697,59677,59656,59635,59614,59593,
        59572,59551,59530,59509,59488,59467,59446,59425,
        59404,59382,59361,59340,59318,59297,59276,59254,
        59233,59211,59190,59168,59146,59125,59103,59081,
        59059,59038,59016,58994,58972,58950,58928,58906,
        58884,58862,58840,58818,58795,58773,58751,58729,
        58706,58684,58662,58639,58617,58594,58572,58549,
        58527,58504,58481,58459,58436,58413,58390,58367,
        58345,58322,58299,58276,58253,58230,58207,58183,
        58160,58137,58114,58091,58067,58044,58021,57997,
        57974,57950,57927,57903,57880,57856,57833,57809,
        57785,57762,57738,57714,57690,57666,57642,57618,
        57594,57570,57546,57522,57498,57474,57450,57426,
        57402,57377,57353,57329,57304,57280,57255,57231,
        57206,57182,57157,57133,57108,57083,57059,57034,
        57009,56984,56959,56935,56910,56885,56860,56835,
        56810,56785,56760,56734,56709,56684,56659,56633,
        56608,56583,56557,56532,56507,56481,56456,56430,
        56404,56379,56353,56328,56302,56276,56250,56225,
        56199,56173,56147,56121,56095,56069,56043,56017,
        55991,55965,55938,55912,55886,55860,55833,55807,
        55781,55754,55728,55701,55675,55648,55622,55595,
        55569,55542,55515,55489,55462,55435,55408,55381,
        55354,55327,55300,55274,55246,55219,55192,55165,
        55138,55111,55084,55056,55029,55002,54974,54947,
        54920,54892,54865,54837,54810,54782,54755,54727,
        54699,54672,54644,54616,54588,54560,54533,54505,
        54477,54449,54421,54393,54365,54337,54308,54280,
        54252,54224,54196,54167,54139,54111,54082,54054,
        54026,53997,53969,53940,53911,53883,53854,53826,
        53797,53768,53739,53711,53682,53653,53624,53595,
        53566,53537,53508,53479,53450,53421,53392,53363,
        53334,53304,53275,53246,53216,53187,53158,53128,
        53099,53069,53040,53010,52981,52951,52922,52892,
        52862,52832,52803,52773,52743,52713,52683,52653,
        52624,52594,52564,52534,52503,52473,52443,52413,
        52383,52353,52322,52292,52262,52231,52201,52171,
        52140,52110,52079,52049,52018,51988,51957,51926,
        51896,51865,51834,51803,51773,51742,51711,51680,
        51649,51618,51587,51556,51525,51494,51463,51432,
        51401,51369,51338,51307,51276,51244,51213,51182,
        51150,51119,51087,51056,51024,50993,50961,50929,
        50898,50866,50834,50803,50771,50739,50707,50675,
        50644,50612,50580,50548,50516,50484,50452,50420,
        50387,50355,50323,50291,50259,50226,50194,50162,
        50129,50097,50065,50032,50000,49967,49935,49902,
        49869,49837,49804,49771,49739,49706,49673,49640,
        49608,49575,49542,49509,49476,49443,49410,49377,
        49344,49311,49278,49244,49211,49178,49145,49112,
        49078,49045,49012,48978,48945,48911,48878,48844,
        48811,48777,48744,48710,48676,48643,48609,48575,
        48542,48508,48474,48440,48406,48372,48338,48304,
        48271,48237,48202,48168,48134,48100,48066,48032,
        47998,47963,47929,47895,47860,47826,47792,47757,
        47723,47688,47654,47619,47585,47550,47516,47481,
        47446,47412,47377,47342,47308,47273,47238,47203,
        47168,47133,47098,47063,47028,46993,46958,46923,
        46888,46853,46818,46783,46747,46712,46677,46642,
        46606,46571,46536,46500,46465,46429,46394,46358,
        46323,46287,46252,46216,46180,46145,46109,46073,
        46037,46002,45966,45930,45894,45858,45822,45786,
        45750,45714,45678,45642,45606,45570,45534,45498,
        45462,45425,45389,45353,45316,45280,45244,45207,
        45171,45135,45098,45062,45025,44989,44952,44915,
        44879,44842,44806,44769,44732,44695,44659,44622,
        44585,44548,44511,44474,44437,44400,44363,44326,
        44289,44252,44215,44178,44141,44104,44067,44029,
        43992,43955,43918,43880,43843,43806,43768,43731,
        43693,43656,43618,43581,43543,43506,43468,43430,
        43393,43355,43317,43280,43242,43204,43166,43128,
        43091,43053,43015,42977,42939,42901,42863,42825,
        42787,42749,42711,42672,42634,42596,42558,42520,
        42481,42443,42405,42366,42328,42290,42251,42213,
        42174,42136,42097,42059,42020,41982,41943,41904,
        41866,41827,41788,41750,41711,41672,41633,41595,
        41556,41517,41478,41439,41400,41361,41322,41283,
        41244,41205,41166,41127,41088,41048,41009,40970,
        40931,40891,40852,40813,40773,40734,40695,40655,
        40616,40576,40537,40497,40458,40418,40379,40339,
        40300,40260,40220,40180,40141,40101,40061,40021,
        39982,39942,39902,39862,39822,39782,39742,39702,
        39662,39622,39582,39542,39502,39462,39422,39382,
        39341,39301,39261,39221,39180,39140,39100,39059,
        39019,38979,38938,38898,38857,38817,38776,38736,
        38695,38655,38614,38573,38533,38492,38451,38411,
        38370,38329,38288,38248,38207,38166,38125,38084,
        38043,38002,37961,37920,37879,37838,37797,37756,
        37715,37674,37633,37592,37551,37509,37468,37427,
        37386,37344,37303,37262,37220,37179,37137,37096,
        37055,37013,36972,36930,36889,36847,36805,36764,
        36722,36681,36639,36597,36556,36514,36472,36430,
        36388,36347,36305,36263,36221,36179,36137,36095,
        36053,36011,35969,35927,35885,35843,35801,35759,
        35717,35675,35633,35590,35548,35506,35464,35421,
        35379,35337,35294,35252,35210,35167,35125,35082,
        35040,34997,34955,34912,34870,34827,34785,34742,
        34699,34657,34614,34571,34529,34486,34443,34400,
        34358,34315,34272,34229,34186,34143,34100,34057,
        34015,33972,33929,33886,33843,33799,33756,33713,
        33670,33627,33584,33541,33498,33454,33411,33368,
        33325,33281,33238,33195,33151,33108,33065,33021,
        32978,32934,32891,32847,32804,32760,32717,32673,
        32630,32586,32542,32499,32455,32411,32368,32324,
        32280,32236,32193,32149,32105,32061,32017,31974,
        31930,31886,31842,31798,31754,31710,31666,31622,
        31578,31534,31490,31446,31402,31357,31313,31269,
        31225,31181,31136,31092,31048,31004,30959,30915,
        30871,30826,30782,30738,30693,30649,30604,30560,
        30515,30471,30426,30382,30337,30293,30248,30204,
        30159,30114,30070,30025,29980,29936,29891,29846,
        29801,29757,29712,29667,29622,29577,29533,29488,
        29443,29398,29353,29308,29263,29218,29173,29128,
        29083,29038,28993,28948,28903,28858,28812,28767,
        28722,28677,28632,28586,28541,28496,28451,28405,
        28360,28315,28269,28224,28179,28133,28088,28042,
        27997,27952,27906,27861,27815,27770,27724,27678,
        27633,27587,27542,27496,27450,27405,27359,27313,
        27268,27222,27176,27131,27085,27039,26993,26947,
        26902,26856,26810,26764,26718,26672,26626,26580,
        26534,26488,26442,26396,26350,26304,26258,26212,
        26166,26120,26074,26028,25982,25936,25889,25843,
        25797,25751,25705,25658,25612,25566,25520,25473,
        25427,25381,25334,25288,25241,25195,25149,25102,
        25056,25009,24963,24916,24870,24823,24777,24730,
        24684,24637,24591,24544,24497,24451,24404,24357,
        24311,24264,24217,24171,24124,24077,24030,23984,
        23937,23890,23843,23796,23750,23703,23656,23609,
        23562,23515,23468,23421,23374,23327,23280,23233,
        23186,23139,23092,23045,22998,22951,22904,22857,
        22810,22763,22716,22668,22621,22574,22527,22480,
        22433,22385,22338,22291,22243,22196,22149,22102,
        22054,22007,21960,21912,21865,21817,21770,21723,
        21675,21628,21580,21533,21485,21438,21390,21343,
        21295,21248,21200,21153,21105,21057,21010,20962,
        20915,20867,20819,20772,20724,20676,20629,20581,
        20533,20485,20438,20390,20342,20294,20246,20199,
        20151,20103,20055,20007,19959,19912,19864,19816,
        19768,19720,19672,19624,19576,19528,19480,19432,
        19384,19336,19288,19240,19192,19144,19096,19048,
        19000,18951,18903,18855,18807,18759,18711,18663,
        18614,18566,18518,18470,18421,18373,18325,18277,
        18228,18180,18132,18084,18035,17987,17939,17890,
        17842,17793,17745,17697,17648,17600,17551,17503,
        17455,17406,17358,17309,17261,17212,17164,17115,
        17067,17018,16970,16921,16872,16824,16775,16727,
        16678,16629,16581,16532,16484,16435,16386,16338,
        16289,16240,16191,16143,16094,16045,15997,15948,
        15899,15850,15802,15753,15704,15655,15606,15557,
        15509,15460,15411,15362,15313,15264,15215,15167,
        15118,15069,15020,14971,14922,14873,14824,14775,
        14726,14677,14628,14579,14530,14481,14432,14383,
        14334,14285,14236,14187,14138,14089,14040,13990,
        13941,13892,13843,13794,13745,13696,13646,13597,
        13548,13499,13450,13401,13351,13302,13253,13204,
        13154,13105,13056,13007,12957,12908,12859,12810,
        12760,12711,12662,12612,12563,12514,12464,12415,
        12366,12316,12267,12218,12168,12119,12069,12020,
        11970,11921,11872,11822,11773,11723,11674,11624,
        11575,11525,11476,11426,11377,11327,11278,11228,
        11179,11129,11080,11030,10981,10931,10882,10832,
        10782,10733,10683,10634,10584,10534,10485,10435,
        10386,10336,10286,10237,10187,10137,10088,10038,
        9988,9939,9889,9839,9790,9740,9690,9640,
        9591,9541,9491,9442,9392,9342,9292,9243,
        9193,9143,9093,9043,8994,8944,8894,8844,
        8794,8745,8695,8645,8595,8545,8496,8446,
        8396,8346,8296,8246,8196,8147,8097,8047,
        7997,7947,7897,7847,7797,7747,7697,7648,
        7598,7548,7498,7448,7398,7348,7298,7248,
        7198,7148,7098,7048,6998,6948,6898,6848,
        6798,6748,6698,6648,6598,6548,6498,6448,
        6398,6348,6298,6248,6198,6148,6098,6048,
        5998,5948,5898,5848,5798,5748,5697,5647,
        5597,5547,5497,5447,5397,5347,5297,5247,
        5197,5146,5096,5046,4996,4946,4896,4846,
        4796,4745,4695,4645,4595,4545,4495,4445,
        4394,4344,4294,4244,4194,4144,4093,4043,
        3993,3943,3893,3843,3792,3742,3692,3642,
        3592,3541,3491,3441,3391,3341,3291,3240,
        3190,3140,3090,3039,2989,2939,2889,2839,
        2788,2738,2688,2638,2587,2537,2487,2437,
        2387,2336,2286,2236,2186,2135,2085,2035,
        1985,1934,1884,1834,1784,1733,1683,1633,
        1583,1532,1482,1432,1382,1331,1281,1231,
        1181,1130,1080,1030,980,929,879,829,
        779,728,678,628,578,527,477,427,
        376,326,276,226,175,125,75,25,
        -25,-75,-125,-175,-226,-276,-326,-376,
        -427,-477,-527,-578,-628,-678,-728,-779,
        -829,-879,-929,-980,-1030,-1080,-1130,-1181,
        -1231,-1281,-1331,-1382,-1432,-1482,-1532,-1583,
        -1633,-1683,-1733,-1784,-1834,-1884,-1934,-1985,
        -2035,-2085,-2135,-2186,-2236,-2286,-2336,-2387,
        -2437,-2487,-2537,-2588,-2638,-2688,-2738,-2788,
        -2839,-2889,-2939,-2989,-3039,-3090,-3140,-3190,
        -3240,-3291,-3341,-3391,-3441,-3491,-3541,-3592,
        -3642,-3692,-3742,-3792,-3843,-3893,-3943,-3993,
        -4043,-4093,-4144,-4194,-4244,-4294,-4344,-4394,
        -4445,-4495,-4545,-4595,-4645,-4695,-4745,-4796,
        -4846,-4896,-4946,-4996,-5046,-5096,-5146,-5197,
        -5247,-5297,-5347,-5397,-5447,-5497,-5547,-5597,
        -5647,-5697,-5748,-5798,-5848,-5898,-5948,-5998,
        -6048,-6098,-6148,-6198,-6248,-6298,-6348,-6398,
        -6448,-6498,-6548,-6598,-6648,-6698,-6748,-6798,
        -6848,-6898,-6948,-6998,-7048,-7098,-7148,-7198,
        -7248,-7298,-7348,-7398,-7448,-7498,-7548,-7598,
        -7648,-7697,-7747,-7797,-7847,-7897,-7947,-7997,
        -8047,-8097,-8147,-8196,-8246,-8296,-8346,-8396,
        -8446,-8496,-8545,-8595,-8645,-8695,-8745,-8794,
        -8844,-8894,-8944,-8994,-9043,-9093,-9143,-9193,
        -9243,-9292,-9342,-9392,-9442,-9491,-9541,-9591,
        -9640,-9690,-9740,-9790,-9839,-9889,-9939,-9988,
        -10038,-10088,-10137,-10187,-10237,-10286,-10336,-10386,
        -10435,-10485,-10534,-10584,-10634,-10683,-10733,-10782,
        -10832,-10882,-10931,-10981,-11030,-11080,-11129,-11179,
        -11228,-11278,-11327,-11377,-11426,-11476,-11525,-11575,
        -11624,-11674,-11723,-11773,-11822,-11872,-11921,-11970,
        -12020,-12069,-12119,-12168,-12218,-12267,-12316,-12366,
        -12415,-12464,-12514,-12563,-12612,-12662,-12711,-12760,
        -12810,-12859,-12908,-12957,-13007,-13056,-13105,-13154,
        -13204,-13253,-13302,-13351,-13401,-13450,-13499,-13548,
        -13597,-13647,-13696,-13745,-13794,-13843,-13892,-13941,
        -13990,-14040,-14089,-14138,-14187,-14236,-14285,-14334,
        -14383,-14432,-14481,-14530,-14579,-14628,-14677,-14726,
        -14775,-14824,-14873,-14922,-14971,-15020,-15069,-15118,
        -15167,-15215,-15264,-15313,-15362,-15411,-15460,-15509,
        -15557,-15606,-15655,-15704,-15753,-15802,-15850,-15899,
        -15948,-15997,-16045,-16094,-16143,-16191,-16240,-16289,
        -16338,-16386,-16435,-16484,-16532,-16581,-16629,-16678,
        -16727,-16775,-16824,-16872,-16921,-16970,-17018,-17067,
        -17115,-17164,-17212,-17261,-17309,-17358,-17406,-17455,
        -17503,-17551,-17600,-17648,-17697,-17745,-17793,-17842,
        -17890,-17939,-17987,-18035,-18084,-18132,-18180,-18228,
        -18277,-18325,-18373,-18421,-18470,-18518,-18566,-18614,
        -18663,-18711,-18759,-18807,-18855,-18903,-18951,-19000,
        -19048,-19096,-19144,-19192,-19240,-19288,-19336,-19384,
        -19432,-19480,-19528,-19576,-19624,-19672,-19720,-19768,
        -19816,-19864,-19912,-19959,-20007,-20055,-20103,-20151,
        -20199,-20246,-20294,-20342,-20390,-20438,-20485,-20533,
        -20581,-20629,-20676,-20724,-20772,-20819,-20867,-20915,
        -20962,-21010,-21057,-21105,-21153,-21200,-21248,-21295,
        -21343,-21390,-21438,-21485,-21533,-21580,-21628,-21675,
        -21723,-21770,-21817,-21865,-21912,-21960,-22007,-22054,
        -22102,-22149,-22196,-22243,-22291,-22338,-22385,-22433,
        -22480,-22527,-22574,-22621,-22668,-22716,-22763,-22810,
        -22857,-22904,-22951,-22998,-23045,-23092,-23139,-23186,
        -23233,-23280,-23327,-23374,-23421,-23468,-23515,-23562,
        -23609,-23656,-23703,-23750,-23796,-23843,-23890,-23937,
        -23984,-24030,-24077,-24124,-24171,-24217,-24264,-24311,
        -24357,-24404,-24451,-24497,-24544,-24591,-24637,-24684,
        -24730,-24777,-24823,-24870,-24916,-24963,-25009,-25056,
        -25102,-25149,-25195,-25241,-25288,-25334,-25381,-25427,
        -25473,-25520,-25566,-25612,-25658,-25705,-25751,-25797,
        -25843,-25889,-25936,-25982,-26028,-26074,-26120,-26166,
        -26212,-26258,-26304,-26350,-26396,-26442,-26488,-26534,
        -26580,-26626,-26672,-26718,-26764,-26810,-26856,-26902,
        -26947,-26993,-27039,-27085,-27131,-27176,-27222,-27268,
        -27313,-27359,-27405,-27450,-27496,-27542,-27587,-27633,
        -27678,-27724,-27770,-27815,-27861,-27906,-27952,-27997,
        -28042,-28088,-28133,-28179,-28224,-28269,-28315,-28360,
        -28405,-28451,-28496,-28541,-28586,-28632,-28677,-28722,
        -28767,-28812,-28858,-28903,-28948,-28993,-29038,-29083,
        -29128,-29173,-29218,-29263,-29308,-29353,-29398,-29443,
        -29488,-29533,-29577,-29622,-29667,-29712,-29757,-29801,
        -29846,-29891,-29936,-29980,-30025,-30070,-30114,-30159,
        -30204,-30248,-30293,-30337,-30382,-30426,-30471,-30515,
        -30560,-30604,-30649,-30693,-30738,-30782,-30826,-30871,
        -30915,-30959,-31004,-31048,-31092,-31136,-31181,-31225,
        -31269,-31313,-31357,-31402,-31446,-31490,-31534,-31578,
        -31622,-31666,-31710,-31754,-31798,-31842,-31886,-31930,
        -31974,-32017,-32061,-32105,-32149,-32193,-32236,-32280,
        -32324,-32368,-32411,-32455,-32499,-32542,-32586,-32630,
        -32673,-32717,-32760,-32804,-32847,-32891,-32934,-32978,
        -33021,-33065,-33108,-33151,-33195,-33238,-33281,-33325,
        -33368,-33411,-33454,-33498,-33541,-33584,-33627,-33670,
        -33713,-33756,-33799,-33843,-33886,-33929,-33972,-34015,
        -34057,-34100,-34143,-34186,-34229,-34272,-34315,-34358,
        -34400,-34443,-34486,-34529,-34571,-34614,-34657,-34699,
        -34742,-34785,-34827,-34870,-34912,-34955,-34997,-35040,
        -35082,-35125,-35167,-35210,-35252,-35294,-35337,-35379,
        -35421,-35464,-35506,-35548,-35590,-35633,-35675,-35717,
        -35759,-35801,-35843,-35885,-35927,-35969,-36011,-36053,
        -36095,-36137,-36179,-36221,-36263,-36305,-36347,-36388,
        -36430,-36472,-36514,-36555,-36597,-36639,-36681,-36722,
        -36764,-36805,-36847,-36889,-36930,-36972,-37013,-37055,
        -37096,-37137,-37179,-37220,-37262,-37303,-37344,-37386,
        -37427,-37468,-37509,-37551,-37592,-37633,-37674,-37715,
        -37756,-37797,-37838,-37879,-37920,-37961,-38002,-38043,
        -38084,-38125,-38166,-38207,-38248,-38288,-38329,-38370,
        -38411,-38451,-38492,-38533,-38573,-38614,-38655,-38695,
        -38736,-38776,-38817,-38857,-38898,-38938,-38979,-39019,
        -39059,-39100,-39140,-39180,-39221,-39261,-39301,-39341,
        -39382,-39422,-39462,-39502,-39542,-39582,-39622,-39662,
        -39702,-39742,-39782,-39822,-39862,-39902,-39942,-39982,
        -40021,-40061,-40101,-40141,-40180,-40220,-40260,-40299,
        -40339,-40379,-40418,-40458,-40497,-40537,-40576,-40616,
        -40655,-40695,-40734,-40773,-40813,-40852,-40891,-40931,
        -40970,-41009,-41048,-41087,-41127,-41166,-41205,-41244,
        -41283,-41322,-41361,-41400,-41439,-41478,-41517,-41556,
        -41595,-41633,-41672,-41711,-41750,-41788,-41827,-41866,
        -41904,-41943,-41982,-42020,-42059,-42097,-42136,-42174,
        -42213,-42251,-42290,-42328,-42366,-42405,-42443,-42481,
        -42520,-42558,-42596,-42634,-42672,-42711,-42749,-42787,
        -42825,-42863,-42901,-42939,-42977,-43015,-43053,-43091,
        -43128,-43166,-43204,-43242,-43280,-43317,-43355,-43393,
        -43430,-43468,-43506,-43543,-43581,-43618,-43656,-43693,
        -43731,-43768,-43806,-43843,-43880,-43918,-43955,-43992,
        -44029,-44067,-44104,-44141,-44178,-44215,-44252,-44289,
        -44326,-44363,-44400,-44437,-44474,-44511,-44548,-44585,
        -44622,-44659,-44695,-44732,-44769,-44806,-44842,-44879,
        -44915,-44952,-44989,-45025,-45062,-45098,-45135,-45171,
        -45207,-45244,-45280,-45316,-45353,-45389,-45425,-45462,
        -45498,-45534,-45570,-45606,-45642,-45678,-45714,-45750,
        -45786,-45822,-45858,-45894,-45930,-45966,-46002,-46037,
        -46073,-46109,-46145,-46180,-46216,-46252,-46287,-46323,
        -46358,-46394,-46429,-46465,-46500,-46536,-46571,-46606,
        -46642,-46677,-46712,-46747,-46783,-46818,-46853,-46888,
        -46923,-46958,-46993,-47028,-47063,-47098,-47133,-47168,
        -47203,-47238,-47273,-47308,-47342,-47377,-47412,-47446,
        -47481,-47516,-47550,-47585,-47619,-47654,-47688,-47723,
        -47757,-47792,-47826,-47860,-47895,-47929,-47963,-47998,
        -48032,-48066,-48100,-48134,-48168,-48202,-48236,-48271,
        -48304,-48338,-48372,-48406,-48440,-48474,-48508,-48542,
        -48575,-48609,-48643,-48676,-48710,-48744,-48777,-48811,
        -48844,-48878,-48911,-48945,-48978,-49012,-49045,-49078,
        -49112,-49145,-49178,-49211,-49244,-49278,-49311,-49344,
        -49377,-49410,-49443,-49476,-49509,-49542,-49575,-49608,
        -49640,-49673,-49706,-49739,-49771,-49804,-49837,-49869,
        -49902,-49935,-49967,-50000,-50032,-50065,-50097,-50129,
        -50162,-50194,-50226,-50259,-50291,-50323,-50355,-50387,
        -50420,-50452,-50484,-50516,-50548,-50580,-50612,-50644,
        -50675,-50707,-50739,-50771,-50803,-50834,-50866,-50898,
        -50929,-50961,-50993,-51024,-51056,-51087,-51119,-51150,
        -51182,-51213,-51244,-51276,-51307,-51338,-51369,-51401,
        -51432,-51463,-51494,-51525,-51556,-51587,-51618,-51649,
        -51680,-51711,-51742,-51773,-51803,-51834,-51865,-51896,
        -51926,-51957,-51988,-52018,-52049,-52079,-52110,-52140,
        -52171,-52201,-52231,-52262,-52292,-52322,-52353,-52383,
        -52413,-52443,-52473,-52503,-52534,-52564,-52594,-52624,
        -52653,-52683,-52713,-52743,-52773,-52803,-52832,-52862,
        -52892,-52922,-52951,-52981,-53010,-53040,-53069,-53099,
        -53128,-53158,-53187,-53216,-53246,-53275,-53304,-53334,
        -53363,-53392,-53421,-53450,-53479,-53508,-53537,-53566,
        -53595,-53624,-53653,-53682,-53711,-53739,-53768,-53797,
        -53826,-53854,-53883,-53911,-53940,-53969,-53997,-54026,
        -54054,-54082,-54111,-54139,-54167,-54196,-54224,-54252,
        -54280,-54308,-54337,-54365,-54393,-54421,-54449,-54477,
        -54505,-54533,-54560,-54588,-54616,-54644,-54672,-54699,
        -54727,-54755,-54782,-54810,-54837,-54865,-54892,-54920,
        -54947,-54974,-55002,-55029,-55056,-55084,-55111,-55138,
        -55165,-55192,-55219,-55246,-55274,-55300,-55327,-55354,
        -55381,-55408,-55435,-55462,-55489,-55515,-55542,-55569,
        -55595,-55622,-55648,-55675,-55701,-55728,-55754,-55781,
        -55807,-55833,-55860,-55886,-55912,-55938,-55965,-55991,
        -56017,-56043,-56069,-56095,-56121,-56147,-56173,-56199,
        -56225,-56250,-56276,-56302,-56328,-56353,-56379,-56404,
        -56430,-56456,-56481,-56507,-56532,-56557,-56583,-56608,
        -56633,-56659,-56684,-56709,-56734,-56760,-56785,-56810,
        -56835,-56860,-56885,-56910,-56935,-56959,-56984,-57009,
        -57034,-57059,-57083,-57108,-57133,-57157,-57182,-57206,
        -57231,-57255,-57280,-57304,-57329,-57353,-57377,-57402,
        -57426,-57450,-57474,-57498,-57522,-57546,-57570,-57594,
        -57618,-57642,-57666,-57690,-57714,-57738,-57762,-57785,
        -57809,-57833,-57856,-57880,-57903,-57927,-57950,-57974,
        -57997,-58021,-58044,-58067,-58091,-58114,-58137,-58160,
        -58183,-58207,-58230,-58253,-58276,-58299,-58322,-58345,
        -58367,-58390,-58413,-58436,-58459,-58481,-58504,-58527,
        -58549,-58572,-58594,-58617,-58639,-58662,-58684,-58706,
        -58729,-58751,-58773,-58795,-58818,-58840,-58862,-58884,
        -58906,-58928,-58950,-58972,-58994,-59016,-59038,-59059,
        -59081,-59103,-59125,-59146,-59168,-59190,-59211,-59233,
        -59254,-59276,-59297,-59318,-59340,-59361,-59382,-59404,
        -59425,-59446,-59467,-59488,-59509,-59530,-59551,-59572,
        -59593,-59614,-59635,-59656,-59677,-59697,-59718,-59739,
        -59759,-59780,-59801,-59821,-59842,-59862,-59883,-59903,
        -59923,-59944,-59964,-59984,-60004,-60025,-60045,-60065,
        -60085,-60105,-60125,-60145,-60165,-60185,-60205,-60225,
        -60244,-60264,-60284,-60304,-60323,-60343,-60363,-60382,
        -60402,-60421,-60441,-60460,-60479,-60499,-60518,-60537,
        -60556,-60576,-60595,-60614,-60633,-60652,-60671,-60690,
        -60709,-60728,-60747,-60766,-60785,-60803,-60822,-60841,
        -60859,-60878,-60897,-60915,-60934,-60952,-60971,-60989,
        -61007,-61026,-61044,-61062,-61081,-61099,-61117,-61135,
        -61153,-61171,-61189,-61207,-61225,-61243,-61261,-61279,
        -61297,-61314,-61332,-61350,-61367,-61385,-61403,-61420,
        -61438,-61455,-61473,-61490,-61507,-61525,-61542,-61559,
        -61577,-61594,-61611,-61628,-61645,-61662,-61679,-61696,
        -61713,-61730,-61747,-61764,-61780,-61797,-61814,-61831,
        -61847,-61864,-61880,-61897,-61913,-61930,-61946,-61963,
        -61979,-61995,-62012,-62028,-62044,-62060,-62076,-62092,
        -62108,-62125,-62141,-62156,-62172,-62188,-62204,-62220,
        -62236,-62251,-62267,-62283,-62298,-62314,-62329,-62345,
        -62360,-62376,-62391,-62407,-62422,-62437,-62453,-62468,
        -62483,-62498,-62513,-62528,-62543,-62558,-62573,-62588,
        -62603,-62618,-62633,-62648,-62662,-62677,-62692,-62706,
        -62721,-62735,-62750,-62764,-62779,-62793,-62808,-62822,
        -62836,-62850,-62865,-62879,-62893,-62907,-62921,-62935,
        -62949,-62963,-62977,-62991,-63005,-63019,-63032,-63046,
        -63060,-63074,-63087,-63101,-63114,-63128,-63141,-63155,
        -63168,-63182,-63195,-63208,-63221,-63235,-63248,-63261,
        -63274,-63287,-63300,-63313,-63326,-63339,-63352,-63365,
        -63378,-63390,-63403,-63416,-63429,-63441,-63454,-63466,
        -63479,-63491,-63504,-63516,-63528,-63541,-63553,-63565,
        -63578,-63590,-63602,-63614,-63626,-63638,-63650,-63662,
        -63674,-63686,-63698,-63709,-63721,-63733,-63745,-63756,
        -63768,-63779,-63791,-63803,-63814,-63825,-63837,-63848,
        -63859,-63871,-63882,-63893,-63904,-63915,-63927,-63938,
        -63949,-63960,-63971,-63981,-63992,-64003,-64014,-64025,
        -64035,-64046,-64057,-64067,-64078,-64088,-64099,-64109,
        -64120,-64130,-64140,-64151,-64161,-64171,-64181,-64192,
        -64202,-64212,-64222,-64232,-64242,-64252,-64261,-64271,
        -64281,-64291,-64301,-64310,-64320,-64330,-64339,-64349,
        -64358,-64368,-64377,-64387,-64396,-64405,-64414,-64424,
        -64433,-64442,-64451,-64460,-64469,-64478,-64487,-64496,
        -64505,-64514,-64523,-64532,-64540,-64549,-64558,-64566,
        -64575,-64584,-64592,-64601,-64609,-64617,-64626,-64634,
        -64642,-64651,-64659,-64667,-64675,-64683,-64691,-64699,
        -64707,-64715,-64723,-64731,-64739,-64747,-64754,-64762,
        -64770,-64777,-64785,-64793,-64800,-64808,-64815,-64822,
        -64830,-64837,-64844,-64852,-64859,-64866,-64873,-64880,
        -64887,-64895,-64902,-64908,-64915,-64922,-64929,-64936,
        -64943,-64949,-64956,-64963,-64969,-64976,-64982,-64989,
        -64995,-65002,-65008,-65015,-65021,-65027,-65033,-65040,
        -65046,-65052,-65058,-65064,-65070,-65076,-65082,-65088,
        -65094,-65099,-65105,-65111,-65117,-65122,-65128,-65133,
        -65139,-65144,-65150,-65155,-65161,-65166,-65171,-65177,
        -65182,-65187,-65192,-65197,-65202,-65207,-65212,-65217,
        -65222,-65227,-65232,-65237,-65242,-65246,-65251,-65256,
        -65260,-65265,-65270,-65274,-65279,-65283,-65287,-65292,
        -65296,-65300,-65305,-65309,-65313,-65317,-65321,-65325,
        -65329,-65333,-65337,-65341,-65345,-65349,-65352,-65356,
        -65360,-65363,-65367,-65371,-65374,-65378,-65381,-65385,
        -65388,-65391,-65395,-65398,-65401,-65404,-65408,-65411,
        -65414,-65417,-65420,-65423,-65426,-65429,-65431,-65434,
        -65437,-65440,-65442,-65445,-65448,-65450,-65453,-65455,
        -65458,-65460,-65463,-65465,-65467,-65470,-65472,-65474,
        -65476,-65478,-65480,-65482,-65484,-65486,-65488,-65490,
        -65492,-65494,-65496,-65497,-65499,-65501,-65502,-65504,
        -65505,-65507,-65508,-65510,-65511,-65513,-65514,-65515,
        -65516,-65518,-65519,-65520,-65521,-65522,-65523,-65524,
        -65525,-65526,-65527,-65527,-65528,-65529,-65530,-65530,
        -65531,-65531,-65532,-65532,-65533,-65533,-65534,-65534,
        -65534,-65535,-65535,-65535,-65535,-65535,-65535,-65535,
        -65535,-65535,-65535,-65535,-65535,-65535,-65535,-65534,
        -65534,-65534,-65533,-65533,-65532,-65532,-65531,-65531,
        -65530,-65530,-65529,-65528,-65527,-65527,-65526,-65525,
        -65524,-65523,-65522,-65521,-65520,-65519,-65518,-65516,
        -65515,-65514,-65513,-65511,-65510,-65508,-65507,-65505,
        -65504,-65502,-65501,-65499,-65497,-65496,-65494,-65492,
        -65490,-65488,-65486,-65484,-65482,-65480,-65478,-65476,
        -65474,-65472,-65470,-65467,-65465,-65463,-65460,-65458,
        -65455,-65453,-65450,-65448,-65445,-65442,-65440,-65437,
        -65434,-65431,-65429,-65426,-65423,-65420,-65417,-65414,
        -65411,-65408,-65404,-65401,-65398,-65395,-65391,-65388,
        -65385,-65381,-65378,-65374,-65371,-65367,-65363,-65360,
        -65356,-65352,-65349,-65345,-65341,-65337,-65333,-65329,
        -65325,-65321,-65317,-65313,-65309,-65305,-65300,-65296,
        -65292,-65287,-65283,-65279,-65274,-65270,-65265,-65260,
        -65256,-65251,-65246,-65242,-65237,-65232,-65227,-65222,
        -65217,-65212,-65207,-65202,-65197,-65192,-65187,-65182,
        -65177,-65171,-65166,-65161,-65155,-65150,-65144,-65139,
        -65133,-65128,-65122,-65117,-65111,-65105,-65099,-65094,
        -65088,-65082,-65076,-65070,-65064,-65058,-65052,-65046,
        -65040,-65033,-65027,-65021,-65015,-65008,-65002,-64995,
        -64989,-64982,-64976,-64969,-64963,-64956,-64949,-64943,
        -64936,-64929,-64922,-64915,-64908,-64902,-64895,-64887,
        -64880,-64873,-64866,-64859,-64852,-64844,-64837,-64830,
        -64822,-64815,-64808,-64800,-64793,-64785,-64777,-64770,
        -64762,-64754,-64747,-64739,-64731,-64723,-64715,-64707,
        -64699,-64691,-64683,-64675,-64667,-64659,-64651,-64642,
        -64634,-64626,-64617,-64609,-64601,-64592,-64584,-64575,
        -64566,-64558,-64549,-64540,-64532,-64523,-64514,-64505,
        -64496,-64487,-64478,-64469,-64460,-64451,-64442,-64433,
        -64424,-64414,-64405,-64396,-64387,-64377,-64368,-64358,
        -64349,-64339,-64330,-64320,-64310,-64301,-64291,-64281,
        -64271,-64261,-64252,-64242,-64232,-64222,-64212,-64202,
        -64192,-64181,-64171,-64161,-64151,-64140,-64130,-64120,
        -64109,-64099,-64088,-64078,-64067,-64057,-64046,-64035,
        -64025,-64014,-64003,-63992,-63981,-63971,-63960,-63949,
        -63938,-63927,-63915,-63904,-63893,-63882,-63871,-63859,
        -63848,-63837,-63825,-63814,-63803,-63791,-63779,-63768,
        -63756,-63745,-63733,-63721,-63709,-63698,-63686,-63674,
        -63662,-63650,-63638,-63626,-63614,-63602,-63590,-63578,
        -63565,-63553,-63541,-63528,-63516,-63504,-63491,-63479,
        -63466,-63454,-63441,-63429,-63416,-63403,-63390,-63378,
        -63365,-63352,-63339,-63326,-63313,-63300,-63287,-63274,
        -63261,-63248,-63235,-63221,-63208,-63195,-63182,-63168,
        -63155,-63141,-63128,-63114,-63101,-63087,-63074,-63060,
        -63046,-63032,-63019,-63005,-62991,-62977,-62963,-62949,
        -62935,-62921,-62907,-62893,-62879,-62865,-62850,-62836,
        -62822,-62808,-62793,-62779,-62764,-62750,-62735,-62721,
        -62706,-62692,-62677,-62662,-62648,-62633,-62618,-62603,
        -62588,-62573,-62558,-62543,-62528,-62513,-62498,-62483,
        -62468,-62453,-62437,-62422,-62407,-62391,-62376,-62360,
        -62345,-62329,-62314,-62298,-62283,-62267,-62251,-62236,
        -62220,-62204,-62188,-62172,-62156,-62141,-62125,-62108,
        -62092,-62076,-62060,-62044,-62028,-62012,-61995,-61979,
        -61963,-61946,-61930,-61913,-61897,-61880,-61864,-61847,
        -61831,-61814,-61797,-61780,-61764,-61747,-61730,-61713,
        -61696,-61679,-61662,-61645,-61628,-61611,-61594,-61577,
        -61559,-61542,-61525,-61507,-61490,-61473,-61455,-61438,
        -61420,-61403,-61385,-61367,-61350,-61332,-61314,-61297,
        -61279,-61261,-61243,-61225,-61207,-61189,-61171,-61153,
        -61135,-61117,-61099,-61081,-61062,-61044,-61026,-61007,
        -60989,-60971,-60952,-60934,-60915,-60897,-60878,-60859,
        -60841,-60822,-60803,-60785,-60766,-60747,-60728,-60709,
        -60690,-60671,-60652,-60633,-60614,-60595,-60576,-60556,
        -60537,-60518,-60499,-60479,-60460,-60441,-60421,-60402,
        -60382,-60363,-60343,-60323,-60304,-60284,-60264,-60244,
        -60225,-60205,-60185,-60165,-60145,-60125,-60105,-60085,
        -60065,-60045,-60025,-60004,-59984,-59964,-59944,-59923,
        -59903,-59883,-59862,-59842,-59821,-59801,-59780,-59759,
        -59739,-59718,-59697,-59677,-59656,-59635,-59614,-59593,
        -59572,-59551,-59530,-59509,-59488,-59467,-59446,-59425,
        -59404,-59382,-59361,-59340,-59318,-59297,-59276,-59254,
        -59233,-59211,-59189,-59168,-59146,-59125,-59103,-59081,
        -59059,-59038,-59016,-58994,-58972,-58950,-58928,-58906,
        -58884,-58862,-58840,-58818,-58795,-58773,-58751,-58729,
        -58706,-58684,-58662,-58639,-58617,-58594,-58572,-58549,
        -58527,-58504,-58481,-58459,-58436,-58413,-58390,-58367,
        -58345,-58322,-58299,-58276,-58253,-58230,-58207,-58183,
        -58160,-58137,-58114,-58091,-58067,-58044,-58021,-57997,
        -57974,-57950,-57927,-57903,-57880,-57856,-57833,-57809,
        -57785,-57762,-57738,-57714,-57690,-57666,-57642,-57618,
        -57594,-57570,-57546,-57522,-57498,-57474,-57450,-57426,
        -57402,-57377,-57353,-57329,-57304,-57280,-57255,-57231,
        -57206,-57182,-57157,-57133,-57108,-57083,-57059,-57034,
        -57009,-56984,-56959,-56935,-56910,-56885,-56860,-56835,
        -56810,-56785,-56760,-56734,-56709,-56684,-56659,-56633,
        -56608,-56583,-56557,-56532,-56507,-56481,-56456,-56430,
        -56404,-56379,-56353,-56328,-56302,-56276,-56250,-56225,
        -56199,-56173,-56147,-56121,-56095,-56069,-56043,-56017,
        -55991,-55965,-55938,-55912,-55886,-55860,-55833,-55807,
        -55781,-55754,-55728,-55701,-55675,-55648,-55622,-55595,
        -55569,-55542,-55515,-55489,-55462,-55435,-55408,-55381,
        -55354,-55327,-55300,-55274,-55246,-55219,-55192,-55165,
        -55138,-55111,-55084,-55056,-55029,-55002,-54974,-54947,
        -54920,-54892,-54865,-54837,-54810,-54782,-54755,-54727,
        -54699,-54672,-54644,-54616,-54588,-54560,-54533,-54505,
        -54477,-54449,-54421,-54393,-54365,-54337,-54308,-54280,
        -54252,-54224,-54196,-54167,-54139,-54111,-54082,-54054,
        -54026,-53997,-53969,-53940,-53911,-53883,-53854,-53826,
        -53797,-53768,-53739,-53711,-53682,-53653,-53624,-53595,
        -53566,-53537,-53508,-53479,-53450,-53421,-53392,-53363,
        -53334,-53304,-53275,-53246,-53216,-53187,-53158,-53128,
        -53099,-53069,-53040,-53010,-52981,-52951,-52922,-52892,
        -52862,-52832,-52803,-52773,-52743,-52713,-52683,-52653,
        -52624,-52594,-52564,-52534,-52503,-52473,-52443,-52413,
        -52383,-52353,-52322,-52292,-52262,-52231,-52201,-52171,
        -52140,-52110,-52079,-52049,-52018,-51988,-51957,-51926,
        -51896,-51865,-51834,-51803,-51773,-51742,-51711,-51680,
        -51649,-51618,-51587,-51556,-51525,-51494,-51463,-51432,
        -51401,-51369,-51338,-51307,-51276,-51244,-51213,-51182,
        -51150,-51119,-51087,-51056,-51024,-50993,-50961,-50929,
        -50898,-50866,-50834,-50803,-50771,-50739,-50707,-50675,
        -50644,-50612,-50580,-50548,-50516,-50484,-50452,-50420,
        -50387,-50355,-50323,-50291,-50259,-50226,-50194,-50162,
        -50129,-50097,-50065,-50032,-50000,-49967,-49935,-49902,
        -49869,-49837,-49804,-49771,-49739,-49706,-49673,-49640,
        -49608,-49575,-49542,-49509,-49476,-49443,-49410,-49377,
        -49344,-49311,-49278,-49244,-49211,-49178,-49145,-49112,
        -49078,-49045,-49012,-48978,-48945,-48911,-48878,-48844,
        -48811,-48777,-48744,-48710,-48676,-48643,-48609,-48575,
        -48542,-48508,-48474,-48440,-48406,-48372,-48338,-48305,
        -48271,-48237,-48202,-48168,-48134,-48100,-48066,-48032,
        -47998,-47963,-47929,-47895,-47860,-47826,-47792,-47757,
        -47723,-47688,-47654,-47619,-47585,-47550,-47516,-47481,
        -47446,-47412,-47377,-47342,-47307,-47273,-47238,-47203,
        -47168,-47133,-47098,-47063,-47028,-46993,-46958,-46923,
        -46888,-46853,-46818,-46783,-46747,-46712,-46677,-46642,
        -46606,-46571,-46536,-46500,-46465,-46429,-46394,-46358,
        -46323,-46287,-46251,-46216,-46180,-46145,-46109,-46073,
        -46037,-46002,-45966,-45930,-45894,-45858,-45822,-45786,
        -45750,-45714,-45678,-45642,-45606,-45570,-45534,-45498,
        -45462,-45425,-45389,-45353,-45316,-45280,-45244,-45207,
        -45171,-45135,-45098,-45062,-45025,-44989,-44952,-44915,
        -44879,-44842,-44806,-44769,-44732,-44695,-44659,-44622,
        -44585,-44548,-44511,-44474,-44437,-44400,-44363,-44326,
        -44289,-44252,-44215,-44178,-44141,-44104,-44067,-44029,
        -43992,-43955,-43918,-43880,-43843,-43806,-43768,-43731,
        -43693,-43656,-43618,-43581,-43543,-43506,-43468,-43430,
        -43393,-43355,-43317,-43280,-43242,-43204,-43166,-43128,
        -43091,-43053,-43015,-42977,-42939,-42901,-42863,-42825,
        -42787,-42749,-42711,-42672,-42634,-42596,-42558,-42520,
        -42481,-42443,-42405,-42366,-42328,-42290,-42251,-42213,
        -42174,-42136,-42097,-42059,-42020,-41982,-41943,-41904,
        -41866,-41827,-41788,-41750,-41711,-41672,-41633,-41595,
        -41556,-41517,-41478,-41439,-41400,-41361,-41322,-41283,
        -41244,-41205,-41166,-41127,-41087,-41048,-41009,-40970,
        -40931,-40891,-40852,-40813,-40773,-40734,-40695,-40655,
        -40616,-40576,-40537,-40497,-40458,-40418,-40379,-40339,
        -40299,-40260,-40220,-40180,-40141,-40101,-40061,-40021,
        -39982,-39942,-39902,-39862,-39822,-39782,-39742,-39702,
        -39662,-39622,-39582,-39542,-39502,-39462,-39422,-39382,
        -39341,-39301,-39261,-39221,-39180,-39140,-39100,-39059,
        -39019,-38979,-38938,-38898,-38857,-38817,-38776,-38736,
        -38695,-38655,-38614,-38573,-38533,-38492,-38451,-38411,
        -38370,-38329,-38288,-38248,-38207,-38166,-38125,-38084,
        -38043,-38002,-37961,-37920,-37879,-37838,-37797,-37756,
        -37715,-37674,-37633,-37592,-37550,-37509,-37468,-37427,
        -37386,-37344,-37303,-37262,-37220,-37179,-37137,-37096,
        -37055,-37013,-36972,-36930,-36889,-36847,-36805,-36764,
        -36722,-36681,-36639,-36597,-36556,-36514,-36472,-36430,
        -36388,-36347,-36305,-36263,-36221,-36179,-36137,-36095,
        -36053,-36011,-35969,-35927,-35885,-35843,-35801,-35759,
        -35717,-35675,-35633,-35590,-35548,-35506,-35464,-35421,
        -35379,-35337,-35294,-35252,-35210,-35167,-35125,-35082,
        -35040,-34997,-34955,-34912,-34870,-34827,-34785,-34742,
        -34699,-34657,-34614,-34571,-34529,-34486,-34443,-34400,
        -34358,-34315,-34272,-34229,-34186,-34143,-34100,-34057,
        -34015,-33972,-33929,-33886,-33843,-33799,-33756,-33713,
        -33670,-33627,-33584,-33541,-33498,-33454,-33411,-33368,
        -33325,-33281,-33238,-33195,-33151,-33108,-33065,-33021,
        -32978,-32934,-32891,-32847,-32804,-32760,-32717,-32673,
        -32630,-32586,-32542,-32499,-32455,-32411,-32368,-32324,
        -32280,-32236,-32193,-32149,-32105,-32061,-32017,-31974,
        -31930,-31886,-31842,-31798,-31754,-31710,-31666,-31622,
        -31578,-31534,-31490,-31446,-31402,-31357,-31313,-31269,
        -31225,-31181,-31136,-31092,-31048,-31004,-30959,-30915,
        -30871,-30826,-30782,-30738,-30693,-30649,-30604,-30560,
        -30515,-30471,-30426,-30382,-30337,-30293,-30248,-30204,
        -30159,-30114,-30070,-30025,-29980,-29936,-29891,-29846,
        -29801,-29757,-29712,-29667,-29622,-29577,-29533,-29488,
        -29443,-29398,-29353,-29308,-29263,-29218,-29173,-29128,
        -29083,-29038,-28993,-28948,-28903,-28858,-28812,-28767,
        -28722,-28677,-28632,-28586,-28541,-28496,-28451,-28405,
        -28360,-28315,-28269,-28224,-28179,-28133,-28088,-28042,
        -27997,-27952,-27906,-27861,-27815,-27770,-27724,-27678,
        -27633,-27587,-27542,-27496,-27450,-27405,-27359,-27313,
        -27268,-27222,-27176,-27131,-27085,-27039,-26993,-26947,
        -26902,-26856,-26810,-26764,-26718,-26672,-26626,-26580,
        -26534,-26488,-26442,-26396,-26350,-26304,-26258,-26212,
        -26166,-26120,-26074,-26028,-25982,-25936,-25889,-25843,
        -25797,-25751,-25705,-25658,-25612,-25566,-25520,-25473,
        -25427,-25381,-25334,-25288,-25241,-25195,-25149,-25102,
        -25056,-25009,-24963,-24916,-24870,-24823,-24777,-24730,
        -24684,-24637,-24591,-24544,-24497,-24451,-24404,-24357,
        -24311,-24264,-24217,-24171,-24124,-24077,-24030,-23984,
        -23937,-23890,-23843,-23796,-23750,-23703,-23656,-23609,
        -23562,-23515,-23468,-23421,-23374,-23327,-23280,-23233,
        -23186,-23139,-23092,-23045,-22998,-22951,-22904,-22857,
        -22810,-22763,-22716,-22668,-22621,-22574,-22527,-22480,
        -22432,-22385,-22338,-22291,-22243,-22196,-22149,-22102,
        -22054,-22007,-21960,-21912,-21865,-21817,-21770,-21723,
        -21675,-21628,-21580,-21533,-21485,-21438,-21390,-21343,
        -21295,-21248,-21200,-21153,-21105,-21057,-21010,-20962,
        -20915,-20867,-20819,-20772,-20724,-20676,-20629,-20581,
        -20533,-20485,-20438,-20390,-20342,-20294,-20246,-20199,
        -20151,-20103,-20055,-20007,-19959,-19912,-19864,-19816,
        -19768,-19720,-19672,-19624,-19576,-19528,-19480,-19432,
        -19384,-19336,-19288,-19240,-19192,-19144,-19096,-19048,
        -19000,-18951,-18903,-18855,-18807,-18759,-18711,-18663,
        -18614,-18566,-18518,-18470,-18421,-18373,-18325,-18277,
        -18228,-18180,-18132,-18084,-18035,-17987,-17939,-17890,
        -17842,-17793,-17745,-17697,-17648,-17600,-17551,-17503,
        -17455,-17406,-17358,-17309,-17261,-17212,-17164,-17115,
        -17067,-17018,-16970,-16921,-16872,-16824,-16775,-16727,
        -16678,-16629,-16581,-16532,-16484,-16435,-16386,-16338,
        -16289,-16240,-16191,-16143,-16094,-16045,-15997,-15948,
        -15899,-15850,-15802,-15753,-15704,-15655,-15606,-15557,
        -15509,-15460,-15411,-15362,-15313,-15264,-15215,-15167,
        -15118,-15069,-15020,-14971,-14922,-14873,-14824,-14775,
        -14726,-14677,-14628,-14579,-14530,-14481,-14432,-14383,
        -14334,-14285,-14236,-14187,-14138,-14089,-14040,-13990,
        -13941,-13892,-13843,-13794,-13745,-13696,-13647,-13597,
        -13548,-13499,-13450,-13401,-13351,-13302,-13253,-13204,
        -13154,-13105,-13056,-13007,-12957,-12908,-12859,-12810,
        -12760,-12711,-12662,-12612,-12563,-12514,-12464,-12415,
        -12366,-12316,-12267,-12217,-12168,-12119,-12069,-12020,
        -11970,-11921,-11872,-11822,-11773,-11723,-11674,-11624,
        -11575,-11525,-11476,-11426,-11377,-11327,-11278,-11228,
        -11179,-11129,-11080,-11030,-10981,-10931,-10882,-10832,
        -10782,-10733,-10683,-10634,-10584,-10534,-10485,-10435,
        -10386,-10336,-10286,-10237,-10187,-10137,-10088,-10038,
        -9988,-9939,-9889,-9839,-9790,-9740,-9690,-9640,
        -9591,-9541,-9491,-9442,-9392,-9342,-9292,-9243,
        -9193,-9143,-9093,-9043,-8994,-8944,-8894,-8844,
        -8794,-8745,-8695,-8645,-8595,-8545,-8496,-8446,
        -8396,-8346,-8296,-8246,-8196,-8147,-8097,-8047,
        -7997,-7947,-7897,-7847,-7797,-7747,-7697,-7648,
        -7598,-7548,-7498,-7448,-7398,-7348,-7298,-7248,
        -7198,-7148,-7098,-7048,-6998,-6948,-6898,-6848,
        -6798,-6748,-6698,-6648,-6598,-6548,-6498,-6448,
        -6398,-6348,-6298,-6248,-6198,-6148,-6098,-6048,
        -5998,-5948,-5898,-5848,-5798,-5747,-5697,-5647,
        -5597,-5547,-5497,-5447,-5397,-5347,-5297,-5247,
        -5197,-5146,-5096,-5046,-4996,-4946,-4896,-4846,
        -4796,-4745,-4695,-4645,-4595,-4545,-4495,-4445,
        -4394,-4344,-4294,-4244,-4194,-4144,-4093,-4043,
        -3993,-3943,-3893,-3843,-3792,-3742,-3692,-3642,
        -3592,-3541,-3491,-3441,-3391,-3341,-3291,-3240,
        -3190,-3140,-3090,-3039,-2989,-2939,-2889,-2839,
        -2788,-2738,-2688,-2638,-2588,-2537,-2487,-2437,
        -2387,-2336,-2286,-2236,-2186,-2135,-2085,-2035,
        -1985,-1934,-1884,-1834,-1784,-1733,-1683,-1633,
        -1583,-1532,-1482,-1432,-1382,-1331,-1281,-1231,
        -1181,-1130,-1080,-1030,-980,-929,-879,-829,
        -779,-728,-678,-628,-578,-527,-477,-427,
        -376,-326,-276,-226,-175,-125,-75,-25    
    };
    
    /* MAES: Java can't use that much direct init data in a single class (finesine alone is well beyond that)
     * so a first workaround is to generate the data procedurally.
     * If this proves to cause accuracy problems, an external data file could be used.
     * Another workaround is to define the int table in its own file and then import or "implement" it.
     */
    
 // Re-use data, is just PI/2 phase shift.
  public static final int[] finecosine=new int[FINEANGLES];

  /* MAES: Java can't use that much direct init data in a single class so a first workaround 
   * s to generate the data procedurally.
   * If this proves to cause accuracy problems, an external data file could be used.
   * Another workaround is to define the int table in its own file and then import 
   * or "implement" it.
   */

  static{
  System.arraycopy(finesine, FINEANGLES/4,finecosine, 0, FINEANGLES);
  }

  private SineCosine(){
      
  }
    
}

package data;

public enum mobjtype_t {
	    MT_PLAYER,
	    MT_POSSESSED,
	    MT_SHOTGUY,
	    MT_VILE,
	    MT_FIRE,
	    MT_UNDEAD,
	    MT_TRACER,
	    MT_SMOKE,
	    MT_FATSO,
	    MT_FATSHOT,
	    MT_CHAINGUY,
	    MT_TROOP,
	    MT_SERGEANT,
	    MT_SHADOWS,
	    MT_HEAD,
	    MT_BRUISER,
	    MT_BRUISERSHOT,
	    MT_KNIGHT,
	    MT_SKULL,
	    MT_SPIDER,
	    MT_BABY,
	    MT_CYBORG,
	    MT_PAIN,
	    MT_WOLFSS,
	    MT_KEEN,
	    MT_BOSSBRAIN,
	    MT_BOSSSPIT,
	    MT_BOSSTARGET,
	    MT_SPAWNSHOT,
	    MT_SPAWNFIRE,
	    MT_BARREL,
	    MT_TROOPSHOT,
	    MT_HEADSHOT,
	    MT_ROCKET,
	    MT_PLASMA,
	    MT_BFG,
	    MT_ARACHPLAZ,
	    MT_PUFF,
	    MT_BLOOD,
	    MT_TFOG,
	    MT_IFOG,
	    MT_TELEPORTMAN,
	    MT_EXTRABFG,
	    MT_MISC0,
	    MT_MISC1,
	    MT_MISC2,
	    MT_MISC3,
	    MT_MISC4,
	    MT_MISC5,
	    MT_MISC6,
	    MT_MISC7,
	    MT_MISC8,
	    MT_MISC9,
	    MT_MISC10,
	    MT_MISC11,
	    MT_MISC12,
	    MT_INV,
	    MT_MISC13,
	    MT_INS,
	    MT_MISC14,
	    MT_MISC15,
	    MT_MISC16,
	    MT_MEGA,
	    MT_CLIP,
	    MT_MISC17,
	    MT_MISC18,
	    MT_MISC19,
	    MT_MISC20,
	    MT_MISC21,
	    MT_MISC22,
	    MT_MISC23,
	    MT_MISC24,
	    MT_MISC25,
	    MT_CHAINGUN,
	    MT_MISC26,
	    MT_MISC27,
	    MT_MISC28,
	    MT_SHOTGUN,
	    MT_SUPERSHOTGUN,
	    MT_MISC29,
	    MT_MISC30,
	    MT_MISC31,
	    MT_MISC32,
	    MT_MISC33,
	    MT_MISC34,
	    MT_MISC35,
	    MT_MISC36,
	    MT_MISC37,
	    MT_MISC38,
	    MT_MISC39,
	    MT_MISC40,
	    MT_MISC41,
	    MT_MISC42,
	    MT_MISC43,
	    MT_MISC44,
	    MT_MISC45,
	    MT_MISC46,
	    MT_MISC47,
	    MT_MISC48,
	    MT_MISC49,
	    MT_MISC50,
	    MT_MISC51,
	    MT_MISC52,
	    MT_MISC53,
	    MT_MISC54,
	    MT_MISC55,
	    MT_MISC56,
	    MT_MISC57,
	    MT_MISC58,
	    MT_MISC59,
	    MT_MISC60,
	    MT_MISC61,
	    MT_MISC62,
	    MT_MISC63,
	    MT_MISC64,
	    MT_MISC65,
	    MT_MISC66,
	    MT_MISC67,
	    MT_MISC68,
	    MT_MISC69,
	    MT_MISC70,
	    MT_MISC71,
	    MT_MISC72,
	    MT_MISC73,
	    MT_MISC74,
	    MT_MISC75,
	    MT_MISC76,
	    MT_MISC77,
	    MT_MISC78,
	    MT_MISC79,
	    MT_MISC80,
	    MT_MISC81,
	    MT_MISC82,
	    MT_MISC83,
	    MT_MISC84,
	    MT_MISC85,
	    MT_MISC86,
	    NUMMOBJTYPES
	    };


package data;

import p.ActionType1;
import p.ActionType2;
import p.ActionTypeSS;
import doom.think_t;
import defines.statenum_t;
import static data.Defines.TIC_MUL;

public class state_t {

	public state_t(){
		
	}
       
        public state_t(spritenum_t sprite, int frame, int tics, think_t action,
            statenum_t nextstate, int misc1, int misc2) {
        this.sprite = sprite;
        this.frame = frame;
        this.tics = tics*TIC_MUL;
        this.action = action;
        this.nextstate = nextstate;
        this.misc1 = misc1;
        this.misc2 = misc2;
        }
        
        public spritenum_t   sprite;
        /** The frame should indicate which one of the frames available in the 
         *  available spritenum should be used. This can also be flagged with
         *  0x8000 indicating bright sprites.
         */
 
        public int          frame;
        public int          tics;
        //TODO: proper implementation of (*action)
        // MAES: was actionp_t... which is typedeffed to think_t anyway,
        // and this is the only place it's invoked explicitly.
        /** OK...this is the most infamous part of Doom to implement in Java.
         *  We can't have proper "function pointers" in java without either losing a LOT
         *  of speed (through reflection) or cluttering syntax and heap significantly
         *  (callback objects, which also need to be aware of context).
         *  Therefore, I decided to implement an "action dispatcher".
         *  This a
         *  
         */
        public think_t         action;
        public ActionType1 acp1;
        public ActionType2 acp2;
        public ActionTypeSS acpss;
        
        public statenum_t            nextstate;
        public int misc1, misc2;
        /** relative index in state array. Needed sometimes. */
        public int id;
		
        
        public String toString(){
            sb.setLength(0);
            sb.append(this.getClass().getName());
            sb.append(" sprite ");
            sb.append(this.sprite.name());
            sb.append(" frame ");
            sb.append(this.frame);

            return sb.toString();
            
        }
        
        protected static StringBuilder sb=new StringBuilder();

        /*@Override
        public void read(DoomFile f)
                throws IOException {
            this.sprite = spritenum_t.values()[f.readLEInt()];
            this.frame = f.readLEInt();
            this.tics = f.readLong();
            this.action = think_t.values()[f.readInt()];
            this.nextstate = statenum_t.values()[f.readInt()];
            this.misc1 = f.readInt();
            this.misc2 = f.readInt();
            
        } */
}

package data;
// Emacs style mode select   -*- C++ -*- 
//-----------------------------------------------------------------------------
//
// $Id: sounds.java,v 1.1 2010/06/30 08:58:51 velktron Exp $
//
// Copyright (C) 1993-1996 by id Software, Inc.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// $Log: sounds.java,v $
// Revision 1.1  2010/06/30 08:58:51  velktron
// Let's see if this stuff will finally commit....
//
//
// Most stuff is still  being worked on. For a good place to start and get an idea of what is being done, I suggest checking out the "testers" package.
//
// Revision 1.1  2010/06/29 11:07:34  velktron
// Release often, release early they say...
//
// Commiting ALL stuff done so far. A lot of stuff is still broken/incomplete, and there's still mixed C code in there. I suggest you load everything up in Eclpise and see what gives from there.
//
// A good place to start is the testers/ directory, where you  can get an idea of how a few of the implemented stuff works.
//
//
// DESCRIPTION:
//	Created by a sound utility.
//	Kept as a sample, DOOM2 sounds.
//
//-----------------------------------------------------------------------------

public class sounds{

//
// Information about all the music
//

public static musicinfo_t[] S_music= 
{
    new musicinfo_t(null),
    new musicinfo_t( "e1m1", 0 ),
    new musicinfo_t( "e1m2", 0 ),
    new musicinfo_t( "e1m3", 0 ),
    new musicinfo_t( "e1m4", 0 ),
    new musicinfo_t( "e1m5", 0 ),
    new musicinfo_t( "e1m6", 0 ),
    new musicinfo_t( "e1m7", 0 ),
    new musicinfo_t( "e1m8", 0 ),
    new musicinfo_t( "e1m9", 0 ),
    new musicinfo_t( "e2m1", 0 ),
    new musicinfo_t( "e2m2", 0 ),
    new musicinfo_t( "e2m3", 0 ),
    new musicinfo_t( "e2m4", 0 ),
    new musicinfo_t( "e2m5", 0 ),
    new musicinfo_t( "e2m6", 0 ),
    new musicinfo_t( "e2m7", 0 ),
    new musicinfo_t( "e2m8", 0 ),
    new musicinfo_t( "e2m9", 0 ),
    new musicinfo_t( "e3m1", 0 ),
    new musicinfo_t( "e3m2", 0 ),
    new musicinfo_t( "e3m3", 0 ),
    new musicinfo_t( "e3m4", 0 ),
    new musicinfo_t( "e3m5", 0 ),
    new musicinfo_t( "e3m6", 0 ),
    new musicinfo_t( "e3m7", 0 ),
    new musicinfo_t( "e3m8", 0 ),
    new musicinfo_t( "e3m9", 0 ),
    new musicinfo_t( "inter", 0 ),
    new musicinfo_t( "intro", 0 ),
    new musicinfo_t( "bunny", 0 ),
    new musicinfo_t( "victor", 0 ),
    new musicinfo_t( "introa", 0 ),
    new musicinfo_t( "runnin", 0 ),
    new musicinfo_t( "stalks", 0 ),
    new musicinfo_t( "countd", 0 ),
    new musicinfo_t( "betwee", 0 ),
    new musicinfo_t( "doom", 0 ),
    new musicinfo_t( "the_da", 0 ),
    new musicinfo_t( "shawn", 0 ),
    new musicinfo_t( "ddtblu", 0 ),
    new musicinfo_t( "in_cit", 0 ),
    new musicinfo_t( "dead", 0 ),
    new musicinfo_t( "stlks2", 0 ),
    new musicinfo_t( "theda2", 0 ),
    new musicinfo_t( "doom2", 0 ),
    new musicinfo_t( "ddtbl2", 0 ),
    new musicinfo_t( "runni2", 0 ),
    new musicinfo_t( "dead2", 0 ),
    new musicinfo_t( "stlks3", 0 ),
    new musicinfo_t( "romero", 0 ),
    new musicinfo_t( "shawn2", 0 ),
    new musicinfo_t( "messag", 0 ),
    new musicinfo_t( "count2", 0 ),
    new musicinfo_t( "ddtbl3", 0 ),
    new musicinfo_t( "ampie", 0 ),
    new musicinfo_t( "theda3", 0 ),
    new musicinfo_t( "adrian", 0 ),
    new musicinfo_t( "messg2", 0 ),
    new musicinfo_t( "romer2", 0 ),
    new musicinfo_t( "tense", 0 ),
    new musicinfo_t( "shawn3", 0 ),
    new musicinfo_t( "openin", 0 ),
    new musicinfo_t( "evil", 0 ),
    new musicinfo_t( "ultima", 0 ),
    new musicinfo_t( "read_m", 0 ),
    new musicinfo_t( "dm2ttl", 0 ),
    new musicinfo_t( "dm2int", 0 ) 
};


//
// Information about all the sfx
//

public static sfxinfo_t nullSfxLink;

public static sfxinfo_t[] S_sfx =
{
  // S_sfx[0] needs to be a dummy for odd reasons.
  new sfxinfo_t( "none", false,  0, -1, -1, 0 ),

  new sfxinfo_t( "pistol", false, 64, -1, -1, 0 ),
  new sfxinfo_t( "shotgn", false, 64,  -1, -1, 0 ),
  new sfxinfo_t( "sgcock", false, 64,  -1, -1, 0 ),
  new sfxinfo_t( "dshtgn", false, 64,  -1, -1, 0 ),
  new sfxinfo_t( "dbopn", false, 64,  -1, -1, 0 ),
  new sfxinfo_t( "dbcls", false, 64,  -1, -1, 0 ),
  new sfxinfo_t( "dbload", false, 64,  -1, -1, 0 ),
  new sfxinfo_t( "plasma", false, 64,  -1, -1, 0 ),
  new sfxinfo_t( "bfg", false, 64,  -1, -1, 0 ),
  new sfxinfo_t( "sawup", false, 64,  -1, -1, 0 ),
  new sfxinfo_t( "sawidl", false, 118,  -1, -1, 0 ),
  new sfxinfo_t( "sawful", false, 64,  -1, -1, 0 ),
  new sfxinfo_t( "sawhit", false, 64,  -1, -1, 0 ),
  new sfxinfo_t( "rlaunc", false, 64,  -1, -1, 0 ),
  new sfxinfo_t( "rxplod", false, 70,  -1, -1, 0 ),
  new sfxinfo_t( "firsht", false, 70,  -1, -1, 0 ),
  new sfxinfo_t( "firxpl", false, 70,  -1, -1, 0 ),
  new sfxinfo_t( "pstart", false, 100,  -1, -1, 0 ),
  new sfxinfo_t( "pstop", false, 100,  -1, -1, 0 ),
  new sfxinfo_t( "doropn", false, 100,  -1, -1, 0 ),
  new sfxinfo_t( "dorcls", false, 100,  -1, -1, 0 ),
  new sfxinfo_t( "stnmov", false, 119,  -1, -1, 0 ),
  new sfxinfo_t( "swtchn", false, 78,  -1, -1, 0 ),
  new sfxinfo_t( "swtchx", false, 78,  -1, -1, 0 ),
  new sfxinfo_t( "plpain", false, 96,  -1, -1, 0 ),
  new sfxinfo_t( "dmpain", false, 96,  -1, -1, 0 ),
  new sfxinfo_t( "popain", false, 96,  -1, -1, 0 ),
  new sfxinfo_t( "vipain", false, 96,  -1, -1, 0 ),
  new sfxinfo_t( "mnpain", false, 96,  -1, -1, 0 ),
  new sfxinfo_t( "pepain", false, 96,  -1, -1, 0 ),
  new sfxinfo_t( "slop", false, 78,  -1, -1, 0 ),
  new sfxinfo_t( "itemup", true, 78,  -1, -1, 0 ),
  new sfxinfo_t( "wpnup", true, 78,  -1, -1, 0 ),
  new sfxinfo_t( "oof", false, 96,  -1, -1, 0 ),
  new sfxinfo_t( "telept", false, 32,  -1, -1, 0 ),
  new sfxinfo_t( "posit1", true, 98,  -1, -1, 0 ),
  new sfxinfo_t( "posit2", true, 98,  -1, -1, 0 ),
  new sfxinfo_t( "posit3", true, 98,  -1, -1, 0 ),
  new sfxinfo_t( "bgsit1", true, 98,  -1, -1, 0 ),
  new sfxinfo_t( "bgsit2", true, 98,  -1, -1, 0 ),
  new sfxinfo_t( "sgtsit", true, 98,  -1, -1, 0 ),
  new sfxinfo_t( "cacsit", true, 98,  -1, -1, 0 ),
  new sfxinfo_t( "brssit", true, 94,  -1, -1, 0 ),
  new sfxinfo_t( "cybsit", true, 92,  -1, -1, 0 ),
  new sfxinfo_t( "spisit", true, 90,  -1, -1, 0 ),
  new sfxinfo_t( "bspsit", true, 90,  -1, -1, 0 ),
  new sfxinfo_t( "kntsit", true, 90,  -1, -1, 0 ),
  new sfxinfo_t( "vilsit", true, 90,  -1, -1, 0 ),
  new sfxinfo_t( "mansit", true, 90,  -1, -1, 0 ),
  new sfxinfo_t( "pesit", true, 90,  -1, -1, 0 ),
  new sfxinfo_t( "sklatk", false, 70,  -1, -1, 0 ),
  new sfxinfo_t( "sgtatk", false, 70,  -1, -1, 0 ),
  new sfxinfo_t( "skepch", false, 70,  -1, -1, 0 ),
  new sfxinfo_t( "vilatk", false, 70,  -1, -1, 0 ),
  new sfxinfo_t( "claw", false, 70,  -1, -1, 0 ),
  new sfxinfo_t( "skeswg", false, 70,  -1, -1, 0 ),
  new sfxinfo_t( "pldeth", false, 32,  -1, -1, 0 ),
  new sfxinfo_t( "pdiehi", false, 32,  -1, -1, 0 ),
  new sfxinfo_t( "podth1", false, 70,  -1, -1, 0 ),
  new sfxinfo_t( "podth2", false, 70,  -1, -1, 0 ),
  new sfxinfo_t( "podth3", false, 70,  -1, -1, 0 ),
  new sfxinfo_t( "bgdth1", false, 70,  -1, -1, 0 ),
  new sfxinfo_t( "bgdth2", false, 70,  -1, -1, 0 ),
  new sfxinfo_t( "sgtdth", false, 70,  -1, -1, 0 ),
  new sfxinfo_t( "cacdth", false, 70,  -1, -1, 0 ),
  new sfxinfo_t( "skldth", false, 70,  -1, -1, 0 ),
  new sfxinfo_t( "brsdth", false, 32,  -1, -1, 0 ),
  new sfxinfo_t( "cybdth", false, 32,  -1, -1, 0 ),
  new sfxinfo_t( "spidth", false, 32,  -1, -1, 0 ),
  new sfxinfo_t( "bspdth", false, 32,  -1, -1, 0 ),
  new sfxinfo_t( "vildth", false, 32,  -1, -1, 0 ),
  new sfxinfo_t( "kntdth", false, 32,  -1, -1, 0 ),
  new sfxinfo_t( "pedth", false, 32,  -1, -1, 0 ),
  new sfxinfo_t( "skedth", false, 32,  -1, -1, 0 ),
  new sfxinfo_t( "posact", true, 120,  -1, -1, 0 ),
  new sfxinfo_t( "bgact", true, 120,  -1, -1, 0 ),
  new sfxinfo_t( "dmact", true, 120,  -1, -1, 0 ),
  new sfxinfo_t( "bspact", true, 100,  -1, -1, 0 ),
  new sfxinfo_t( "bspwlk", true, 100,  -1, -1, 0 ),
  new sfxinfo_t( "vilact", true, 100,  -1, -1, 0 ),
  new sfxinfo_t( "noway", false, 78,  -1, -1, 0 ),
  new sfxinfo_t( "barexp", false, 60,  -1, -1, 0 ),
  new sfxinfo_t( "punch", false, 64,  -1, -1, 0 ),
  new sfxinfo_t( "hoof", false, 70,  -1, -1, 0 ),
  new sfxinfo_t( "metal", false, 70,  -1, -1, 0 ),
  // MAES: here C referenced a field before it was defined. 
  // We'll make do by defining a new "linked" boolean field, and
  // handling special cases in a separate initializer.
  new sfxinfo_t( "chgun", false, 64, true, 150, 0, 0 ),
  new sfxinfo_t( "tink", false, 60,  -1, -1, 0 ),
  new sfxinfo_t( "bdopn", false, 100,  -1, -1, 0 ),
  new sfxinfo_t( "bdcls", false, 100,  -1, -1, 0 ),
  new sfxinfo_t( "itmbk", false, 100,  -1, -1, 0 ),
  new sfxinfo_t( "flame", false, 32,  -1, -1, 0 ),
  new sfxinfo_t( "flamst", false, 32,  -1, -1, 0 ),
  new sfxinfo_t( "getpow", false, 60,  -1, -1, 0 ),
  new sfxinfo_t( "bospit", false, 70,  -1, -1, 0 ),
  new sfxinfo_t( "boscub", false, 70,  -1, -1, 0 ),
  new sfxinfo_t( "bossit", false, 70,  -1, -1, 0 ),
  new sfxinfo_t( "bospn", false, 70,  -1, -1, 0 ),
  new sfxinfo_t( "bosdth", false, 70,  -1, -1, 0 ),
  new sfxinfo_t( "manatk", false, 70,  -1, -1, 0 ),
  new sfxinfo_t( "mandth", false, 70,  -1, -1, 0 ),
  new sfxinfo_t( "sssit", false, 70,  -1, -1, 0 ),
  new sfxinfo_t( "ssdth", false, 70,  -1, -1, 0 ),
  new sfxinfo_t( "keenpn", false, 70,  -1, -1, 0 ),
  new sfxinfo_t( "keendt", false, 70,  -1, -1, 0 ),
  new sfxinfo_t( "skeact", false, 70,  -1, -1, 0 ),
  new sfxinfo_t( "skesit", false, 70,  -1, -1, 0 ),
  new sfxinfo_t( "skeatk", false, 70,  -1, -1, 0 ),
  new sfxinfo_t( "radio", false, 60, -1, -1, 0 )
};

/** MAES: This method is here to handle exceptions in definitions of static sfx.
 *  Only the chaingun sound needs to be cross-linked to the pistol sound, but in 
 *  Java you can't do that until the array is actually created. So remember to run
 *  this explicitly, and add any further rules you want!
 *  
 */

public static void init(){
    for (int i=0;i<S_sfx.length;i++){
        if (S_sfx[i].linked){
            // MAES: Rule for chgun
            if (S_sfx[i].name.compareToIgnoreCase("chgun")==0) {
                S_sfx[i].link=S_sfx[sfxenum_t.sfx_pistol.ordinal()];
            }
        }
    }
    
}

public static enum  musicenum_t
{
    mus_None,
    mus_e1m1,
    mus_e1m2,
    mus_e1m3,
    mus_e1m4,
    mus_e1m5,
    mus_e1m6,
    mus_e1m7,
    mus_e1m8,
    mus_e1m9,
    mus_e2m1,
    mus_e2m2,
    mus_e2m3,
    mus_e2m4,
    mus_e2m5,
    mus_e2m6,
    mus_e2m7,
    mus_e2m8,
    mus_e2m9,
    mus_e3m1,
    mus_e3m2,
    mus_e3m3,
    mus_e3m4,
    mus_e3m5,
    mus_e3m6,
    mus_e3m7,
    mus_e3m8,
    mus_e3m9,
    mus_inter,
    mus_intro,
    mus_bunny,
    mus_victor,
    mus_introa,
    mus_runnin,
    mus_stalks,
    mus_countd,
    mus_betwee,
    mus_doom,
    mus_the_da,
    mus_shawn,
    mus_ddtblu,
    mus_in_cit,
    mus_dead,
    mus_stlks2,
    mus_theda2,
    mus_doom2,
    mus_ddtbl2,
    mus_runni2,
    mus_dead2,
    mus_stlks3,
    mus_romero,
    mus_shawn2,
    mus_messag,
    mus_count2,
    mus_ddtbl3,
    mus_ampie,
    mus_theda3,
    mus_adrian,
    mus_messg2,
    mus_romer2,
    mus_tense,
    mus_shawn3,
    mus_openin,
    mus_evil,
    mus_ultima,
    mus_read_m,
    mus_dm2ttl,
    mus_dm2int,
    NUMMUSIC
};

public static enum  sfxenum_t
{
    sfx_None,
    sfx_pistol,
    sfx_shotgn,
    sfx_sgcock,
    sfx_dshtgn,
    sfx_dbopn,
    sfx_dbcls,
    sfx_dbload,
    sfx_plasma,
    sfx_bfg,
    sfx_sawup,
    sfx_sawidl,
    sfx_sawful,
    sfx_sawhit,
    sfx_rlaunc,
    sfx_rxplod,
    sfx_firsht,
    sfx_firxpl,
    sfx_pstart,
    sfx_pstop,
    sfx_doropn,
    sfx_dorcls,
    sfx_stnmov,
    sfx_swtchn,
    sfx_swtchx,
    sfx_plpain,
    sfx_dmpain,
    sfx_popain,
    sfx_vipain,
    sfx_mnpain,
    sfx_pepain,
    sfx_slop,
    sfx_itemup,
    sfx_wpnup,
    sfx_oof,
    sfx_telept,
    sfx_posit1,
    sfx_posit2,
    sfx_posit3,
    sfx_bgsit1,
    sfx_bgsit2,
    sfx_sgtsit,
    sfx_cacsit,
    sfx_brssit,
    sfx_cybsit,
    sfx_spisit,
    sfx_bspsit,
    sfx_kntsit,
    sfx_vilsit,
    sfx_mansit,
    sfx_pesit,
    sfx_sklatk,
    sfx_sgtatk,
    sfx_skepch,
    sfx_vilatk,
    sfx_claw,
    sfx_skeswg,
    sfx_pldeth,
    sfx_pdiehi,
    sfx_podth1,
    sfx_podth2,
    sfx_podth3,
    sfx_bgdth1,
    sfx_bgdth2,
    sfx_sgtdth,
    sfx_cacdth,
    sfx_skldth,
    sfx_brsdth,
    sfx_cybdth,
    sfx_spidth,
    sfx_bspdth,
    sfx_vildth,
    sfx_kntdth,
    sfx_pedth,
    sfx_skedth,
    sfx_posact,
    sfx_bgact,
    sfx_dmact,
    sfx_bspact,
    sfx_bspwlk,
    sfx_vilact,
    sfx_noway,
    sfx_barexp,
    sfx_punch,
    sfx_hoof,
    sfx_metal,
    sfx_chgun,
    sfx_tink,
    sfx_bdopn,
    sfx_bdcls,
    sfx_itmbk,
    sfx_flame,
    sfx_flamst,
    sfx_getpow,
    sfx_bospit,
    sfx_boscub,
    sfx_bossit,
    sfx_bospn,
    sfx_bosdth,
    sfx_manatk,
    sfx_mandth,
    sfx_sssit,
    sfx_ssdth,
    sfx_keenpn,
    sfx_keendt,
    sfx_skeact,
    sfx_skesit,
    sfx_skeatk,
    sfx_radio,
    NUMSFX
};



}


package data;

import static data.Tables.FINEANGLES;
import static data.Tables.SLOPERANGE;
import static data.Tables.tantoangle;
import static m.fixed_t.*;

// Emacs style mode select   -*- C++ -*- 
//-----------------------------------------------------------------------------
//
// $Id: Tables.java,v 1.22 2011/05/06 09:21:59 velktron Exp $
//
// Copyright (C) 1993-1996 by id Software, Inc.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// $Log: Tables.java,v $
// Revision 1.22  2011/05/06 09:21:59  velktron
// Cleaned up and reorganized common renderer code.
//
// Revision 1.21  2011/02/11 00:11:13  velktron
// A MUCH needed update to v1.3.
//
// Revision 1.20  2010/12/20 17:15:08  velktron
// Made the renderer more OO -> TextureManager and other changes as well.
//
// Revision 1.19  2010/12/10 17:38:56  velktron
// pspritescale fixed, weapon actions won't crash (but not work either).
//
// Revision 1.18  2010/11/25 20:12:44  velktron
// Fixed blockmap bug and viewangletox overflow bug.
//
// Revision 1.17  2010/11/22 01:17:16  velktron
// Fixed blockmap (for the most part), some actions implemented and functional, ambient animation/lighting functional.
//
// Revision 1.16  2010/11/17 23:55:06  velktron
// Kind of playable/controllable.
//
// Revision 1.15  2010/11/15 17:15:54  velktron
// Fixed masked columns rendering, introduced unrolled span and column functions from Boom (thanks, Lee Killough :-)
//
// Revision 1.14  2010/11/14 20:00:21  velktron
// Bleeding floor bug fixed!
//
// Revision 1.13  2010/11/12 13:37:25  velktron
// Rationalized the LUT system - now it's 100% procedurally generated.
//
// Revision 1.12  2010/11/03 16:48:04  velktron
// "Bling" view angles fixed (perhaps related to the "bleeding line bug"?)
//
// Revision 1.11  2010/10/14 18:37:14  velktron
// Rendering kinda works. Wow.
//
// Revision 1.10  2010/10/08 16:55:50  velktron
// Duh
//
// Revision 1.9  2010/10/01 16:47:51  velktron
// Fixed tab interception.
//
// Revision 1.8  2010/09/27 15:07:44  velktron
// meh
//
// Revision 1.7  2010/09/27 02:27:29  velktron
// BEASTLY update
//
// Revision 1.6  2010/09/22 16:40:02  velktron
// MASSIVE changes in the status passing model.
// DoomMain and DoomGame unified.
// Doomstat merged into DoomMain (now status and game functions are one).
//
// Most of DoomMain implemented. Possible to attempt a "classic type" start but will stop when reading sprites.
//
// Revision 1.5  2010/09/21 15:53:37  velktron
// Split the Map ...somewhat...
//
// Revision 1.4  2010/09/16 00:16:27  velktron
// Velvet FM 96.8
//
// Revision 1.3  2010/09/15 16:17:38  velktron
// Arithmetic
//
// Revision 1.2  2010/09/09 16:09:09  velktron
// Yer more enhancements to the display system...
//
// Revision 1.1  2010/07/05 16:18:40  velktron
// YOU DON'T WANNA KNOW
//
// Revision 1.1  2010/06/30 08:58:51  velktron
// Let's see if this stuff will finally commit....
//
//
// Most stuff is still  being worked on. For a good place to start and get an idea of what is being done, I suggest checking out the "testers" package.
//
// Revision 1.1  2010/06/29 11:07:34  velktron
// Release often, release early they say...
//
// Commiting ALL stuff done so far. A lot of stuff is still broken/incomplete, and there's still mixed C code in there. I suggest you load everything up in Eclpise and see what gives from there.
//
// A good place to start is the testers/ directory, where you  can get an idea of how a few of the implemented stuff works.
//
//
// DESCRIPTION:
//	Lookup tables.
//	Do not try to look them up :-).
//	In the order of appearance: 
//
//	int finetangent[4096]	- Tangens LUT.
//	 Should work with BAM fairly well (12 of 16bit,
//      effectively, by shifting).
//
//	int finesine[10240]		- Sine lookup.
//	 Guess what, serves as cosine, too.
//	 Remarkable thing is, how to use BAMs with this?
//
//	int tantoangle[2049]	- ArcTan LUT,
//	  maps tan(angle) to angle fast. Gotta search.
//	
//    
//-----------------------------------------------------------------------------

public final class Tables {

    public static final String rcsid="$Id:";

   public static final double PI =             3.141592657;

   /** Normally set to 12, and this sets the value of other constants too.
    *  Howevever changing it will also distort the view, resulting in a 
    *  nightmare-like vision. There are some practical minimum and 
    *  maximums as well.
    *  
    * 
    */
   
  public static final int BITSPRECISION = 12;
  public static final int FINEANGLES  =    2<<BITSPRECISION;
  public static final int FINETANS = FINEANGLES/2; // 4096 for normal precision.
  public static final int QUARTERMARK = 2<<BITSPRECISION-2;
  public static final int FINEMASK =       (FINEANGLES-1);
  /** Mod long angle_t's with this value to cut off rollover */
  public static final long ANGLEMODULE = 0x100000000L;
  
  /** AND with this to remove unwanted sign extensions */
  public static final long BITS32 = 0x00000000FFFFFFFFL;
  
  /** Sign elimination */
  public static final int BITS31 = 0x7FFFFFFF;

  
  // Maes: we have to procedurally generate finesine/finecosine, else we run into a Java static limit.
  // Either that, or I split the files. Guess what I did.
 // public static int PRECISION = 10240 ;

  /** 0x100000000 to 0x2000 */
  public static final int ANGLETOFINESHIFT =   31-BITSPRECISION;
    
/* Binary Angle Measurement.
 * Some maths: their definition means that a range of 2pi is actually
 * mapped to 2^32 values!!! But the lookup tables are only 8K (2^13)
 * long (for sine/cosine), which means that we're 19 bits too precise
 * -> ergo, >>ANGLETOFINESHIFT must be applied.
 * 
 * Also, the original angle_t type was "unsigned int", so we should be
 * using longs here. However, as BAM is used only after shifting, so 
 * using ints doesn't cause a problem for LUT access.
 *  
 * However, some problems may arise with comparisons and ordinary arithmetic: 
 * e.g. ANG270 is supposed to be larger not only than ANG180, but also from 
 * ANG45, which does not hold true if those constants were stored as ints.
 * 
 * As a rule of thumb, whenever you need to store JUST a BAM index, then 
 * ints are ok (actually, you'll have to cast down to int anyway).
 * 
 * Whenever you need to add or compare angles directly however, you need 
 * longs. Furthermore, you must account for possible rollovers by modding
 * with 0x100000000 or else long ints will store angles exceeding 360 degrees!
 * Under no circumstances the value actually stored in the "long angles" should
 * exceed 0xFFFFFFFF.
 * 
 * An example: comparing any two long angles directly is OK, provided they were
 * constructed correctly.
 * 
 * Adding, subtracting, multiplying etc. with two or more angles however requires
 * rollover compensation (e.g. result=(a+b+c) is wrong, result=(a+b+c)%0xFFFFFFFF
 * is correct and will produce an angle you can "trust".
 * 
 * 
 */
  
  /** Doom angle constants. */
  
public static final long ANG45 =  0x20000000L,
                        ANG90  =     0x40000000L,
                        ANG180 =     0x80000000L,
                        ANG270 =     0xc0000000L;

public static final int SLOPERANGE =    (2<<(BITSPRECISION-2)); // Normally 2048.
public static final int SLOPEBITS  =     BITSPRECISION-1;
public static final int DBITS=FRACBITS-SLOPEBITS;

//  typedef unsigned angle_t;


  // Effective size is 2049;
  // The +1 size is to handle the case when x==y
  //  without additional checking.
  //extern angle_t      tantoangle[SLOPERANGE+1];

/**
 * Original size was 10240, but includes 5PI/4 periods. We can get away with
 * ints on this one because of the small range. MAES: WTF? -64 ~ 64K
 * range... so 17-bit accuracy? heh.
 */

public static final int[] finesine=new int[FINEANGLES+QUARTERMARK];
public static final int[] finecosine=new int[FINEANGLES];

/** Any denominator smaller than 512 will result in 
 *  maximum slope (45 degrees, or an index into tantoangle of 2048)
 *  The original method used unsigned args. So if this returns NEGATIVES
 *  in any way, it means you fucked up. Another "consistency" for Doom.
 *  Even though it was called upon fixed_t signed numbers.
 * 
 */
public static final int SlopeDiv ( long	num, long den)
{
    int 	ans;
    
    if (den < 512)
	return SLOPERANGE;

    ans = (int) ((num<<3)/(den>>>8));

    return ans <= SLOPERANGE ? ans : SLOPERANGE;
}

/** Finetangent table. It only has 4096 values corresponding roughly
 * to -90/+90 angles, with values between are -/+ 2607 for "infinity".
 * 
 * Since in vanilla accesses to the table can overflow way beyond 4096
 * indexes, the access index must be clipped to 4K tops via an accessor,
 * or, in order to simulate some aspects of vanilla overflowing, replicate
 * 4K of finesine's values AFTER the 4K index. This removes the need
 * for access checking, at the cost of some extra memory. It also allows
 * a small degree of "vanilla like" compatibility.
 * 
 * 
 */

public final static int[] finetangent=new int[2*FINETANS];


// MAES: original range 2049
// This obviously 
// Range goes from 0x00000000 to 0x20000000, so in theory plain ints should be enough...

/** This maps a value 0-2048 to a BAM unsigned integer angle, ranging from 0x0 to 0x2000000:
 * 
 * In practice, this means there are only tangent values for angles up to 45 degrees. 
 *  
 * These values are valid BAM measurements in the first quadrant
 * 
 * 
 */

public static final int[] tantoangle=new int[SLOPERANGE+1];

/** Use this to get a value from the finesine table. It will be automatically shifte, 
 *  Equivalent to finesine[angle>>>ANGLETOFINESHIFT]
 * 
 * @param angle in BAM units
 * @return
 */
public static final int finesine(int angle){
    return finesine[angle>>>ANGLETOFINESHIFT];
}

/** Use this to get a value from the finesine table using a long argument.
 * It will automatically shift, apply rollover module and cast.
 * 
 * Equivalent to finesine[(int) ((angle>>ANGLETOFINESHIFT)%ANGLEMODULE)];
 * 
 * @param angle in BAM units
 * @return
 */
public static final int finesine(long angle){
    return finesine[(int) ((angle&BITS32)>>>ANGLETOFINESHIFT)];
}

/** Use this to get a value from the finecosine table. It will be automatically shifted, 
 * Equivalent to finecosine[angle>>>ANGLETOFINESHIFT]
 * @param angle in BAM units
 * @return
 */
public static final int finecosine(int angle){
    return finecosine[angle>>>ANGLETOFINESHIFT];
}

/** Use this to get a value from the finecosine table. 
 * It will automatically shift, apply rollover module and cast.
 *  
 * Equivalent to finecosine[(int) ((angle&BITS32)>>>ANGLETOFINESHIFT)]
 * @param angle in BAM units
 * @return
 */
public static final int finecosine(long angle){
    return finecosine[(int) ((angle&BITS32)>>>ANGLETOFINESHIFT)];
}

/** Compare BAM angles in 32-bit format 
 *  "Greater or Equal" bam0>bam1
 * */

public static final boolean GE(int bam0, int bam1){
    // Handle easy case.
    if (bam0==bam1) return true;
    
    // bam0 is greater than 180 degrees.
    if (bam0<0 && bam1>=0) return true;
    // bam1 is greater than 180 degrees.
    if (bam0>=0 && bam1<0) return false;
    
    // Both "greater than 180", No other way to compare.
    bam0&=BITS31;
    bam1&=BITS31;        
    return bam0>bam1;
}

public static final boolean GT(int bam0, int bam1){       
    // bam0 is greater than 180 degrees.
    if (bam0<0 && bam1>=0) return true;
    // bam1 is greater than 180 degrees.
    if (bam0>=0 && bam1<0) return false;
    
    // Both "greater than 180", No other way to compare.
    bam0&=BITS31;
    bam1&=BITS31;        
    return bam0>bam1;
}

public static final int BAMDiv(int bam0, int bam1){       
    // bam0 is greater than 180 degrees.
    if (bam0>=0) return bam0/bam1;
    // bam0 is greater than 180 degrees.
    // We have to make is so that ANG270 0xC0000000 becomes ANG135, aka 60000000
    if (bam1>=0)
    return (int) ((long)(0x0FFFFFFFFL&bam0)/bam1);
    
    return (int) ((long)(0x0FFFFFFFFL&bam0)/(0x0FFFFFFFFL&bam1));
}

/** Converts a long angle to a BAM LUT-ready angle (13 bits, between 0-8191). 
 *  Cuts away rollover. 
 *  
 * @param angle
 * @return
 */

public static final int toBAMIndex(long angle){
    return (int) ((angle&BITS32)>>>ANGLETOFINESHIFT);
}

/** Converts a long angle to a TAN BAM  LUT-ready angle (12 bits, between 0-4195). 
 *  Cuts away rollover. 
 *  
 * @param angle
 * @return
 */

public static final int toFineTanIndex(long angle){
    return (int) ((angle&BITS31)>>>ANGLETOFINESHIFT);
}

/** Converts an 32-bit int angle to a BAM LUT-ready angle (13 bits, between 0-8192). 
 * 
 * @param angle
 * @return
 */

public static final int toBAMIndex(int angle){
    return angle>>>ANGLETOFINESHIFT;
}

/** Add two long angles and correct for overflow */

public static final long addAngles(long a, long b) {
    return ((a+b)&BITS32);
}

/** MAES: I brought this function "back from the dead" since
 *  Java has some pretty low static limits for statically defined LUTs.
 *  In order to keep the codebase clutter and static allocation to a minimum,
 *  I decided to procedurally generate the tables during runtime, 
 *  using the original functions.
 *    
 *  The code has been thoroughly checked in both Sun's JDK and GCC and was
 *  found to, indeed, produce the same values found in the finesine/finecosine
 *  and finetangent tables, at least on Intel.
 *  
 *  The "tantoangle" table is also generated procedurally, but since there 
 *  was no "dead code" to build upon, it was recreated through reverse
 *  engineering and also found to be 100% faithful to the original data.  
 * 
 * 
 */

public static void InitTables(){
    int     i;
    float   a;
    float   fv;
    int     t;
    
    // viewangle tangent table
    for (i=0 ; i<FINEANGLES/2 ; i++)
    {
    a = (float)((i-FINEANGLES/4+0.5)*PI*2)/FINEANGLES;
    fv = (float)(FRACUNIT*Math.tan (a));
    t = (int)fv;
    finetangent[i] = t;
    } 
    
    // finesine table
    for (i=0 ; i<FINEANGLES+QUARTERMARK ; i++)
    {
    // OPTIMIZE: mirror...
    a = (float)((i+0.5)*PI*2)/FINEANGLES;
    t = (int)(FRACUNIT*Math.sin (a));
    finesine[i] = t;
    if (i>=QUARTERMARK){
        finecosine[i-QUARTERMARK] = t;
        }
    }
    
    // HACK: replicate part of finesine after finetangent, to
    // simulate overflow behavior and remove need for capping
    // indexes
    // viewangle tangent table
    for (i=FINEANGLES/2 ; i<FINEANGLES ; i++)
    {
    finetangent[i] = finesine[i-FINEANGLES/2];
    } 
    
    /* tantoangle table
     * There was actually no dead code for that one, so this is a close recreation.
     * Since there are 2049 values, and the maximum angle considered is 536870912 (0x20000000)
     * which is 45 degrees in BAM, we have to fill in the atan for values up to 45 degrees.
     * Since the argument is a slope ranging from 0...2048, we have 2049 equally spaced (?)
     *  values, with 2048 being being the unitary slope (0x10000 in fixed_t). That value is only
     *  accessed in special cases (overflow) so we only need to consider 0..2047 aka 11 bits.
     *  So: we take "minislopes" 0-2048, we blow them up to a full fixed_t unit with <<5.
     *  We make this into a float (?), then use trigonometric ATAN, and then go to BAM.
     *  
     *  Any questions?
     *  
     */    
    
    /* This is the recreated code 
    for (i=0 ; i<SLOPERANGE+1 ; i++)
    {
    
    a=(float)((i<<DBITS)/65536.0);
    t=(int)((float)(2*Math.atan(a)/PI)*0x40000000); 
    tantoangle[i] = t;
    } 
    */
    
    // This is the original R_InitPointToAngle code that created this table.
    for (i=0 ; i<=SLOPERANGE ; i++)
    {
 a = (float) (Math.atan( (double)i/SLOPERANGE )/(3.141592657*2));
 t = (int) (0xffffffffL*a);
 tantoangle[i] = (int) t;
    }
    
    }

private Tables(){
    
}

}


package data;

import java.io.DataOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

import w.CacheableDoomObject;
import w.DoomBuffer;
import w.IPackableDoomObject;
import w.IWritableDoomObject;

/**
 * A SideDef, defining the visual appearance of a wall, by setting textures and
 * offsets. ON-DISK.
 */

public class mapsidedef_t implements CacheableDoomObject,IWritableDoomObject,IPackableDoomObject{

    public mapsidedef_t() {

    }

    public short textureoffset;

    public short rowoffset;

    // 8-char strings.
    public String toptexture;

    public String bottomtexture;

    public String midtexture;

    /** Front sector, towards viewer. */
    public short sector;

    public static int sizeOf() {
        return 30;
    }

    @Override
    public void unpack(ByteBuffer buf)
            throws IOException {
        buf.order(ByteOrder.LITTLE_ENDIAN);
        this.textureoffset = buf.getShort();
        this.rowoffset = buf.getShort();
        this.toptexture=DoomBuffer.getNullTerminatedString(buf,8).toUpperCase();
        this.bottomtexture=DoomBuffer.getNullTerminatedString(buf,8).toUpperCase();
        this.midtexture=DoomBuffer.getNullTerminatedString(buf,8).toUpperCase();
        this.sector = buf.getShort();
        
    }

	@Override
	public void pack(ByteBuffer buf) throws IOException {
	    buf.order(ByteOrder.LITTLE_ENDIAN);
	    buf.putShort(textureoffset);
	    buf.putShort(rowoffset);
	    DoomBuffer.putNullTerminatedString(buf,toptexture,8);
	    DoomBuffer.putNullTerminatedString(buf,bottomtexture,8);
	    DoomBuffer.putNullTerminatedString(buf,midtexture,8);
	    buf.putShort(sector);		
	}

	@Override
	public void write(DataOutputStream dos) throws IOException {
        // More efficient, avoids duplicating code and
        // handles little endian better.
        iobuffer.position(0);
        iobuffer.order(ByteOrder.LITTLE_ENDIAN);
        this.pack(iobuffer);
        dos.write(iobuffer.array());				
	}
	
	private static ByteBuffer iobuffer=ByteBuffer.allocate(mapsidedef_t.sizeOf());
}

package i;

import doom.ticcmd_t;

public interface IDoomSystem {

	public void AllocLow(int length);

	public void BeginRead();

	public void EndRead();

	public void WaitVBL(int count);

	public byte[] ZoneBase(int size);

	public int GetHeapSize();

	public void Tactile(int on, int off, int total);

	public void Quit();

	public ticcmd_t BaseTiccmd();

	public void Error(String error, Object ... args);

	void Error(String error);
	
	void Init();
	
	/** Generate a blocking alert with the intention of continuing or aborting
	 * a certain game-altering action. E.g. loading PWADs, or upon critical 
	 * level loading failures. This can be either a popup panel or console 
	 * message.
	 *  
	 * @param cause Provide a clear string explaining why the alert was generated
	 * @return true if we should continue, false if an alternate action should be taken.
	 */
	boolean GenerateAlert(String title,String cause);


}

package i;

import doom.DoomStatus;
import rr.patch_t;
import v.DoomVideoRenderer;
import v.IVideoScale;
import w.IWadLoader;

public class DiskDrawer implements IDiskDrawer,DoomStatusAware {

	private patch_t disk;
	private IWadLoader W;
	private DoomVideoRenderer<?,?> V;
	private IVideoScale VS;
	private int timer=0;
	private String diskname;
	
	public static final String STDISK="STDISK";
	public static final String STCDROM="STCDROM";
	
	public DiskDrawer(DoomStatus<?,?> DM,String icon){		
		this.updateStatus(DM);
		this.diskname=icon;
	}

	@Override
	public void Init(){
		this.disk=W.CachePatchName(diskname);
	}
	
	@Override
	public void Drawer() {
		if (timer>0){
			if (timer%2==0)
		V.DrawScaledPatch(304,184,DoomVideoRenderer.SCREEN_FG,VS, disk);
		}
		if (timer>=0)
			timer--;
	}

	@Override
	public void updateStatus(DoomStatus<?,?> DC) {
		this.W=DC.W;
		this.V=DC.V;		
	    }

	@Override
	public void setVideoScale(IVideoScale vs) {
		this.VS = vs;
	}

	@Override
	public void initScaling() {

	}

	@Override
	public void setReading(int reading) {
		timer=reading;
	}

	@Override
	public boolean isReading() {
		return timer>0;
	}

	@Override
	public boolean justDoneReading() {
		return timer==0;
	}
	
}

// Emacs style mode select   -*- C++ -*- 
//-----------------------------------------------------------------------------
//
// $Id: system.java,v 1.5 2011/02/11 00:11:13 velktron Exp $
//
// Copyright (C) 1993-1996 by id Software, Inc.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// $Log: system.java,v $
// Revision 1.5  2011/02/11 00:11:13  velktron
// A MUCH needed update to v1.3.
//
// Revision 1.1  2010/06/30 08:58:50  velktron
// Let's see if this stuff will finally commit....
//
//
// Most stuff is still  being worked on. For a good place to start and get an idea of what is being done, I suggest checking out the "testers" package.
//
// Revision 1.1  2010/06/29 11:07:34  velktron
// Release often, release early they say...
//
// Commiting ALL stuff done so far. A lot of stuff is still broken/incomplete, and there's still mixed C code in there. I suggest you load everything up in Eclpise and see what gives from there.
//
// A good place to start is the testers/ directory, where you  can get an idea of how a few of the implemented stuff works.
//
//
// DESCRIPTION:
//
//-----------------------------------------------------------------------------

package i;

public class system{
    
/*
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include <stdarg.h>
#include <sys/time.h>
#include <unistd.h>

#include "doomdef.h"
#include "m_misc.h"
#include "i_video.h"
#include "i_sound.h"

#include "d_net.h"
#include "g_game.h"

#ifdef __GNUG__
#pragma implementation "i_system.h"
#endif
#include "i_system.h"

*/


static int	mb_used = 6;


public void
Tactile
( int	on,
  int	off,
  int	total )
{
  // UNUSED.
  on = off = total = 0;
}

/*
ticcmd_t	emptycmd;
ticcmd_t*	I_BaseTiccmd(void)
{
    return &emptycmd;
}

*/

public static int  GetHeapSize ()
{
    return mb_used*1024*1024;
}

/*
byte* I_ZoneBase (int*	size)
{
    *size = mb_used*1024*1024;
    return (byte *) malloc (*size);
}
*/


//
// I_GetTime
// returns time in 1/70th second tics
//
/*
int  I_GetTime ()
{
    struct timeval	tp;
    struct timezone	tzp;
    int			newtics;
    static int		basetime=0;
  
    gettimeofday(&tp, &tzp);
    if (!basetime)
	basetime = tp.tv_sec;
    newtics = (tp.tv_sec-basetime)*TICRATE + tp.tv_usec*TICRATE/1000000;
    return newtics;
}
*/


//
// I_Init
//
/*
void I_Init (void)
{
    I_InitSound();
    //  I_InitGraphics();
}

//
// I_Quit
//
void I_Quit (void)
{
    D_QuitNetGame ();
    I_ShutdownSound();
    I_ShutdownMusic();
    M_SaveDefaults ();
    I_ShutdownGraphics();
    exit(0);
}

void I_WaitVBL(int count)
{
#ifdef SGI
    sginap(1);                                           
#else
#ifdef SUN
    sleep(0);
#else
    usleep (count * (1000000/70) );                                
#endif
#endif
}

void I_BeginRead(void)
{
}

void I_EndRead(void)
{
}

byte*	I_AllocLow(int length)
{
    byte*	mem;
        
    mem = (byte *)malloc (length);
    memset (mem,0,length);
    return mem;
}

*/

//
// I_Error
//
public static boolean demorecording;

public static void Error (String error, Object ... args)
{
    //va_list	argptr;

    // Message first.
    //va_start (argptr,error);
    System.err.print("Error: ");
    System.err.printf(error,args);
    System.err.print("\n");
    //va_end (argptr);

    //fflush( stderr );

    // Shutdown. Here might be other errors.
    //if (demorecording)
	//G_CheckDemoStatus();

    //D_QuitNetGame ();
    //I_ShutdownGraphics();
    
    System.exit(-1);
}
}

package i;

/** Interface for Doom-to-System event handling methods
 * 
 * @author Velktron
 *
 */


public interface DoomEventInterface {

	/** The implementation is windowing subsystem-specific 
	 *  e.g. DOS, XServer, AWT or Swing or whatever.
	 * 
	 */
	public void GetEvent();


}

package i;

import v.IVideoScaleAware;

public interface IDiskDrawer extends IDrawer,IVideoScaleAware{
	
	/** Set a timeout (in tics) for displaying the disk icon 
	 * 
	 * @param timeout
	 */
	
	void setReading(int reading);
	
	/** Disk displayer is currently active
	 * 
	 * @return
	 */
	boolean isReading();
	
	/** Only call after the Wadloader is instantiated and
	 *  initialized itself.
	 * 
	 */
	void Init();
	
	/**Status only valid after the last tic has been drawn.
	 * Use to know when to redraw status bar.
	 * 
	 * @return
	 */
	boolean justDoneReading();
    	
}

package i;

public final class Strings {
	public static final String MOCHA_DOOM_TITLE="Mocha Doom Alpha 1.6";	
	
	public static final String MODIFIED_GAME=
		("===========================================================================\n"+
		 "ATTENTION:  This version of DOOM has been modified.  If you would like to\n"+
         "get a copy of the original game, call 1-800-IDGAMES or see the readme file.\n"+
         "        You will not receive technical support for modified games.\n"+
         "                      press enter to continue\n"+
         "===========================================================================\n");
	
	public static final String MODIFIED_GAME_TITLE="Modified game alert";
	
	public static final String MODIFIED_GAME_DIALOG=
		
		("<html><center>"+
		"===========================================================================<br>"+
		 "ATTENTION:  This version of DOOM has been modified.  If you would like to<br>"+
         "get a copy of the original game, call 1-800-IDGAMES or see the readme file.<br>"+
         "        You will not receive technical support for modified games.<br>"+
         "                      press OK to continue<br>"+
         "===========================================================================<br>"+
         "</center></html>");
	
	public static final String LEVEL_FAILURE_TITLE="Level loading failure";
	
	public static final String LEVEL_FAILURE_CAUSE=
		
		("<html><center>"+
		"Level loading failed!<br>"+
		 "Press OK to end this game without exiting, or cancel to quit Doom."+
         "</center></html>");
	
}

package i;

import doom.ticcmd_t;

public class DummySystem implements IDoomSystem{

    @Override
    public void AllocLow(int length) {
        // TODO Auto-generated method stub
        
    }

    @Override
    public void BeginRead() {
        // TODO Auto-generated method stub
        
    }

    @Override
    public void EndRead() {
        // TODO Auto-generated method stub
        
    }

    @Override
    public void WaitVBL(int count) {
        // TODO Auto-generated method stub
        
    }

    @Override
    public byte[] ZoneBase(int size) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public int GetHeapSize() {
        // TODO Auto-generated method stub
        return 0;
    }

    @Override
    public void Tactile(int on, int off, int total) {
        // TODO Auto-generated method stub
        
    }

    @Override
    public void Quit() {
        // TODO Auto-generated method stub
        
    }

    @Override
    public ticcmd_t BaseTiccmd() {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public void Error(String error, Object... args) {
        // TODO Auto-generated method stub
        
    }

    @Override
    public void Error(String error) {
        // TODO Auto-generated method stub
        
    }

    @Override
    public void Init() {
        // TODO Auto-generated method stub
        
    }

    @Override
    public boolean GenerateAlert(String title, String cause) {
        // TODO Auto-generated method stub
        return false;
    }

}

package i;

// Emacs style mode select   -*- C++ -*- 
//-----------------------------------------------------------------------------
//
// $Id: DoomSoundInterface.java,v 1.3 2011/02/11 00:11:13 velktron Exp $
//
// Copyright (C) 1993-1996 by id Software, Inc.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
//
// DESCRIPTION:
//	System interface, sound.
//
//-----------------------------------------------------------------------------



import data.sfxinfo_t;


/*
// UNIX hack, to be removed.
#ifdef SNDSERV
#include <stdio.h>
extern FILE* sndserver;
extern char* sndserver_filename;
#endif*/


public interface DoomSoundInterface{


// Init at program start...
public void I_InitSound();

// ... update sound buffer and audio device at runtime...
public void I_UpdateSound();
public void I_SubmitSound();

// ... shut down and relase at program termination.
public void I_ShutdownSound();


//
//  SFX I/O
//

// Initialize channels?
void I_SetChannels();

// Get raw data lump index for sound descriptor.
public int I_GetSfxLumpNum (sfxinfo_t sfxinfo );


// Starts a sound in a particular sound channel.
public int
I_StartSound
( int		id,
  int		vol,
  int		sep,
  int		pitch,
  int		priority );


// Stops a sound channel.
public void I_StopSound(int handle);

// Called by S_*() functions
//  to see if a channel is still playing.
// Returns 0 if no longer playing, 1 if playing.
public boolean I_SoundIsPlaying(int handle);

// Updates the volume, separation,
//  and pitch of a sound channel.
public void
I_UpdateSoundParams
( int		handle,
  int		vol,
  int		sep,
  int		pitch );


//
//  MUSIC I/O
//
public void I_InitMusic();
public void I_ShutdownMusic();
// Volume.
public void I_SetMusicVolume(int volume);
// PAUSE game handling.
public void I_PauseSong(int handle);
public void I_ResumeSong(int handle);
// Registers a song handle to song data.
public int I_RegisterSong(byte[] data);
// Called by anything that wishes to start music.
//  plays a song, and when the song is done,
//  starts playing it again in an endless loop.
// Horrible thing to do, considering.
public void
I_PlaySong
( int		handle,
  int		looping );
// Stops a song over 3 seconds.
public void I_StopSong(int handle);
// See above (register), then think backwards
public void I_UnRegisterSong(int handle);
}
package i;

public enum BppMode {
    HiColor,
    Indexed,
    TrueColor,
    TrueColor32
}

package i;

public interface IDrawer {

	public void Drawer();
	
}

package i;

import doom.DoomStatus;

public interface DoomStatusAware {
	  public void updateStatus(DoomStatus<?,?> DC);
}

package i;

/** Methods specific to Doom-System video interfacing. 
 *  In essence, whatever you are using as a final system-specific way to display
 *  the screens, should be able to respond to these commands. In particular,
 *  screen update requests must be honored, and palette/gamma request changes
 *  must be intercepted before they are forwarded to the renderers (in case they
 *  are system-specific, rather than renderer-specific).
 *  
 *  The idea is that the final screen rendering module sees/handles as less as
 *  possible, and only gets a screen to render, no matter what depth it is.
 * 
 *  
 */

public interface DoomVideoInterface<K> {

	public void StartFrame();

	public void StartTic();

	public void SetPalette(int palette);

	public void InitGraphics();

	public void FinishUpdate();

	public void UpdateNoBlit();

	public void ShutdownGraphics();

	public void SetGamma(int gammalevel);
	
	public void ReadScreen(K linear);

}

// Emacs style mode select   -*- C++ -*- 
//-----------------------------------------------------------------------------
//
// $Id: DoomSystem.java,v 1.18 2013/06/04 11:29:39 velktron Exp $
//
// Copyright (C) 1993-1996 by id Software, Inc.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// $Log: DoomSystem.java,v $
// Revision 1.18  2013/06/04 11:29:39  velktron
// Shut up logger
//
// Revision 1.17  2013/06/03 10:54:11  velktron
// System interface allows for a logging subsystem, but implementation details may very well vary.
//
// Revision 1.16.2.1  2012/11/19 22:14:35  velktron
// Sync tooling shutdown.
//
// Revision 1.16  2012/11/06 16:05:29  velktron
// Variables manager now part of Main.
//
// Revision 1.15  2012/09/24 17:16:22  velktron
// Massive merge between HiColor and HEAD. There's no difference from now on, and development continues on HEAD.
//
// Revision 1.14.2.2  2012/09/17 15:57:53  velktron
// Aware of IVariablesManager
//
// Revision 1.14.2.1  2012/06/14 22:38:06  velktron
// Update to handle new disk flasher
//
// Revision 1.14  2011/10/24 02:11:27  velktron
// Stream compliancy
//
// Revision 1.13  2011/09/29 15:16:04  velktron
// Modal popup generation moved here.
//
// Revision 1.12  2011/06/12 21:54:31  velktron
// Separate music + sound closing.
//
// Revision 1.11  2011/06/05 22:52:28  velktron
// Proper audio subsystem shutdown.
//
// Revision 1.10  2011/05/29 22:15:32  velktron
// Introduced IRandom interface.
//
// Revision 1.9  2011/05/26 17:56:32  velktron
// Removed ticker functionality, moved to ITicker interface.
//
// Revision 1.8  2011/05/18 16:53:29  velktron
// Implements IDoomSystem now.
//
// Revision 1.7  2011/05/17 16:54:09  velktron
// Switched to DoomStatus
//
// Revision 1.6  2011/05/13 17:44:24  velktron
// Global error function, shutdown on demos.
//
// Revision 1.5  2011/02/11 00:11:13  velktron
// A MUCH needed update to v1.3.
//
// Revision 1.4  2010/12/15 16:12:19  velktron
// Changes in Wiper code and alternate timing method, hoping to fix the Athlon X2
//
// Revision 1.3  2010/09/24 17:58:39  velktron
// Menus and HU  functional -mostly.
//
// Revision 1.2  2010/09/23 20:36:45  velktron
// *** empty log message ***
//
// Revision 1.1  2010/09/23 15:11:57  velktron
// A bit closer...
//
// Revision 1.3  2010/09/07 16:23:00  velktron
// *** empty log message ***
//
// Revision 1.2  2010/08/30 15:53:19  velktron
// Screen wipes work...Finale coded but untested.
// GRID.WAD included for testing.
//
// Revision 1.1  2010/06/30 08:58:50  velktron
// Let's see if this stuff will finally commit....
//
//
// Most stuff is still  being worked on. For a good place to start and get an idea of what is being done, I suggest checking out the "testers" package.
//
// Revision 1.1  2010/06/29 11:07:34  velktron
// Release often, release early they say...
//
// Commiting ALL stuff done so far. A lot of stuff is still broken/incomplete, and there's still mixed C code in there. I suggest you load everything up in Eclpise and see what gives from there.
//
// A good place to start is the testers/ directory, where you  can get an idea of how a few of the implemented stuff works.
//
//
// DESCRIPTION:
//
//-----------------------------------------------------------------------------

package i;

import java.io.IOException;

import sun.misc.VM;

import awt.MsgBox;
import m.MenuMisc;
import doom.DoomMain;
import doom.DoomStatus;
import doom.ticcmd_t;
import static data.Defines.TICRATE;

public class DoomSystem implements IDoomSystem, DoomStatusAware{
 


static int	mb_used = 6;

// Even the SYSTEM needs to know about DOOM!!!!
protected DoomMain<?,?> DM;

@Override
public void
Tactile
( int	on,
  int	off,
  int	total )
{
  // UNUSED.
  on = off = total = 0;
}


public ticcmd_t	emptycmd;

@Override
public ticcmd_t	BaseTiccmd()
{
    return emptycmd;
}


@Override
public int  GetHeapSize ()
{
    return mb_used*1024*1024;
}

@Override
public byte[] ZoneBase (int	size)
{
    return (new byte[mb_used*1024*1024]);
}

//
//I_Quit
//
@Override
public void Quit ()
{

 //DM.CheckDemoStatus();
 try {
	DM.QuitNetGame ();
} catch (IOException e) {
	// TODO Auto-generated catch block
	e.printStackTrace();
}
 //DM.debugEnd();
 DM.ISND.ShutdownSound();
 DM.IMUS.ShutdownMusic();
 DM.commit();
 DM.VM.SaveDefaults(DM.VM.getDefaultFile());
 DM.VI.ShutdownGraphics();
 System.exit(0);
}


/**
 * I_Init
 */
@Override
public void Init ()
{
    //TODO: InitSound();
    //TODO: InitGraphics();
}


@Override
public void WaitVBL(int count)
{
    try {
        Thread.sleep(count*1000/70);
    } catch (InterruptedException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }                                
}
@Override
public void BeginRead()
{
	if (DM.DD!=null)
	   if (!DM.DD.isReading()) {
		   // Set 8 tick reading time
		   DM.DD.setReading(8);
	   }
	   	
}

@Override
public void EndRead()
{
}

@Override
public void	AllocLow(int length)
{
 ; // Dummy
}

//
// I_Error
//
@Override
public void Error (String error, Object ... args)
{

    System.err.print("Error: ");
    System.err.printf(error,args);
    System.err.print("\n");
    //va_end (argptr);

    //fflush( stderr );

    // Shutdown. Here might be other errors.
    if (DM.demorecording)
	DM.DG.CheckDemoStatus();
    if (DM.VI!=null)
    	DM.VI.ShutdownGraphics();    
    
    try {
		DM.QuitNetGame ();
	} catch (IOException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}
   // DM.VI.ShutdownGraphics();
    
    System.exit(-1);
}

@Override
public void Error (String error)
{
    //va_list	argptr;

    // Message first.
    //va_start (argptr,error);
    System.err.print("Error: ");
    System.err.printf(error);
    System.err.print("\n");
    //va_end (argptr);

    //fflush( stderr );

    // Shutdown. Here might be other errors.
    //if (demorecording)
	//G_CheckDemoStatus();

    //D_QuitNetGame ();
    //I_ShutdownGraphics();
    
    System.exit(-1);
}

public DoomSystem(){
emptycmd=new ticcmd_t();
}

public static void MiscError(String error, Object ... args) {
    System.err.print("Error: ");
    System.err.printf(error);
    System.err.print("\n");    
}


@Override
public void updateStatus(DoomStatus DS) {
    this.DM=DS.DM;
    
}


// This particular implementation will generate a popup box.// 
@Override
public boolean GenerateAlert(String title,String cause) {
    MsgBox alert=new MsgBox(null, title, cause, true);
      return alert.isOk();
}

}

package i;

import java.io.IOException;

import m.IVariablesManager;
import m.VarsManager;

import rr.LightsAndColors;

import v.VideoScaleInfo;
import doom.CommandLine;
import doom.DoomMain;
import doom.ICommandLineManager;

//Emacs style mode select   -*- C++ -*- 
//-----------------------------------------------------------------------------
//
//$Id: Main.java,v 1.14 2016/06/06 14:22:17 velktron Exp $
//
//Copyright (C) 1993-1996 by id Software, Inc.
//
//This program is free software; you can redistribute it and/or
//modify it under the terms of the GNU General Public License
//as published by the Free Software Foundation; either version 2
//of the License, or (at your option) any later version.
//
//This program is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//GNU General Public License for more details.
//
//
//DESCRIPTION:
//Main program, simply calls D_DoomMain high level loop after loading 
//some essential settings and determining what "flavor" we're going to run.
//
//-----------------------------------------------------------------------------



public class Main {
    static final String rcsid = "$Id: Main.java,v 1.14 2016/06/06 14:22:17 velktron Exp $";

    /** Global setting for initializing the renderer's bitplane mode */
    public static BppMode bpp;
    /** Global setting for initializing the renderer's light levels */
    public static int lightlevels;
    
    public static void main(String[] argv) throws IOException{

    	  //First, get the command line parameters.
          ICommandLineManager CLM=new CommandLine(argv);
            
          // Handles variables and settings from default.cfg
          IVariablesManager VM=new VarsManager(CLM);
          
          // load before initting other systems, but don't apply them yet.          
          System.out.print ("M_LoadDefaults: Load system defaults.\n");
          VM.LoadDefaults (VM.getDefaultFile());
        
          bpp= BppMode.Indexed;
          lightlevels=5;
          
          //if (VM.getSetting(name), value))
          
            
          if (VM.isSettingLiteral("color_depth","hicolor"))
              bpp=BppMode.HiColor;
          if (VM.isSettingLiteral("color_depth","truecolor"))
              bpp=BppMode.TrueColor;
          
          if (CLM.CheckParmBool("-hicolor")) bpp=BppMode.HiColor;
              else
          if (CLM.CheckParmBool("-truecolor")) bpp=BppMode.TrueColor;
          
          
          
          // Here we create DOOM
          DoomMain<?, ?> DM=null;
          // Create a dummy. This will force static init to run.
          
          switch(bpp){
          case Indexed:
              System.out.println("Indexed 8-bit mode selected...");
              DM=new DoomMain.Indexed();
              break;
          case HiColor:
              System.out.println("HiColor (Alpha) 16-bit mode selected...");
              DM=new DoomMain.HiColor();
              break;
          case TrueColor:
          case TrueColor32:
              System.out.println("TrueColor (extended colormaps) 24-bit mode selected...");
              DM=new DoomMain.TrueColor();
              break;

          }    

          DM.setCommandLineArgs(CLM);
          DM.registerVariableManager(VM);
          DM.Init();
          DM.Start();

          return;
        } 
    }

package i;

// Emacs style mode select   -*- C++ -*- 
//-----------------------------------------------------------------------------
//
// $Id: SystemSoundInterface.java,v 1.2 2011/05/17 16:51:20 velktron Exp $
//
// Copyright (C) 1993-1996 by id Software, Inc.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
//
// DESCRIPTION:
//	System interface, sound.
//
//-----------------------------------------------------------------------------



import data.sfxinfo_t;


/*
// UNIX hack, to be removed.
#ifdef SNDSERV
#include <stdio.h>
extern FILE* sndserver;
extern char* sndserver_filename;
#endif*/


public interface SystemSoundInterface{


// Init at program start...
public void InitSound();

// ... update sound buffer and audio device at runtime...
public void UpdateSound();
public void SubmitSound();

// ... shut down and relase at program termination.
public void ShutdownSound();


//
//  SFX I/O
//

// Initialize channels?
void SetChannels();

// Get raw data lump index for sound descriptor.
public int GetSfxLumpNum (sfxinfo_t sfxinfo );


// Starts a sound in a particular sound channel.
public int
StartSound
( int		id,
  int		vol,
  int		sep,
  int		pitch,
  int		priority );


// Stops a sound channel.
public void StopSound(int handle);

// Called by S_*() functions
//  to see if a channel is still playing.
// Returns 0 if no longer playing, 1 if playing.
public boolean SoundIsPlaying(int handle);

// Updates the volume, separation,
//  and pitch of a sound channel.
public void
UpdateSoundParams
( int		handle,
  int		vol,
  int		sep,
  int		pitch );


//
//  MUSIC I/O
//
public void InitMusic();
public void ShutdownMusic();
// Volume.
public void SetMusicVolume(int volume);
// PAUSE game handling.
public void PauseSong(int handle);
public void ResumeSong(int handle);
// Registers a song handle to song data.
public int RegisterSong(byte[] data);
// Called by anything that wishes to start music.
//  plays a song, and when the song is done,
//  starts playing it again in an endless loop.
// Horrible thing to do, considering.
public void
PlaySong
( int		handle,
  int		looping );
// Stops a song over 3 seconds.
public void StopSong(int handle);
// See above (register), then think backwards
public void UnRegisterSong(int handle);
}

/** Blatantly ripped off Jake 2. Sieg heil! */

/*
Copyright (C) 1997-2001 Id Software, Inc.

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

*/
package i;

import java.awt.event.*;
import java.util.LinkedList;

import doom.event_t;
import doom.evtype_t;

/**
 * InputListener
 */
public final class InputListener implements KeyListener, MouseListener, 
        MouseMotionListener, ComponentListener, MouseWheelListener {

    // modifications of eventQueue must be thread safe!
    private static LinkedList<event_t> eventQueue = new LinkedList<event_t>();

    static void addEvent(event_t ev) {
        synchronized (eventQueue) {
            eventQueue.addLast(ev);
        }
    }

    public static event_t nextEvent() {
        event_t ev;
        synchronized (eventQueue) {
            ev = (!eventQueue.isEmpty())?(event_t)eventQueue.removeFirst():null;
        }
        return ev;
    }

    public void keyPressed(KeyEvent e) {
        if (!((e.getModifiersEx() & InputEvent.ALT_GRAPH_DOWN_MASK) != 0)) {
            addEvent(new event_t(evtype_t.ev_keydown, e.getKeyCode()));
        }
    }

    public void keyReleased(KeyEvent e) {
        addEvent(new event_t(evtype_t.ev_keyup, e.getKeyCode()));
    }

    public void keyTyped(KeyEvent e) {
        if ((e.getModifiersEx() & InputEvent.ALT_GRAPH_DOWN_MASK) != 0) {
            addEvent(new event_t(evtype_t.ev_keydown, e.getKeyCode()));
            addEvent(new event_t(evtype_t.ev_keyup, e.getKeyCode()));
        }       
    }

    public void mouseClicked(MouseEvent e) {
    }

    public void mouseEntered(MouseEvent e) {
    }

    public void mouseExited(MouseEvent e) {
    }

    public void mousePressed(MouseEvent e) {
        addEvent(new event_t(evtype_t.ev_mouse, e.getButton()));
    }

    public void mouseReleased(MouseEvent e) {
        addEvent(new event_t(evtype_t.ev_mouse, e.getButton()));
    }

    public void mouseDragged(MouseEvent e) {
        addEvent(new event_t(evtype_t.ev_mouse, e.getButton(),e.getX(),e.getY()));
    }

    public void mouseMoved(MouseEvent e) {
        addEvent(new event_t(evtype_t.ev_mouse, e.getButton(),e.getX(),e.getY()));
    }
    
    public void mouseWheelMoved(MouseWheelEvent e) {
        addEvent(new event_t(evtype_t.ev_mousewheel, e.getWheelRotation()));
    }   

   // Don't listen?
    public void componentHidden(ComponentEvent e) {
    }

    public void componentMoved(ComponentEvent e) {
       // addEvent(new event_t(event_t.ConfigureNotify, e));
    }

    public void componentResized(ComponentEvent e) {
      //  addEvent(new event_t(event_t.ConfigureNotify, e));
    }

    public void componentShown(ComponentEvent e) {
    //    JOGLKBD.c = e.getComponent();
    //    addEvent(new event_t(event_t.CreateNotify, e));
    }

}


package rr;

import java.io.DataInputStream;
import java.io.IOException;
import java.nio.ByteBuffer;

import p.Resettable;

import static m.fixed_t.FRACBITS;
import w.DoomIO;
import w.IPackableDoomObject;
import w.IReadableDoomObject;

/**
 * The SideDef.
 * 
 * @author admin
 */
public class side_t
        implements IReadableDoomObject, IPackableDoomObject, Resettable {
    /** (fixed_t) add this to the calculated texture column */
    public int textureoffset;

    /** (fixed_t) add this to the calculated texture top */
    public int rowoffset;

    /**
     * Texture indices. We do not maintain names here.
     */
    public short toptexture;

    public short bottomtexture;

    public short midtexture;

    /** Sector the SideDef is facing. MAES: pointer */
    public sector_t sector;

    public int sectorid;

    public int special;

    public side_t() {
    }

    public side_t(int textureoffset, int rowoffset, short toptexture,
            short bottomtexture, short midtexture, sector_t sector) {
        super();
        this.textureoffset = textureoffset;
        this.rowoffset = rowoffset;
        this.toptexture = toptexture;
        this.bottomtexture = bottomtexture;
        this.midtexture = midtexture;
        this.sector = sector;
    }

    @Override
    public void read(DataInputStream f)
            throws IOException {
        this.textureoffset = DoomIO.readLEShort(f) << FRACBITS;
        this.rowoffset = DoomIO.readLEShort(f) << FRACBITS;
        this.toptexture = DoomIO.readLEShort(f);
        this.bottomtexture = DoomIO.readLEShort(f);
        this.midtexture = DoomIO.readLEShort(f);
        // this.sectorid=f.readLEInt();

    }

    @Override
    public void pack(ByteBuffer buffer) {
        buffer.putShort((short) (textureoffset >> FRACBITS));
        buffer.putShort((short) (rowoffset >> FRACBITS));
        buffer.putShort(toptexture);
        buffer.putShort(bottomtexture);
        buffer.putShort(midtexture);
    }

    @Override
    public void reset() {
        textureoffset = 0;
        rowoffset = 0;
        toptexture = 0;
        bottomtexture = 0;
        midtexture = 0;
        sector = null;
        sectorid = 0;
        special = 0;

    }

}

package rr;

public interface IDetailAware {

    public static int HIGH_DETAIL=0;
    public static int LOW_DETAIL=1;
    void setDetail(int detailshift);
}

package rr;

import static data.Tables.ANG180;
import static data.Tables.ANG270;
import static data.Tables.ANG90;
import static data.Tables.SlopeDiv;
import static data.Tables.tantoangle;
import utils.C2JUtils;
import doom.player_t;

public class ViewVars {
    
    // Found in draw_c. Only ever used in renderer.
    public int windowx;
    public int windowy;
    public int width;
    public int height;
    
    // MAES: outsiders have no business peeking into this.
    // Or...well..maybe they do. It's only used to center the "pause" X
    // position.
    // TODO: get rid of this?
    public int scaledwidth;
    public int centerx;
    public int centery;
    
    /** Used to determine the view center and projection in view units fixed_t */
    public int centerxfrac, centeryfrac, projection;

    /** fixed_t */
    public int x, y, z;

    // MAES: an exception to strict type safety. These are used only in here,
    // anyway (?) and have no special functions.
    // Plus I must use them as indexes. angle_t
    public long angle;

    /** fixed */
    public int cos, sin;

    public player_t player;

    /** Heretic/freeview stuff? */

    public int lookdir;
    
    // 0 = high, 1 = low. Normally only the menu and the interface can change
    // that.
    public int detailshift;
    
    public int WEAPONADJUST;
    public int BOBADJUST;
	
	/**
	 * constant arrays used for psprite clipping and initializing clipping
	 */
    public short[] negonearray; // MAES: in scaling
    public short[] screenheightarray;// MAES: in scaling
    
    /** Mirrors the one in renderer... */
    public long[] xtoviewangle;
	
    public final void initNegOneArray(int screenwidth){
	       C2JUtils.memset(negonearray, (short)-1,screenwidth);
		}
    
    public final long PointToAngle(int x, int y) {
        // MAES: note how we don't use &BITS32 here. That is because
        // we know that the maximum possible value of tantoangle is angle
        // This way, we are actually working with vectors emanating
        // from our current position.
        x -= this.x;
        y -= this.y;

        if ((x == 0) && (y == 0))
            return 0;

        if (x >= 0) {
            // x >=0
            if (y >= 0) {
                // y>= 0

                if (x > y) {
                    // octant 0
                    return tantoangle[SlopeDiv(y, x)];
                } else {
                    // octant 1
                    return (ANG90 - 1 - tantoangle[SlopeDiv(x, y)]);
                }
            } else {
                // y<0
                y = -y;

                if (x > y) {
                    // octant 8
                    return (-tantoangle[SlopeDiv(y, x)]);
                } else {
                    // octant 7
                    return (ANG270 + tantoangle[SlopeDiv(x, y)]);
                }
            }
        } else {
            // x<0
            x = -x;

            if (y >= 0) {
                // y>= 0
                if (x > y) {
                    // octant 3
                    return (ANG180 - 1 - tantoangle[SlopeDiv(y, x)]);
                } else {
                    // octant 2
                    return (ANG90 + tantoangle[SlopeDiv(x, y)]);
                }
            } else {
                // y<0
                y = -y;

                if (x > y) {
                    // octant 4
                    return (ANG180 + tantoangle[SlopeDiv(y, x)]);
                } else {
                    // octant 5
                    return (ANG270 - 1 - tantoangle[SlopeDiv(x, y)]);
                }
            }
        }
        // This is actually unreachable.
        // return 0;
    }
    
    public final int getViewWindowX(){
        return windowx;
    }

    public final int getViewWindowY(){
        return windowy;
    }
        
    public final int getScaledViewWidth(){
        return scaledwidth;
    }

    public final int getScaledViewHeight() {
        return height;
    }

}

package rr;

public interface planefunction_t {

}

package rr;

import java.io.IOException;

import rr.parallel.IGetSmpColumn;

/** All texture, flat and sprite management operations should be handled
 *  by an implementing class. As of now, the renderer does both, though it's
 *  not really the most ideal.
 *  
 * @author Velktron
 *
 */

public interface TextureManager<T> extends IGetColumn<T>, IGetCachedColumn<T>,IGetSmpColumn{

    public final static String[] texturelumps={"TEXTURE1","TEXTURE2"};
    public final static int NUMTEXLUMPS=texturelumps.length;
    public final static int TEXTURE1=0;
    public final static int TEXTURE2=1;
    
	int TextureNumForName(String texname);
	
	
	/**The "num" expected here is the internal flat number,
	 * not the absolute lump number. So impement accordingly.
	 * 
	 * @param flatname
	 * @return
	 */
	int FlatNumForName(String flatname);
	
	void PrecacheLevel() throws IOException;
	
	void GenerateComposite(int tex);
	
	int getTextureheight(int texnum);	
		
	int getTextureTranslation(int texnum);
	
	int getFlatTranslation(int flatnum);
	
	void setTextureTranslation(int texnum, int amount);
	
	void setFlatTranslation(int flatnum,int amount);

	int CheckTextureNumForName(String texnamem);

	String CheckTextureNameForNum(int texnum);
	
    int getTexturewidthmask(int tex);
   
    int getTextureColumnLump(int tex, int col);
   
    char getTextureColumnOfs(int tex, int col);

    T[] getTextureComposite(int tex);

    T getTextureComposite(int tex, int col);

    void InitFlats();

    void InitTextures() throws IOException;

    //int getFirstFlat();

    int getSkyTextureMid();

    int getSkyFlatNum();

    int getSkyTexture();

    void setSkyTexture(int skytexture);

    int InitSkyMap();

    void setSkyFlatNum(int skyflatnum);

    void GenerateLookup(int texnum)
            throws IOException;
    
    int getFlatLumpNum(int flatnum);


	T getRogueColumn(int lump, int column);

    patch_t getMaskedComposite(int tex);


    void GenerateMaskedComposite(int texnum);
    
    /** Return a "sanitized" patch. If data is insufficient, return
     *  a default patch or attempt a partial draw.
     * 
     * @param patchnum
     * @return
     */
    
    public T getSafeFlat(int flatnum);


    column_t GetColumnStruct(int tex, int col);


    void setSMPVars(int nUMMASKEDTHREADS);
    
    }

package rr;

import static rr.line_t.*;
import static data.Defines.ANGLETOSKYSHIFT;
import static data.Defines.NF_SUBSECTOR;
import static data.Defines.PU_CACHE;
import static data.Defines.SIL_BOTH;
import static data.Defines.SIL_BOTTOM;
import static data.Defines.SIL_TOP;
import static data.Limits.MAXHEIGHT;
import static data.Limits.MAXSEGS;
import static data.Limits.MAXWIDTH;
import static data.Tables.ANG180;
import static data.Tables.ANG270;
import static data.Tables.ANG90;
import static data.Tables.ANGLETOFINESHIFT;
import static data.Tables.BITS32;
import static data.Tables.DBITS;
import static data.Tables.FINEANGLES;
import static data.Tables.QUARTERMARK;
import static data.Tables.SlopeDiv;
import static data.Tables.addAngles;
import static data.Tables.finecosine;
import static data.Tables.finesine;
import static data.Tables.finetangent;
import static data.Tables.tantoangle;
import static m.BBox.BOXBOTTOM;
import static m.BBox.BOXLEFT;
import static m.BBox.BOXRIGHT;
import static m.BBox.BOXTOP;
import static m.fixed_t.FRACBITS;
import static m.fixed_t.FRACUNIT;
import static m.fixed_t.FixedDiv;
import static m.fixed_t.FixedMul;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import m.IDoomMenu;
import m.MenuMisc;
import p.AbstractLevelLoader;
import p.UnifiedGameMap;
import p.mobj_t;
import rr.UnifiedRenderer.Segs;
import rr.drawfuns.ColFuncs;
import rr.drawfuns.ColVars;
import rr.drawfuns.DoomColumnFunction;
import rr.drawfuns.DoomSpanFunction;
import rr.drawfuns.R_DrawColumnBoom;
import rr.drawfuns.R_DrawColumnBoomLow;
import rr.drawfuns.R_DrawColumnBoomOpt;
import rr.drawfuns.R_DrawColumnBoomOptLow;
import rr.drawfuns.R_DrawFuzzColumn;
import rr.drawfuns.R_DrawFuzzColumnLow;
import rr.drawfuns.R_DrawSpanLow;
import rr.drawfuns.R_DrawSpanUnrolled;
import rr.drawfuns.R_DrawTLColumn;
import rr.drawfuns.R_DrawTranslatedColumn;
import rr.drawfuns.R_DrawTranslatedColumnLow;
import rr.drawfuns.SpanVars;
import i.IDoomSystem;
import utils.C2JUtils;
import v.DoomVideoRenderer;
import v.IVideoScale;
import v.IVideoScaleAware;
import w.IWadLoader;
import data.Defines;
import data.Tables;
import doom.DoomMain;
import doom.DoomStatus;
import doom.IDoomGameNetworking;
import doom.player_t;
import doom.think_t;
import doom.thinker_t;
import static rr.LightsAndColors.*;

/**
 * Most shared -essential- status information, methods and classes related to
 * the software rendering subsystem are found here, shared between the various
 * implementations of the Doom's renderer. Not the cleanest or more OO way
 * possible, but still a good way to avoid duplicating common code. Some stuff
 * like Texture, Flat and Sprite management are also found -or at least
 * implemented temporarily- here, until a cleaner split can be made. This is a
 * kind of "Jack of all trades" class, but hopefully not for long.
 * 
 * @author velktron
 */

public abstract class RendererState<T, V>
        implements Renderer<T, V>, ILimitResettable {

    protected static final boolean DEBUG = false;

    protected static final boolean DEBUG2 = false;

    // ///////////////////// STATUS ////////////////////////

    protected DoomMain<T, V> DM;

    protected IDoomGameNetworking DGN;

    protected AbstractLevelLoader LL;

    protected ISegDrawer MySegs;

    protected IDoomMenu Menu;

    protected BSP MyBSP;

    protected PlaneDrawer<T, V> MyPlanes;

    protected IMaskedDrawer<T, V> MyThings;

    protected DoomVideoRenderer<T, V> V;

    protected UnifiedGameMap P;

    public IWadLoader W;

    public ISpriteManager SM;

    public IVisSpriteManagement<V> VIS;

    public IDoomSystem I;

    protected TextureManager<T> TexMan;

    public ViewVars view;

    public LightsAndColors<V> colormaps;

    public SegVars seg_vars;

    public Visplanes vp_vars;

    // Rendering subsystems that are detailshift-aware

    protected List<IDetailAware> detailaware;

    // The only reason to query scaledviewwidth from outside the renderer, is
    // this.
    public boolean isFullHeight() {
        return (view.height == SCREENHEIGHT);
    }

    public boolean isFullWidth() {
        return (view.scaledwidth == SCREENWIDTH);
    }

    public boolean isFullScreen() {
        return isFullWidth() && isFullHeight();
    }

    /**
     * Increment every time a check is made For some reason, this needs to be
     * visible even by enemies thinking :-S
     */
    protected int validcount = 1;

    /** Who can set this? A: The Menu. */
    protected boolean setsizeneeded;

    protected int setblocks;

    protected int setdetail;

    // private BSPVars bspvars;

    /**
     * R_SetViewSize Do not really change anything here, because it might be in
     * the middle of a refresh. The change will take effect next refresh.
     * 
     * @param blocks
     *        11 is full screen, 9 default.
     * @param detail
     *        0= high, 1 =low.
     */

    public void SetViewSize(int blocks, int detail) {
        // System.out.println("SetViewSize");
        setsizeneeded = true;
        setblocks = blocks;
        setdetail = detail;

        for (IDetailAware d : detailaware) {
            d.setDetail(setdetail);
        }
    }

    /**
     * R_SetupFrame
     */
    public void SetupFrame(player_t player) {
        int i;

        view.player = player;
        view.x = player.mo.x;
        view.y = player.mo.y;
        // viewangle = addAngles(player.mo.angle , viewangleoffset);
        view.angle = player.mo.angle & BITS32;
        // With 32 colormaps, a bump of 1 or 2 is normal.
        // With more than 32, it should be obviously higher.

        int bumplight = Math.max(LBITS - 5, 0);
        // Be a bit more generous, otherwise the effect is not
        // as evident with truecolor maps.
        bumplight += (bumplight > 0) ? 1 : 0;

        colormaps.extralight = player.extralight << bumplight;

        view.z = player.viewz;
        view.lookdir = (int) player.lookdir;
        int tempCentery;

        // MAES: hacks based on Heretic. Weapon movement needs to be compensated
        if (setblocks == 11)
            tempCentery =
                (view.height / 2)
                        + (int) (view.lookdir * SCREEN_MUL * setblocks) / 11;
        else
            tempCentery =
                (view.height / 2)
                        + (int) (view.lookdir * SCREEN_MUL * setblocks) / 10;

        if (view.centery != tempCentery) {
            view.centery = tempCentery;
            view.centeryfrac = view.centery << FRACBITS;
            int yslope[] = vp_vars.yslope;
            for (i = 0; i < view.height; i++) {
                yslope[i] =
                    FixedDiv(
                        (view.width << view.detailshift) / 2 * FRACUNIT,
                        Math.abs(((i - view.centery) << FRACBITS) + FRACUNIT
                                / 2));
            }

            skydcvars.centery =
                maskedcvars.centery = dcvars.centery = view.centery;
        }

        view.sin = Tables.finesine(view.angle);
        view.cos = Tables.finecosine(view.angle);

        sscount = 0;

        if (player.fixedcolormap != 0) {
            colormaps.fixedcolormap = colormaps.colormaps[player.fixedcolormap];
            // Offset by fixedcolomap
            // pfixedcolormap =player.fixedcolormap*256;

            colormaps.walllights = colormaps.scalelightfixed;

            for (i = 0; i < MAXLIGHTSCALE; i++)
                colormaps.scalelightfixed[i] = colormaps.fixedcolormap;
        } else
            colormaps.fixedcolormap = null;

        framecount++;
        validcount++;
    }

    /**
     * R_SetupFrame for a particular actor.
     */
    public void SetupFrame(mobj_t actor) {

        // viewplayer = player;
        view.x = actor.x;
        view.y = actor.y;
        // viewangle = addAngles(player.mo.angle , viewangleoffset);
        view.angle = actor.angle & BITS32;
        // extralight = actor.extralight;

        view.z = actor.z + actor.height;

        view.sin = finesine(view.angle);
        view.cos = finecosine(view.angle);

        sscount = 0;

        framecount++;
        validcount++;
    }

    public RendererState(DoomStatus<T, V> DS) {
        this.updateStatus(DS);

        // These don't change between implementations, yet.
        this.MyBSP = new BSP();

        this.view = new ViewVars();
        this.seg_vars = new SegVars();
        this.dcvars=new ColVars<T,V>();
        this.dsvars=new SpanVars<T,V>();        
        this.maskedcvars=new ColVars<T,V>();
        this.skydcvars=new ColVars<T,V>();
        this.colfunclow=new ColFuncs<T,V>();
        this.colfunchi=new ColFuncs<T,V>();

        this.detailaware = new ArrayList<IDetailAware>();
        this.colormaps=new LightsAndColors<V>();
        // It's better to construct this here
        this.TexMan = (TextureManager<T>) new SimpleTextureManager(DS);
        this.SM=new SpriteManager(DS);

        // Visplane variables
        this.vp_vars = new Visplanes(view, TexMan);
        // Initialize array of minus ones for sprite clipping
        view.initNegOneArray(SCREENWIDTH);

        // Set rendering functions only after screen sizes
        // and stuff have been set.
        
        this.MyPlanes = new Planes(this);
        this.VIS=new VisSprites<V>(this);
        this.MyThings = new SimpleThings<T,V>(this);
        
    }

    @SuppressWarnings("unchecked")
    @Override
    public void updateStatus(DoomStatus<?, ?> DC) {
        this.DM = (DoomMain<T, V>) DC.DM;
        this.DGN = DC.DGN;
        this.LL = DC.LL;
        this.W = DC.W;
        this.P = DC.P;
        // We must also connect screen to V. Don't forget it. Do it in Init(),
        // OK?
        this.V = (DoomVideoRenderer<T, V>) DC.V;
        this.SM = DC.SM;
        this.I = DC.I;
        if (VIS != null)
            this.VIS.updateStatus(this);
    }

    // ////////////////////////////// THINGS ////////////////////////////////

    protected final class BSP
            extends BSPVars {

        /** newend is one past the last valid seg (cliprange_t) */
        int newend;

        cliprange_t[] solidsegs;

        public BSP() {
            solidsegs = new cliprange_t[MAXSEGS + 1];
            C2JUtils.initArrayOfObjects(solidsegs);

        }

        /**
         * R_ClipSolidWallSegment Does handle solid walls, single sided LineDefs
         * (middle texture) that entirely block the view VERTICALLY. Handles
         * "clipranges" for a solid wall, aka where it blocks the view.
         * 
         * @param first
         *        starting y coord?
         * @param last
         *        ending y coord?
         */

        private void ClipSolidWallSegment(int first, int last) {

            int next;
            int start;
            // int maxlast=Integer.MIN_VALUE;

            start = 0; // within solidsegs

            // Find the first cliprange that touches the range.
            // Actually, the first one not completely hiding it (its last must
            // be lower than first.

            while (solidsegs[start].last < first - 1)
                start++;

            // If the post begins above the lastly found cliprange...
            if (first < solidsegs[start].first) {
                // ..and ends above it, too (no overlapping)
                if (last < solidsegs[start].first - 1) {
                    // ... then the post is entirely visible (above start),
                    // so insert a new clippost. Calling this function
                    // tells the renderer that there is an obstruction.
                    MySegs.StoreWallRange(first, last);

                    // Newend should have a value of 2 if we are at the
                    // beginning of a new frame.
                    next = newend;
                    newend++;

                    if (next >= solidsegs.length)
                        ResizeSolidSegs();
                    while (next != start) {
                        // *next=*(next-1);
                        /*
                         * MAES: I think this is supposed to copy the structs
                         * solidsegs[next] = solidsegs[next-1].clone(); OK, so
                         * basically the last solidseg copies its previous, and
                         * so on until we reach the start. This means that at
                         * some point, the value of the start solidseg is
                         * duplicated.
                         */

                        solidsegs[next].copy(solidsegs[next - 1]);

                        next--;
                    }

                    // At this point, next points at start.
                    // Therefore, start
                    solidsegs[next].first = first;
                    solidsegs[next].last = last;
                    return;
                }

                // There is a fragment above *start. This can occur if it a
                // post does start before another, but its lower edge overlaps
                // (partial, upper occlusion)
                MySegs.StoreWallRange(first, solidsegs[start].first - 1);
                // Now adjust the clip size.
                solidsegs[start].first = first;
            }

            // We can reach this only if a post starts AFTER another

            // Bottom contained in start? Obviously it won't be visible.
            if (last <= solidsegs[start].last)
                return;

            next = start;
            while (last >= solidsegs[(next + 1)].first - 1) {
                // There is a fragment between two posts.
                MySegs.StoreWallRange(solidsegs[next].last + 1,
                    solidsegs[next + 1].first - 1);
                next++;

                if (last <= solidsegs[next].last) {
                    // Bottom is contained in next.
                    // Adjust the clip size.
                    solidsegs[start].last = solidsegs[next].last;
                    // goto crunch;

                    { // crunch code
                        if (next == start) {
                            // Post just extended past the bottom of one post.
                            return;
                        }

                        while (next++ != newend) {
                            // Remove a post.
                            // MAES: this is a struct copy.
                            if (next >= solidsegs.length)
                                ResizeSolidSegs();
                            solidsegs[++start].copy(solidsegs[next]);
                        }

                        newend = start + 1;
                        return;
                    }
                }
            }

            // There is a fragment after *next.
            MySegs.StoreWallRange(solidsegs[next].last + 1, last);
            // Adjust the clip size.
            solidsegs[start].last = last;

            // Remove start+1 to next from the clip list,
            // because start now covers their area.

            { // crunch code
                if (next == start) {
                    // Post just extended past the bottom of one post.
                    return;
                }

                while (next++ != newend) {
                    // Remove a post.
                    // MAES: this is a struct copy.
                    // MAES: this can overflow, breaking e.g. MAP30 of Final
                    // Doom.
                    if (next >= solidsegs.length)
                        ResizeSolidSegs();
                    solidsegs[++start].copy(solidsegs[next]);
                }

                newend = start + 1;
                return;
            }
        }

        protected final void ResizeSolidSegs() {
            solidsegs = C2JUtils.resize(solidsegs, solidsegs.length * 2);
        }

        //
        // R_ClipPassWallSegment
        // Clips the given range of columns,
        // but does not includes it in the clip list.
        // Does handle windows,
        // e.g. LineDefs with upper and lower texture.
        //
        private void ClipPassWallSegment(int first, int last) {

            // Find the first range that touches the range
            // (adjacent pixels are touching).
            int start = 0;

            while (solidsegs[start].last < first - 1)
                start++;

            if (first < solidsegs[start].first) {
                if (last < solidsegs[start].first - 1) {
                    // Post is entirely visible (above start).
                    MySegs.StoreWallRange(first, last);
                    return;
                }

                // There is a fragment above *start.
                MySegs.StoreWallRange(first, solidsegs[start].first - 1);
            }

            // Bottom contained in start?
            if (last <= solidsegs[start].last)
                return;

            // MAES: Java absolutely can't do without a sanity check here.
            // if (startptr>=MAXSEGS-2) return;

            while (last >= solidsegs[start + 1].first - 1) {
                // There is a fragment between two posts.
                MySegs.StoreWallRange(solidsegs[start].last + 1,
                    solidsegs[start + 1].first - 1);
                start++;
                // if (startptr>=MAXSEGS-2) return;
                // start=solidsegs[startptr];

                if (last <= solidsegs[start].last)
                    return;
            }

            // There is a fragment after *next.
            MySegs.StoreWallRange(solidsegs[start].last + 1, last);
        }

        /**
         * R_ClearClipSegs Clears the clipping segs list. The list is actually
         * fixed size for efficiency reasons, so it just tells Doom to use the
         * first two solidsegs, which are "neutered". It's interesting to note
         * how the solidsegs begin and end just "outside" the visible borders of
         * the screen.
         */

        public void ClearClipSegs() {
            solidsegs[0].first = -0x7fffffff;
            solidsegs[0].last = -1;
            solidsegs[1].first = view.width;
            solidsegs[1].last = 0x7fffffff;
            newend = 2; // point so solidsegs[2];
        }

        /**
         * R_AddLine Called after a SubSector BSP trasversal ends up in a
         * "final" subsector. Clips the given segment and adds any visible
         * pieces to the line list. It also determines what kind of boundary
         * (line) visplane clipping should be performed. E.g. window, final
         * 1-sided line, closed door etc.) CAREFUL: was the source of much
         * frustration with visplanes...
         */
        private void AddLine(seg_t line) {
            if (DEBUG)
                System.out.println("Entered AddLine for " + line);
            int x1;
            int x2;
            long angle1;
            long angle2;
            long span;
            long tspan;

            curline = line;

            // OPTIMIZE: quickly reject orthogonal back sides.
            angle1 = view.PointToAngle(line.v1x, line.v1y);
            angle2 = view.PointToAngle(line.v2x, line.v2y);

            // Clip to view edges.
            // OPTIMIZE: make constant out of 2*clipangle (FIELDOFVIEW).

            span = addAngles(angle1, -angle2);

            // Back side? I.e. backface culling?
            if (span >= ANG180)
                return;

            // Global angle needed by segcalc.
            MySegs.setGlobalAngle(angle1);
            angle1 -= view.angle;
            angle2 -= view.angle;

            angle1 &= BITS32;
            angle2 &= BITS32;

            tspan = addAngles(angle1, clipangle);

            if (tspan > CLIPANGLE2) {
                tspan -= CLIPANGLE2;
                tspan &= BITS32;

                // Totally off the left edge?
                if (tspan >= span)
                    return;

                angle1 = clipangle;
            }
            tspan = addAngles(clipangle, -angle2);

            if (tspan > CLIPANGLE2) {
                tspan -= CLIPANGLE2;
                tspan &= BITS32;

                // Totally off the left edge?
                if (tspan >= span)
                    return;
                angle2 = -clipangle;
                angle2 &= BITS32;
            }

            // The seg is in the view range,
            // but not necessarily visible.

            angle1 = ((angle1 + ANG90) & BITS32) >>> ANGLETOFINESHIFT;
            angle2 = ((angle2 + ANG90) & BITS32) >>> ANGLETOFINESHIFT;
            x1 = viewangletox[(int) angle1];
            x2 = viewangletox[(int) angle2];

            // Does not cross a pixel?
            if (x1 == x2)
                return;

            backsector = line.backsector;

            // Single sided line?

            if (backsector == null) {
                if (DEBUG)
                    System.out
                            .println("Entering ClipSolidWallSegment SS with params "
                                    + x1 + " " + (x2 - 1));
                ClipSolidWallSegment(x1, x2 - 1); // to clipsolid
                if (DEBUG)
                    System.out.println("Exiting ClipSolidWallSegment");
                return;
            }

            // Closed door.
            if (backsector.ceilingheight <= frontsector.floorheight
                    || backsector.floorheight >= frontsector.ceilingheight) {
                if (DEBUG)
                    System.out
                            .println("Entering ClipSolidWallSegment Closed door with params "
                                    + x1 + " " + (x2 - 1));
                ClipSolidWallSegment(x1, x2 - 1);
                ; // to clipsolid
                return;
            }

            // Window. This includes same-level floors with different textures
            if (backsector.ceilingheight != frontsector.ceilingheight
                    || backsector.floorheight != frontsector.floorheight) {
                if (DEBUG)
                    System.out
                            .println("Entering ClipSolidWallSegment window with params "
                                    + x1 + " " + (x2 - 1));
                ClipPassWallSegment(x1, x2 - 1); // to clippass
                return;
            }

            // Reject empty lines used for triggers
            // and special events.
            // Identical floor and ceiling on both sides,
            // identical light levels on both sides,
            // and no middle texture.
            if (backsector.ceilingpic == frontsector.ceilingpic
                    && backsector.floorpic == frontsector.floorpic
                    && backsector.lightlevel == frontsector.lightlevel
                    && curline.sidedef.midtexture == 0) {
                return;
            }

            // If nothing of the previous holds, then we are
            // treating the case of same-level, differently
            // textured floors. ACHTUNG, this caused the "bleeding floor"
            // bug, which is now fixed.
            // Fucking GOTOs....
            ClipPassWallSegment(x1, x2 - 1); // to clippass
            if (DEBUG)
                System.out.println("Exiting AddLine for " + line);
        }

        //
        // R_CheckBBox
        // Checks BSP node/subtree bounding box.
        // Returns true
        // if some part of the bbox might be visible.
        //
        private int[][] checkcoord = { { 3, 0, 2, 1 }, { 3, 0, 2, 0 },
                { 3, 1, 2, 0 }, { 0 }, { 2, 0, 2, 1 }, { 0, 0, 0, 0 },
                { 3, 1, 3, 0 }, { 0 }, { 2, 0, 3, 1 }, { 2, 1, 3, 1 },
                { 2, 1, 3, 0 } };

        /**
         * @param bspcoord
         *        (fixed_t* as bbox)
         * @return
         */
        public boolean CheckBBox(int[] bspcoord) {
            int boxx;
            int boxy;
            int boxpos;

            // fixed_t
            int x1;
            int y1;
            int x2;
            int y2;

            // angle_t
            long angle1;
            long angle2;
            long span;
            long tspan;

            cliprange_t start;

            int sx1;
            int sx2;

            // Find the corners of the box
            // that define the edges from current viewpoint.
            if (view.x <= bspcoord[BOXLEFT])
                boxx = 0;
            else if (view.x < bspcoord[BOXRIGHT])
                boxx = 1;
            else
                boxx = 2;

            if (view.y >= bspcoord[BOXTOP])
                boxy = 0;
            else if (view.y > bspcoord[BOXBOTTOM])
                boxy = 1;
            else
                boxy = 2;

            boxpos = (boxy << 2) + boxx;
            if (boxpos == 5)
                return true;

            x1 = bspcoord[checkcoord[boxpos][0]];
            y1 = bspcoord[checkcoord[boxpos][1]];
            x2 = bspcoord[checkcoord[boxpos][2]];
            y2 = bspcoord[checkcoord[boxpos][3]];

            // check clip list for an open space
            angle1 = view.PointToAngle(x1, y1) - view.angle;
            angle2 = view.PointToAngle(x2, y2) - view.angle;

            angle1 &= BITS32;
            angle2 &= BITS32;

            span = angle1 - angle2;

            span &= BITS32;

            // Sitting on a line?
            if (span >= ANG180)
                return true;

            tspan = angle1 + clipangle;
            tspan &= BITS32;

            if (tspan > CLIPANGLE2) {
                tspan -= CLIPANGLE2;
                tspan &= BITS32;
                // Totally off the left edge?
                if (tspan >= span)
                    return false;

                angle1 = clipangle;
            }
            tspan = (clipangle - angle2) & BITS32;
            ;
            if (tspan > CLIPANGLE2) {
                tspan -= CLIPANGLE2;
                tspan &= BITS32;

                // Totally off the left edge?
                if (tspan >= span)
                    return false;

                angle2 = -clipangle;
                angle2 &= BITS32;
            }

            // Find the first clippost
            // that touches the source post
            // (adjacent pixels are touching).
            angle1 = ((angle1 + ANG90) & BITS32) >>> ANGLETOFINESHIFT;
            angle2 = ((angle2 + ANG90) & BITS32) >>> ANGLETOFINESHIFT;
            sx1 = viewangletox[(int) angle1];
            sx2 = viewangletox[(int) angle2];

            // Does not cross a pixel.
            if (sx1 == sx2)
                return false;
            sx2--;

            int pstart = 0;
            start = solidsegs[pstart];
            // FIXME: possible solidseg overflow here overflows
            while (start.last < sx2 && pstart < MAXSEGS)
                start = solidsegs[pstart++];

            if (sx1 >= start.first && sx2 <= start.last) {
                // The clippost contains the new span.
                return false;
            }

            return true;
        }

        /**
         * R_Subsector Determine floor/ceiling planes. Add sprites of things in
         * sector. Draw one or more line segments. It also alters the visplane
         * list!
         * 
         * @param num
         *        Subsector from subsector_t list in Lever Loader.
         */

        private void Subsector(int num) {
            if (DEBUG)
                System.out.println("\t\tSubSector " + num + " to render");
            int count;
            int line; // pointer into a list of segs instead of seg_t
            subsector_t sub;

            if (RANGECHECK) {
                if (num >= LL.numsubsectors)
                    I.Error("R_Subsector: ss %d with numss = %d", num,
                        LL.numsubsectors);
            }

            sscount++;
            sub = LL.subsectors[num];

            frontsector = sub.sector;
            if (DEBUG)
                System.out.println("Frontsector to render :" + frontsector);
            count = sub.numlines;
            // line = LL.segs[sub.firstline];
            line = sub.firstline;

            if (DEBUG)
                System.out
                        .println("Trying to find an existing FLOOR visplane...");
            if (frontsector.floorheight < view.z) {
                vp_vars.floorplane =
                    vp_vars.FindPlane(frontsector.floorheight,
                        frontsector.floorpic, frontsector.lightlevel);
            } else
                // FIXME: unclear what would happen with a null visplane used
                // It's never checked explicitly for either condition, just
                // called straight.
                vp_vars.floorplane = -1; // in lieu of NULL

            // System.out.println("Trying to find an existing CEILING visplane...");

            if (frontsector.ceilingheight > view.z
                    || frontsector.ceilingpic == TexMan.getSkyFlatNum()) {
                vp_vars.ceilingplane =
                    vp_vars.FindPlane(frontsector.ceilingheight,
                        frontsector.ceilingpic, frontsector.lightlevel);
            } else
                vp_vars.ceilingplane = -1; // In lieu of NULL. Will bomb if
                                           // actually
            // used.

            VIS.AddSprites(frontsector);

            if (DEBUG)
                System.out.println("Enter Addline for SubSector " + num
                        + " count " + count);
            while (count-- > 0) {
                AddLine(LL.segs[line]);
                line++;
            }
            if (DEBUG)
                System.out.println("Exit Addline for SubSector " + num);
        }

        /**
         * RenderBSPNode Renders all subsectors below a given node, traversing
         * subtree recursively. Just call with BSP root.
         */
        public void RenderBSPNode(int bspnum) {
            if (DEBUG)
                System.out.println("Processing BSP Node " + bspnum);

            node_t bsp;
            int side;

            // Found a subsector? Then further decisions are taken, in, well,
            // SubSector.
            if (C2JUtils.flags(bspnum, NF_SUBSECTOR)) {
                if (DEBUG)
                    System.out.println("Subsector found.");
                if (bspnum == -1)
                    Subsector(0);
                else
                    Subsector(bspnum & (~NF_SUBSECTOR));
                return;
            }

            bsp = LL.nodes[bspnum];

            // Decide which side the view point is on.
            side = bsp.PointOnSide(view.x, view.y);
            if (DEBUG)
                System.out.println("\tView side: " + side);

            // Recursively divide front space.
            if (DEBUG)
                System.out.println("\tEnter Front space of " + bspnum);
            RenderBSPNode(bsp.children[side]);
            if (DEBUG)
                System.out.println("\tReturn Front space of " + bspnum);

            // Possibly divide back space.

            if (CheckBBox(bsp.bbox[side ^ 1].bbox)) {
                if (DEBUG)
                    System.out.println("\tEnter Back space of " + bspnum);
                RenderBSPNode(bsp.children[side ^ 1]);
                if (DEBUG)
                    System.out.println("\tReturn Back space of " + bspnum);
            }
        }

    }

    protected abstract class SegDrawer
            implements ISegDrawer {

        protected static final int HEIGHTBITS = 12;

        protected static final int HEIGHTUNIT = (1 << HEIGHTBITS);

        protected final Visplanes vp_vars;

        protected final SegVars seg_vars;

        // Fast blanking buffers.
        protected short[] BLANKFLOORCLIP;

        protected short[] BLANKCEILINGCLIP;

        @Override
        public short[] getBLANKFLOORCLIP() {
            return BLANKFLOORCLIP;
        }

        @Override
        public short[] getBLANKCEILINGCLIP() {
            return BLANKCEILINGCLIP;
        }

        /** fixed_t */
        protected int pixhigh, pixlow, pixhighstep, pixlowstep, topfrac,
                topstep, bottomfrac, bottomstep;

        protected int worldtop, worldbottom, worldhigh, worldlow;

        /** True if any of the segs textures might be visible. */
        protected boolean segtextured;

        /**
         * Clip values are the solid pixel bounding the range. floorclip starts
         * out SCREENHEIGHT ceilingclip starts out -1
         */
        protected short[] floorclip, ceilingclip;

        public final short[] getFloorClip() {
            return floorclip;
        }

        public final short[] getCeilingClip() {
            return ceilingclip;
        }

        /** False if the back side is the same plane. */
        protected boolean markfloor, markceiling;

        protected boolean maskedtexture;

        protected int toptexture;

        protected int bottomtexture;

        protected int midtexture;

        /** angle_t, used after adding ANG90 in StoreWallRange */
        protected long rw_normalangle;

        /** angle to line origin */
        protected long rw_angle1;

        //
        // regular wall
        //
        protected int rw_x;

        protected int rw_stopx;

        protected long rw_centerangle; // angle_t

        /** fixed_t */
        protected int rw_offset, rw_distance, rw_scale, rw_scalestep,
                rw_midtexturemid, rw_toptexturemid, rw_bottomtexturemid;

        public void resetLimits() {
            drawseg_t[] tmp = new drawseg_t[seg_vars.MAXDRAWSEGS];
            System.arraycopy(seg_vars.drawsegs, 0, tmp, 0, seg_vars.MAXDRAWSEGS);

            // Now, that was quite a haircut!.
            seg_vars.drawsegs = tmp;

            // System.out.println("Drawseg buffer cut back to original limit of "+MAXDRAWSEGS);
        }
        
        public void sync(){
            // Nothing required if serial.
        }

        /**
         * R_StoreWallRange A wall segment will be drawn between start and stop
         * pixels (inclusive). This is the only place where
         * markceiling/markfloor can be set. Can only be called from
         * ClipSolidWallSegment and ClipPassWallSegment.
         * 
         * @throws IOException
         */

        public void StoreWallRange(int start, int stop) {

            if (DEBUG2)
                System.out.println("\t\t\t\tStorewallrange called between "
                        + start + " and " + stop);

            int hyp; // fixed_t
            int sineval; // fixed_t
            int distangle;
            long offsetangle; // angle_t
            int vtop; // fixed_t
            int lightnum;
            drawseg_t seg;

            // don't overflow and crash
            if (seg_vars.ds_p == seg_vars.drawsegs.length)
                seg_vars.ResizeDrawsegs();

            if (RANGECHECK) {
                if (start >= view.width || start > stop)
                    I.Error("Bad R_RenderWallRange: %d to %d", start, stop);
            }

            seg = seg_vars.drawsegs[seg_vars.ds_p];

            MyBSP.sidedef = MyBSP.curline.sidedef;
            MyBSP.linedef = MyBSP.curline.linedef;

            // mark the segment as visible for auto map
            MyBSP.linedef.flags |= ML_MAPPED;

            // calculate rw_distance for scale calculation
            rw_normalangle = addAngles(MyBSP.curline.angle, ANG90);

            /*
             * MAES: ok, this is a tricky spot. angle_t's are supposed to be
             * always positive 32-bit unsigned integers, so a subtraction should
             * be always positive by definition, right? WRONG: this fucking spot
             * caused "blind spots" at certain angles because ONLY HERE angles
             * are supposed to be treated as SIGNED and result in differences
             * <180 degrees -_- The only way to coerce this behavior is to cast
             * both as signed ints.
             */
            offsetangle = Math.abs((int) rw_normalangle - (int) rw_angle1);

            if (offsetangle > ANG90)
                offsetangle = ANG90;

            // It should fit even in a signed int, by now.
            distangle = (int) (ANG90 - offsetangle);
            hyp = PointToDist(MyBSP.curline.v1x, MyBSP.curline.v1y);
            sineval = finesine(distangle);
            rw_distance = FixedMul(hyp, sineval);

            seg.x1 = rw_x = start;
            seg.x2 = stop;
            seg.curline = MyBSP.curline;
            /*
             * This is the only place it's ever explicitly assigned. Therefore
             * it always starts at stop+1.
             */
            rw_stopx = stop + 1;

            // calculate scale at both ends and step
            // this is the ONLY place where rw_scale is set.
            seg.scale1 =
                rw_scale =
                    ScaleFromGlobalAngle((view.angle + xtoviewangle[start]));

            if (stop > start) {
                seg.scale2 =
                    ScaleFromGlobalAngle(view.angle + xtoviewangle[stop]);
                seg.scalestep =
                    rw_scalestep = (seg.scale2 - rw_scale) / (stop - start);
            } else {
                // UNUSED: try to fix the stretched line bug
                /*
                 * #if 0 if (rw_distance < FRACUNIT/2) { fixed_t trx,try;
                 * fixed_t gxt,gyt; trx = curline.v1.x - viewx; try =
                 * curline.v1.y - viewy; gxt = FixedMul(trx,viewcos); gyt =
                 * -FixedMul(try,viewsin); seg.scale1 = FixedDiv(projection,
                 * gxt-gyt)<<detailshift; } #endif
                 */
                seg.scale2 = seg.scale1;
            }

            // calculate texture boundaries
            // and decide if floor / ceiling marks are needed
            worldtop = MyBSP.frontsector.ceilingheight - view.z;
            worldbottom = MyBSP.frontsector.floorheight - view.z;

            midtexture = toptexture = bottomtexture = 0;
            maskedtexture = false;
            seg.setMaskedTextureCol(null, 0);
            // seg.maskedtexturecol = null;

            if (MyBSP.backsector == null) {
                // single sided line
                midtexture =
                    TexMan.getTextureTranslation(MyBSP.sidedef.midtexture);
                // a single sided line is terminal, so it must mark ends
                markfloor = markceiling = true;
                if ((MyBSP.linedef.flags & ML_DONTPEGBOTTOM) != 0) {
                    vtop =
                        MyBSP.frontsector.floorheight
                                + TexMan.getTextureheight(MyBSP.sidedef.midtexture);
                    // bottom of texture at bottom
                    rw_midtexturemid = vtop - view.z;
                } else {
                    // top of texture at top
                    rw_midtexturemid = worldtop;
                }
                rw_midtexturemid += MyBSP.sidedef.rowoffset;

                seg.silhouette = SIL_BOTH;
                seg.setSprTopClip(view.screenheightarray, 0);
                seg.setSprBottomClip(view.negonearray, 0);
                seg.bsilheight = Integer.MAX_VALUE;
                seg.tsilheight = Integer.MIN_VALUE;
            } else {
                // two sided line
                seg.setSprTopClip(null, 0);
                seg.setSprBottomClip(null, 0);
                seg.silhouette = 0;

                if (MyBSP.frontsector.floorheight > MyBSP.backsector.floorheight) {
                    seg.silhouette = SIL_BOTTOM;
                    seg.bsilheight = MyBSP.frontsector.floorheight;
                } else if (MyBSP.backsector.floorheight > view.z) {
                    seg.silhouette = SIL_BOTTOM;
                    seg.bsilheight = Integer.MAX_VALUE;
                    // seg.sprbottomclip = negonearray;
                }

                if (MyBSP.frontsector.ceilingheight < MyBSP.backsector.ceilingheight) {
                    seg.silhouette |= SIL_TOP;
                    seg.tsilheight = MyBSP.frontsector.ceilingheight;
                } else if (MyBSP.backsector.ceilingheight < view.z) {
                    seg.silhouette |= SIL_TOP;
                    seg.tsilheight = Integer.MIN_VALUE;
                    // seg.sprtopclip = screenheightarray;
                }

                if (MyBSP.backsector.ceilingheight <= MyBSP.frontsector.floorheight) {
                    seg.setSprBottomClip(view.negonearray, 0);
                    seg.bsilheight = Integer.MAX_VALUE;
                    seg.silhouette |= SIL_BOTTOM;
                }

                if (MyBSP.backsector.floorheight >= MyBSP.frontsector.ceilingheight) {
                    seg.setSprTopClip(view.screenheightarray, 0);
                    seg.tsilheight = Integer.MIN_VALUE;
                    seg.silhouette |= SIL_TOP;
                }

                worldhigh = MyBSP.backsector.ceilingheight - view.z;
                worldlow = MyBSP.backsector.floorheight - view.z;

                // hack to allow height changes in outdoor areas
                if (MyBSP.frontsector.ceilingpic == TexMan.getSkyFlatNum()
                        && MyBSP.backsector.ceilingpic == TexMan
                                .getSkyFlatNum()) {
                    worldtop = worldhigh;
                }

                if (worldlow != worldbottom
                        || MyBSP.backsector.floorpic != MyBSP.frontsector.floorpic
                        || MyBSP.backsector.lightlevel != MyBSP.frontsector.lightlevel) {
                    markfloor = true;
                } else {
                    // same plane on both sides
                    markfloor = false;
                }

                if (worldhigh != worldtop
                        || MyBSP.backsector.ceilingpic != MyBSP.frontsector.ceilingpic
                        || MyBSP.backsector.lightlevel != MyBSP.frontsector.lightlevel) {
                    markceiling = true;
                } else {
                    // same plane on both sides
                    markceiling = false;
                }

                if (MyBSP.backsector.ceilingheight <= MyBSP.frontsector.floorheight
                        || MyBSP.backsector.floorheight >= MyBSP.frontsector.ceilingheight) {
                    // closed door
                    markceiling = markfloor = true;
                }

                if (worldhigh < worldtop) {
                    // top texture
                    toptexture =
                        TexMan.getTextureTranslation(MyBSP.sidedef.toptexture);
                    if ((MyBSP.linedef.flags & ML_DONTPEGTOP) != 0) {
                        // top of texture at top
                        rw_toptexturemid = worldtop;
                    } else {
                        vtop =
                            MyBSP.backsector.ceilingheight
                                    + TexMan.getTextureheight(MyBSP.sidedef.toptexture);

                        // bottom of texture
                        rw_toptexturemid = vtop - view.z;
                    }
                }
                if (worldlow > worldbottom) {
                    // bottom texture
                    bottomtexture =
                        TexMan.getTextureTranslation(MyBSP.sidedef.bottomtexture);

                    if ((MyBSP.linedef.flags & ML_DONTPEGBOTTOM) != 0) {
                        // bottom of texture at bottom
                        // top of texture at top
                        rw_bottomtexturemid = worldtop;
                    } else
                        // top of texture at top
                        rw_bottomtexturemid = worldlow;
                }
                rw_toptexturemid += MyBSP.sidedef.rowoffset;
                rw_bottomtexturemid += MyBSP.sidedef.rowoffset;

                // allocate space for masked texture tables
                if (MyBSP.sidedef.midtexture != 0) {
                    // masked midtexture
                    maskedtexture = true;
                    seg_vars.maskedtexturecol = vp_vars.openings;
                    seg_vars.pmaskedtexturecol = vp_vars.lastopening - rw_x;
                    seg.setMaskedTextureCol(seg_vars.maskedtexturecol,
                        seg_vars.pmaskedtexturecol);
                    vp_vars.lastopening += rw_stopx - rw_x;
                }
            }

            // calculate rw_offset (only needed for textured lines)
            segtextured =
                (((midtexture | toptexture | bottomtexture) != 0) | maskedtexture);

            if (segtextured) {
                offsetangle = addAngles(rw_normalangle, -rw_angle1);

                // Another "tricky spot": negative of an unsigned number?
                if (offsetangle > ANG180)
                    offsetangle = (-(int) offsetangle) & BITS32;

                if (offsetangle > ANG90)
                    offsetangle = ANG90;

                sineval = finesine(offsetangle);
                rw_offset = FixedMul(hyp, sineval);

                // Another bug: we CAN'T assume that the result won't wrap
                // around.
                // If that assumption is made, then texture alignment issues
                // appear
                if (((rw_normalangle - rw_angle1) & BITS32) < ANG180)
                    rw_offset = -rw_offset;

                rw_offset += MyBSP.sidedef.textureoffset + MyBSP.curline.offset;
                // This is OK, however: we can add as much shit as we want,
                // as long as we trim it to the 32 LSB. Proof as to why
                // this is always true is left as an exercise to the reader.
                rw_centerangle = (ANG90 + view.angle - rw_normalangle) & BITS32;

                // calculate light table
                // use different light tables
                // for horizontal / vertical / diagonal
                // OPTIMIZE: get rid of LIGHTSEGSHIFT globally
                if (colormaps.fixedcolormap == null) {
                    lightnum =
                        (MyBSP.frontsector.lightlevel >> LIGHTSEGSHIFT)
                                + colormaps.extralight;

                    if (MyBSP.curline.v1y == MyBSP.curline.v2y)
                        lightnum--;
                    else if (MyBSP.curline.v1x == MyBSP.curline.v2x)
                        lightnum++;

                    if (lightnum < 0)
                        colormaps.walllights = colormaps.scalelight[0];
                    else if (lightnum >= LIGHTLEVELS)
                        colormaps.walllights =
                            colormaps.scalelight[LIGHTLEVELS - 1];
                    else
                        colormaps.walllights = colormaps.scalelight[lightnum];
                }
            }

            // if a floor / ceiling plane is on the wrong side
            // of the view plane, it is definitely invisible
            // and doesn't need to be marked.

            if (MyBSP.frontsector.floorheight >= view.z) {
                // above view plane
                markfloor = false;
            }

            if (MyBSP.frontsector.ceilingheight <= view.z
                    && MyBSP.frontsector.ceilingpic != TexMan.getSkyFlatNum()) {
                // below view plane
                markceiling = false;
            }

            // calculate incremental stepping values for texture edges
            worldtop >>= 4;
            worldbottom >>= 4;

            topstep = -FixedMul(rw_scalestep, worldtop);
            topfrac = (view.centeryfrac >> 4) - FixedMul(worldtop, rw_scale);

            bottomstep = -FixedMul(rw_scalestep, worldbottom);
            bottomfrac =
                (view.centeryfrac >> 4) - FixedMul(worldbottom, rw_scale);

            if (MyBSP.backsector != null) {
                worldhigh >>= 4;
                worldlow >>= 4;

                if (worldhigh < worldtop) {
                    pixhigh =
                        (view.centeryfrac >> 4) - FixedMul(worldhigh, rw_scale);
                    pixhighstep = -FixedMul(rw_scalestep, worldhigh);
                }

                if (worldlow > worldbottom) {
                    pixlow =
                        (view.centeryfrac >> 4) - FixedMul(worldlow, rw_scale);
                    pixlowstep = -FixedMul(rw_scalestep, worldlow);
                }
            }

            // render it
            if (markceiling) {
                // System.out.println("Markceiling");
                vp_vars.ceilingplane =
                    vp_vars.CheckPlane(vp_vars.ceilingplane, rw_x, rw_stopx - 1);
            }

            if (markfloor) {
                // System.out.println("Markfloor");
                vp_vars.floorplane =
                    vp_vars.CheckPlane(vp_vars.floorplane, rw_x, rw_stopx - 1);
            }

            RenderSegLoop();

            // After rendering is actually performed, clipping is set.

            // save sprite clipping info ... no top clipping?
            if ((C2JUtils.flags(seg.silhouette, SIL_TOP) || maskedtexture)
                    && seg.nullSprTopClip()) {

                // memcpy (lastopening, ceilingclip+start, 2*(rw_stopx-start));
                System.arraycopy(ceilingclip, start, vp_vars.openings,
                    vp_vars.lastopening, rw_stopx - start);

                seg.setSprTopClip(vp_vars.openings, vp_vars.lastopening - start);
                // seg.setSprTopClipPointer();
                vp_vars.lastopening += rw_stopx - start;
            }
            // no floor clipping?
            if ((C2JUtils.flags(seg.silhouette, SIL_BOTTOM) || maskedtexture)
                    && seg.nullSprBottomClip()) {
                // memcpy (lastopening, floorclip+start, 2*(rw_stopx-start));
                System.arraycopy(floorclip, start, vp_vars.openings,
                    vp_vars.lastopening, rw_stopx - start);
                seg.setSprBottomClip(vp_vars.openings, vp_vars.lastopening
                        - start);
                vp_vars.lastopening += rw_stopx - start;
            }

            if (maskedtexture && C2JUtils.flags(seg.silhouette, SIL_TOP)) {
                seg.silhouette |= SIL_TOP;
                seg.tsilheight = Integer.MIN_VALUE;
            }
            if (maskedtexture && (seg.silhouette & SIL_BOTTOM) == 0) {
                seg.silhouette |= SIL_BOTTOM;
                seg.bsilheight = Integer.MAX_VALUE;
            }
            seg_vars.ds_p++;
        }

        /**
         * R_RenderSegLoop Draws zero, one, or two textures (and possibly a
         * masked texture) for walls. Can draw or mark the starting pixel of
         * floor and ceiling textures. Also sets the actual sprite clipping info
         * (where sprites should be cut) Since rw_x ranges are non-overlapping,
         * rendering all walls means completing the clipping list as well. The
         * only difference between the parallel and the non-parallel version is
         * that the parallel doesn't draw immediately but rather, generates
         * RWIs. This can surely be unified to avoid replicating code. CALLED:
         * CORE LOOPING ROUTINE.
         */

        protected void RenderSegLoop() {
            int angle; // angle_t
            int index;
            int yl; // low
            int yh; // hight
            int mid;
            int texturecolumn = 0; // fixed_t
            int top;
            int bottom;

            for (; rw_x < rw_stopx; rw_x++) {
                // mark floor / ceiling areas
                yl = (topfrac + HEIGHTUNIT - 1) >> HEIGHTBITS;

                // no space above wall?
                if (yl < ceilingclip[rw_x] + 1)
                    yl = ceilingclip[rw_x] + 1;

                if (markceiling) {
                    top = ceilingclip[rw_x] + 1;
                    bottom = yl - 1;

                    if (bottom >= floorclip[rw_x])
                        bottom = floorclip[rw_x] - 1;

                    if (top <= bottom) {
                        vp_vars.visplanes[vp_vars.ceilingplane].setTop(rw_x,
                            (char) top);
                        vp_vars.visplanes[vp_vars.ceilingplane].setBottom(rw_x,
                            (char) bottom);
                    }
                }

                yh = bottomfrac >> HEIGHTBITS;

                if (yh >= floorclip[rw_x])
                    yh = floorclip[rw_x] - 1;

                // A particular seg has been identified as a floor marker.

                if (markfloor) {
                    top = yh + 1;
                    bottom = floorclip[rw_x] - 1;
                    if (top <= ceilingclip[rw_x])
                        top = ceilingclip[rw_x] + 1;
                    if (top <= bottom) {
                        vp_vars.visplanes[vp_vars.floorplane].setTop(rw_x,
                            (char) top);
                        vp_vars.visplanes[vp_vars.floorplane].setBottom(rw_x,
                            (char) bottom);
                    }
                }

                // texturecolumn and lighting are independent of wall tiers
                if (segtextured) {
                    // calculate texture offset

                    // CAREFUL: a VERY anomalous point in the code. Their sum is
                    // supposed
                    // to give an angle not exceeding 45 degrees (or an index of
                    // 0x0FFF after
                    // shifting). If added with pure unsigned rules, this
                    // doesn't hold anymore,
                    // not even if accounting for overflow.
                    angle =
                        Tables.toBAMIndex(rw_centerangle
                                + (int) xtoviewangle[rw_x]);

                    // FIXME: We are accessing finetangent here, the code seems
                    // pretty confident in that angle won't exceed 4K no matter
                    // what.
                    // But xtoviewangle alone can yield 8K when shifted.
                    // This usually only overflows if we idclip and look at
                    // certain directions (probably angles get fucked up),
                    // however it seems rare
                    // enough to just "swallow" the exception. You can eliminate
                    // it by anding
                    // with 0x1FFF if you're so inclined.
                    // FIXED by allowing overflow. See Tables for details.

                    texturecolumn =
                        rw_offset - FixedMul(finetangent[angle], rw_distance);
                    texturecolumn >>= FRACBITS;
                    // calculate lighting
                    index = rw_scale >> LIGHTSCALESHIFT;

                    if (index >= MAXLIGHTSCALE)
                        index = MAXLIGHTSCALE - 1;

                    dcvars.dc_colormap = colormaps.walllights[index];
                    dcvars.dc_x = rw_x;
                    dcvars.dc_iscale = (int) (0xffffffffL / rw_scale);
                }

                // draw the wall tiers
                if (midtexture != 0) {
                    // single sided line
                    dcvars.dc_yl = yl;
                    dcvars.dc_yh = yh;
                    dcvars.dc_texheight =
                        TexMan.getTextureheight(midtexture) >> FRACBITS; // killough
                    dcvars.dc_texturemid = rw_midtexturemid;
                    dcvars.dc_source_ofs = 0;
                    dcvars.dc_source =
                        TexMan.GetCachedColumn(midtexture, texturecolumn);
                    CompleteColumn();
                    ceilingclip[rw_x] = (short) view.height;
                    floorclip[rw_x] = -1;
                } else {
                    // two sided line
                    if (toptexture != 0) {
                        // top wall
                        mid = pixhigh >> HEIGHTBITS;
                        pixhigh += pixhighstep;

                        if (mid >= floorclip[rw_x])
                            mid = floorclip[rw_x] - 1;

                        if (mid >= yl) {
                            dcvars.dc_yl = yl;
                            dcvars.dc_yh = mid;
                            dcvars.dc_texturemid = rw_toptexturemid;
                            dcvars.dc_texheight =
                                TexMan.getTextureheight(toptexture) >> FRACBITS;
                            dcvars.dc_source =
                                (T) TexMan.GetCachedColumn(toptexture,
                                    texturecolumn);
                            dcvars.dc_source_ofs = 0;
                            if (dcvars.dc_colormap == null)
                                System.out.println("Two-sided");
                            CompleteColumn();
                            ceilingclip[rw_x] = (short) mid;
                        } else
                            ceilingclip[rw_x] = (short) (yl - 1);
                    } else {
                        // no top wall
                        if (markceiling)
                            ceilingclip[rw_x] = (short) (yl - 1);
                    }

                    if (bottomtexture != 0) {
                        // bottom wall
                        mid = (pixlow + HEIGHTUNIT - 1) >> HEIGHTBITS;
                        pixlow += pixlowstep;

                        // no space above wall?
                        if (mid <= ceilingclip[rw_x])
                            mid = ceilingclip[rw_x] + 1;

                        if (mid <= yh) {
                            dcvars.dc_yl = mid;
                            dcvars.dc_yh = yh;
                            dcvars.dc_texturemid = rw_bottomtexturemid;
                            dcvars.dc_texheight =
                                TexMan.getTextureheight(bottomtexture) >> FRACBITS;
                            dcvars.dc_source =
                                (T) TexMan.GetCachedColumn(bottomtexture,
                                    texturecolumn);
                            dcvars.dc_source_ofs = 0;
                            CompleteColumn();

                            floorclip[rw_x] = (short) mid;
                        } else
                            floorclip[rw_x] = (short) (yh + 1);
                    } else {
                        // no bottom wall
                        if (markfloor)
                            floorclip[rw_x] = (short) (yh + 1);
                    }

                    if (maskedtexture) {
                        // save texturecol
                        // for backdrawing of masked mid texture
                        seg_vars.maskedtexturecol[seg_vars.pmaskedtexturecol
                                + rw_x] = (short) texturecolumn;
                    }
                }

                rw_scale += rw_scalestep;
                topfrac += topstep;
                bottomfrac += bottomstep;
            }
        }

        @Override
        public void ClearClips() {
            System.arraycopy(BLANKFLOORCLIP, 0, floorclip, 0, view.width);
            System.arraycopy(BLANKCEILINGCLIP, 0, ceilingclip, 0, view.width);
        }

        /**
         * Called from RenderSegLoop. This should either invoke the column
         * function, or store a wall rendering instruction in the parallel
         * version. It's the only difference between the parallel and serial
         * renderer, BTW. So override and implement accordingly.
         */
        protected abstract void CompleteColumn();

        @Override
        public void ExecuteSetViewSize(int viewwidth) {
            for (int i = 0; i < viewwidth; i++) {
                BLANKFLOORCLIP[i] = (short) view.height;
                BLANKCEILINGCLIP[i] = -1;
            }
        }
        
        public void CompleteRendering(){
            // Nothing to do for serial. 
        }

        protected column_t col;

        public SegDrawer(Renderer<?, ?> R) {
            this.vp_vars = R.getVPVars();
            this.seg_vars = R.getSegVars();
            col = new column_t();
            seg_vars.drawsegs = new drawseg_t[seg_vars.MAXDRAWSEGS];
            C2JUtils.initArrayOfObjects(seg_vars.drawsegs);
        }

        /**
         * R_ScaleFromGlobalAngle Returns the texture mapping scale for the
         * current line (horizontal span) at the given angle. rw_distance must
         * be calculated first.
         */

        protected final int ScaleFromGlobalAngle(long visangle) {
            int scale; // fixed_t
            long anglea;
            long angleb;
            int sinea;
            int sineb;
            int num; // fixed_t
            int den;

            // UNUSED
            /*
             * { fixed_t dist; fixed_t z; fixed_t sinv; fixed_t cosv; sinv =
             * finesine[(visangle-rw_normalangle)>>ANGLETOFINESHIFT]; dist =
             * FixedDiv (rw_distance, sinv); cosv =
             * finecosine[(viewangle-visangle)>>ANGLETOFINESHIFT]; z =
             * abs(FixedMul (dist, cosv)); scale = FixedDiv(projection, z);
             * return scale; }
             */

            anglea = (ANG90 + visangle - view.angle) & BITS32;
            angleb = (ANG90 + visangle - rw_normalangle) & BITS32;

            // both sines are allways positive
            sinea = finesine(anglea);
            sineb = finesine(angleb);
            num = FixedMul(view.projection, sineb) << view.detailshift;
            den = FixedMul(rw_distance, sinea);

            if (den > num >> 16) {
                scale = FixedDiv(num, den);

                if (scale > 64 * FRACUNIT)
                    scale = 64 * FRACUNIT;
                else if (scale < 256)
                    scale = 256;
            } else
                scale = 64 * FRACUNIT;

            return scale;
        }

        public void setGlobalAngle(long angle) {
            this.rw_angle1 = angle;
        }
        
        public void initScaling() {
            this.floorclip = new short[vs.getScreenWidth()];
            this.ceilingclip = new short[vs.getScreenWidth()];
            BLANKFLOORCLIP = new short[vs.getScreenWidth()];
            BLANKCEILINGCLIP = new short[vs.getScreenWidth()];
        }

        @Override
        public void setVideoScale(IVideoScale vs) {
            this.vs = vs;
        }

        IVideoScale vs;
    }

    protected interface IPlaneDrawer
            extends IVideoScaleAware {

        void InitPlanes();

        void MapPlane(int y, int x1, int x2);

        void DrawPlanes();

        int[] getDistScale();
        
        /** Sync up in case there's concurrent planes/walls rendering */
        public void sync();

    }

    protected interface ISegDrawer
            extends IVideoScaleAware, ILimitResettable {
        public void ClearClips();

        short[] getBLANKCEILINGCLIP();

        short[] getBLANKFLOORCLIP();

        short[] getFloorClip();

        short[] getCeilingClip();

        void ExecuteSetViewSize(int viewwidth);

        public void setGlobalAngle(long angle1);

        public void StoreWallRange(int first, int last);

        /** If there is anything to do beyond the BPS traversal,
         * e.g. parallel rendering
         */
        public void CompleteRendering();

        /** Sync up in case there's concurrent planes/walls rendering */
        public void sync();
    }

    protected final class Planes
            extends PlaneDrawer<T, V> {

        public Planes(RendererState<T, V> R) {
            super(R);
        }

        /**
         * R_DrawPlanes At the end of each frame. This also means that visplanes
         * must have been set BEFORE we called this function. Therefore, look
         * for errors behind.
         * 
         * @throws IOException
         */

        @Override
        public final void DrawPlanes() {
            if (DEBUG)
                System.out.println(" >>>>>>>>>>>>>>>>>>>>>   DrawPlanes: "
                        + vp_vars.lastvisplane);
            visplane_t pln = null; // visplane_t
            int light;
            int x;
            int stop;
            int angle;

            if (RANGECHECK) {
                rangeCheckErrors();
            }

            for (int pl = 0; pl < vp_vars.lastvisplane; pl++) {
                pln = vp_vars.visplanes[pl];
                if (DEBUG2)
                    System.out.println(pln);

                if (pln.minx > pln.maxx)
                    continue;
                // sky flat
                if (pln.picnum == TexMan.getSkyFlatNum()) {
                    // Cache skytexture stuff here. They aren't going to change
                    // while
                    // being drawn, after all, are they?
                    int skytexture = TexMan.getSkyTexture();
                    skydcvars.dc_texheight =
                        TexMan.getTextureheight(skytexture) >> FRACBITS;
                    skydcvars.dc_iscale =
                        vpvars.getSkyScale() >> view.detailshift;

                    /*
                     * Sky is allways drawn full bright, i.e. colormaps[0] is
                     * used. Because of this hack, sky is not affected by INVUL
                     * inverse mapping.
                     */
                    skydcvars.dc_colormap = colormap.colormaps[0];
                    skydcvars.dc_texturemid = TexMan.getSkyTextureMid();
                    for (x = pln.minx; x <= pln.maxx; x++) {

                        skydcvars.dc_yl = pln.getTop(x);
                        skydcvars.dc_yh = pln.getBottom(x);

                        if (skydcvars.dc_yl <= skydcvars.dc_yh) {
                            angle =
                                (int) (addAngles(view.angle, xtoviewangle[x]) >>> ANGLETOSKYSHIFT);
                            skydcvars.dc_x = x;
                            // Optimized: texheight is going to be the same
                            // during normal skies drawing...right?
                            skydcvars.dc_source =
                                TexMan.GetCachedColumn(skytexture, angle);
                            colfunc.sky.invoke();
                        }
                    }
                    continue;
                }

                // regular flat
                dsvars.ds_source = TexMan.getSafeFlat(pln.picnum);

                planeheight = Math.abs(pln.height - view.z);
                light = (pln.lightlevel >> LIGHTSEGSHIFT) + colormap.extralight;

                if (light >= LIGHTLEVELS)
                    light = LIGHTLEVELS - 1;

                if (light < 0)
                    light = 0;

                planezlight = colormap.zlight[light];

                // We set those values at the border of a plane's top to a
                // "sentinel" value...ok.
                pln.setTop(pln.maxx + 1, visplane_t.SENTINEL);
                pln.setTop(pln.minx - 1, visplane_t.SENTINEL);

                stop = pln.maxx + 1;

                for (x = pln.minx; x <= stop; x++) {
                    MakeSpans(x, pln.getTop(x - 1), pln.getBottom(x - 1),
                        pln.getTop(x), pln.getBottom(x));
                }

                // Z_ChangeTag (ds_source, PU_CACHE);
            }
        }

    } // End Plane class

    // /////////////////////// LIGHTS, POINTERS, COLORMAPS ETC. ////////////////

    // /// FROM R_DATA, R_MAIN , R_DRAW //////////

    /**
     * OK< this is supposed to "peg" into screen buffer 0. It will work AS LONG
     * AS SOMEONE FUCKING ACTUALLY SETS IT !!!!
     */
    protected V screen;

    protected static final boolean RANGECHECK = false;

    /**
     * These are actually offsets inside screen 0 (or any screen). Therefore
     * anything using them should "draw" inside screen 0
     */
    protected int[] ylookup = new int[MAXHEIGHT];

    /** Columns offset to set where?! */
    protected int[] columnofs = new int[MAXWIDTH];

    /**
     * General purpose. Used for solid walls and as an intermediary for
     * threading
     */

    protected ColVars<T, V> dcvars;

    /** Used for spans */

    protected SpanVars<T, V> dsvars;

    // Used for sky drawer, to avoid clashing with shared dcvars
    protected ColVars<T, V> skydcvars;

    /**
     * Masked drawing functions get "pegged" to this set of dcvars, passed upon
     * initialization. However, multi-threaded vars are better off carrying each
     * their own ones.
     */
    protected ColVars<T, V> maskedcvars;

    /**
     * e6y: wide-res Borrowed from PrBoom+;
     */

    /*
     * protected int wide_centerx, wide_ratio, wide_offsetx, wide_offset2x,
     * wide_offsety, wide_offset2y; protected final base_ratio_t[]
     * BaseRatioSizes = { new base_ratio_t(960, 600, 0, 48, 1.333333f), // 4:3
     * new base_ratio_t(1280, 450, 0, 48 * 3 / 4, 1.777777f), // 16:9 new
     * base_ratio_t(1152, 500, 0, 48 * 5 / 6, 1.6f), // 16:10 new
     * base_ratio_t(960, 600, 0, 48, 1.333333f), new base_ratio_t(960, 640,
     * (int) (6.5 * FRACUNIT), 48 * 15 / 16, 1.25f) // 5:4 };
     */

    /** just for profiling purposes */
    protected int framecount;

    protected int sscount;

    protected int linecount;

    protected int loopcount;

    //
    // precalculated math tables
    //
    protected long clipangle;

    // Set to 2*clipangle later.
    protected long CLIPANGLE2;

    // The viewangletox[viewangle + FINEANGLES/4] lookup
    // maps the visible view angles to screen X coordinates,
    // flattening the arc to a flat projection plane.
    // There will be many angles mapped to the same X.

    protected final int[] viewangletox = new int[FINEANGLES / 2];

    /**
     * The xtoviewangle[] table maps a screen pixel to the lowest viewangle that
     * maps back to x ranges from clipangle to -clipangle.
     */
    protected long[] xtoviewangle;// MAES: to resize

    // UNUSED.
    // The finetangentgent[angle+FINEANGLES/4] table
    // holds the fixed_t tangent values for view angles,
    // ranging from MININT to 0 to MAXINT.
    // fixed_t finetangent[FINEANGLES/2];

    // fixed_t finesine[5*FINEANGLES/4];
    // MAES: uh oh. So now all these ints must become finesines? fuck that.
    // Also wtf @ this hack....this points to approx 1/4th of the finesine
    // table, but what happens if I read past it?
    // int[] finecosine = finesine[FINEANGLES/4];

    /*
     * MAES: what's going on with light tables here. OK...so these should be
     * "unsigned bytes", since, after all, they'll be used as pointers inside an
     * array to finally pick a color, so they should be expanded to shorts.
     */

    // //////////// SOME UTILITY METHODS /////////////

    /**
     * Assigns a point of view before calling PointToAngle CAREFUL: this isn't a
     * pure function, as it alters the renderer's state!
     */

    public final long PointToAngle2(int x1, int y1, int x2, int y2) {
        // Careful with assignments...
        view.x = x1;
        view.y = y1;

        return view.PointToAngle(x2, y2);
    }

    //
    // R_InitPointToAngle
    //
    /*
     * protected final void InitPointToAngle () { // UNUSED - now getting from
     * tables.c if (false){ int i; long t; float f; // // slope (tangent) to
     * angle lookup // for (i=0 ; i<=SLOPERANGE ; i++) { f = (float) Math.atan(
     * (double)(i/SLOPERANGE )/(3.141592657*2)); t = (long) (0xffffffffL*f);
     * tantoangle[i] = (int) t; } } }
     */

    /**
     * Public, static, stateless version of PointToAngle2. Call this one when
     * "renderless" use of PointToAngle2 is required.
     */

    public final static long PointToAngle(int viewx, int viewy, int x, int y) {
        // MAES: note how we don't use &BITS32 here. That is because
        // we know that the maximum possible value of tantoangle is angle
        // This way, we are actually working with vectors emanating
        // from our current position.
        x -= viewx;
        y -= viewy;

        if ((x == 0) && (y == 0))
            return 0;

        if (x >= 0) {
            // x >=0
            if (y >= 0) {
                // y>= 0

                if (x > y) {
                    // octant 0
                    return tantoangle[SlopeDiv(y, x)];
                } else {
                    // octant 1
                    return (ANG90 - 1 - tantoangle[SlopeDiv(x, y)]);
                }
            } else {
                // y<0
                y = -y;

                if (x > y) {
                    // octant 8
                    return (-tantoangle[SlopeDiv(y, x)]);
                } else {
                    // octant 7
                    return (ANG270 + tantoangle[SlopeDiv(x, y)]);
                }
            }
        } else {
            // x<0
            x = -x;

            if (y >= 0) {
                // y>= 0
                if (x > y) {
                    // octant 3
                    return (ANG180 - 1 - tantoangle[SlopeDiv(y, x)]);
                } else {
                    // octant 2
                    return (ANG90 + tantoangle[SlopeDiv(x, y)]);
                }
            } else {
                // y<0
                y = -y;

                if (x > y) {
                    // octant 4
                    return (ANG180 + tantoangle[SlopeDiv(y, x)]);
                } else {
                    // octant 5
                    return (ANG270 - 1 - tantoangle[SlopeDiv(x, y)]);
                }
            }
        }
        // This is actually unreachable.
        // return 0;
    }

    //
    // R_InitTables
    //
    protected final void InitTables() {
        // UNUSED: now getting from tables.c
        /*
         * int i; float a; float fv; int t; // viewangle tangent table for (i=0
         * ; i<FINEANGLES/2 ; i++) { a = (i-FINEANGLES/4+0.5)*PI*2/FINEANGLES;
         * fv = FRACUNIT*tan (a); t = fv; finetangent[i] = t; } // finesine
         * table for (i=0 ; i<5*FINEANGLES/4 ; i++) { // OPTIMIZE: mirro.. a =
         * (i+0.5)*PI*2/FINEANGLES; t = FRACUNIT*sin (a); finesine[i] = t; }
         */

    }

    /**
     * R_PointToDist
     * 
     * @param x
     *        fixed_t
     * @param y
     *        fixed_t
     * @return
     */

    protected final int PointToDist(int x, int y) {
        int angle;
        int dx;
        int dy;
        int temp;
        int dist;

        dx = Math.abs(x - view.x);
        dy = Math.abs(y - view.y);

        // If something is farther north/south than west/east, it gets swapped.
        // Probably as a crude way to avoid divisions by zero. This divides
        // the field into octants, rather than quadrants, where the biggest
        // angle to
        // consider is 45...right? So dy/dx can never exceed 1.0, in theory.

        if (dy > dx) {
            temp = dx;
            dx = dy;
            dy = temp;
        }

        // If one or both of the distances are *exactly* zero at this point,
        // then this means that the wall is in your face anyway, plus we want to
        // avoid a division by zero. So you get zero.
        if (dx == 0)
            return 0;

        /*
         * If dx is zero, this is going to bomb. Fixeddiv will return MAXINT aka
         * 7FFFFFFF, >> DBITS will make it 3FFFFFF, which is more than enough to
         * break tantoangle[]. In the original C code, this probably didn't
         * matter: there would probably be garbage orientations thrown all
         * around. However this is unacceptable in Java. OK, so the safeguard
         * above prevents that. Still... this method is only called once per
         * visible wall per frame, so one check more or less at this point won't
         * change much. It's better to be safe than sorry.
         */

        // This effectively limits the angle to
        // angle = Math.max(FixedDiv(dy, dx), 2048) >> DBITS;
        angle = (FixedDiv(dy, dx) & 0x1FFFF) >> DBITS;

        // Since the division will be 0xFFFF at most, DBITS will restrict
        // the maximum angle index to 7FF, about 45, so adding ANG90 with
        // no other safeguards is OK.
        angle = (int) ((tantoangle[angle] + ANG90) >> ANGLETOFINESHIFT);

        // use as cosine
        dist = FixedDiv(dx, finesine[angle]);

        return dist;
    }

    // //////////// COMMON RENDERING GLOBALS ////////////////

    // //////////////// COLUMN AND SPAN FUNCTIONS //////////////

    protected ColFuncs<T, V> colfunc;

    // Keep two sets of functions.
    protected ColFuncs<T, V> colfunchi;

    protected ColFuncs<T, V> colfunclow;

    protected final void setHiColFuns() {
        colfunchi.main = colfunchi.base = DrawColumn;
        colfunchi.masked = DrawColumnMasked;
        colfunchi.fuzz = DrawFuzzColumn;
        colfunchi.trans = DrawTranslatedColumn;
        colfunchi.glass = DrawTLColumn;
        colfunchi.player = DrawColumnPlayer;
        colfunchi.sky = DrawColumnSkies;
    }

    protected final void setLowColFuns() {
        colfunclow.main = colfunclow.base = DrawColumnLow;
        colfunclow.masked = DrawColumnMaskedLow;
        colfunclow.fuzz = DrawFuzzColumnLow;
        colfunclow.trans = DrawTranslatedColumnLow;
        colfunclow.glass = DrawTLColumn;
        colfunclow.player = DrawColumnMaskedLow;
        colfunclow.sky = DrawColumnSkiesLow;
    }
    
    public ColFuncs<T,V> getColFuncsHi(){
        return this.colfunchi;
    }
   
    public ColFuncs<T,V> getColFuncsLow(){
        return this.colfunclow;
    }
    
    public ColVars<T, V> getMaskedDCVars(){
        return this.maskedcvars;
    }
    
    
    // These column functions are "fixed" for a given renderer, and are
    // not used directly, but only after passing them to colfuncs

    protected DoomColumnFunction<T, V> DrawTranslatedColumn;

    protected DoomColumnFunction<T, V> DrawTranslatedColumnLow;

    protected DoomColumnFunction<T, V> DrawColumnPlayer;

    protected DoomColumnFunction<T, V> DrawColumnSkies;

    protected DoomColumnFunction<T, V> DrawColumnSkiesLow;

    protected DoomColumnFunction<T, V> DrawFuzzColumn;

    protected DoomColumnFunction<T, V> DrawFuzzColumnLow;

    protected DoomColumnFunction<T, V> DrawColumn;

    protected DoomColumnFunction<T, V> DrawColumnLow;

    protected DoomColumnFunction<T, V> DrawColumnMasked;

    protected DoomColumnFunction<T, V> DrawColumnMaskedLow;

    protected DoomColumnFunction<T, V> DrawTLColumn;

    /** to be set in UnifiedRenderer */
    protected DoomSpanFunction<T, V> DrawSpan, DrawSpanLow;

    // ////////////// r_draw methods //////////////

    /**
     * Copy a screen buffer. Actually, it's hardcoded to copy stuff from screen
     * 1 to screen 0. Used to overlay stuff like beveled edges that don't need
     * to be updated that often. * LFB copy. This might not be a good idea if
     * memcpy is not optiomal, e.g. byte by byte on a 32bit CPU, as GNU
     * GCC/Linux libc did at one point.
     */
    public void VideoErase(int ofs, int count) {

        // memcpy (screens[0]+ofs, screens[1]+ofs, count);
        System.arraycopy(V.getScreen(DoomVideoRenderer.SCREEN_BG), ofs,
            V.getScreen(DoomVideoRenderer.SCREEN_FG), ofs, count);

    }

    /*
     * R_DrawViewBorder Draws the border around the view for different size
     * windows?
     */

    public void DrawViewBorder() {
        int top;
        int side;
        int ofs;
        int i;

        if (view.scaledwidth == SCREENWIDTH)
            return;

        top = ((SCREENHEIGHT - DM.ST.getHeight()) - view.height) / 2;
        side = (SCREENWIDTH - view.scaledwidth) / 2;

        // copy top and one line of left side
        this.VideoErase(0, top * SCREENWIDTH + side);

        // copy one line of right side and bottom
        ofs = (view.height + top) * SCREENWIDTH - side;
        this.VideoErase(ofs, top * SCREENWIDTH + side);

        // copy sides using wraparound
        ofs = top * SCREENWIDTH + SCREENWIDTH - side;
        side <<= 1;

        for (i = 1; i < view.height; i++) {
            this.VideoErase(ofs, side);
            ofs += SCREENWIDTH;
        }

        // ?
        V.MarkRect(0, 0, SCREENWIDTH, SCREENHEIGHT - DM.ST.getHeight());
    }

    public void ExecuteSetViewSize() {
        int cosadj;
        int dy;
        int i;
        int j;
        int level;
        int startmap;

        setsizeneeded = false;

        // 11 Blocks means "full screen"

        if (setblocks == 11) {
            view.scaledwidth = SCREENWIDTH;
            view.height = SCREENHEIGHT;
        } else {
            view.scaledwidth = setblocks * (SCREENWIDTH / 10);
            // Height can only be a multiple of 8.
            view.height =
                (short) ((setblocks * (SCREENHEIGHT - DM.ST.getHeight()) / 10) & ~7);
        }

        skydcvars.viewheight =
            maskedcvars.viewheight = dcvars.viewheight = view.height;

        view.detailshift = setdetail;
        view.width = view.scaledwidth >> view.detailshift;

        view.centery = view.height / 2;
        view.centerx = view.width / 2;
        view.centerxfrac = (view.centerx << FRACBITS);
        view.centeryfrac = (view.centery << FRACBITS);
        view.projection = view.centerxfrac;

        skydcvars.centery = maskedcvars.centery = dcvars.centery = view.centery;

        // High detail
        if (view.detailshift == 0) {

            colfunc = colfunchi;
            dsvars.spanfunc = DrawSpan;
        } else {
            // Low detail
            colfunc = colfunclow;
            dsvars.spanfunc = DrawSpanLow;

        }

        InitBuffer(view.scaledwidth, view.height);

        InitTextureMapping();

        // psprite scales
        // pspritescale = FRACUNIT*viewwidth/SCREENWIDTH;
        // pspriteiscale = FRACUNIT*SCREENWIDTH/viewwidth;

        MyThings.setPspriteScale((int) (FRACUNIT
                * ((float) SCREEN_MUL * view.width) / SCREENWIDTH));
        MyThings.setPspriteIscale((int) (FRACUNIT * (SCREENWIDTH / (view.width * (float) SCREEN_MUL))));
        vp_vars.setSkyScale((int) (FRACUNIT * (SCREENWIDTH / (view.width * (float) SCREEN_MUL))));

        view.BOBADJUST = (int) (this.vs.getSafeScaling() << 15);
        view.WEAPONADJUST = (int) ((SCREENWIDTH / (2 * SCREEN_MUL)) * FRACUNIT);

        // thing clipping
        for (i = 0; i < view.width; i++)
            view.screenheightarray[i] = (short) view.height;

        // planes
        for (i = 0; i < view.height; i++) {
            dy = ((i - view.height / 2) << FRACBITS) + FRACUNIT / 2;
            dy = Math.abs(dy);
            vp_vars.yslope[i] =
                FixedDiv((view.width << view.detailshift) / 2 * FRACUNIT, dy);
            // MyPlanes.yslopef[i] = ((viewwidth<<detailshift)/2)/ dy;
        }

        // double cosadjf;
        for (i = 0; i < view.width; i++) {
            // MAES: In this spot we must interpet it as SIGNED, else it's
            // pointless, right?
            // MAES: this spot caused the "warped floor bug", now fixed. Don't
            // forget xtoviewangle[i]!
            cosadj = Math.abs(finecosine(xtoviewangle[i]));
            // cosadjf =
            // Math.abs(Math.cos((double)xtoviewangle[i]/(double)0xFFFFFFFFL));
            MyPlanes.getDistScale()[i] = FixedDiv(FRACUNIT, cosadj);
            // MyPlanes.distscalef[i] = (float) (1.0/cosadjf);
        }

        // Calculate the light levels to use
        // for each level / scale combination.
        for (i = 0; i < LIGHTLEVELS; i++) {
            startmap = ((LIGHTLEVELS-LIGHTBRIGHT-i)*2)*NUMCOLORMAPS/LIGHTLEVELS;
            for (j = 0; j < MAXLIGHTSCALE; j++) {
                level =
                    startmap - j/ DISTMAP;
                if (level < 0)
                    level = 0;
                if (level >= NUMCOLORMAPS)
                    level = NUMCOLORMAPS - 1;
                colormaps.scalelight[i][j] = colormaps.colormaps[level];
            }
        }

        MySegs.ExecuteSetViewSize(view.width);

    }

    /**
     * R_FillBackScreen Fills the back screen with a pattern for variable screen
     * sizes Also draws a beveled edge. This is actually stored in screen 1, and
     * is only OCCASIONALLY written to screen 0 (the visible one) by calling
     * R_VideoErase.
     */

    public void FillBackScreen() {
        flat_t src;
        int x;
        int y;
        patch_t patch;

        // DOOM border patch.
        String name1 = "FLOOR7_2";

        // DOOM II border patch.
        String name2 = "GRNROCK";

        String name;

        if (view.scaledwidth == SCREENWIDTH)
            return;

        if (DM.isCommercial())
            name = name2;
        else
            name = name1;

        /* This is a flat we're reading here */
        src = (flat_t) (W.CacheLumpName(name, PU_CACHE, flat_t.class));

        /*
         * This part actually draws the border itself, without bevels MAES:
         * improved drawing routine for extended bit-depth compatibility.
         */

        for (y = 0; y < SCREENHEIGHT - DM.ST.getHeight(); y += 64) {

            int y_maxdraw = Math.min(SCREENHEIGHT - DM.ST.getHeight() - y, 64);

            // Draw whole blocks.
            for (x = 0; x < SCREENWIDTH; x += 64) {
                int x_maxdraw = Math.min(SCREENWIDTH - x, 64);
                V.DrawBlock(x, y, DoomVideoRenderer.SCREEN_BG, x_maxdraw,
                    y_maxdraw, (T) src.data);
            }
        }

        patch = (patch_t) W.CachePatchName("BRDR_T", PU_CACHE);

        for (x = 0; x < view.scaledwidth; x += 8)
            V.DrawPatch(view.windowx + x, view.windowy - 8, 1, patch);
        patch = (patch_t) W.CachePatchName("BRDR_B", PU_CACHE);

        for (x = 0; x < view.scaledwidth; x += 8)
            V.DrawPatch(view.windowx + x, view.windowy + view.height, 1, patch);
        patch = (patch_t) W.CachePatchName("BRDR_L", PU_CACHE);

        for (y = 0; y < view.height; y += 8)
            V.DrawPatch(view.windowx - 8, view.windowy + y, 1, patch);
        patch = (patch_t) W.CachePatchName("BRDR_R", PU_CACHE);

        for (y = 0; y < view.height; y += 8)
            V.DrawPatch(view.windowx + view.scaledwidth, view.windowy + y, 1,
                patch);

        // Draw beveled edge. Top-left
        V.DrawPatch(view.windowx - 8, view.windowy - 8, 1,
            (patch_t) W.CachePatchName("BRDR_TL", PU_CACHE));

        // Top-right.
        V.DrawPatch(view.windowx + view.scaledwidth, view.windowy - 8, 1,
            (patch_t) W.CachePatchName("BRDR_TR", PU_CACHE));

        // Bottom-left
        V.DrawPatch(view.windowx - 8, view.windowy + view.height, 1,
            (patch_t) W.CachePatchName("BRDR_BL", PU_CACHE));
        // Bottom-right.
        V.DrawPatch(view.windowx + view.width, view.windowy + view.height, 1,
            (patch_t) W.CachePatchName("BRDR_BR", PU_CACHE));
    }

    /**
     * R_Init
     */

    public void Init()

    {
        // Any good reason for this to be here?
        // drawsegs=new drawseg_t[MAXDRAWSEGS];
        // C2JUtils.initArrayOfObjects(drawsegs);

        // DON'T FORGET ABOUT MEEEEEE!!!11!!!
        this.screen = this.V.getScreen(DoomVideoRenderer.SCREEN_FG);

        System.out.print("\nR_InitData");
        InitData();
        // InitPointToAngle ();
        System.out.print("\nR_InitPointToAngle");

        // ds.DM.viewwidth / ds.viewheight / detailLevel are set by the defaults
        System.out.print("\nR_InitTables");
        InitTables();

        SetViewSize(DM.M.getScreenBlocks(), DM.M.getDetailLevel());

        System.out.print("\nR_InitPlanes");
        MyPlanes.InitPlanes();

        System.out.print("\nR_InitLightTables");
        InitLightTables();

        System.out.print("\nR_InitSkyMap: " + TexMan.InitSkyMap());

        System.out.print("\nR_InitTranslationsTables");
        InitTranslationTables();

        System.out.print("\nR_InitTranMap: ");
        R_InitTranMap(0);

        System.out.print("\nR_InitDrawingFunctions: ");
        R_InitDrawingFunctions();
        
        framecount = 0;
    }        
    
    /**
     * R_InitBuffer Creates lookup tables that avoid multiplies and other
     * hazzles for getting the framebuffer address of a pixel to draw. MAES:
     * this is "pinned" to screen[0] of a Video Renderer. We will handle this
     * differently elsewhere...
     */

    protected final void InitBuffer(int width, int height) {
        int i;

        // Handle resize,
        // e.g. smaller view windows
        // with border and/or status bar.
        view.windowx = (SCREENWIDTH - width) >> 1;

        // Column offset. For windows.
        for (i = 0; i < width; i++)
            columnofs[i] = view.windowx + i;

        // SamE with base row offset.
        if (width == SCREENWIDTH)
            view.windowy = 0;
        else
            view.windowy = (SCREENHEIGHT - DM.ST.getHeight() - height) >> 1;

        // Preclaculate all row offsets.
        for (i = 0; i < height; i++)
            ylookup[i] = /* screens[0] + */(i + view.windowy) * SCREENWIDTH;
    }

    /**
     * R_InitTextureMapping Not moved into the TextureManager because it's
     * tighly coupled to the visuals, rather than textures. Perhaps the name is
     * not the most appropriate.
     */
    protected final void InitTextureMapping() {
        int i, x, t;
        int focallength; // fixed_t
        int fov = FIELDOFVIEW;

        // For widescreen displays, increase the FOV so that the middle part of
        // the
        // screen that would be visible on a 4:3 display has the requested FOV.
        /*
         * UNUSED if (wide_centerx != centerx) { // wide_centerx is what centerx
         * would be // if the display was not widescreen fov = (int)
         * (Math.atan((double) centerx Math.tan((double) fov * Math.PI /
         * FINEANGLES) / (double) wide_centerx) FINEANGLES / Math.PI); if (fov >
         * 130 * FINEANGLES / 360) fov = 130 * FINEANGLES / 360; }
         */

        // Use tangent table to generate viewangletox:
        // viewangletox will give the next greatest x
        // after the view angle.
        //
        // Calc focallength
        // so FIELDOFVIEW angles covers SCREENWIDTH.
        focallength =
            FixedDiv(view.centerxfrac, finetangent[QUARTERMARK + FIELDOFVIEW
                    / 2]);

        for (i = 0; i < FINEANGLES / 2; i++) {
            if (finetangent[i] > FRACUNIT * 2)
                t = -1;
            else if (finetangent[i] < -FRACUNIT * 2)
                t = view.width + 1;
            else {
                t = FixedMul(finetangent[i], focallength);
                t = (view.centerxfrac - t + FRACUNIT - 1) >> FRACBITS;

                if (t < -1)
                    t = -1;
                else if (t > view.width + 1)
                    t = view.width + 1;
            }
            viewangletox[i] = t;
        }

        // Scan viewangletox[] to generate xtoviewangle[]:
        // xtoviewangle will give the smallest view angle
        // that maps to x.
        for (x = 0; x <= view.width; x++) {
            i = 0;
            while (viewangletox[i] > x)
                i++;
            xtoviewangle[x] = addAngles((i << ANGLETOFINESHIFT), -ANG90);
        }

        // Take out the fencepost cases from viewangletox.
        for (i = 0; i < FINEANGLES / 2; i++) {
            t = FixedMul(finetangent[i], focallength);
            t = view.centerx - t;

            if (viewangletox[i] == -1)
                viewangletox[i] = 0;
            else if (viewangletox[i] == view.width + 1)
                viewangletox[i] = view.width;
        }

        clipangle = xtoviewangle[0];
        // OPTIMIZE: assign constant for optimization.
        CLIPANGLE2 = (2 * clipangle) & BITS32;
    }

    //
    // R_InitLightTables
    // Only inits the zlight table,
    // because the scalelight table changes with view size.
    //
    protected final static int DISTMAP = 2;

    protected final void InitLightTables() {
        int i;
        int j;
        int startmap;
        int scale;

        // Calculate the light levels to use
        // for each level / distance combination.
        for (i = 0; i < LIGHTLEVELS; i++) {
            startmap = ((LIGHTLEVELS - LIGHTBRIGHT - i) * 2) * NUMCOLORMAPS / LIGHTLEVELS;
            for (j = 0; j < MAXLIGHTZ; j++) {
                // CPhipps - use 320 here instead of SCREENWIDTH, otherwise hires is
                //           brighter than normal res
                
                scale = FixedDiv((320 / 2 * FRACUNIT), (j + 1) << LIGHTZSHIFT);
                int t, level = startmap - (scale >>= LIGHTSCALESHIFT)/DISTMAP;

                if (level < 0)
                    level = 0;

                if (level >= NUMCOLORMAPS)
                    level = NUMCOLORMAPS - 1;

                // zlight[i][j] = colormaps + level*256;
                colormaps.zlight[i][j] = colormaps.colormaps[level];
            }
        }
    }

    protected static final int TSC = 12; /*
                                          * number of fixed point digits in
                                          * filter percent
                                          */

    byte[] main_tranmap;

    /**
     * A faster implementation of the tranmap calculations. Almost 10x faster
     * than the old one!
     * 
     * @param progress
     */

    protected void R_InitTranMap(int progress) {
        int lump = W.CheckNumForName("TRANMAP");

        long ta = System.nanoTime();
        int p;
        String tranmap;

        // PRIORITY: a map file has been specified from commandline. Try to read
        // it. If OK, this trumps even those specified in lumps.

        if ((p = DM.CM.CheckParm("-tranmap")) != 0) {
            if ((tranmap = DM.CM.getArgv(p + 1)) != null) {
                if (C2JUtils.testReadAccess(tranmap)) {
                    System.out
                            .printf(
                                "Translucency map file %s specified in -tranmap arg. Attempting to use...\n",
                                tranmap);
                    main_tranmap = new byte[256 * 256]; // killough 4/11/98
                    int result = MenuMisc.ReadFile(tranmap, main_tranmap);
                    if (result > 0)
                        return;
                    System.out.print("...failure.\n");
                }
            }
        }

        // Next, if a tranlucency filter map lump is present, use it
        if (lump != -1) { // Set a pointer to the translucency filter maps.
            System.out
                    .print("Translucency map found in lump. Attempting to use...");
            // main_tranmap=new byte[256*256]; // killough 4/11/98
            main_tranmap = W.CacheLumpNumAsRawBytes(lump, Defines.PU_STATIC); // killough
                                                                              // 4/11/98
            // Tolerate 64K or more.
            if (main_tranmap.length >= 0x10000)
                return;
            System.out.print("...failure.\n"); // Not good, try something else.
        }

        // A default map file already exists. Try to read it.
        if (C2JUtils.testReadAccess("tranmap.dat")) {
            System.out
                    .print("Translucency map found in default tranmap.dat file. Attempting to use...");
            main_tranmap = new byte[256 * 256]; // killough 4/11/98
            int result = MenuMisc.ReadFile("tranmap.dat", main_tranmap);
            if (result > 0)
                return; // Something went wrong, so fuck that.
        }

        // Nothing to do, so we must synthesize it from scratch. And, boy, is it
        // slooow.
        { // Compose a default transparent filter map based on PLAYPAL.
            System.out
                    .print("Computing translucency map from scratch...that's gonna be SLOW...");
            byte[] playpal =
                W.CacheLumpNameAsRawBytes("PLAYPAL", Defines.PU_STATIC);
            main_tranmap = new byte[256 * 256]; // killough 4/11/98
            int[] basepal = new int[3 * 256];
            int[] mixedpal = new int[3 * 256 * 256];

            main_tranmap = new byte[256 * 256];

            // Init array of base colors.
            for (int i = 0; i < 256; i++) {
                basepal[3 * i] = 0Xff & playpal[i * 3];
                basepal[1 + 3 * i] = 0Xff & playpal[1 + i * 3];
                basepal[2 + 3 * i] = 0Xff & playpal[2 + i * 3];
            }

            // Init array of mixed colors. These are true RGB.
            // The diagonal of this array will be the original colors.

            for (int i = 0; i < 256 * 3; i += 3) {
                for (int j = 0; j < 256 * 3; j += 3) {
                    mixColors(basepal, basepal, mixedpal, i, j, j * 256 + i);

                }
            }

            // Init distance map. Every original palette colour has a
            // certain distance from all the others. The diagonal is zero.
            // The interpretation is that e.g. the mixture of color 2 and 8 will
            // have a RGB value, which is closest to euclidean distance to
            // e.g. original color 9. Therefore we should put "9" in the (2,8)
            // and (8,2) cells of the tranmap.

            final float[] tmpdist = new float[256];

            for (int a = 0; a < 256; a++) {
                for (int b = a; b < 256; b++) {
                    // We evaluate the mixture of a and b
                    // Construct distance table vs all of the ORIGINAL colors.
                    for (int k = 0; k < 256; k++) {
                        tmpdist[k] =
                            colorDistance(mixedpal, basepal, 3 * (a + b * 256),
                                k * 3);
                    }

                    main_tranmap[(a << 8) | b] = (byte) findMin(tmpdist);
                    main_tranmap[(b << 8) | a] = main_tranmap[(a << 8) | b];
                }
            }
            System.out.print("...done\n");
            if (MenuMisc.WriteFile("tranmap.dat", main_tranmap,
                main_tranmap.length))
                System.out
                        .print("TRANMAP.DAT saved to disk for your convenience! Next time will be faster.\n");
        }
        long b = System.nanoTime();

        System.out.printf("Tranmap %d\n", (b - ta) / 1000000);
    }

    /** Mixes two RGB colors. Nuff said */

    protected final void mixColors(int[] a, int[] b, int[] c, int pa, int pb,
            int pc) {
        c[pc] = (a[pa] + b[pb]) / 2;
        c[pc + 1] = (a[pa + 1] + b[pb + 1]) / 2;
        c[pc + 2] = (a[pa + 2] + b[pb + 2]) / 2;

    }

    /** Returns the euclidean distance of two RGB colors. Nuff said */

    protected final float colorDistance(int[] a, int[] b, int pa, int pb) {
        return (float) Math.sqrt((a[pa] - b[pb]) * (a[pa] - b[pb])
                + (a[pa + 1] - b[pb + 1]) * (a[pa + 1] - b[pb + 1])
                + (a[pa + 2] - b[pb + 2]) * (a[pa + 2] - b[pb + 2]));
    }

    
    /** Stuff that is trivially initializable, even with generics,
     *  but is only safe to do after all constructors have completed.
     */
    
    protected final void completeInit(){
        this.detailaware.add(MyThings);        
    }
    
    protected final int findMin(float[] a) {
        int minindex = 0;
        float min = Float.POSITIVE_INFINITY;

        for (int i = 0; i < a.length; i++)
            if (a[i] < min) {
                min = a[i];
                minindex = i;
            }

        return minindex;

    }

    /**
     * R_DrawMaskedColumnSinglePost. Used to handle some special cases where
     * cached columns get used as "masked" middle textures. Will be treated as a
     * single-run post of capped length.
     */

    /*
     * protected final void DrawCompositeColumnPost(byte[] column) { int
     * topscreen; int bottomscreen; int basetexturemid; // fixed_t int
     * topdelta=0; // Fixed value int length; basetexturemid = dc_texturemid; //
     * That's true for the whole column. dc_source = column; // for each post...
     * while (topdelta==0) { // calculate unclipped screen coordinates // for
     * post topscreen = sprtopscreen + spryscale * 0; length = column.length;
     * bottomscreen = topscreen + spryscale * length; dc_yl = (topscreen +
     * FRACUNIT - 1) >> FRACBITS; dc_yh = (bottomscreen - 1) >> FRACBITS; if
     * (dc_yh >= mfloorclip[p_mfloorclip + dc_x]) dc_yh =
     * mfloorclip[p_mfloorclip + dc_x] - 1; if (dc_yl <=
     * mceilingclip[p_mceilingclip + dc_x]) dc_yl = mceilingclip[p_mceilingclip
     * + dc_x] + 1; // killough 3/2/98, 3/27/98: Failsafe against
     * overflow/crash: if (dc_yl <= dc_yh && dc_yh < viewheight) { // Set
     * pointer inside column to current post's data // Rremember, it goes
     * {postlen}{postdelta}{pad}[data]{pad} dc_source_ofs = 0; // pointer + 3;
     * dc_texturemid = basetexturemid - (topdelta << FRACBITS); // Drawn by
     * either R_DrawColumn // or (SHADOW) R_DrawFuzzColumn. dc_texheight=0; //
     * Killough try { maskedcolfunc.invoke(); } catch (Exception e){
     * System.err.printf("Error rendering %d %d %d\n", dc_yl,dc_yh,dc_yh-dc_yl);
     * } } topdelta--; } dc_texturemid = basetexturemid; }
     */

    protected abstract void InitColormaps()
            throws IOException;

    // Only used b
    protected byte[] BLURRY_MAP;

    /**
     * R_InitData Locates all the lumps that will be used by all views Must be
     * called after W_Init.
     */

    public void InitData() {
        try {
            System.out.print("\nInit Texture and Flat Manager");
            TexMan = this.DM.TM;
            System.out.print("\nInitTextures");
            TexMan.InitTextures();
            System.out.print("\nInitFlats");
            TexMan.InitFlats();
            System.out.print("\nInitSprites");
            SM.InitSpriteLumps();
            MyThings.cacheSpriteManager(SM);
            VIS.cacheSpriteManager(SM);
            System.out.print("\nInitColormaps");
            InitColormaps();

        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    protected int spritememory;

    /**
     * To be called right after PrecacheLevel from SetupLevel in LevelLoader.
     * It's an ugly hack, in that it must communicate with the "Game map" class
     * and determine what kinds of monsters are actually in the level and
     * whether it should load their graphics or not. Whenever we implement it,
     * it's going to be ugly and not neatly separated anyway.
     * 
     * @return
     */
    public void PreCacheThinkers() {

        boolean[] spritepresent;
        thinker_t th;
        spriteframe_t sf;
        int i, j, k;
        int lump;

        final spritedef_t[] sprites = SM.getSprites();
        final int numsprites = SM.getNumSprites();

        spritepresent = new boolean[numsprites];

        for (th = P.getThinkerCap().next; th != P.getThinkerCap(); th = th.next) {
            if (th.function == think_t.P_MobjThinker)
                spritepresent[((mobj_t) th).sprite.ordinal()] = true;
        }

        spritememory = 0;
        for (i = 0; i < numsprites; i++) {
            if (!spritepresent[i])
                continue;

            for (j = 0; j < sprites[i].numframes; j++) {
                sf = sprites[i].spriteframes[j];
                for (k = 0; k < 8; k++) {
                    lump = SM.getFirstSpriteLump() + sf.lump[k];
                    spritememory += W.GetLumpInfo(lump).size;
                    W.CacheLumpNum(lump, PU_CACHE, patch_t.class);
                }
            }
        }
    }

    /**
     * R_InitTranslationTables Creates the translation tables to map the green
     * color ramp to gray, brown, red. Assumes a given structure of the PLAYPAL.
     * Could be read from a lump instead.
     */

    protected final void InitTranslationTables() {
        int i;

        final int TR_COLORS = 28;

        // translationtables = Z_Malloc (256*3+255, PU_STATIC, 0);
        // translationtables = (byte *)(( (int)translationtables + 255 )& ~255);
        byte[][] translationtables =
            colormaps.translationtables = new byte[TR_COLORS][256];

        // translate just the 16 green colors
        for (i = 0; i < 256; i++) {
            translationtables[0][i] = (byte) i;

            if (i >= 0x70 && i <= 0x7f) {
                // Remap green range to other ranges.
                translationtables[1][i] = (byte) (0x60 + (i & 0xf)); // gray
                translationtables[2][i] = (byte) (0x40 + (i & 0xf)); // brown
                translationtables[3][i] = (byte) (0x20 + (i & 0xf)); // red
                translationtables[4][i] = (byte) (0x10 + (i & 0xf)); // pink
                translationtables[5][i] = (byte) (0x30 + (i & 0xf)); // skin
                translationtables[6][i] = (byte) (0x50 + (i & 0xf)); // metal
                translationtables[7][i] = (byte) (0x80 + (i & 0xf)); // copper
                translationtables[8][i] = (byte) (0xB0 + (i & 0xf)); // b.red
                translationtables[9][i] = (byte) (0xC0 + (i & 0xf)); // electric
                                                                     // blue
                translationtables[10][i] = (byte) (0xD0 + (i & 0xf)); // guantanamo
                // "Halfhue" colors for which there are only 8 distinct hues
                translationtables[11][i] = (byte) (0x90 + (i & 0xf) / 2); // brown2
                translationtables[12][i] = (byte) (0x98 + (i & 0xf) / 2); // gray2
                translationtables[13][i] = (byte) (0xA0 + (i & 0xf) / 2); // piss
                translationtables[14][i] = (byte) (0xA8 + (i & 0xf) / 2); // gay
                translationtables[15][i] = (byte) (0xE0 + (i & 0xf) / 2); // yellow
                translationtables[16][i] = (byte) (0xE8 + (i & 0xf) / 2); // turd
                translationtables[17][i] = (byte) (0xF0 + (i & 0xf) / 2); // compblue
                translationtables[18][i] = (byte) (0xF8 + (i & 0xf) / 2); // whore
                translationtables[19][i] = (byte) (0x05 + (i & 0xf) / 2); // nigga
                // "Pimped up" colors, using mixed hues.
                translationtables[20][i] = (byte) (0x90 + (i & 0xf)); // soldier
                translationtables[21][i] = (byte) (0xA0 + (i & 0xf)); // drag
                                                                      // queen
                translationtables[22][i] = (byte) (0xE0 + (i & 0xf)); // shit &
                                                                      // piss
                translationtables[23][i] = (byte) (0xF0 + (i & 0xf)); // raver
                translationtables[24][i] = (byte) (0x70 + (0xf - i & 0xf)); // inv.marine
                translationtables[25][i] = (byte) (0xF0 + (0xf - i & 0xf)); // inv.raver
                translationtables[26][i] = (byte) (0xE0 + (0xf - i & 0xf)); // piss
                                                                            // &
                                                                            // shit
                translationtables[27][i] = (byte) (0xA0 + (i & 0xf)); // shitty
                                                                      // gay
            } else {
                for (int j = 1; j < TR_COLORS; j++)
                    // Keep all other colors as is.
                    translationtables[j][i] = (byte) i;
            }
        }
    }

    // ///////////////// Generic rendering methods /////////////////////

    public IMaskedDrawer<T, V> getThings() {
        return this.MyThings;        
    }

    /**
     * e6y: this is a precalculated value for more precise flats drawing (see
     * R_MapPlane) "Borrowed" from PrBoom+
     */
    protected float viewfocratio;

    protected int projectiony;

    // Some more isolation methods....

    public final int getValidCount() {
        return validcount;
    }

    public final void increaseValidCount(int amount) {
        validcount += amount;
    }

    public boolean getSetSizeNeeded() {
        return setsizeneeded;
    }

    @Override
    public TextureManager<T> getTextureManager() {
        return TexMan;
    }

    public PlaneDrawer<T, V> getPlaneDrawer() {
        return this.MyPlanes;
    }

    public ViewVars getView() {
        return this.view;
    }

    public SpanVars<T, V> getDSVars() {
        return this.dsvars;
    }

    public LightsAndColors<V> getColorMap() {
        return this.colormaps;
    }

    public IDoomSystem getDoomSystem() {
        return this.I;
    }

    public Visplanes getVPVars() {
        return this.vp_vars;
    }

    @Override
    public SegVars getSegVars() {
        return this.seg_vars;
    }

    public IWadLoader getWadLoader() {
        return this.W;
    }
    
    public ISpriteManager getSpriteManager(){
        return this.SM;
    }

    public BSPVars getBSPVars(){
        return this.MyBSP;
    }

    public IVisSpriteManagement<V> getVisSpriteManager(){
        return this.VIS;
    }

    // //////////////VIDEO SCALE STUFF ///////////////////////

    protected int SCREENWIDTH;

    protected int SCREENHEIGHT;

    protected float SCREEN_MUL;

    protected IVideoScale vs;

    @Override
    public void setVideoScale(IVideoScale vs) {
        this.vs = vs;
    }

    @Override
    public void initScaling() {
        this.SCREENHEIGHT = vs.getScreenHeight();
        this.SCREENWIDTH = vs.getScreenWidth();
        this.SCREEN_MUL = vs.getScreenMul();

        // Pre-scale stuff.

        view.negonearray = new short[SCREENWIDTH]; // MAES: in scaling
        view.screenheightarray = new short[SCREENWIDTH];// MAES: in scaling
        // Mirror in view
        view.xtoviewangle = xtoviewangle = new long[SCREENWIDTH + 1];

        // Initialize children objects\
        MySegs.setVideoScale(vs);
        MyPlanes.setVideoScale(vs);
        vp_vars.setVideoScale(vs);
        MyThings.setVideoScale(vs);

        MySegs.initScaling();
        MyPlanes.initScaling();
        vp_vars.initScaling();
        MyThings.initScaling();

    }

    /**
     * Initializes the various drawing functions. They are all "pegged" to the
     * same dcvars/dsvars object. Any initializations of e.g. parallel renderers
     * and their supporting subsystems should occur here.
     */

    protected void R_InitDrawingFunctions(){
        this.setHiColFuns();
        this.setLowColFuns();
    }

    // //////////////////////////// LIMIT RESETTING //////////////////
    @Override
    public void resetLimits() {
        // Call it only at the beginning of new levels.
        VIS.resetLimits();
        MySegs.resetLimits();
    }

    /**
     * R_RenderView As you can guess, this renders the player view of a
     * particular player object. In practice, it could render the view of any
     * mobj too, provided you adapt the SetupFrame method (where the viewing
     * variables are set). This is the "vanilla" implementation which just works
     * for most cases.
     */

    public void RenderPlayerView(player_t player) {

        // Viewing variables are set according to the player's mobj. Interesting
        // hacks like
        // free cameras or monster views can be done.

		/* Uncommenting this will result in a very existential experience.
		 * Basically, the player's view will randomly switch between different mobj_t's in the map, 
		 * "possessing" them at the same time and turning them into (partial) Doomguys. Limited
		 * control is possible (e.g. moving) but trying to use things or shoot while in this state will
		 * result in a NPE.
		 */
    	
    /*
  	if (Math.random()>0.99){
	  thinker_t shit=P.getRandomThinker();
	  try {		  
	  mobj_t crap=(mobj_t)shit; // It works, because practically all thinkers are also mobj_t's. There are no "bodyless" thinkers
	  player.mo=crap;
	  SetupFrame(crap);
	  } catch (ClassCastException e){

	  }
  	} else{
    
        SetupFrame(player);
  	}
  	*/
  	

        // Clear buffers.
        MyBSP.ClearClipSegs();
        seg_vars.ClearDrawSegs();
        vp_vars.ClearPlanes();
        MySegs.ClearClips();
        VIS.ClearSprites();

        // Check for new console commands.
        DGN.NetUpdate();

        // The head node is the last node output.
        MyBSP.RenderBSPNode(LL.numnodes - 1);

        // Check for new console commands.
        DGN.NetUpdate();

        // FIXME: "Warped floor" fixed, now to fix same-height visplane
        // bleeding.
        MyPlanes.DrawPlanes();

        // Check for new console commands.
        DGN.NetUpdate();

        MyThings.DrawMasked();

        colfunc.main = colfunc.base;

        // Check for new console commands.
        DGN.NetUpdate();
    }

}

package rr;

import static m.fixed_t.FRACBITS;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

import p.Resettable;

import w.CacheableDoomObject;

/** This is the vertex structure used IN MEMORY with fixed-point arithmetic.
 *  It's DIFFERENT than the one used on disk, which has 16-bit signed shorts.
 *  However, it must be parsed. 
 *
 */

public class vertex_t  implements CacheableDoomObject, Resettable{

    public vertex_t(){
        
    }
    /** treat as (fixed_t) */
    public  int x,y;
    
    
    /** Notice how we auto-expand to fixed_t */
    @Override
    public void unpack(ByteBuffer buf)
            throws IOException {
        buf.order(ByteOrder.LITTLE_ENDIAN);
        this.x=buf.getShort()<<FRACBITS;
        this.y=buf.getShort()<<FRACBITS;
        
    }

    @Override
    public void reset() {
        x=0; y=0;        
    }


    public static int sizeOf() {
        return 4;
    }
    
}
package rr;

import java.io.ByteArrayOutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * Utilities to synthesize patch_t format images from multiple patches
 * (with transparency).
 * 
 * 
 * @author velktron
 *
 */

public class MultiPatchSynthesizer {

    
    static class PixelRange {
        public PixelRange(int start, int end) {
            this.start=start;
            this.end=end;
        }
        
        public int getLength(){
            return (end-start+1);
        }
        
        public int start;
        public int end;
    }
    
    public static patch_t synthesizePatchFromFlat(String name,byte[] flat, int width, int height){
        
        byte[] expected=new byte[width *height];        
        byte[][] pixels=new byte[width][height];
        boolean[][] solid=new boolean[width][height];
        
        // Copy as much data as possible.
        System.arraycopy(flat,0,expected,0,Math.min(flat.length,expected.length));

        for (int i=0;i<width;i++){           
            Arrays.fill(solid[i],true);
            for (int j=0;j<height;j++){
                pixels[i][j]=expected[i+j*width];
            }
        }
        
        patch_t result=new patch_t(name,width,height,0,0);
        column_t[] columns=new column_t[width];
        
        for (int x=0;x<width;x++)
            columns[x]=getColumnStream(pixels[x],solid[x],height);
        
       result.columns=columns;
        return result;
    }
    
    public static patch_t synthesize(String name,byte[][] pixels, boolean[][] solid, int width, int height){
        
        patch_t result=new patch_t(name,width,height,0,0);
        column_t[] columns=new column_t[width];
        
        for (int x=0;x<width;x++)
            columns[x]=getColumnStream(pixels[x],solid[x],height);
        
       result.columns=columns;
        return result;
    }

    public static column_t getColumnStream(byte[] pixels, boolean[] solid, int height){
    
        column_t result=new column_t();
        int start=-1;
        int end=-1;
        
        List<PixelRange> ranges=new ArrayList<PixelRange>();
        
        // Scan column for continuous pixel ranges                
        for (int i=0;i<height;i++){
            
            // Encountered solid start.
            if (solid[i] && start==-1){
                start=i; // mark start
            }
                
            // Last solid pixel
            if (solid[i] && i==height-1 && start!=-1 ){
                end=i;
                ranges.add(new PixelRange(start,end));
                start=end=-1; // reset start/end
            }
               
            // Start defined and ending not yet detected
            if (!solid[i] && start!=-1 && end ==-1){
                end=i-1; // Single-pixel runs would be e.g. 1-2 -> 1-1
            }            

            if (start!=-1 && end!=-1){
                // Range complete.
                ranges.add(new PixelRange(start,end));
                start=end=-1; // reset start/end
            }
        }
        
        // There should be at least an empty post
        if (ranges.size()==0){
            ranges.add(new PixelRange(0,-1));
        }
        
        // Ideal for this use, since we don't know how big the patch is going to be a-priori
        ByteArrayOutputStream file=new ByteArrayOutputStream();
        int topdelta=0;
        int n=ranges.size();
        short topdeltas[]=new short[n];
        int postofs[]=new int[n];
        short postlens[]=new short[n];
        

        
        for (int i=0;i<n;i++){
            PixelRange pr=ranges.get(i);
            topdelta=pr.start; // cumulative top delta  
            
            // Precomputed column data
            postofs[i]=(short) file.size()+3; // Last written post +3, pointing at first pixel of data.
            topdeltas[i]=(short) topdelta;
            postlens[i]=(short) (pr.getLength()); // Post lengths are at net of padding  
                      
            file.write(topdeltas[i]);
            file.write(postlens[i]);
            file.write(0); // padding
            file.write(pixels,pr.start,pr.getLength()); // data
            file.write(0); // padding
            }
    
        file.write(0xFF); // Terminator
        
        result.data=file.toByteArray();
        result.postdeltas=topdeltas;
        result.postlen=postlens;
        result.postofs=postofs;
        result.posts=ranges.size();
        
        // The ranges tell us where continuous posts
        
        return result;
    }
    
    /*
    public static patch_t synthesize(byte[][] pixels, boolean[][] solid, int width, int height, int picture_top, int picture_left){
        // Ideal for this use, since we don't know how big the patch is going to be a-priori
        ByteArrayOutputStream file=new ByteArrayOutputStream();
        
        int offset;
        
        int[] columnofs=new int[width];
        
        // Patch header
        file.write(width);
        file.write(height);
        file.write(picture_top);
        file.write(picture_left);

        int column_array=0;
        int x=0,y;
        byte dummy_value;
        while (x<width){

            //write memory buffer position to end of column_array
            columnofs[column_array]=file.size();
            column_array++;

            y = 0;

            boolean operator = true;
            int pixel_count = 0;
            
            while (y < height){
                byte val=pixels[x][y];
                boolean transparent=!solid[x][y];
                
                
                // Pixel is transparent
                if (transparent && !operator ) {
                    dummy_value = 0;                    
                    file.write(dummy_value);
                    operator = true;
                    }
                else //Pixel not transparent, and operator condition set.
                    if (!transparent && operator){
                    int row_start = y;                    
                    pixel_count = 0;
                    dummy_value = 0;
                    // write above post data to memory buffer

                    offset = file.size(); //current post position in memory buffer

                    operator = false;
                    } else 
                   if (!transparent && !operator){
                       pixel_count++; // increment current post pixel_count
                   }

                    if (offset > 0 && pixel_count > 0){
                        previous_offset = current post position

                        seek back in memory buffer by offset - 2

                        write pixel_count to memory buffer

                        seek back to previous_offset
                    end block
                    
                    write pixel to memory buffer
                end block

                increment y by 1

            end block

            if operator = true or y = height then
                Pixel = 0

                write Pixel to memory buffer

                rowstart = 255
                
                write rowstart to memory buffer
            end block

            increment x by 1

        end block

        seek memory buffer position to 0

        block_size = picture_width * size of dword

        allocate block_memory, filled with 0's, with block_size

        write block_memory to file, using block_size as size

        offset = current file_position

        free block_memory

        seek to position 8 in file from start

        for loop, count = 0, break on count = number of elements in column_array
            column_offset = column_array[count] + offset

            write column_offset to file
        end block

        write memory buffer to file
    }
    } */
    
}

package rr;

/** Interface for sprite managers. Handles loading sprites, fixing
 *  rotations etc. and helping retrieving spritedefs when required.
 * 
 * @author velktron.
 *
 */

public interface ISpriteManager {
    
    /** Default known sprite names for DOOM */
    public final static String[] doomsprnames = {
        "TROO","SHTG","PUNG","PISG","PISF","SHTF","SHT2","CHGG","CHGF","MISG",
        "MISF","SAWG","PLSG","PLSF","BFGG","BFGF","BLUD","PUFF","BAL1","BAL2",
        "PLSS","PLSE","MISL","BFS1","BFE1","BFE2","TFOG","IFOG","PLAY","POSS",
        "SPOS","VILE","FIRE","FATB","FBXP","SKEL","MANF","FATT","CPOS","SARG",
        "HEAD","BAL7","BOSS","BOS2","SKUL","SPID","BSPI","APLS","APBX","CYBR",
        "PAIN","SSWV","KEEN","BBRN","BOSF","ARM1","ARM2","BAR1","BEXP","FCAN",
        "BON1","BON2","BKEY","RKEY","YKEY","BSKU","RSKU","YSKU","STIM","MEDI",
        "SOUL","PINV","PSTR","PINS","MEGA","SUIT","PMAP","PVIS","CLIP","AMMO",
        "ROCK","BROK","CELL","CELP","SHEL","SBOX","BPAK","BFUG","MGUN","CSAW",
        "LAUN","PLAS","SHOT","SGN2","COLU","SMT2","GOR1","POL2","POL5","POL4",
        "POL3","POL1","POL6","GOR2","GOR3","GOR4","GOR5","SMIT","COL1","COL2",
        "COL3","COL4","CAND","CBRA","COL6","TRE1","TRE2","ELEC","CEYE","FSKU",
        "COL5","TBLU","TGRN","TRED","SMBT","SMGT","SMRT","HDB1","HDB2","HDB3",
        "HDB4","HDB5","HDB6","POB1","POB2","BRS1","TLMP","TLP2"
    };
    
    void InitSpriteLumps();
    
    int getNumSprites();

    int getFirstSpriteLump();
    
    spritedef_t[] getSprites();
    
    spritedef_t getSprite(int index);
    
    int[] getSpriteWidth();
    int[] getSpriteOffset();
    int[] getSpriteTopOffset();

    int getSpriteWidth(int index);
    int getSpriteOffset(int index);
    int getSpriteTopOffset(int index);

    void InitSprites(String[] namelist);

    
}
package rr;

import p.Resettable;

/**
 * 
 * A SubSector. References a Sector. Basically, this is a list of LineSegs,
 * indicating the visible walls that define (all or some) sides of a convex BSP
 * leaf.
 * 
 * @author admin
 */
public class subsector_t implements Resettable{

	public subsector_t() {
		this(null,  0,  0);
	} 

	public subsector_t(sector_t sector, int numlines, int firstline) {
		this.sector = sector;
		this.numlines = numlines;
		this.firstline = firstline;
	}

	// Maes: single pointer
	public sector_t sector;
	// e6y: support for extended nodes
	// 'int' instead of 'short'
	public int numlines;
	public int firstline;
	
	public String toString(){
		sb.setLength(0);
		sb.append("Subsector");
		sb.append('\t');
		sb.append("Sector: ");
		sb.append(sector);
		sb.append('\t');
		sb.append("numlines ");
		sb.append(numlines);
		sb.append('\t');
		sb.append("firstline ");
		sb.append(firstline);
		return sb.toString();
		
		
	}
	
	private static StringBuilder sb=new StringBuilder();

    @Override
    public void reset() {
        sector=null;
        firstline=numlines=0;        
    }
	

}

package rr;

/**
 * A single patch from a texture definition,
 * basically a rectangular area within
 * the texture rectangle.
 * @author admin
 *
 */
public class texpatch_t {
// Block origin (allways UL),
// which has allready accounted
// for the internal origin of the patch.
int     originx;    
int     originy;
int     patch;

public void copyFromMapPatch(mappatch_t mpp) {
   this.originx=mpp.originx;
   this.originy=mpp.originy;
   this.patch=mpp.patch;
    }
}
package rr;

import static data.Defines.FF_FRAMEMASK;
import static data.Defines.FF_FULLBRIGHT;
import static data.Limits.MAXVISSPRITES;
import static data.Tables.ANG45;
import static data.Tables.BITS32;
import static m.fixed_t.FRACBITS;
import static m.fixed_t.FRACUNIT;
import static m.fixed_t.FixedDiv;
import static m.fixed_t.FixedMul;
import static p.mobj_t.MF_SHADOW;
import static rr.LightsAndColors.*;
import static rr.Renderer.MINZ;

import i.IDoomSystem;

import java.util.Arrays;

import p.mobj_t;
import utils.C2JUtils;

/** Visualized sprite manager. Depends on: SpriteManager, DoomSystem,
 *  Colormaps, Current View.
 *  
 * @author velktron
 *
 * @param <V>
 */

public final class VisSprites<V>
        implements IVisSpriteManagement<V> {

    private final static boolean DEBUG = false;

    private final static boolean RANGECHECK = false;

    protected IDoomSystem I;

    protected ISpriteManager SM;

    protected ViewVars view;

    protected LightsAndColors<V> colormaps;

    protected RendererState<?, V> R;

    public VisSprites(RendererState<?, V> R) {
        updateStatus(R);
        vissprite_t<V> tmp = new vissprite_t<V>();
        vissprites = C2JUtils.createArrayOfObjects(tmp, MAXVISSPRITES);
    }

    public void updateStatus(RendererState<?, V> R) {
        this.R = R;
        this.view = R.view;
        this.I = R.I;
        this.SM = R.SM;
        this.colormaps = R.colormaps;

    }

    protected vissprite_t<V>[] vissprites;

    protected int vissprite_p;

    protected int newvissprite;

    // UNUSED
    // private final vissprite_t unsorted;
    // private final vissprite_t vsprsortedhead;

    // Cache those you get from the sprite manager
    protected int[] spritewidth, spriteoffset, spritetopoffset;

    /**
     * R_AddSprites During BSP traversal, this adds sprites by sector.
     */

    @Override
    public void AddSprites(sector_t sec) {
        if (DEBUG)
            System.out.println("AddSprites");
        mobj_t thing;
        int lightnum;

        // BSP is traversed by subsector.
        // A sector might have been split into several
        // subsectors during BSP building.
        // Thus we check whether its already added.
        if (sec.validcount == R.getValidCount())
            return;

        // Well, now it will be done.
        sec.validcount = R.getValidCount();

        lightnum = (sec.lightlevel >> LIGHTSEGSHIFT) + colormaps.extralight;

        if (lightnum < 0)
            colormaps.spritelights = colormaps.scalelight[0];
        else if (lightnum >= LIGHTLEVELS)
            colormaps.spritelights = colormaps.scalelight[LIGHTLEVELS - 1];
        else
            colormaps.spritelights = colormaps.scalelight[lightnum];

        // Handle all things in sector.
        for (thing = sec.thinglist; thing != null; thing = (mobj_t) thing.snext)
            ProjectSprite(thing);
    }

    /**
     * R_ProjectSprite Generates a vissprite for a thing if it might be visible.
     * 
     * @param thing
     */
    protected final void ProjectSprite(mobj_t thing) {
        int tr_x, tr_y;
        int gxt, gyt;
        int tx, tz;

        int xscale, x1, x2;

        spritedef_t sprdef;
        spriteframe_t sprframe;
        int lump;

        int rot;
        boolean flip;

        int index;

        vissprite_t<V> vis;

        long ang;
        int iscale;

        // transform the origin point
        tr_x = thing.x - view.x;
        tr_y = thing.y - view.y;

        gxt = FixedMul(tr_x, view.cos);
        gyt = -FixedMul(tr_y, view.sin);

        tz = gxt - gyt;

        // thing is behind view plane?
        if (tz < MINZ)
            return;
        /* MAES: so projection/tz gives horizontal scale */
        xscale = FixedDiv(view.projection, tz);

        gxt = -FixedMul(tr_x, view.sin);
        gyt = FixedMul(tr_y, view.cos);
        tx = -(gyt + gxt);

        // too far off the side?
        if (Math.abs(tx) > (tz << 2))
            return;

        // decide which patch to use for sprite relative to player
        if (RANGECHECK) {
            if (thing.sprite.ordinal() >= SM.getNumSprites())
                I.Error("R_ProjectSprite: invalid sprite number %d ",
                    thing.sprite);
        }
        sprdef = SM.getSprite(thing.sprite.ordinal());
        if (RANGECHECK) {
            if ((thing.frame & FF_FRAMEMASK) >= sprdef.numframes)
                I.Error("R_ProjectSprite: invalid sprite frame %d : %d ",
                    thing.sprite, thing.frame);
        }
        sprframe = sprdef.spriteframes[thing.frame & FF_FRAMEMASK];

        if (sprframe.rotate != 0) {
            // choose a different rotation based on player view
            ang = view.PointToAngle(thing.x, thing.y);
            rot = (int) ((ang - thing.angle + (ANG45 * 9) / 2) & BITS32) >>> 29;
            lump = sprframe.lump[rot];
            flip = (boolean) (sprframe.flip[rot] != 0);
        } else {
            // use single rotation for all views
            lump = sprframe.lump[0];
            flip = (boolean) (sprframe.flip[0] != 0);
        }

        // calculate edges of the shape
        tx -= spriteoffset[lump];
        x1 = (view.centerxfrac + FixedMul(tx, xscale)) >> FRACBITS;

        // off the right side?
        if (x1 > view.width)
            return;

        tx += spritewidth[lump];
        x2 = ((view.centerxfrac + FixedMul(tx, xscale)) >> FRACBITS) - 1;

        // off the left side
        if (x2 < 0)
            return;

        // store information in a vissprite
        vis = NewVisSprite();
        vis.mobjflags = thing.flags;
        vis.scale = xscale << view.detailshift;
        vis.gx = thing.x;
        vis.gy = thing.y;
        vis.gz = thing.z;
        vis.gzt = thing.z + spritetopoffset[lump];
        vis.texturemid = vis.gzt - view.z;
        vis.x1 = x1 < 0 ? 0 : x1;
        vis.x2 = x2 >= view.width ? view.width - 1 : x2;
        /*
         * This actually determines the general sprite scale) iscale = 1/xscale,
         * if this was floating point.
         */
        iscale = FixedDiv(FRACUNIT, xscale);

        if (flip) {
            vis.startfrac = spritewidth[lump] - 1;
            vis.xiscale = -iscale;
        } else {
            vis.startfrac = 0;
            vis.xiscale = iscale;
        }

        if (vis.x1 > x1)
            vis.startfrac += vis.xiscale * (vis.x1 - x1);
        vis.patch = lump;

        // get light level
        if ((thing.flags & MF_SHADOW) != 0) {
            // shadow draw
            vis.colormap = null;
        } else if (colormaps.fixedcolormap != null) {
            // fixed map
            vis.colormap = (V) colormaps.fixedcolormap;
            // vis.pcolormap=0;
        } else if ((thing.frame & FF_FULLBRIGHT) != 0) {
            // full bright
            vis.colormap = (V) colormaps.colormaps[0];
            // vis.pcolormap=0;
        }

        else {
            // diminished light
            index = xscale >> (LIGHTSCALESHIFT - view.detailshift);

            if (index >= MAXLIGHTSCALE)
                index = MAXLIGHTSCALE - 1;

            vis.colormap = colormaps.spritelights[index];
            // vis.pcolormap=index;
        }
    }

    /**
     * R_NewVisSprite Returns either a "new" sprite (actually, reuses a pool),
     * or a special "overflow sprite" which just gets overwritten with bogus
     * data. It's a bit of dumb thing to do, since the overflow sprite is never
     * rendered but we have to copy data over it anyway. Would make more sense
     * to check for it specifically and avoiding copying data, which should be
     * more time consuming. Fixed by making this fully limit-removing.
     * 
     * @return
     */
    protected final vissprite_t<V> NewVisSprite() {
        if (vissprite_p == (vissprites.length - 1)) {
            ResizeSprites();
        }
        // return overflowsprite;

        vissprite_p++;
        return vissprites[vissprite_p - 1];
    }

    @Override
    public void cacheSpriteManager(ISpriteManager SM) {
        this.spritewidth = SM.getSpriteWidth();
        this.spriteoffset = SM.getSpriteOffset();
        this.spritetopoffset = SM.getSpriteTopOffset();
    }

    /**
     * R_ClearSprites Called at frame start.
     */

    @Override
    public void ClearSprites() {
        // vissprite_p = vissprites;
        vissprite_p = 0;
    }

    // UNUSED private final vissprite_t overflowsprite = new vissprite_t();

    protected final void ResizeSprites() {
        vissprites =
            C2JUtils.resize(vissprites[0], vissprites, vissprites.length * 2); // Bye
                                                                               // bye,
                                                                               // old
                                                                               // vissprites.
    }

    /**
     * R_SortVisSprites UNUSED more efficient Comparable sorting + built-in
     * Arrays.sort function used.
     */

    @Override
    public final void SortVisSprites() {
        Arrays.sort(vissprites, 0, vissprite_p);

        // Maes: got rid of old vissprite sorting code. Java's is better
        // Hell, almost anything was better than that.

    }

    @Override
    public int getNumVisSprites() {
        return vissprite_p;
    }

    @Override
    public vissprite_t<V>[] getVisSprites() {
        return vissprites;
    }

    public void resetLimits() {
        vissprite_t<V>[] tmp =
            C2JUtils.createArrayOfObjects(vissprites[0], MAXVISSPRITES);
        System.arraycopy(vissprites, 0, tmp, 0, MAXVISSPRITES);

        // Now, that was quite a haircut!.
        vissprites = tmp;    }
}
package rr;

import static m.fixed_t.FRACBITS;
import static m.fixed_t.FixedMul;

import p.Resettable;

/**
 * The LineSeg. Must be built from on-disk mapsegs_t, which are much simpler.
 * 
 * @author Maes
 */

public class seg_t
        implements Resettable {

    /** To be used as references */
    public vertex_t v1, v2;

    /** Local caching. Spares us using one extra reference level */
    public int v1x, v1y, v2x, v2y;

    /** (fixed_t) */
    public int offset;

    /** (angle_t) */
    public long angle;

    // MAES: all were single pointers.

    public side_t sidedef;

    public line_t linedef;

    /**
     * Sector references. Could be retrieved from linedef, too. backsector is
     * NULL for one sided lines
     */
    public sector_t frontsector, backsector;

    // Boom stuff
    public boolean miniseg;

    public float length;

    /** proff 11/05/2000: needed for OpenGL */
    public int iSegID;

    public void assignVertexValues() {
        this.v1x = v1.x;
        this.v1y = v1.y;
        this.v2x = v2.x;
        this.v2y = v2.y;

    }

    /**
     * R_PointOnSegSide
     * 
     * @param x
     * @param y
     * @param line
     * @return
     */
    public static int PointOnSegSide(int x, int y, seg_t line) {
        int lx;
        int ly;
        int ldx;
        int ldy;
        int dx;
        int dy;
        int left;
        int right;

        lx = line.v1x;
        ly = line.v1y;

        ldx = line.v2x - lx;
        ldy = line.v2y - ly;

        if (ldx == 0) {
            if (x <= lx)
                return (ldy > 0) ? 1 : 0;

            return (ldy < 0) ? 1 : 0;
        }
        if (ldy == 0) {
            if (y <= ly)
                return (ldx < 0) ? 1 : 0;

            return (ldx > 0) ? 1 : 0;
        }

        dx = x - lx;
        dy = y - ly;

        // Try to quickly decide by looking at sign bits.
        if (((ldy ^ ldx ^ dx ^ dy) & 0x80000000) != 0) {
            if (((ldy ^ dx) & 0x80000000) != 0) {
                // (left is negative)
                return 1;
            }
            return 0;
        }

        left = FixedMul(ldy >> FRACBITS, dx);
        right = FixedMul(dy, ldx >> FRACBITS);

        if (right < left) {
            // front side
            return 0;
        }
        // back side
        return 1;
    }

    /**
     * R_PointOnSegSide
     * 
     * @param x
     * @param y
     * @param line
     * @return
     */
    public int PointOnSegSide(int x, int y) {
        int lx;
        int ly;
        int ldx;
        int ldy;
        int dx;
        int dy;
        int left;
        int right;

        lx = this.v1x;
        ly = this.v1y;

        ldx = this.v2x - lx;
        ldy = this.v2y - ly;

        if (ldx == 0) {
            if (x <= lx)
                return (ldy > 0) ? 1 : 0;

            return (ldy < 0) ? 1 : 0;
        }
        if (ldy == 0) {
            if (y <= ly)
                return (ldx < 0) ? 1 : 0;

            return (ldx > 0) ? 1 : 0;
        }

        dx = x - lx;
        dy = y - ly;

        // Try to quickly decide by looking at sign bits.
        if (((ldy ^ ldx ^ dx ^ dy) & 0x80000000) != 0) {
            if (((ldy ^ dx) & 0x80000000) != 0) {
                // (left is negative)
                return 1;
            }
            return 0;
        }

        left = FixedMul(ldy >> FRACBITS, dx);
        right = FixedMul(dy, ldx >> FRACBITS);

        if (right < left) {
            // front side
            return 0;
        }
        // back side
        return 1;
    }

    public String toString() {
        return String
                .format(
                    "Seg %d\n\tFrontsector: %s\n\tBacksector: %s\n\tVertexes: %x %x %x %x",
                    iSegID, frontsector, backsector, v1x, v1y, v2x, v2y);
    }

    @Override
    public void reset() {
        v1 = v2 = null;
        v1x = v1y = v2x = v2y = 0;
        angle = 0;
        frontsector = backsector = null;
        iSegID = 0;
        linedef = null;
        miniseg = false;
        offset = 0;
        length = 0;
    }

}

package rr;

import static data.Tables.ANGLETOFINESHIFT;
import static data.Tables.BITS32;
import static data.Tables.finecosine;
import static data.Tables.finesine;
import static m.fixed_t.FixedMul;
import static rr.LightsAndColors.*;

import i.IDoomSystem;

import rr.RendererState.IPlaneDrawer;
import rr.drawfuns.SpanVars;
import v.IVideoScale;

public abstract class PlaneDrawer<T,V> implements IPlaneDrawer{

    private static final boolean DEBUG2=false;


    protected final boolean RANGECHECK = false;
    
    //
    // spanstart holds the start of a plane span
    // initialized to 0 at start
    //
    protected int[] spanstart, spanstop;

    //
    // texture mapping
    //
    protected V[] planezlight; // The distance lighting effect you see
    /** To treat as fixed_t */
    protected int planeheight;
    /** To treat as fixed_t */
    protected int[] distscale;

    /** To treat as fixed_t */
    protected int[] cacheddistance, cachedxstep, cachedystep;

    protected final ViewVars view;    

    protected final SegVars seg_vars;
    protected final SpanVars<T,V> dsvars;
    /** The visplane data. Set separately. For threads, use the same for
     *  everyone.
     */
    protected Visplanes vpvars;
    protected final LightsAndColors<V> colormap;
    protected final TextureManager<T> TexMan;
    protected final IDoomSystem I;
    
    
    protected PlaneDrawer(Renderer<T,V> R){
        this.view=R.getView();
        this.vpvars=R.getVPVars();
        this.dsvars=R.getDSVars();
        this.seg_vars=R.getSegVars();
        this.colormap=R.getColorMap();
        this.TexMan=R.getTextureManager();
        this.I=R.getDoomSystem();
    }

    /**
     * R_MapPlane
     * 
     * Called only by R_MakeSpans.
     * 
     * This is where the actual span drawing function is called.
     * 
     * Uses global vars: planeheight ds_source -> flat data has already been
     * set. basexscale -> actual drawing angle and position is computed from
     * these baseyscale viewx viewy
     * 
     * BASIC PRIMITIVE
     */

    public void MapPlane(int y, int x1, int x2) {
        // MAES: angle_t
        int angle;
        // fixed_t
        int distance;
        int length;
        int index;

        if (RANGECHECK) {
            rangeCheck(x1,x2,y);
        }

        if (planeheight != vpvars.cachedheight[y]) {
            vpvars.cachedheight[y] = planeheight;
            distance = cacheddistance[y] = FixedMul(planeheight, vpvars.yslope[y]);
            dsvars.ds_xstep = cachedxstep[y] = FixedMul(distance, vpvars.basexscale);
            dsvars.ds_ystep = cachedystep[y] = FixedMul(distance, vpvars.baseyscale);
        } else {
            distance = cacheddistance[y];
            dsvars.ds_xstep = cachedxstep[y];
            dsvars.ds_ystep = cachedystep[y];
        }

        length = FixedMul(distance, distscale[x1]);
        angle = (int) (((view.angle + view.xtoviewangle[x1]) & BITS32) >>> ANGLETOFINESHIFT);
        dsvars.ds_xfrac = view.x + FixedMul(finecosine[angle], length);
        dsvars.ds_yfrac = -view.y - FixedMul(finesine[angle], length);

        if (colormap.fixedcolormap != null)
            dsvars.ds_colormap = colormap.fixedcolormap;
        else {
            index = distance >>> LIGHTZSHIFT;

            if (index >= MAXLIGHTZ)
                index = MAXLIGHTZ - 1;

            dsvars.ds_colormap = planezlight[index];
        }

        dsvars.ds_y = y;
        dsvars.ds_x1 = x1;
        dsvars.ds_x2 = x2;

        // high or low detail
        dsvars.spanfunc.invoke();
    }

    protected final void rangeCheck(int x1,int x2,int y) {
        if (x2 < x1 || x1 < 0 || x2 >= view.width || y > view.height)
            I.Error("%s: %d, %d at %d",this.getClass().getName(), x1, x2, y);
        }
  
        
    /**
     * R_MakeSpans
     * 
     * Called only by DrawPlanes. If you wondered where the actual
     * boundaries for the visplane flood-fill are laid out, this is it.
     * 
     * The system of coords seems to be defining a sort of cone.
     * 
     * 
     * @param x
     *            Horizontal position
     * @param t1
     *            Top-left y coord?
     * @param b1
     *            Bottom-left y coord?
     * @param t2
     *            Top-right y coord ?
     * @param b2
     *            Bottom-right y coord ?
     * 
     */

    protected void MakeSpans(int x, int t1, int b1, int t2, int b2) {

        // If t1 = [sentinel value] then this part won't be executed.
        while (t1 < t2 && t1 <= b1) {
            this.MapPlane(t1, spanstart[t1], x - 1);
            t1++;
        }
        while (b1 > b2 && b1 >= t1) {
            this.MapPlane(b1, spanstart[b1], x - 1);
            b1--;
        }

        // So...if t1 for some reason is < t2, we increase t2 AND store the
        // current x
        // at spanstart [t2] :-S
        while (t2 < t1 && t2 <= b2) {
            // System.out.println("Increasing t2");
            spanstart[t2] = x;
            t2++;
        }

        // So...if t1 for some reason b2 > b1, we decrease b2 AND store the
        // current x
        // at spanstart [t2] :-S

        while (b2 > b1 && b2 >= t2) {
            // System.out.println("Decreasing b2");
            spanstart[b2] = x;
            b2--;
        }
    }

    /**
     * R_InitPlanes Only at game startup.
     */

    public void InitPlanes() {
        // Doh!
    }

   /////////// VIDEO SCALE STUFF ///////////

    protected int SCREENWIDTH;
    protected int SCREENHEIGHT;
    protected IVideoScale vs;

    @Override
    public void setVideoScale(IVideoScale vs) {
        this.vs = vs;
    }

    @Override
    public void initScaling() {
        this.SCREENHEIGHT = vs.getScreenHeight();
        this.SCREENWIDTH = vs.getScreenWidth();

        // Pre-scale stuff.

        spanstart = new int[SCREENHEIGHT];
        spanstop = new int[SCREENHEIGHT];
        distscale = new int[SCREENWIDTH];        
        cacheddistance = new int[SCREENHEIGHT];
        cachedxstep = new int[SCREENHEIGHT];
        cachedystep = new int[SCREENHEIGHT];

        // HACK: visplanes are initialized globally.
        visplane_t.setVideoScale(vs);
        visplane_t.initScaling();
        vpvars.initVisplanes();

    }
    
    protected final void rangeCheckErrors(){
        if (seg_vars.ds_p > seg_vars.MAXDRAWSEGS)
            I.Error("R_DrawPlanes: drawsegs overflow (%d)", seg_vars.ds_p);

        if (vpvars.lastvisplane > vpvars.MAXVISPLANES)
            I.Error(" R_DrawPlanes: visplane overflow (%d)",
                vpvars.lastvisplane);

        if (vpvars.lastopening > vpvars.MAXOPENINGS)
            I.Error("R_DrawPlanes: opening overflow (%d)", vpvars.lastopening);
    }

    /** Default implementation which DOES NOTHING. MUST OVERRIDE */
    
    public void DrawPlanes(){
        
    }
    
    public void sync(){
        // Nothing required if serial.
    }
    
    /////////////// VARIOUS BORING GETTERS ////////////////////


    @Override
    public int[] getDistScale() {
        return distscale;
    }
    
}

package rr;

import static data.Defines.PU_CACHE;
import static data.Defines.PU_STATIC;
import static data.Defines.SKYFLATNAME;
import static m.fixed_t.FRACBITS;
import static m.fixed_t.FRACUNIT;

import i.IDoomSystem;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Hashtable;
import p.AbstractLevelLoader;
import doom.DoomStatus;
import w.DoomBuffer;
import w.IWadLoader;
import w.li_namespace;
import w.lumpinfo_t;

/** An attempt to separate texture mapping functionality from
 *  the rest of the rendering. Seems to work like a charm, and
 *  it makes it clearer what needs and what doesn't need to be 
 *  exposed.
 * 
 * @author Maes
 *
 */

public class SimpleTextureManager
        implements TextureManager<byte[]> {
    
    IWadLoader W;
    IDoomSystem I;
    AbstractLevelLoader LL;
    DoomStatus<?,?> DM;
    
    //
    // Graphics.
    // DOOM graphics for walls and sprites
    // is stored in vertical runs of opaque pixels (posts).
    // A column is composed of zero or more posts,
    // a patch or sprite is composed of zero or more columns.
    // 

    protected int     firstflat;
    protected int     lastflat;
    protected int     numflats;
    /** HACK */
    protected flat_t[] flats;
    
    //protected int     firstpatch;
    //protected int     lastpatch;
    protected int     numpatches;


    protected int     numtextures;
    
    /** The unchached textures themselves, stored just as patch lists and various properties */
    protected texture_t[] textures;

    /** Width per texture? */
    protected int[]            texturewidthmask;
    /** fixed_t[] needed for texture pegging */
    
    /** How tall each composite texture is supposed to be */
    protected int[]        textureheight;    
    
    /** How large each composite texture is supposed to be */
    protected int[]            texturecompositesize;
    /** Tells us which patch lump covers which column of which texture */
    protected short[][]         texturecolumnlump;
    
    /** This is supposed to store indexes into a patch_t lump which point to the columns themselves 
     *  Instead, we're going to return indexes to columns inside a particular patch.
     *  In the case of patches inside a non-cached multi-patch texture (e.g. those made of non-overlapping
     *  patches), we're storing indexes INSIDE A PARTICULAR PATCH. E.g. for STARTAN1, which is made of two
     *  32-px wide patches, it should go something like 0, 1,2 ,3...31, 0,1,2,....31.
     *  
     * */
    protected char[][]    texturecolumnofs;
    
    /** couple with texturecomposite */
    protected char texturecoloffset;
    //short[][]    texturecolumnindexes;
    /** Stores [textures][columns][data]. */
    protected byte[][][] texturecomposite;
    
    /** HACK to store "composite masked textures", a Boomism. */ 
    protected patch_t[] patchcomposite;

    /** for global animation. Storage stores actual lumps, translation is a relative -> relative map */
    protected int[]        flattranslation, flatstorage,texturetranslation;
    
    // This is also in DM, but one is enough, really.
    protected int skytexture,skytexturemid,skyflatnum;
    
    public SimpleTextureManager(DoomStatus<?,?> DC) {
        this.DM=DC;
        this.W=DM.W;
        this.I=DM.I;
        this.LL=DM.LL;
        FlatPatchCache=new Hashtable<Integer, patch_t>();
    }
  
    /** Hash table used for matching flat <i>lump</i> to flat <i>num</i> */

    Hashtable<Integer, Integer> FlatCache;
    
    Hashtable<Integer, patch_t> FlatPatchCache;

      /**
       * R_CheckTextureNumForName Check whether texture is available. Filter out
       * NoTexture indicator. Can be sped up with a hash table, but it's pointless.
       */
    
        @Override
       public int CheckTextureNumForName(String name) {
          Integer i;
          // "NoTexture" marker.
          if (name.charAt(0) == '-')  return 0;
          
          i=TextureCache.get(name);
          if (i==null) return -1;
          else return i;

          /* for (i = 0; i < numtextures; i++)
              if (textures[i].name.compareToIgnoreCase(name) == 0)
                  return i;

          return -1; */
      }
    
        /** Hash table used for fast texture lookup */

        Hashtable<String, Integer> TextureCache;
        
        /**
         * R_TextureNumForName
         * Calls R_CheckTextureNumForName,
         * aborts with error message.
         */
      
        public int TextureNumForName(String name) {
            int i;

            i = CheckTextureNumForName(name);

            if (i == -1) {
                I.Error("R_TextureNumForName: %s not found", name);
            }
            return i;
        }

    /**
     * R_InitTextures
     * Initializes the texture list
     *  with the textures from the world map.
     */
    
    public void InitTextures () throws IOException
    {
        // This drives the rest
        maptexture_t mtexture = new maptexture_t();
        texture_t texture;
        mappatch_t[] mpatch;
        texpatch_t[] patch;
        ByteBuffer[] maptex = new ByteBuffer[texturelumps.length];
        int[] patchlookup;
        int totalwidth;
        int offset;
        int[] maxoff = new int[texturelumps.length];
        int[] _numtextures = new int[texturelumps.length];
        int directory = 1;
        int texset=TEXTURE1;
        // Load the patch names from pnames.lmp.
        //name[8] = 0;    
        patchlookup=loadPatchNames("PNAMES");
        
        // Load the map texture definitions from textures.lmp.
        // The data is contained in one or two lumps,
        //  TEXTURE1 for shareware, plus TEXTURE2 for commercial.
        
        for (int i=0;i<texturelumps.length;i++){
            String TEXTUREx=texturelumps[i];
            if (W.CheckNumForName (TEXTUREx) != -1){
            maptex[i] = W.CacheLumpName (TEXTUREx, PU_STATIC).getBuffer();
            maptex[i].rewind();
            maptex[i].order(ByteOrder.LITTLE_ENDIAN);
            _numtextures[i] = maptex[i].getInt();
            maxoff[i] = W.LumpLength (W.GetNumForName (TEXTUREx));
            if (_numtextures[i]!= (maxoff[i]-4)/8) {
            	System.err.printf("Warning! Possibly malformed TEXTURE%d lump. Stated number of textures: %d Possible: %d\n",i, _numtextures[i] , (maxoff[i]-4)/8);
            }
           
            }
        }
        
        // Total number of textures.
        numtextures = _numtextures[0] + _numtextures[1];
        
        textures = new texture_t[numtextures];
        // MAES: Texture hashtable.          
        TextureCache=new Hashtable<String, Integer>(numtextures);
        
        texturecolumnlump = new short[numtextures][];
        texturecolumnofs = new char[numtextures][];
        patchcomposite=new patch_t[numtextures];
        texturecomposite = new byte[numtextures][][];
        texturecompositesize = new int[numtextures];
        texturewidthmask = new int[numtextures];
        textureheight = new int[numtextures];

        totalwidth = 0;
        
        //  Really complex printing shit...
        System.out.print("[");
        for (int i=0 ; i<numtextures ; i++,directory++)
        {
        
        if ((i&63)==0)
            System.out.print ('.');

        if (i == _numtextures[TEXTURE1])
        {
            // Start looking in second texture file.
            texset=TEXTURE2;
            directory = 1; // offset "1" inside maptex buffer
            //System.err.print("Starting looking into TEXTURE2\n");
        }
        
        offset = maptex[texset].getInt(directory<<2);
        
        if (offset > maxoff[texset])
            I.Error("R_InitTextures: bad texture directory");
        
        maptex[texset].position(offset);
        // Read "maptexture", which is the on-disk form.
        mtexture.unpack(maptex[texset]);

        // MAES: the HashTable only needs to know the correct names.
        TextureCache.put(mtexture.name.toUpperCase(), new Integer(i));
        
        // We don't need to manually copy trivial fields.
        textures[i]=new texture_t();
        textures[i].copyFromMapTexture(mtexture);
        texture = textures[i];
        
        // However we do need to correct the "patch.patch" field through the patchlookup
        mpatch = mtexture.patches;
        patch = texture.patches;
        
        for (int j=0 ; j<texture.patchcount ; j++)
        {
            //System.err.printf("Texture %d name %s patch %d lookup %d\n",i,mtexture.name,j,mpatch[j].patch);
            patch[j].patch = patchlookup[mpatch[j].patch%patchlookup.length];
            if (patch[j].patch == -1)
            {
            I.Error ("R_InitTextures: Missing patch in texture %s",
                 texture.name);
            }
        }       
        
        // Columns and offsets of taxture = textures[i]
        texturecolumnlump[i] = new short[texture.width];
        //C2JUtils.initArrayOfObjects( texturecolumnlump[i], column_t.class);
        texturecolumnofs[i] = new char[texture.width];
        
        int j = 1;
        while (j*2 <= texture.width)
            j<<=1;

        texturewidthmask[i] = j-1;
        textureheight[i] = texture.height<<FRACBITS;
            
        totalwidth += texture.width;
        }
        
        // Precalculate whatever possible.  
        for (int i=0 ; i<numtextures ; i++)
        GenerateLookup (i);
        
        // Create translation table for global animation.
        texturetranslation = new int[numtextures];
        
        for (int i=0 ; i<numtextures ; i++)
            texturetranslation[i] = i;
    }
    
    /** Assigns proper lumpnum to patch names. Check whether flats and patches of the same name coexist.
     *  If yes, priority should go to patches. Otherwise, it's a "flats on walls" case.
     * 
     * @param pnames
     * @return
     * @throws IOException
     */
    
    private int[] loadPatchNames(String pnames) throws IOException {
        int[] patchlookup;
        int nummappatches;
        String name;
        
        ByteBuffer names = W.CacheLumpName (pnames, PU_STATIC).getBuffer();        
        names.order(ByteOrder.LITTLE_ENDIAN);
        
        // Number of patches.
        names.rewind();
        nummappatches = names.getInt();        
        patchlookup = new int[nummappatches];
        
        for (int i=0 ; i<nummappatches ; i++)
        {
        // Get a size limited string;
        name=DoomBuffer.getNullTerminatedString(names, 8).toUpperCase();
        
        // Resolve clashes
        int[] stuff= W.CheckNumsForName (name);
        
        // Move backwards.
        for (int k=0;k<stuff.length;k++){
            
            // Prefer non-flat, with priority
            if (W.GetLumpInfo(stuff[k]).namespace != li_namespace.ns_flats) {
                patchlookup[i]=stuff[k];
                break;            
            }            
             
            // Suck it down :-/
            patchlookup[i]=stuff[k];
        }
        }
        
        return patchlookup;
    }

    private patch_t retrievePatchSafe(int lump){
        
        // If this is a known troublesome lump, get it from the cache.
        if (FlatPatchCache.containsKey(lump)){
            return FlatPatchCache.get(lump);
            }
        
        lumpinfo_t info = W.GetLumpInfo(lump);
        patch_t realpatch;
        
        // Patch is actually a flat or something equally nasty. Ouch.
        if (info.namespace==li_namespace.ns_flats) {
                byte[] flat=W.CacheLumpNumAsRawBytes(lump, PU_CACHE);
                realpatch= MultiPatchSynthesizer.synthesizePatchFromFlat(info.name,flat,64, 64);
                this.FlatPatchCache.put(lump, realpatch);
                W.UnlockLumpNum(lump);
        }
        else
            // It's probably safe, at this point.
            realpatch = (patch_t) W.CacheLumpNum (lump, PU_CACHE,patch_t.class);
        
        return realpatch;
    }
    
    /**
     * R_GenerateLookup
     * 
     * Creates the lookup tables for a given texture (aka, where inside the texture cache
     * is the offset for particular column... I think.
     * 
     * @throws IOException 
     */
    @Override
    public void GenerateLookup (int texnum) throws IOException
    {
        texture_t      texture;
        short[]       patchcount; //Keeps track of how many patches overlap a column.
        texpatch_t[]     patch;  
        patch_t        realpatch = null;
        int         x;
        int         x1;
        int         x2;

        short[]      collump;
         char[] colofs;
        
        texture = textures[texnum];

        // Composited texture not created yet.
        texturecomposite[texnum] = null;
        
        // We don't know ho large the texture will be, yet, but it will be a multiple of its height.
        texturecompositesize[texnum] = 0;

        // This is the only place where those can be actually modified.
        // They are still null at this point.
        collump = texturecolumnlump[texnum];
        colofs = texturecolumnofs[texnum];
        
        /* Now count the number of columns  that are covered by more 
         * than one patch. Fill in the lump / offset, so columns
         * with only a single patch are all done.
         */

        patchcount = new short[texture.width];
        patch = texture.patches;
            
        // for each patch in a texture...
        for (int i=0; i<texture.patchcount;i++)
        {
        // Retrieve patch...if it IS a patch.
        realpatch=this.retrievePatchSafe(patch[i].patch);

        x1 = patch[i].originx;
        x2 = x1 + realpatch.width;
        
        // Where does the patch start, inside the compositetexture?
        if (x1 < 0)
            x = 0;
        else
            x = x1;

        // Correct, starts at originx. Where does it end?
        
        if (x2 > texture.width)
            x2 = texture.width;
        for ( ; x<x2 ; x++)
        {
            /* Obviously, if a patch starts at x it does cover the x-th column
             *  of a texture, even if transparent. 
             */
            patchcount[x]++;
            // Column "x" of composite texture "texnum" is covered by this patch.
            collump[x] = (short) patch[i].patch;
            
            /* This is supposed to be a raw pointer to the beginning of the column
             * data, as it appears inside the PATCH.
             * 
             * Instead, we can return the actual column index (x-x1)
             * As an example, the second patch of STARTAN1 (width 64) starts
             * at column 32. Therefore colofs should be something like
             * 0,1,2,...,31,0,1,....31, indicating that the 32-th column of
             * STARTAN1 is the 0-th column of the patch that is assigned to that column
             * (the latter can be looked up in texturecolumnlump[texnum].
             * 
             * Any questions?
             * 
             */
            colofs[x] = (char) (x-x1);
            // This implies that colofs[x] is 0 for a void column?
                
        } // end column of patch.
        } // end patch
        
        // Now check all columns again.
        for ( x=0 ; x<texture.width ; x++)
        {
        // Can only occur if a column isn't covered by a patch at all, not even a transparent one.
        if (patchcount[x]==0)
        {
            // TODO: somehow handle this. 
            System.err.print (realpatch.width);
            System.err.print ("R_GenerateLookup: column without a patch ("+texture.name+")\n");
            //return;
        }
        // I_Error ("R_GenerateLookup: column without a patch");
        
        
        // Columns where more than one patch overlaps.
        if (patchcount[x] > 1)
        {
            // Use the cached block. This column won't be read from the wad system.
            collump[x] = -1;    
            colofs[x] = (char) texturecompositesize[texnum];
            
            /* Do we really mind?
            if (texturecompositesize[texnum] > 0x10000-texture.height)
            {
            I.Error ("R_GenerateLookup: texture no %d (%s) is >64k",
                 texnum,textures[texnum].name);
            } */
            
            texturecompositesize[texnum] += texture.height;
        }
        }   
    }

    /**
     * R_GenerateComposite
     * Using the texture definition, the composite texture is created 
     * from the patches and each column is cached. This method is "lazy"
     * aka it's only called when a cached/composite texture is needed.
     * 
     * @param texnum
     * 
     */
    
    public void GenerateComposite (int texnum) 
    {
        byte[][]       block;
        texture_t      texture;
        texpatch_t[]     patch;  
        patch_t        realpatch=null;
        int         x;
        int         x1;
        int         x2;
        column_t       patchcol;
        short[]      collump;
        char[] colofs; // unsigned short
       // short[] colidxs; // unsigned short
        
        texture = textures[texnum];

        // BOth allocate the composite texture, and assign it to block.
        // texturecompositesize indicates a size in BYTES. We need a number of columns, though.
        // Now block is divided into columns. We need to allocate enough data for each column
     
        block = texturecomposite[texnum]=new byte[texture.width][texture.height];
        
        // Lump where a certain column will be read from (actually, a patch)
        collump = texturecolumnlump[texnum];
        
        // Offset of said column into the patch.
        colofs = texturecolumnofs[texnum];
        
       // colidxs = texturecolumnindexes[texnum];
        
        // Composite the columns together.
        patch = texture.patches;
        
        // For each patch in the texture...
        for (int i=0 ;i<texture.patchcount; i++)
        {
        // Retrieve patch...if it IS a patch.
        realpatch=this.retrievePatchSafe(patch[i].patch);
            
        x1 = patch[i].originx;
        x2 = x1 + realpatch.width;

        if (x1<0)
            x = 0;
        else
            x = x1;
        
        if (x2 > texture.width)
            x2 = texture.width;

        for ( ; x<x2 ; x++)
        {
            // Column does not have multiple patches?
            if (collump[x] >= 0)
            continue;
            
           // patchcol = (column_t *)((byte *)realpatch
            //            + LONG(realpatch.columnofs[x-x1]));
            
            
            // We can look this up cleanly in Java. Ha!
            patchcol=realpatch.columns[x-x1];
            DrawColumnInCache (patchcol,
                     block[x], colofs[x],
                     patch[i].originy,
                     texture.height);
        }
                            
        }
    }
    
    /**
     * R_GenerateMaskedComposite
     * 
     * Generates a "masked composite texture": the result is a MASKED texture
     * (with see-thru holes), but this time  multiple patches can be used to 
     * assemble it, unlike standard Doom where this is not allowed.
     *  
     * Called only if a request for a texture in the general purpose GetColumn 
     * method (used only for masked renders) turns out not to be pointing to a standard 
     * cached texture, nor to a disk lump(which is the standard Doom way of indicating a 
     * composite single patch texture) but to a cached one which, however, is composite.
     * 
     * Confusing, huh?
     * 
     * Normally, this results in a disaster, as the masked rendering methods 
     * don't expect cached/composite textures at all, and you get all sorts of nasty
     * tutti frutti and medusa effects. Not anymore ;-) 
     * 
     * @param texnum
     * 
     */
    
    @Override
    public void GenerateMaskedComposite(int texnum) {
        byte[][] block;
        boolean[][] pixmap; // Solidity map
        texture_t texture;
        texpatch_t[] patch;
        patch_t realpatch = null;
        int x;
        int x1;
        int x2;
        column_t patchcol;
        short[] collump;
        char[] colofs; // unsigned short

        texture = textures[texnum];

        // MAES: we don't want to save a solid block this time. Will only use
        // it for synthesis.

        block = new byte[texture.width][texture.height];
        pixmap = new boolean[texture.width][texture.height]; // True values = solid

        // Lump where a certain column will be read from (actually, a patch)
        collump = texturecolumnlump[texnum];

        // Offset of said column into the patch.
        colofs = texturecolumnofs[texnum];

        // Composite the columns together.
        patch = texture.patches;

        // For each patch in the texture...
        for (int i = 0; i < texture.patchcount; i++) {

            realpatch = W.CachePatchNum(patch[i].patch);
            x1 = patch[i].originx;
            x2 = x1 + realpatch.width;

            if (x1 < 0)
                x = 0;
            else
                x = x1;

            if (x2 > texture.width)
                x2 = texture.width;

            for (; x < x2; x++) {
                // Column does not have multiple patches?
                if (collump[x] >= 0)
                    continue;

                // patchcol = (column_t *)((byte *)realpatch
                // + LONG(realpatch.columnofs[x-x1]));

                // We can look this up cleanly in Java. Ha!
                patchcol = realpatch.columns[x - x1];
                DrawColumnInCache(patchcol, block[x], pixmap[x], colofs[x],
                    patch[i].originy, texture.height);
            }

        }
        
        // Patch drawn on cache, synthesize patch_t using it. 
        this.patchcomposite[texnum]=MultiPatchSynthesizer.synthesize(this.CheckTextureNameForNum(texnum),block, pixmap,texture.width,texture.height);
    }
    
    /**
     *  R_DrawColumnInCache
     *  Clip and draw a column from a patch into a cached post.
     *  
     *  This means that columns are effectively "uncompressed" into cache, here,
     *  and that composite textures are generally uncompressed...right?
     *  
     *  Actually: "compressed" or "masked" textures are retrieved in the same way.
     *  There are both "masked" and "unmasked" drawing methods. If a masked
     *  column is passed to a method that expects a full, dense column...well,
     *  it will look fugly/overflow/crash. Vanilla Doom tolerated this, 
     *  we're probably going to have more problems.
     *  
     *  @param patch Actually it's a single column to be drawn. May overdraw existing ones or void space.
     *  @param cache the column cache itself. Actually it's the third level [texture][column]->data.
     *  @param offset an offset inside the column cache (UNUSED)
     *  @param originy vertical offset. Caution with masked stuff!
     *  @param cacheheight the maximum height it's supposed to reach when drawing?
     *  
     */
    
    public void DrawColumnInCache(column_t patch, byte[] cache, int offset,
            int originy, int cacheheight) {
        int count;
        int position;
        int source = 0; // treat as pointers

        /*
         * Iterate inside column. This is starkly different from the C code,
         * because post positions AND offsets are already precomputed at load
         * time
         */

        for (int i = 0; i < patch.posts; i++) {

            // This should position us at the beginning of the next post
            source = patch.postofs[i];

            count = patch.postlen[i]; // length of this particular post
            position = originy + patch.postdeltas[i]; // Position to draw inside
                                                      // cache.

            // Post starts outside of texture's bounds. Adjust offset.

            if (position < 0) {
                count += position; // Consider that we have a "drawing debt".
                position = 0;
            }

            // Post will go too far outside.
            if (position + count > cacheheight)
                count = cacheheight - position;

            if (count > 0) // Draw this post. Won't draw posts that start
                           // "outside"
                // Will start at post's start, but will only draw enough pixels
                // not to overdraw.
                System.arraycopy(patch.data, source, cache, position, count);

        }
    }
    
    
    // Version also drawing on a supplied transparency map
    public void DrawColumnInCache(column_t patch, byte[] cache,
            boolean[] pixmap, int offset, int originy, int cacheheight) {
        int count;
        int position;
        int source = 0; // treat as pointers

        /*
         * Iterate inside column. This is starkly different from the C code,
         * because post positions AND offsets are already precomputed at load
         * time
         */

        for (int i = 0; i < patch.posts; i++) {

            // This should position us at the beginning of the next post
            source = patch.postofs[i];

            count = patch.postlen[i]; // length of this particular post
            position = originy + patch.postdeltas[i]; // Position to draw inside
                                                      // cache.

            // Post starts outside of texture's bounds. Adjust offset.

            if (position < 0) {
                count += position; // Consider that we have a "drawing debt".
                position = 0;
            }

            // Post will go too far outside.
            if (position + count > cacheheight)
                count = cacheheight - position;

            if (count > 0) {
                // Draw post, AND fill solidity map
                System.arraycopy(patch.data, source, cache, position, count);
                Arrays.fill(pixmap, position, position+count, true);
            }
            // Repeat for next post(s), if any.
        }
    }

    /**
     * R_InitFlats
     *
     * Scans WADs for F_START/F_END lumps, and also any additional
     * F1_ and F2_ pairs.
     * 
     * Correct behavior would be to detect F_START/F_END lumps, 
     * and skip any marker lumps sandwiched in between. If F_START and F_END are external, 
     * use external override.
     * 
     * Also, in the presence of external FF_START lumps, merge their contents 
     * with those previously read.
     * 
     * The method is COMPATIBLE with resource pre-coalesing, however it's not 
     * trivial to change back to the naive code because of the "translationless"
     * system used (all flats are assumed to lie in a linear space). This 
     * speeds up lookups.
     *
     */
    
    @Override
    public final void InitFlats ()
    {
        numflats=0;
        int extendedflatstart=-1;
        firstflat=W.GetNumForName(LUMPSTART); // This is the start of normal lumps.
        if (FlatCache==null) FlatCache=new Hashtable<Integer,Integer>(); 
        else FlatCache.clear();
        Hashtable<String,Integer> FlatNames=new Hashtable<String,Integer> (); // Store names here.
        
        // Normally, if we don't use Boom features, we could look for F_END and that's it.
        // However we want to avoid using flat translation and instead store absolute lump numbers.
        // So we need to do a clean parse.
        
        // The rule is: we scan from the very first F_START to the very first F_END.
        // We discard markers, and only assign sequential numbers to valid lumps.
        // These are the vanilla flats, and will work with fully merged PWADs too.
        
        // Normally, this marks the end of regular lumps. However, if DEUTEX extension
        // are present, it will actually mark the end of the extensions due to lump
        // priority, so its usefulness as an absolute end-index for regular flats
        // is dodgy at best. Gotta love the inconsistent mundo hacks!
        
        //int lastflatlump=W.GetNumForName(LUMPEND);
        
		// 
        int lump=firstflat;
        int seq=0;
        String name;
        while (!(name=W.GetNameForNum(lump)).equalsIgnoreCase(LUMPEND)){
            if (!W.isLumpMarker(lump)){
                // Not a marker. Put in cache.
                FlatCache.put(lump, seq);
                // Save its name too.
                FlatNames.put(name, lump);
                seq++; // Advance sequence
                numflats++; // Total flats do increase
            }     
            lump++; // Advance lump.
        }

        
        extendedflatstart=W.CheckNumForName(DEUTEX_START); // This is the start of DEUTEX flats.
        if (extendedflatstart>-1){
       	// If extended ones are present, then Advance slowly.
        lump=extendedflatstart;
        
        // Safeguard: FF_START without corresponding F_END (e.g. in helltest.wad)
    
        name=W.GetNameForNum(lump);
            
        // The end of those extended flats is also marked by F_END or FF_END, as noted above.
        // It can also be non-existent in some broken maps like helltest.wad. Jesus.
        while (!(name==null || name.equalsIgnoreCase(LUMPEND)||name.equalsIgnoreCase(DEUTEX_END))){
            if (!W.isLumpMarker(lump)){
                // Not a marker. Check if it's supposed to replace something.
                if (FlatNames.containsKey(name)){
                    // Well, it is. Off with its name, save the lump number though.
                    int removed=FlatNames.remove(name);
                    // Put new name in list
                    FlatNames.put(name, lump);
                    // Remove old lump, but keep sequence.
                    int oldseq=FlatCache.remove(removed);
                    // Put new lump number with old sequence. 
                    FlatCache.put(lump, oldseq);
                    } else {  // Add normally
                        FlatCache.put(lump, seq);
                        // Save its name too.
                        FlatNames.put(name, lump);
                        seq++; // Advance sequence
                        numflats++; // Total flats do increase
                    }
            }
            lump++; // Advance lump.
            name=W.GetNameForNum(lump);
        }
        }
        
        // So now we have a lump -> sequence number mapping.

            // Create translation table for global animation.
            flattranslation = new int[numflats];
            flatstorage = new int[numflats];
            
            // MAJOR CHANGE: flattranslation stores absolute lump numbers. Adding
            // firstlump is not necessary anymore.      
            // Now, we're pretty sure that we have a progressive value mapping.
   
            Enumeration<Integer> stuff= FlatCache.keys();
            while (stuff.hasMoreElements()){
                int nextlump=stuff.nextElement();
                flatstorage[FlatCache.get(nextlump)]=nextlump;
                // Lump is used as the key, while the relative lump number is the value.
                //FlatCache.put(j, k-1);
                
                }
            
              for (int i=0;i<numflats;i++){
                  flattranslation[i]=i;
                //  System.out.printf("Verification: flat[%d] is %s in lump %d\n",i,W.GetNameForNum(flattranslation[i]),flatstorage[i]);  
              }
            }
    
    private final static String LUMPSTART="F_START";
    private final static String LUMPEND="F_END";
    private final static String DEUTEX_END="FF_END";
    private final static String DEUTEX_START="FF_START";
    
    /**
     * R_PrecacheLevel
     * Preloads all relevant graphics for the level.
     * 
     * MAES: Everything except sprites.
     * A Texturemanager != sprite manager.
     * So the relevant functionality has been split into
     * PrecacheThinkers (found in common rendering code).
     * 
     * 
     */
    
    
    int     flatmemory;
    int     texturememory;    

    public void PrecacheLevel () throws IOException
    {
        
        this.preCacheFlats();
        this.preCacheTextures();
        
        // recache sprites.
        
        /* MAES: this code into PrecacheThinkers
        spritepresent = new boolean[numsprites];
        
        
        for (th = P.thinkercap.next ; th != P.thinkercap ; th=th.next)
        {
        if (th.function==think_t.P_MobjThinker)
            spritepresent[((mobj_t )th).sprite.ordinal()] = true;
        }
        
        spritememory = 0;
        for (i=0 ; i<numsprites ; i++)
        {
        if (!spritepresent[i])
            continue;

        for (j=0 ; j<sprites[i].numframes ; j++)
        {
            sf = sprites[i].spriteframes[j];
            for (k=0 ; k<8 ; k++)
            {
            lump = firstspritelump + sf.lump[k];
            spritememory += W.lumpinfo[lump].size;
            W.CacheLumpNum(lump , PU_CACHE,patch_t.class);
            }
        }
        }
        */
    }
    
    protected final void preCacheFlats(){
    	boolean[]       flatpresent;
        int         lump;
        
        
        if (DM.demoplayback)
        return;
        
        // Precache flats.
        flatpresent = new boolean[numflats];
        flats=new flat_t[numflats];
        
        for (int i=0 ; i<LL.numsectors ; i++)
        {
        flatpresent[LL.sectors[i].floorpic] = true;
        flatpresent[LL.sectors[i].ceilingpic] = true;
        }
        
        flatmemory = 0;

        for (int i=0 ; i<numflats ; i++)
        {
        if (flatpresent[i])
        {
            lump = firstflat + i;
            flatmemory += W.GetLumpInfo(lump).size;
            flats[i]=(flat_t) W.CacheLumpNum(lump, PU_CACHE,flat_t.class);
        }
        }
    }
    
    protected final void preCacheTextures(){
    	boolean []      texturepresent;
        texture_t      texture;
        int lump;

    	
    	// Precache textures.
        texturepresent = new boolean[numtextures];
        
        for (int i=0 ; i<LL.numsides ; i++)
        {
        texturepresent[LL.sides[i].toptexture] = true;
        texturepresent[LL.sides[i].midtexture] = true;
        texturepresent[LL.sides[i].bottomtexture] = true;
        }

        // Sky texture is always present.
        // Note that F_SKY1 is the name used to
        //  indicate a sky floor/ceiling as a flat,
        //  while the sky texture is stored like
        //  a wall texture, with an episode dependend
        //  name.
        texturepresent[skytexture] = true;
        
        texturememory = 0;
        for (int i=0 ; i<numtextures ; i++)
        {
        if (!texturepresent[i])
            continue;

        texture = textures[i];
        
        for (int j=0 ; j<texture.patchcount ; j++)
        {
            lump = texture.patches[j].patch;
            texturememory += W.GetLumpInfo(lump).size;
            W.CacheLumpNum(lump , PU_CACHE,patch_t.class);
        }
        }
    }
    
    /**
     * R_FlatNumForName
     * Retrieval, get a flat number for a flat name.
     * 
     * Unlike the texture one, this one is not used frequently. Go figure.
     */
     
    @Override
    public final int FlatNumForName(String name) {
        int i;
        
        //System.out.println("Checking for "+name);

        i = W.CheckNumForName(name);

        //System.out.printf("R_FlatNumForName retrieved lump %d for name %s picnum %d\n",i,name,FlatCache.get(i));
        if (i == -1) {
            I.Error("R_FlatNumForName: %s not found", name);
        }

        return FlatCache.get(i);

    }

    @Override
    public final int getTextureColumnLump(int tex, int col) {
        return texturecolumnlump[tex][col];
    }


    @Override
    public final char getTextureColumnOfs(int tex, int col) {
        return texturecolumnofs[tex][col];
    }

    @Override
    public final int getTexturewidthmask(int tex) {
        return texturewidthmask[tex];
    }

    @Override
    public final byte[][] getTextureComposite(int tex) {       
        return texturecomposite[tex];
    }

    @Override
    public final byte[] getTextureComposite(int tex, int col) {
        return texturecomposite[tex][col];
    }
    
    @Override
    public final patch_t getMaskedComposite(int tex) {       
        return this.patchcomposite[tex];
    }
    
    @Override
    public final int getTextureheight(int texnum) {
            return textureheight[texnum];
    }

    @Override
    public final int getTextureTranslation(int texnum) {
        return texturetranslation[texnum];
    }
    
    /** Returns a flat after it has been modified by the translation table e.g. by animations */
    @Override
    public int getFlatTranslation(int flatnum) {
        return flatstorage[flattranslation[flatnum]];
    }

    @Override
    public final void setTextureTranslation(int texnum, int amount) {
        texturetranslation[texnum]=amount;
    }
    
    /** This affects ONLY THE TRANSLATION TABLE, not the lump storage.
     * 
     */
    
    @Override
    public final void setFlatTranslation(int flatnum, int amount) {
        flattranslation[flatnum]=amount;
    }
    

    
    //////////////////////////////////From r_sky.c /////////////////////////////////////

//////////////////////////////////From r_sky.c /////////////////////////////////////

    /**
     * R_InitSkyMap
     * Called whenever the view size changes.
     */

    public int InitSkyMap ()
    {
        skyflatnum = FlatNumForName ( SKYFLATNAME );
        skytexturemid = 100*FRACUNIT;
        return skyflatnum;
    }

    @Override
    public int getSkyFlatNum() {
        return skyflatnum;
    }

    @Override
    public void setSkyFlatNum(int skyflatnum) {
        this.skyflatnum = skyflatnum;
    }



    @Override
    public int getSkyTexture() {
        return skytexture;
    }

    @Override
    public void setSkyTexture(int skytexture) {
        this.skytexture = skytexture;
    }

    /*@Override
    public int getFirstFlat() {
        return firstflat;
    } */

    @Override
    public int getSkyTextureMid() {
        return skytexturemid;
    }

    @Override
    public String CheckTextureNameForNum(int texnum) {       
        return textures[texnum].name;
    }

    @Override
    public int getFlatLumpNum(int flatnum) {
        // TODO Auto-generated method stub
        return 0;
    }
    
    /** Generates a "cached" masked column against a black background.
     *  Synchronized so concurrency issues won't cause random glitching and
     *  errors.
     * 
     * @param lump
     * @param column
     * @return raw, 0-pointed column data.
     */    
    public synchronized byte[] getRogueColumn(int lump, int column) {
		
		// If previously requested, speed up gathering.
		//if (lastrogue==lump)
        //	return rogue[column];
		
		// Not contained? Generate.
		if (!roguePatches.containsKey(lump))
			roguePatches.put(lump,generateRoguePatch(lump));
		
		lastrogue=lump;		
		rogue=roguePatches.get(lump);
		
		return rogue[column];
	}
	
	
	/** Actually generates a tutti-frutti-safe cached patch out of
	 * a masked or unmasked single-patch lump.
	 * 
	 * @param lump
	 * @return
	 */
	
	private byte[][] generateRoguePatch(int lump) {
        // Retrieve patch...if it IS a patch.
        patch_t p=this.retrievePatchSafe(lump);		

		// Allocate space for a cached block.
		byte[][] block=new byte[p.width][p.height];
		
		
		for (int i=0;i<p.width;i++)
			DrawColumnInCache(p.columns[i], block[i], i, 0, p.height);
		
		// Don't keep this twice in memory.
		W.UnlockLumpNum(lump);
		return block;
	}

	int lastrogue=-1;
	byte[][] rogue;
	
	
	HashMap<Integer,byte[][]> roguePatches= new HashMap<Integer,byte[][]> ();
    
	class TextureDirectoryEntry implements Comparable<TextureDirectoryEntry>{
	    /** Where an entry starts within the TEXTUREx lump */
	    int offset;
	    /** Its implicit position as indicated by the directory's ordering */
	    int entry;
	    /** Its MAXIMUM possible length, depending on what follows it. 
	     *  Not trivial to compute without thoroughtly examining the entire lump */
	    int length;
	    
	    /** Entries are ranked according to actual offset */
        @Override
        public int compareTo(TextureDirectoryEntry o) {
            if (this.offset<o.offset) return -1;
            if (this.offset==o.offset) return 0;
            return 1;
        }
	 }

    @Override
    public byte[] getSafeFlat(int flatnum) {
        byte[] flat= ((flat_t)W.CacheLumpNum(getFlatTranslation(flatnum),
            PU_STATIC,flat_t.class)).data;

        if (flat.length<4096){
            System.arraycopy(flat, 0,safepatch,0,flat.length);
            return safepatch;
        }
        
        return flat;
    }
	
    private final byte[] safepatch=new byte[4096];
    
    // COLUMN GETTING METHODS. No idea why those had to be in the renderer...
    
    /**
     * Special version of GetColumn meant to be called concurrently by different
     * (MASKED) seg rendering threads, identfiex by index. This serves to avoid stomping
     * on mutual cached textures and causing crashes.
     * 
     * Returns column_t, so in theory it could be made data-agnostic.
     * 
     */

    public column_t GetSmpColumn(int tex, int col, int id) {
        int lump,ofs;

        col &= getTexturewidthmask(tex);
        lump = getTextureColumnLump(tex, col);
        ofs = getTextureColumnOfs(tex, col);

        // It's always 0 for this kind of access.

        // Speed-increasing trick: speed up repeated accesses to the same
        // texture or patch, if they come from the same lump
        
        if (tex == smp_lasttex[id] && lump == smp_lastlump[id]) {
            if (composite)
                return smp_lastpatch[id].columns[col];
            else
                return smp_lastpatch[id].columns[ofs];
            }

        // If pointing inside a non-zero, positive lump, then it's not a
        // composite texture. Read it from disk.
        if (lump > 0) {
            // This will actually return a pointer to a patch's columns.
            // That is, to the ONE column exactly.{
            // If the caller needs access to a raw column, we must point 3 bytes
            // "ahead".
            smp_lastpatch[id] = W.CachePatchNum(lump);
            smp_lasttex[id] = tex;
            smp_lastlump[id]=lump;
            smp_composite[id]=false;
            // If the column was a disk lump, use ofs.
            return smp_lastpatch[id].columns[ofs];
        }
        
        // Problem. Composite texture requested as if it was masked
        // but it doesn't yet exist. Create it.
        if (getMaskedComposite(tex) == null){
            System.err.printf("Forced generation of composite %s\n",CheckTextureNameForNum(tex),smp_composite[id],col,ofs);
            GenerateMaskedComposite(tex);
            System.err.printf("Composite patch %s %d\n",getMaskedComposite(tex).name,getMaskedComposite(tex).columns.length);
        }
        
        // Last resort. 
        smp_lastpatch[id] = getMaskedComposite(tex);
        smp_lasttex[id]=tex;
        smp_composite[id]=true;
        smp_lastlump[id]=0;
        
        return lastpatch.columns[col];
    }

    // False: disk-mirrored patch. True: improper "transparent composite".
    protected boolean[] smp_composite;// = false;
    protected int[] smp_lasttex;// = -1;
    protected int[] smp_lastlump;// = -1;
    protected patch_t[] smp_lastpatch;// = null;
    
///////////////////////// TEXTURE MANAGEMENT /////////////////////////

    /**
     * R_GetColumn original version: returns raw pointers to byte-based column
     * data. Works for both masked and unmasked columns, but is not
     * tutti-frutti-safe.
     * 
     * Use GetCachedColumn instead, if rendering non-masked stuff, which is also
     * faster.
     * 
     * @throws IOException
     * 
     * 
     */

    public byte[] GetColumn(int tex, int col) {
        int lump,ofs;

        col &= getTexturewidthmask(tex);
        lump = getTextureColumnLump(tex, col);
        ofs = getTextureColumnOfs(tex, col);

        // It's always 0 for this kind of access.

        // Speed-increasing trick: speed up repeated accesses to the same
        // texture or patch, if they come from the same lump
        
        if (tex == lasttex && lump == lastlump) {
            if (composite)
                return lastpatch.columns[col].data;
            else
                return lastpatch.columns[ofs].data;
            }

        // If pointing inside a non-zero, positive lump, then it's not a
        // composite texture. Read it from disk.
        if (lump > 0) {
            // This will actually return a pointer to a patch's columns.
            // That is, to the ONE column exactly.{
            // If the caller needs access to a raw column, we must point 3 bytes
            // "ahead".
            lastpatch = W.CachePatchNum(lump);
            lasttex = tex;
            lastlump=lump;
            composite=false;
            // If the column was a disk lump, use ofs.
            return lastpatch.columns[ofs].data;
        }
        
        // Problem. Composite texture requested as if it was masked
        // but it doesn't yet exist. Create it.
        if (getMaskedComposite(tex) == null){
            System.err.printf("Forced generation of composite %s\n",CheckTextureNameForNum(tex),composite,col,ofs);
            GenerateMaskedComposite(tex);
            System.err.printf("Composite patch %s %d\n",getMaskedComposite(tex).name,getMaskedComposite(tex).columns.length);
        }
        
        // Last resort. 
        lastpatch = getMaskedComposite(tex);
        lasttex=tex;
        composite=true;
        lastlump=0;
        
        return lastpatch.columns[col].data;
    }
    
    /**
     * R_GetColumnStruct: returns actual pointers to columns.
     * Agnostic of the underlying type. 
     * 
     * Works for both masked and unmasked columns, but is not
     * tutti-frutti-safe.
     * 
     * Use GetCachedColumn instead, if rendering non-masked stuff, which is also
     * faster.
     * 
     * @throws IOException
     * 
     * 
     */

    @Override
    public column_t GetColumnStruct(int tex, int col) {
        int lump,ofs;

        col &= getTexturewidthmask(tex);
        lump = getTextureColumnLump(tex, col);
        ofs = getTextureColumnOfs(tex, col);

        // Speed-increasing trick: speed up repeated accesses to the same
        // texture or patch, if they come from the same lump
        
        if (tex == lasttex && lump == lastlump) {
            if (composite)
                return lastpatch.columns[col];
            else
                return lastpatch.columns[ofs];
            }

        // If pointing inside a non-zero, positive lump, then it's not a
        // composite texture. Read it from disk.
        if (lump > 0) {
            // This will actually return a pointer to a patch's columns.
            // That is, to the ONE column exactly.{
            // If the caller needs access to a raw column, we must point 3 bytes
            // "ahead".
            lastpatch = W.CachePatchNum(lump);
            lasttex = tex;
            lastlump=lump;
            composite=false;
            // If the column was a disk lump, use ofs.
            return lastpatch.columns[ofs];
        }
        
        // Problem. Composite texture requested as if it was masked
        // but it doesn't yet exist. Create it.
        if (getMaskedComposite(tex) == null){
            System.err.printf("Forced generation of composite %s\n",CheckTextureNameForNum(tex),composite,col,ofs);
            GenerateMaskedComposite(tex);
            System.err.printf("Composite patch %s %d\n",getMaskedComposite(tex).name,getMaskedComposite(tex).columns.length);
        }
        
        // Last resort. 
        lastpatch = getMaskedComposite(tex);
        lasttex=tex;
        composite=true;
        lastlump=0;
        
        return lastpatch.columns[col];
    }

    // False: disk-mirrored patch. True: improper "transparent composite".
    private boolean composite = false;
    private int lasttex = -1;
    private int lastlump = -1;
    private patch_t lastpatch = null;

    
    
    /**
     * R_GetColumn variation which is tutti-frutti proof. It only returns cached
     * columns, and even pre-caches single-patch textures intead of trashing the
     * WAD manager (should be faster, in theory).
     * 
     * Cannot be used for drawing masked textures, use classic GetColumn
     * instead.
     * 
     * 
     * @throws IOException
     */
    @Override
    public final byte[] GetCachedColumn(int tex, int col) {
        int lump, ofs;

        col &= getTexturewidthmask(tex);
        lump = getTextureColumnLump(tex, col);
        ofs = getTextureColumnOfs(tex, col);

        // In the case of cached columns, this is always 0.
        // Done externally, for now.
        //dcvars.dc_source_ofs = 0;

        // If pointing inside a non-zero, positive lump, then it's not a
        // composite texture.
        // Read from disk, and safeguard vs tutti frutti.
        if (lump > 0) {
            // This will actually return a pointer to a patch's columns.
            return getRogueColumn(lump, ofs);
        }

        // Texture should be composite, but it doesn't yet exist. Create it.
        if (getTextureComposite(tex) == null)
            GenerateComposite(tex);

        return getTextureComposite(tex, col);
    }

    @Override
    public void setSMPVars(int num_threads) {
        smp_composite=new boolean[num_threads];// = false;
        smp_lasttex=new int[num_threads];// = -1;
        smp_lastlump=new int[num_threads];// = -1;
        smp_lastpatch=new patch_t[num_threads];// = null;        
    }

    
}

package rr;

/** Purpose unknown, probably unused.
 *  On a closer examination, it could have been part of a system to
 *  "enqueue" masked draws, not much unlike the current parallel
 *  rendering subsystem, but discarded because of simplifications.
 *  In theory it could be brought back one day if parallel sprite
 *  drawing comes back.. just a thought ;-)
 * 
 * 
 * @author Maes
 *
 */

public class maskdraw_t {
        public int     x1;
        public  int     x2;
        
        public  int     column;
        public  int     topclip;
        public  int     bottomclip;

    }
package rr;
import v.IVideoScaleAware;

/** Draws any masked stuff -sprites, textures, or special 3D floors */

public interface IMaskedDrawer<T,V>
        extends IVideoScaleAware,IDetailAware {

    public static final int BASEYCENTER = 100;
    
    /** Cache the sprite manager, if possible */

    void cacheSpriteManager(ISpriteManager SM);

    void DrawMasked();

    void setPspriteIscale(int i);

    void setPspriteScale(int i);

    /**
     * For serial masked drawer, just complete the column function. For
     * parallel version, store rendering instructions and execute later on.
     * HINT: you need to discern between masked and non-masked draws.
     */

    void completeColumn();
}

package rr;

     
/** Stuff used to pass information between the BSP and the SegDrawer */

public class BSPVars{
/** The sectors of the line currently being considered */
public sector_t frontsector, backsector;
public seg_t curline;
public side_t sidedef;
public line_t linedef;
}

package rr;

import static data.Defines.PU_CACHE;
import static m.fixed_t.FRACBITS;

import i.DoomStatusAware;
import i.IDoomSystem;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Hashtable;
import java.util.List;

import doom.DoomStatus;

import utils.C2JUtils;
import w.IWadLoader;
import w.lumpinfo_t;

/** An stand-alone sprite loader. Surprisingly, it is quite a 
 *  separate concern from the renderer, and only needs to communicate 
 *  occasionally through its getters with the rest of the stuff.
 *  
 *  Helped lighten up the rendering code a lot, too.
 * 
 * @author Maes
 *
 */

public class SpriteManager implements ISpriteManager, DoomStatusAware{
	    
	    /** There seems to be an arbitrary limit of 29 distinct frames per THING */
	    public static final int MAX_SPRITE_FRAMES = 29;
	    
        private SpriteManager(){
            sprtemp = new spriteframe_t[MAX_SPRITE_FRAMES];
            C2JUtils.initArrayOfObjects(sprtemp);
        }
	    
        public SpriteManager(DoomStatus DC) {
            this();
            this.updateStatus(DC);
        }

        // Temporarily contains the frames of a given sprite before they are
        // registered with the rendering system. Apparently, a maximum of 29 frames
        // per sprite is allowed.
        protected spriteframe_t[] sprtemp = new spriteframe_t[29];
        protected int maxframe;
        protected String spritename;
        
        // MAES: Shit taken from things

        protected int firstspritelump;
        protected int lastspritelump;
        protected int numspritelumps;
	    
        // variables used to look up and range check thing_t sprites patches
        //
        protected spritedef_t[] sprites;
        
        protected int numsprites;
        
        /** needed for pre rendering (fixed_t[]) */
        protected int[] spritewidth, spriteoffset, spritetopoffset;

        
	    //
        // R_InitSpriteDefs
        // Pass a null terminated list of sprite names
        // (4 chars exactly) to be used.
        //
        // Builds the sprite rotation matrixes to account
        // for horizontally flipped sprites.
        //
        // Will report an error if the lumps are inconsistent.
        // Only called at startup.
        //
        // Sprite lump names are 4 characters for the actor,
        // a letter for the frame, and a number for the rotation.
        //
        // A sprite that is flippable will have an additional
        // letter/number appended.
        //
        // The rotation character can be 0 to signify no rotations.
        //
        // 1/25/98, 1/31/98 killough : Rewritten for performance
        //
        // Empirically verified to have excellent hash
        // properties across standard Doom sprites:

        protected final void InitSpriteDefs(String[] namelist) {
            int numentries = lastspritelump - firstspritelump + 1;
            Hashtable<Integer, List<Integer>> hash;
            int i;

            if (numentries == 0 || namelist == null)
                return;

            // count the number of sprite names
            i = namelist.length;

            numsprites = i;

            sprites = new spritedef_t[numsprites];
            C2JUtils.initArrayOfObjects(sprites);

            // Create hash table based on just the first four letters of each
            // sprite
            // killough 1/31/98
            // Maes: the idea is to have a chained hastable which can handle
            // multiple entries (sprites) on the same primary key (the 4 first chars of
            // the sprite name)

            hash = new Hashtable<Integer, List<Integer>>(numentries); // allocate
                                                                        // hash
                                                                        // table

            // We have to trasverse this in the opposite order, so that later
            // lumps
            // trump previous ones in order.
            for (i = numentries - 1; i >= 0; i--) {
                int hashcode = SpriteNameHash(W.GetLumpInfo(i + firstspritelump).name);
                // Create chain list for each sprite class (e.g. TROO, POSS,
                // etc.)
                //
                if (!hash.containsKey(hashcode)) {
                    hash.put(hashcode, new ArrayList<Integer>());
                }

                // Store (yet another) lump index for this sprite.
                hash.get(hashcode).add(i);
            }

            // scan all the lump names for each of the names,
            // noting the highest frame letter.

            for (i = 0; i < numsprites; i++) {

                // We only look for entries that are known to be sprites.
                // The hashtable may contain a lot of other shit, at this point
                // which will be hopefully ignored.
                String spritename = namelist[i];
                List<Integer> list = hash.get(SpriteNameHash(spritename));

                // Well, it may have been something else. Fuck it.
                if (list != null && !list.isEmpty()) {

                    // Maes: the original code actually set everything to "-1"
                    // here, including the
                    // "boolean" rotate value. The idea was to create a
                    // "tristate" of sorts, where -1
                    // means a sprite of uncertain status. Goto
                    // InstallSpriteLumps for more.
                    for (int k = 0; k < sprtemp.length; k++) {
                        Arrays.fill(sprtemp[k].flip, (byte) -1);
                        Arrays.fill(sprtemp[k].lump, (short) -1);
                        // This should be INDETERMINATE at this point.
                        sprtemp[k].rotate = -1;
                    }
                    maxframe = -1;

                    // What is stored in the lists are all actual lump numbers
                    // relative
                    // to e.g. TROO. In coalesced lumps, there will be overlap.
                    // This procedure should, in theory, trump older ones.

                    for (Integer j : list) {

                        lumpinfo_t lump = W.GetLumpInfo(j + firstspritelump);

                        // We don't know a-priori which frames exist.
                        // However, we do know how to interpret existing ones,
                        // and have an implicit maximum sequence of 29 Frames.
                        // A frame can also hame multiple rotations.

                        if (lump.name.substring(0, 4).equalsIgnoreCase(
                                spritename.substring(0, 4))) {

                            int frame = lump.name.charAt(4) - 'A';
                            int rotation = lump.name.charAt(5) - '0';

                            if (sprtemp[frame].rotate != -1) {
                                // We already encountered this sprite, but we
                                // may need to trump it with something else

                            }

                            InstallSpriteLump(j + firstspritelump, frame,
                                    rotation, false);
                            if (lump.name.length() >= 7) {
                                frame = lump.name.charAt(6) - 'A';
                                rotation = lump.name.charAt(7) - '0';
                                InstallSpriteLump(j + firstspritelump, frame,
                                        rotation, true);
                            }
                        }
                    }

                    // check the frames that were found for completeness
                    if ((sprites[i].numframes = ++maxframe) != 0) // killough
                                                                    // 1/31/98
                    {
                        int frame;
                        for (frame = 0; frame < maxframe; frame++)
                            switch ((int) sprtemp[frame].rotate) {
                            case -1:
                                // no rotations were found for that frame at all
                                I.Error("R_InitSprites: No patches found for %s frame %c",
                                        namelist[i], frame + 'A');
                                break;

                            case 0:
                                // only the first rotation is needed
                                break;

                            case 1:
                                // must have all 8 frames
                            {
                                int rotation;
                                for (rotation = 0; rotation < 8; rotation++)
                                    if (sprtemp[frame].lump[rotation] == -1)
                                        I.Error("R_InitSprites: Sprite %s frame %c is missing rotations",
                                                namelist[i], frame + 'A');
                                break;
                            }
                            }
                        // allocate space for the frames present and copy
                        // sprtemp to it
                        // MAES: we can do that elegantly in one line.

                        sprites[i].copy(sprtemp, maxframe);
                    }

                }
            }

        }
        
        /**
         * R_InitSpriteLumps Finds the width and hoffset of all sprites in the wad,
         * so the sprite does not need to be cached completely just for having the
         * header info ready during rendering.
         */

        public void InitSpriteLumps() {
            int i;
            patch_t patch;

            firstspritelump = W.GetNumForName("S_START") + 1;
            lastspritelump = W.GetNumForName("S_END") - 1;

            numspritelumps = lastspritelump - firstspritelump + 1;
            spritewidth = new int[numspritelumps];
            spriteoffset = new int[numspritelumps];
            spritetopoffset = new int[numspritelumps];

            for (i = 0; i < numspritelumps; i++) {
                if ((i & 63) == 0)
                    System.out.print(".");

                patch = (patch_t) W.CacheLumpNum(firstspritelump + i, PU_CACHE,
                        patch_t.class);
                spritewidth[i] = patch.width << FRACBITS;
                spriteoffset[i] = patch.leftoffset << FRACBITS;
                spritetopoffset[i] = patch.topoffset << FRACBITS;
            }
        }
        
        /**
         * R_InstallSpriteLump Local function for R_InitSprites.
         * 
         * Boom function, more suited to resource coalescing.
         * 
         */

        public final void InstallSpriteLump(int lump, int frame,
                int rotation, boolean flipped) {
            if (frame >= MAX_SPRITE_FRAMES || rotation > 8)
                I.Error("R_InstallSpriteLump: Bad frame characters in lump %d",
                        lump);

            if ((int) frame > maxframe)
                maxframe = frame;

            if (rotation == 0) { // the lump should be used for all rotations
                int r;
                for (r = 0; r < 8; r++)
                    if (sprtemp[frame].lump[r] == -1) {
                        sprtemp[frame].lump[r] = lump - firstspritelump;
                        sprtemp[frame].flip[r] = (byte) (flipped ? 1 : 0);
                        sprtemp[frame].rotate = 0; // jff 4/24/98 if any subbed,
                                                    // rotless
                    }
                return;
            }

            // the lump is only used for one rotation

            if (sprtemp[frame].lump[--rotation] == -1) {
                sprtemp[frame].lump[rotation] = lump - firstspritelump;
                sprtemp[frame].flip[rotation] = (byte) (flipped ? 1 : 0);
                sprtemp[frame].rotate = 1; // jff 4/24/98 only change if rot
                                            // used
            }
        }

        /**
         * R_InitSprites Called at program start.
         * 
         */

        @Override
        public void InitSprites(String[] namelist) {
            InitSpriteDefs(namelist);
        }
        
        
        protected final int SpriteNameHash(String ss) {
            return ss.substring(0, 4).hashCode();
        }
        
        // GETTERS
        
        @Override
        public final int getFirstSpriteLump(){
            return firstspritelump;
        }
        
        @Override
        public final int getNumSprites(){
            return numsprites;
        }

        @Override
        public final spritedef_t[] getSprites() {
            return sprites;
        }
        
        @Override
        public final spritedef_t getSprite(int index) {
            return sprites[index];
        }
        
        @Override
        public final int[] getSpriteWidth() {
            return spritewidth;
        }

        @Override
        public final int[] getSpriteOffset() {
            return spriteoffset;
        }

        @Override
        public final int[] getSpriteTopOffset() {
            return spritetopoffset;
        }

        @Override
        public final int getSpriteWidth(int index) {
            return spritewidth[index];
        }

        @Override
        public final int getSpriteOffset(int index) {
            return spriteoffset[index];
        }

        @Override
        public final int getSpriteTopOffset(int index) {
            return spritetopoffset[index];
        }

        /////////// STATUS ///////////
        
        @Override
        public void updateStatus(DoomStatus DC) {
            this.W=DC.W;
            this.I=DC.I;
            }
        
        protected IWadLoader W;
        protected IDoomSystem I;




        // Some unused shit
         
         /*
         * R_InstallSpriteLump Local function for R_InitSprites.
         * 
         * Older function, closer to linuxdoom. Using Boom-derived one instead.
         */ 
        
          /*
        protected final void InstallSpriteLump(int lump, int frame,
                int rotation, boolean flipped) {

            // System.out.println("Trying to install "+spritename+" Frame "+
            // (char)('A'+frame)+" rot "+(rotation)
            // +" . Should have rotations: "+sprtemp[frame].rotate);
            int r;

            if (frame >= 29 || rotation > 8)
                I.Error("R_InstallSpriteLump: Bad frame characters in lump %i",
                        lump);

            if ((int) frame > maxframe)
                maxframe = frame;

            // A rotation value of 0 means that we are either checking the first
            // frame of a sprite that HAS rotations, or something that has no
            // rotations at all. The value of rotate doesn't really help us
            // discern here, unless set to "false" a-priori...which can't happen
            // ?!

            if (rotation == 0) {
                
                 // MAES: notice how comparisons are done with strict literals
                 // (true and false) which are actually defined to be 0 and 1,
                 // rather than assuming that true is "any nonzero value". This
                 // happens because rotate's value could be -1 at this point (!),
                 // if a series of circumstances occur. Therefore it's actually a
                 // "tri-state", and the comparison 0==false and
                 // "anything else"==true was not good enough in this case. A
                 // value of -1 doesn't yield either true or false here.
                
                // the lump should be used for all rotations
                if (sprtemp[frame].rotate == 0) {
                    
                     // MAES: Explanation: we stumbled upon this lump before, and
                     // decided that this frame should have no more rotations,
                     // hence we found an error and we bomb everything.
                    
                    I.Error("R_InitSprites: Sprite %s frame %c has multiple rot=0 lump",
                            spritename, 'A' + frame);
                }

                // This should NEVER happen!
                if (sprtemp[frame].rotate == 1) {
                    
                     // MAES: This can only happen if we decided that a sprite's
                     // frame was already decided to have rotations, but now we
                     // stumble upon another occurence of "rotation 0". Or if you
                     // use naive true/false evaluation for .rotate ( -1 is also
                     // an admissible value).

                    I.Error("R_InitSprites: Sprite %s frame %c has rotations and a rot=0 lump",
                            spritename, 'A' + frame);
                }

                // Rotation is acknowledged to be totally false at this point.
                sprtemp[frame].rotate = 0;
                for (r = 0; r < 8; r++) {
                    sprtemp[frame].lump[r] = (short) (lump - firstspritelump);
                    sprtemp[frame].flip[r] = (byte) (flipped ? 1 : 0);
                }
                return;
            }

            // the lump is only used for one rotation
            if (sprtemp[frame].rotate == 0)
                I.Error("R_InitSprites: Sprite %s frame %c has rotations and a rot=0 lump",
                        spritename, 'A' + frame);

            sprtemp[frame].rotate = 1;

            // make 0 based
            rotation--;
            if (sprtemp[frame].lump[rotation] == -1) {
                // FUN FACT: with resource coalesing, this is no longer an
                // error.
                // I.Error
                // ("R_InitSprites: Sprite %s : %c : %c has two lumps mapped to it",
                // spritename, 'A'+frame, '1'+rotation);

                // Everything is OK, we can bless the temporary sprite's frame's
                // rotation.
                sprtemp[frame].lump[rotation] = (short) (lump - firstspritelump);
                sprtemp[frame].flip[rotation] = (byte) (flipped ? 1 : 0);
                sprtemp[frame].rotate = 1; // jff 4/24/98 only change if rot
                                            // used
            }
        }
        */

        

        /*
         * OLDER, UNUSED VERSION
         * 
         * R_InitSpriteDefs Pass a null terminated list of sprite names (4 chars
         * exactly) to be used. Builds the sprite rotation matrixes to account
         * for horizontally flipped sprites. Will report an error if the lumps
         * are inconsistent. Only called at startup.
         * 
         * Sprite lump names are 4 characters for the actor, a letter for the
         * frame, and a number for the rotation. A sprite that is flippable will
         * have an additional letter/number appended. The rotation character can
         * be 0 to signify no rotations.
         */

    /*  public void InitSpriteDefs2(String[] namelist) {

            int intname;
            int frame;
            int rotation;
            int start;
            int end;
            int patched;

            if (namelist == null)
                return;
            numsprites = namelist.length;

            if (numsprites == 0)
                return;

            sprites = new spritedef_t[numsprites];
            C2JUtils.initArrayOfObjects(sprites);

            start = firstspritelump - 1;
            end = lastspritelump + 1;

            // scan all the lump names for each of the names,
            // noting the highest frame letter.
            // Just compare 4 characters as ints
            for (int i = 0; i < numsprites; i++) {
                // System.out.println("Preparing sprite "+i);
                spritename = namelist[i];

                // The original code actually set everything to "-1"
                // here, including the "boolean" rotate value. The idea was 
                // to create a "tristate" of sorts, where -1 means a 
                // sprite of uncertain status. Goto InstallSpriteLumps
                // for more.
                for (int j = 0; j < sprtemp.length; j++) {
                    Arrays.fill(sprtemp[j].flip, (byte) -1);
                    Arrays.fill(sprtemp[j].lump, (short) -1);
                    // This should be INDETERMINATE at this point.
                    sprtemp[j].rotate = -1;
                }

                maxframe = -1;
                intname = name8.getIntName(namelist[i].toUpperCase());

                // scan the lumps,
                // filling in the frames for whatever is found
                for (int l = start + 1; l < end; l++) {
                    // We HOPE it has 8 characters.
                    char[] cname = W.GetLumpInfo(l).name.toCharArray();
                    if (cname.length == 6 || cname.length == 8) // Sprite names
                                                                // must be this
                                                                // way

                        // If the check is successful, we keep looking for more
                        // frames
                        // for a particular sprite e.g. TROOAx, TROOHxHy etc.
                        //
                        if (W.GetLumpInfo(l).intname == intname) {
                            frame = cname[4] - 'A';
                            rotation = cname[5] - '0';

                            if (DM.modifiedgame)
                                patched = W
                                        .GetNumForName(W.GetLumpInfo(l).name);
                            else
                                patched = l;

                            InstallSpriteLump2(patched, frame, rotation, false);

                            // Second set of rotations?
                            if (cname.length > 6 && cname[6] != 0) {
                                frame = cname[6] - 'A';
                                rotation = cname[7] - '0';
                                InstallSpriteLump2(l, frame, rotation, true);
                            }
                        }
                }

                // check the frames that were found for completeness
                // This can only be -1 at this point if we didn't install
                // a single frame successfuly.
                //
                if (maxframe == -1) {
                    // System.out.println("Sprite "+spritename+" has no frames!");
                    getSprites()[i].numframes = 0;
                    // We move on to the next sprite with this one.
                    continue;
                }

                maxframe++;

                for (frame = 0; frame < maxframe; frame++) {
                    switch ((int) sprtemp[frame].rotate) {
                    case -1:
                        // no rotations were found for that frame at all
                        I.Error("R_InitSprites: No patches found for %s frame %c",
                                namelist[i], frame + 'A');
                        break;

                    case 0:
                        // only the first rotation is needed
                        break;

                    case 1:
                        // must have all 8 frames
                        for (rotation = 0; rotation < 8; rotation++)
                            if (sprtemp[frame].lump[rotation] == -1)
                                I.Error("R_InitSprites: Sprite %s frame %c is missing rotations",
                                        namelist[i], frame + 'A');
                        break;
                    }
                }

                // allocate space for the frames present and copy sprtemp to it
                // MAES: we can do that elegantly in one line.

                sprites[i].copy(sprtemp, maxframe);

                // sprites[i].numframes = maxframe;
                // sprites[i].spriteframes = new spriteframe_t[maxframe];
                // C2JUtils.initArrayOfObjects(sprites[i].spriteframes,spriteframe_t.class);

                // for (int j=0;j<)
                // System.arraycopy(src, srcPos, dest, destPos, length)
                // memcpy (sprites[i].spriteframes, sprtemp,
                // maxframe*sizeof(spriteframe_t));
            }

        }
        */
        
	}


package rr;

import java.io.IOException;
import java.nio.ByteBuffer;

import w.CacheableDoomObject;

public class flat_t
        implements CacheableDoomObject {

    public static final int FLAT_SIZE=4096;
    
    public byte[] data;

    public flat_t(){
        this.data=new byte[FLAT_SIZE];
    }

    public flat_t(int size){
        this.data=new byte[size];
    }

    
    @Override
    public void unpack(ByteBuffer buf)
            throws IOException {
        
            //buf.get(this.data);
            this.data=buf.array();

    }

}

package rr;

import static data.Tables.FINEANGLES;
import static m.fixed_t.FRACUNIT;
import rr.drawfuns.ColFuncs;
import rr.drawfuns.ColVars;
import rr.drawfuns.SpanVars;
import i.DoomStatusAware;
import i.IDoomSystem;
import v.IVideoScaleAware;
import w.IWadLoader;
import doom.player_t;

public interface Renderer<T,V> extends IVideoScaleAware,DoomStatusAware{
	


	/** Fineangles in the SCREENWIDTH wide window. */
	public static final int FIELDOFVIEW = FINEANGLES / 4;
	
	public static final int MINZ = (FRACUNIT * 4);

	
	public static final int FUZZTABLE = 50;
	
	/**
	 * killough: viewangleoffset is a legacy from the pre-v1.2 days, when Doom
	 * had Left/Mid/Right viewing. +/-ANG90 offsets were placed here on each
	 * node, by d_net.c, to set up a L/M/R session.
	 */

	public static final long viewangleoffset = 0;
	
	
	public void Init();
	
	public void RenderPlayerView(player_t player);
	
	public void ExecuteSetViewSize();
	
	public void FillBackScreen();
	
	public void DrawViewBorder();
	
	public void SetViewSize(int size, int detaillevel);
	
	public void VideoErase(int offset, int width);
	
	public long PointToAngle2(int x1, int y1, int x2, int y2);
	
	public void PreCacheThinkers();
	
	public int getValidCount();
	
	public void increaseValidCount(int amount);

	public boolean isFullHeight();

	public void resetLimits();

	public boolean getSetSizeNeeded();
	
	public boolean isFullScreen();

	// Isolation methods
	
	public TextureManager<T> getTextureManager();
	
	public PlaneDrawer<T,V> getPlaneDrawer();

    public ViewVars getView();

    public SpanVars<T, V> getDSVars();

    public LightsAndColors<V> getColorMap();

    public IDoomSystem getDoomSystem();
    
    public IWadLoader getWadLoader();

    /** Use this to "peg" visplane drawers (even parallel ones) to
     *  the same set of visplane variables.
     *  
     * @return
     */
    
    public Visplanes getVPVars();

    public SegVars getSegVars();

    public ISpriteManager getSpriteManager();

    public BSPVars getBSPVars();

    public IVisSpriteManagement<V> getVisSpriteManager();

    public ColFuncs<T,V> getColFuncsHi();
    public ColFuncs<T,V> getColFuncsLow();

    public ColVars<T, V> getMaskedDCVars();
    
	
	//public subsector_t PointInSubsector(int x, int y);

}

package rr;

public class cliprange_t {

    public cliprange_t(int first, int last) {
        this.first = first;
        this.last = last;
    }

    public cliprange_t(){
        
    }
    
    public int first;
    public int last;
    
    public void copy(cliprange_t from){
        this.first=from.first;
        this.last=from.last;
    }
}

package rr;

/**
 * A sprite definition:
 * a number of animation frames.
 */

public class spritedef_t {

    /** the very least, primitive fields won't bomb,
     *  and copy constructors can do their job.
     */
    public spritedef_t(){        
    }
    
    public spritedef_t(int numframes){
        this.numframes=numframes;
        this.spriteframes=new spriteframe_t[numframes];        
    }
    
    public spritedef_t(spriteframe_t[] frames){
        this.numframes=frames.length;
        this.spriteframes=new spriteframe_t[numframes];
        // copy shit over...
        for (int i=0;i<numframes;i++){
            spriteframes[i]=frames[i].clone();
        }
    }
    
    /** Use this constructor, as we usually need less than 30 frames 
     *  It will actually clone the frames.
     */
    
    public void copy(spriteframe_t[] from, int maxframes){
        this.numframes=maxframes;
        this.spriteframes=new spriteframe_t[maxframes];
        // copy shit over...
        for (int i=0;i<maxframes;i++){
            spriteframes[i]=from[i].clone();
        }
    }
    
    
 public int         numframes;
 public spriteframe_t[]  spriteframes;

};
package rr;

/** A sprite manager does everything but drawing the sprites. It creates lists
 *  of sprites-per-sector, sorts them, and stuff like that.
 *  that gory visibiliy
 *  
 * @author velkton
 *
 * @param <V>
 */


public interface IVisSpriteManagement<V> extends ILimitResettable {
    
    void AddSprites(sector_t sec);
    
    /** Cache the sprite manager, if possible */
    
    void cacheSpriteManager(ISpriteManager SM);
    
    void SortVisSprites();

    int getNumVisSprites();

    vissprite_t<V>[] getVisSprites();

    void ClearSprites();
    
    void updateStatus(RendererState<?,V> R);
    
}

package rr;

import static rr.LightsAndColors.*;
import java.io.IOException;
import java.util.ArrayList;

import rr.drawfuns.ColVars;
import rr.drawfuns.R_DrawColumnBoom;
import rr.drawfuns.R_DrawColumnBoomLow;
import rr.drawfuns.R_DrawColumnBoomOpt;
import rr.drawfuns.R_DrawColumnBoomOptLow;
import rr.drawfuns.R_DrawFuzzColumn;
import rr.drawfuns.R_DrawFuzzColumnLow;
import rr.drawfuns.R_DrawSpanLow;
import rr.drawfuns.R_DrawSpanUnrolled;
import rr.drawfuns.R_DrawTLColumn;
import rr.drawfuns.R_DrawTranslatedColumn;
import rr.drawfuns.R_DrawTranslatedColumnLow;
import rr.drawfuns.SpanVars;

import v.DoomVideoRenderer;
import doom.DoomMain;
import doom.DoomStatus;

public abstract class UnifiedRenderer<T, V>
        extends RendererState<T,V> {

    public UnifiedRenderer(DoomStatus<T,V> DS) {
        super(DS);
        this.MySegs=new Segs(this);
    }


    /** A very simple Seg (Wall) drawer, which just completes abstract SegDrawer
     *  by calling the final column functions.
     *  
     *  TODO: move out of RendererState.
     *  
     * @author velktron
     *
     */
    
    protected final class Segs
            extends SegDrawer {

        public Segs(Renderer<?, ?> R) {
            super(R);
        }

        /** For serial version, just complete the call */
        @Override
        protected final void CompleteColumn() {
            colfunc.main.invoke();
        }

    }

    ////////////////// The actual rendering calls ///////////////////////

   

    public static final class HiColor
            extends UnifiedRenderer<byte[],short[]> {

        public HiColor(DoomStatus<byte[],short[]> DM) {            
            super(DM);
            
            // Init any video-output dependant stuff            
            

            
            // Init light levels
            colormaps.scalelight = new short[LIGHTLEVELS][MAXLIGHTSCALE][];
            colormaps.scalelightfixed = new short[MAXLIGHTSCALE][];
            colormaps.zlight = new short[LIGHTLEVELS][MAXLIGHTZ][];
            
            completeInit();
        }

        /**
         * R_InitColormaps This is VERY different for hicolor.
         * 
         * @throws IOException
         */
        protected void InitColormaps()
                throws IOException {

            colormaps.colormaps = V.getColorMaps();
            System.out.println("COLORS15 Colormaps: " + colormaps.colormaps.length);

            // MAES: blurry effect is hardcoded to this colormap.
            // Pointless, since we don't use indexes. Instead, a half-brite
            // processing works just fine.
            BLURRY_MAP = null;// colormaps[0];
        }
        
        /** Initializes the various drawing functions. They are all "pegged" to the
         *  same dcvars/dsvars object. Any initializations of e.g. parallel renderers
         *  and their supporting subsystems should occur here. 
         */
        @Override
        protected void R_InitDrawingFunctions(){
            
            
            // Span functions. Common to all renderers unless overriden
            // or unused e.g. parallel renderers ignore them.
            DrawSpan=new R_DrawSpanUnrolled.HiColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,dsvars,screen,I);
            DrawSpanLow=new R_DrawSpanLow.HiColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,dsvars,screen,I);
            
            
            // Translated columns are usually sprites-only.
            DrawTranslatedColumn=new R_DrawTranslatedColumn.HiColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I);
            DrawTranslatedColumnLow=new R_DrawTranslatedColumnLow.HiColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I);
            DrawTLColumn=new R_DrawTLColumn(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I);
            
            // Fuzzy columns. These are also masked.
            DrawFuzzColumn=new R_DrawFuzzColumn.HiColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I);
            DrawFuzzColumnLow=new R_DrawFuzzColumnLow.HiColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I);
            
            // Regular draw for solid columns/walls. Full optimizations.
            DrawColumn=new R_DrawColumnBoomOpt.HiColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,dcvars,screen,I);
            DrawColumnLow=new R_DrawColumnBoomOptLow.HiColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,dcvars,screen,I);
            
            // Non-optimized stuff for masked.
            DrawColumnMasked=new R_DrawColumnBoom.HiColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I);
            DrawColumnMaskedLow=new R_DrawColumnBoomLow.HiColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I);
            
            // Player uses masked
            DrawColumnPlayer=DrawColumnMasked; // Player normally uses masked.
            
            // Skies use their own. This is done in order not to stomp parallel threads.
            
            DrawColumnSkies=new R_DrawColumnBoomOpt.HiColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,skydcvars,screen,I);
            DrawColumnSkiesLow=new R_DrawColumnBoomOptLow.HiColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,skydcvars,screen,I);
            
            super.R_InitDrawingFunctions();
        }

    }       
                       
            public static final class Indexed
            extends UnifiedRenderer<byte[],byte[]> {

        public Indexed(DoomStatus<byte[],byte[]> DM) {            
            super(DM);
            
            // Init light levels
            colormaps.scalelight = new byte[LIGHTLEVELS][MAXLIGHTSCALE][];
            colormaps.scalelightfixed = new byte[MAXLIGHTSCALE][];
            colormaps.zlight = new byte[LIGHTLEVELS][MAXLIGHTZ][];
            
            completeInit();
        }

        /**
         * R_InitColormaps
         * 
         * @throws IOException
         */
        protected void InitColormaps() throws IOException {
            int lump, length;

            // Load in the light tables,
            // 256 byte align tables.
            lump = W.GetNumForName("COLORMAP");
            length = W.LumpLength(lump) + 256;
            colormaps.colormaps = new byte[(length / 256)][256];
            System.out.println("Colormaps: " + colormaps.colormaps.length);

            byte[] tmp = new byte[length];
            W.ReadLump(lump,tmp);

            for (int i = 0; i < colormaps.colormaps.length; i++) {
                System.arraycopy(tmp, i * 256, colormaps.colormaps[i], 0, 256);
            }
            
            // MAES: blurry effect is hardcoded to this colormap.
            BLURRY_MAP=colormaps.colormaps[6];
            // colormaps = (byte *)( ((int)colormaps + 255)&~0xff);     

            
        }
        
        /** Initializes the various drawing functions. They are all "pegged" to the
         *  same dcvars/dsvars object. Any initializations of e.g. parallel renderers
         *  and their supporting subsystems should occur here. 
         */
        
        @Override
        protected void R_InitDrawingFunctions(){
            
            // Span functions. Common to all renderers unless overriden
            // or unused e.g. parallel renderers ignore them.
            DrawSpan=new R_DrawSpanUnrolled.Indexed(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,dsvars,screen,I);
            DrawSpanLow=new R_DrawSpanLow.Indexed(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,dsvars,screen,I);
            
            
            // Translated columns are usually sprites-only.
            DrawTranslatedColumn=new R_DrawTranslatedColumn.Indexed(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I);
            DrawTranslatedColumnLow=new R_DrawTranslatedColumnLow.Indexed(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I);
            //DrawTLColumn=new R_DrawTLColumn(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I);
            
            // Fuzzy columns. These are also masked.
            DrawFuzzColumn=new R_DrawFuzzColumn.Indexed(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I,BLURRY_MAP);
            DrawFuzzColumnLow=new R_DrawFuzzColumnLow.Indexed(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I,BLURRY_MAP);
            
            // Regular draw for solid columns/walls. Full optimizations.
            DrawColumn=new R_DrawColumnBoomOpt.Indexed(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,dcvars,screen,I);
            DrawColumnLow=new R_DrawColumnBoomOptLow.Indexed(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,dcvars,screen,I);
            
            // Non-optimized stuff for masked.
            DrawColumnMasked=new R_DrawColumnBoom.Indexed(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I);
            DrawColumnMaskedLow=new R_DrawColumnBoomLow.Indexed(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I);
            
            // Player uses masked
            DrawColumnPlayer=DrawColumnMasked; // Player normally uses masked.
            
            // Skies use their own. This is done in order not to stomp parallel threads.
            
            DrawColumnSkies=new R_DrawColumnBoomOpt.Indexed(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,skydcvars,screen,I);
            DrawColumnSkiesLow=new R_DrawColumnBoomOptLow.Indexed(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,skydcvars,screen,I);
            super.R_InitDrawingFunctions();
        }

    }       
          
            public static final class TrueColor
            extends UnifiedRenderer<byte[],int[]> {

        public TrueColor(DoomStatus<byte[],int[]> DM) {            
            super(DM);
            
            // Init light levels
            colormaps.scalelight = new int[LIGHTLEVELS][MAXLIGHTSCALE][];
            colormaps.scalelightfixed = new int[MAXLIGHTSCALE][];
            colormaps.zlight = new int[LIGHTLEVELS][MAXLIGHTZ][];
            
            completeInit();
        }

        /**
         * R_InitColormaps This is VERY different for hicolor.
         * 
         * @throws IOException
         */
        protected void InitColormaps()
                throws IOException {

            colormaps.colormaps = V.getColorMaps();
            System.out.println("COLORS32 Colormaps: " + colormaps.colormaps.length);
            BLURRY_MAP = null;

        }
        
        /** Initializes the various drawing functions. They are all "pegged" to the
         *  same dcvars/dsvars object. Any initializations of e.g. parallel renderers
         *  and their supporting subsystems should occur here. 
         */
        
        @Override
        protected void R_InitDrawingFunctions(){
            
            // Span functions. Common to all renderers unless overriden
            // or unused e.g. parallel renderers ignore them.
            DrawSpan=new R_DrawSpanUnrolled.TrueColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,dsvars,screen,I);
            DrawSpanLow=new R_DrawSpanLow.TrueColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,dsvars,screen,I);
            
            
            // Translated columns are usually sprites-only.
            DrawTranslatedColumn=new R_DrawTranslatedColumn.TrueColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I);
            DrawTranslatedColumnLow=new R_DrawTranslatedColumnLow.TrueColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I);
            //DrawTLColumn=new R_DrawTLColumn.TrueColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I);
            
            // Fuzzy columns. These are also masked.
            DrawFuzzColumn=new R_DrawFuzzColumn.TrueColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I);
            DrawFuzzColumnLow=new R_DrawFuzzColumnLow.TrueColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I);
            
            // Regular draw for solid columns/walls. Full optimizations.
            DrawColumn=new R_DrawColumnBoomOpt.TrueColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,dcvars,screen,I);
            DrawColumnLow=new R_DrawColumnBoomOptLow.TrueColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,dcvars,screen,I);
            
            // Non-optimized stuff for masked.
            DrawColumnMasked=new R_DrawColumnBoom.TrueColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I);
            DrawColumnMaskedLow=new R_DrawColumnBoomLow.TrueColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I);
            
            // Player uses masked
            DrawColumnPlayer=DrawColumnMasked; // Player normally uses masked.
            
            // Skies use their own. This is done in order not to stomp parallel threads.
            
            DrawColumnSkies=new R_DrawColumnBoomOpt.TrueColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,skydcvars,screen,I);
            DrawColumnSkiesLow=new R_DrawColumnBoomOptLow.TrueColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,skydcvars,screen,I);
            super.R_InitDrawingFunctions();
        }

    }       

}

package rr;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Hashtable;

import utils.C2JUtils;
import w.CacheableDoomObject;
import w.DoomBuffer;

//Patches.
//A patch holds one or more columns.
//Patches are used for sprites and all masked pictures,
//and we compose textures from the TEXTURE1/2 lists
//of patches.

public class patch_t implements /*IReadableDoomObject,*/CacheableDoomObject{

    /** bounding box size */ 
    public short       width,   height;  
    /** pixels to the left of origin */
    public short       leftoffset;  
    /** pixels below the origin */
    public short       topoffset;   
    /** This used to be an implicit array pointing to raw posts of data. 
     * TODO: get rid of it? It's never used
     * only [width] used the [0] is &columnofs[width] */
    public int[]         columnofs;     
    /** The ACTUAL data is here, nicely deserialized (well, almost) */
    public column_t[] columns;
    
    /** Added for debug aid purposes */
    public String name;
    
    /** Synthesizing constructor.
     * You have to provide the columns yourself, a-posteriori.
     * 
     * @param name
     * @param width
     * @param height
     * @param leftoffset
     * @param topoffset
     */
    public patch_t(String name, int width, int height, int leftoffset, int topoffset){
        this.name=name;
        this.width=(short) width;
        this.height=(short) height;
        this.leftoffset=(short) leftoffset;
        this.columns=new column_t[width];
    }
    
    public patch_t(){
        
    }
    
  /*  @Override
    public void read(DoomFile f) throws IOException{

        long pos=f.getFilePointer();
        this.width=f.readLEShort();
        this.height=f.readLEShort();
        this.leftoffset=f.readLEShort();
        this.topoffset=f.readLEShort();
        // As many columns as width...right???
        this.columnofs=new int[this.width];
        this.columns=new column_t[this.width];
        C2JUtils.initArrayOfObjects( this.columns, column_t.class);
        
        // Read the column offsets.
        f.readIntArray(this.columnofs, this.columnofs.length, ByteOrder.LITTLE_ENDIAN);
        for (int i=0;i<this.width;i++){
            // Go to offset.
            //f.seek(pos+this.columnofs[i]);
            this.columns[i].read(f);
        }
        
    }*/
    
    /** In the C code, reading is "aided", aka they know how long the header + all
     *  posts/columns actually are on disk, and only "deserialize" them when using them.
     *  Here, we strive to keep stuff as elegant and OO as possible, so each column will get 
     *  deserialized one by one. I thought about reading ALL column data as raw data, but
     *  IMO that's shit in the C code, and would be utter shite here too. Ergo, I cleanly 
     *  separate columns at the patch level (an advantage is that it's now easy to address
     *  individual columns). However, column data is still read "raw".
     */
    
    @Override
    public void unpack(ByteBuffer b)
            throws IOException {
        // Remember to reset the ByteBuffer position each time.
        b.position(0);
        // In ByteBuffers, the order can be conveniently set beforehand :-o
        b.order(ByteOrder.LITTLE_ENDIAN);
        
        this.width=b.getShort();
        
        this.height=b.getShort();
        this.leftoffset=b.getShort();
        this.topoffset=b.getShort();
        // As many columns as width...right???
        this.columnofs=new int[this.width];
        this.columns=new column_t[this.width];
        C2JUtils.initArrayOfObjects( this.columns, column_t.class);
        
        // Compute the ACTUAL full-column sizes.
        int[] actualsizes=new int[columns.length];
        
        for (int i=0;i<actualsizes.length-1;i++){
            actualsizes[i]=columnofs[i+1]-columnofs[i];
        }
        
        // The offsets.
        DoomBuffer.readIntArray(b, this.columnofs, this.columnofs.length);
        for (int i=0;i<this.width;i++){
        	// Go to offset.
        	b.position(this.columnofs[i]);
        	
        	try {
        	this.columns[i].unpack(b);
        	} catch (Exception e){
        		// Error during loading of column.
        		// If first column (too bad..) set to special error column.
        		if (i==0)
        			this.columns[i]=getBadColumn(this.height);
        		// Else duplicate previous column. Saves memory, too!
        		else this.columns[i]=this.columns[i-1];
        	}
        }

    }
    
    
    // Special safeguard against badly computed columns. Now they can be any size.
    private static Hashtable<Integer,column_t> badColumns=new Hashtable<Integer,column_t>();

    private static column_t getBadColumn(int size){
    	
    	if (badColumns.get(size)==null){
            column_t tmp=new column_t();
            tmp.data=new byte[size+5];
        for (int i=3;i<size+3;i++){
        	tmp.data[i]=(byte) (i-3);
        }
        
        tmp.data[size+4]=(byte) 0xFF;
        tmp.posts=1;
        //tmp.length=(short) size;
        //tmp.topdelta=0;
        tmp.postofs=new int[]{3};        
        tmp.postdeltas=new short[]{0};
        tmp.postlen=new short[]{(short) (size%256)};
        //tmp.setData();
        badColumns.put(size, tmp);
    	}
    	
    	return badColumns.get(size);
    	
    }
    
}
package rr;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

import utils.C2JUtils;
import w.CacheableDoomObject;
import w.DoomBuffer;

/** Texture definition.
 *  A DOOM wall texture is a list of patches which are to be combined in a predefined order.
 *  This is the ON-DISK structure, to be read from the TEXTURES1 and TEXTURES2 lumps.
 *  In memory, this becomes texture_t.
 *    
 *  @author MAES
 *
 */

public class maptexture_t implements CacheableDoomObject{
    public String        name; /* Fixed, 8-char width */
    public boolean     masked; 
    public short       width; // was signed byte
    public short       height; // was 
    //void**t        columndirectory;  // OBSOLETE (yeah, but we must read a dummy integer here)
    public short       patchcount;
    public mappatch_t[]  patches;
    
    
    @Override
    public void unpack(ByteBuffer buf)
            throws IOException {
        buf.order(ByteOrder.LITTLE_ENDIAN);
        name=DoomBuffer.getNullTerminatedString(buf,8);
        masked=(buf.getInt()!=0);
        width=buf.getShort();
        height=buf.getShort();
        buf.getInt(); // read a dummy integer for obsolete columndirectory.
        patchcount=buf.getShort();        
        
        // Simple sanity check. Do not attempt reading more patches than there
        // are left in the TEXTURE lump.
        patchcount=(short) Math.min(patchcount,(buf.capacity()-buf.position())/mappatch_t.size());
        
        patches=new mappatch_t[patchcount];
        C2JUtils.initArrayOfObjects(patches,mappatch_t.class);
        DoomBuffer.readObjectArray(buf, patches, patchcount);
        
    }
    
};
package rr;

import static data.Limits.MAXINT;
import static data.Limits.MAX_ADJOINING_SECTORS;
import static m.fixed_t.FRACUNIT;
import static m.fixed_t.FRACBITS;
import static p.DoorDefines.*;

import java.io.DataInputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Arrays;

import m.IRandom;
import doom.think_t;
import doom.thinker_t;
import p.Resettable;
import p.ThinkerList;
import p.fireflicker_t;
import p.glow_t;
import p.lightflash_t;
import p.mobj_t;
import p.strobe_t;
import p.vldoor_e;
import p.vldoor_t;
import s.degenmobj_t;
import w.DoomIO;
import w.IPackableDoomObject;
import w.IReadableDoomObject;

/**
 * The SECTORS record, at runtime. Stores things/mobjs. Can be
 * archived/unarchived during savegames.
 * 
 * @author Maes
 */
public class sector_t
        implements IReadableDoomObject, IPackableDoomObject, Resettable {

    public ThinkerList TL;

    public IRandom RND;

    public sector_t() {
        blockbox = new int[4];
        id = -1;
    }

    /** (fixed_t) */
    public int floorheight, ceilingheight;

    public short floorpic;

    public short ceilingpic;

    public short lightlevel;

    public short special;

    public short tag;

    /** 0 = untraversed, 1,2 = sndlines -1 */
    public int soundtraversed;

    /** thing that made a sound (or null) (MAES: single pointer) */
    public mobj_t soundtarget;

    /** mapblock bounding box for height changes */
    public int[] blockbox;

    /**
     * origin for any sounds played by the sector. Used to be degenmobj_t, but
     * that's really a futile distinction.
     */
    public degenmobj_t soundorg;

    /** if == validcount, already checked */
    public int validcount;

    /** list of mobjs in sector (MAES: it's used as a linked list) */
    public mobj_t thinglist;

    /**
     * thinker_t for reversable actions. This actually was a void*, and in
     * practice it could store doors, plats, floors and ceiling objects.
     */
    public SectorAction specialdata;

    public int linecount;

    // struct line_s** lines; // [linecount] size
    // MAES: make this line_t[] for now?
    public line_t[] lines;

    /** Use for internal identification */
    public int id;

    /** killough 1/30/98: improves searches for tags. */
    public int nexttag,firsttag;  

    public String toString() {
        String str =
            String.format("Sector: %d %x %x %d %d %d %d %d", id, floorheight,
                ceilingheight, floorpic, ceilingpic, lightlevel, special, // needed?
                tag); // needed?

        return str;

    }

    //
    // Find minimum light from an adjacent sector
    //
    public int FindMinSurroundingLight(int max) {
        int i;
        int min;
        line_t line;
        sector_t check;

        min = max;
        for (i = 0; i < this.linecount; i++) {
            line = this.lines[i];
            check = line.getNextSector(this);

            if (check == null)
                continue;

            if (check.lightlevel < min)
                min = check.lightlevel;
        }
        return min;
    }

    //
    // P_FindLowestFloorSurrounding()
    // FIND LOWEST FLOOR HEIGHT IN SURROUNDING SECTORS
    //
    public int FindLowestFloorSurrounding() {
        int i;
        line_t check;
        sector_t other;
        int floor = this.floorheight;

        for (i = 0; i < this.linecount; i++) {
            check = this.lines[i];
            other = check.getNextSector(this);

            if (other == null)
                continue;

            if (other.floorheight < floor)
                floor = other.floorheight;
        }
        return floor;
    }

    /**
     * P_FindHighestFloorSurrounding() FIND HIGHEST FLOOR HEIGHT IN SURROUNDING
     * SECTORS Compatibility problem: apparently this is hardcoded for vanilla
     * compatibility (instead of Integer.MIN_VALUE), but it will cause some
     * "semi-Boom" maps not to work, since it won't be able to lower stuff below
     * -500 units. The correct fix here would be to allow for -compatlevel style
     * options. Maybe later.
     * 
     * @param sec
     */

    public int FindHighestFloorSurrounding() {
        int i;
        line_t check;
        sector_t other;

        int floor = -500 * FRACUNIT;

        for (i = 0; i < this.linecount; i++) {
            check = this.lines[i];
            other = check.getNextSector(this);

            // The compiler nagged about this being unreachable, with
            // some older 1.6 JDKs, but that's obviously not true.
            if (other == null)
                continue;

            if (other.floorheight > floor)
                floor = other.floorheight;
        }
        return floor;
    }

    /**
     * P_FindNextHighestFloor FIND NEXT HIGHEST FLOOR IN SURROUNDING SECTORS
     * Note: this should be doable w/o a fixed array.
     * 
     * @param sec
     * @param currentheight
     * @return fixed
     */

    public int FindNextHighestFloor(int currentheight) {
        int i;
        int h;
        int min;
        line_t check;
        sector_t other;
        int height = currentheight;

        int heightlist[] = new int[MAX_ADJOINING_SECTORS];

        for (i = 0, h = 0; i < this.linecount; i++) {
            check = this.lines[i];
            other = check.getNextSector(this);

            if (other == null)
                continue;

            if (other.floorheight > height)
                heightlist[h++] = other.floorheight;

            // Check for overflow. Exit.
            if (h >= MAX_ADJOINING_SECTORS) {
                System.err
                        .print("Sector with more than 20 adjoining sectors\n");
                break;
            }
        }

        // Find lowest height in list
        if (h == 0)
            return currentheight;

        min = heightlist[0];

        // Range checking?
        for (i = 1; i < h; i++)
            if (heightlist[i] < min)
                min = heightlist[i];

        return min;
    }

    //
    // FIND LOWEST CEILING IN THE SURROUNDING SECTORS
    //
    public int FindLowestCeilingSurrounding() {
        int i;
        line_t check;
        sector_t other;
        int height = MAXINT;

        for (i = 0; i < this.linecount; i++) {
            check = this.lines[i];
            other = check.getNextSector(this);

            if (other == null)
                continue;

            if (other.ceilingheight < height)
                height = other.ceilingheight;
        }
        return height;
    }

    //
    // FIND HIGHEST CEILING IN THE SURROUNDING SECTORS
    //
    public int FindHighestCeilingSurrounding() {
        int i;
        line_t check;
        sector_t other;
        int height = 0;

        for (i = 0; i < this.linecount; i++) {
            check = this.lines[i];
            other = check.getNextSector(this);

            if (other == null)
                continue;

            if (other.ceilingheight > height)
                height = other.ceilingheight;
        }
        return height;
    }

    //
    // P_SpawnFireFlicker
    //
    public void SpawnFireFlicker() {
        fireflicker_t flick;

        // Note that we are resetting sector attributes.
        // Nothing special about it during gameplay.
        this.special = 0;

        flick = new fireflicker_t(RND);
        flick.function = think_t.T_FireFlicker;
        TL.AddThinker(flick);
        flick.sector = this;
        flick.maxlight = this.lightlevel;
        flick.minlight = this.FindMinSurroundingLight(this.lightlevel) + 16;
        flick.count = 4;
    }

    /**
     * Spawn a door that opens after 5 minutes
     */

    public void SpawnDoorRaiseIn5Mins(int secnum) {
        vldoor_t door;

        door = new vldoor_t();

        this.specialdata = door;
        this.special = 0;
        door.function = think_t.T_VerticalDoor;
        TL.AddThinker(door);
        door.sector = this;
        door.direction = 2;
        door.type = vldoor_e.raiseIn5Mins;
        door.speed = VDOORSPEED;
        door.topheight = this.FindLowestCeilingSurrounding();
        door.topheight -= 4 * FRACUNIT;
        door.topwait = VDOORWAIT;
        door.topcountdown = 5 * 60 * 35;
    }

    //
    // Spawn a door that closes after 30 seconds
    //
    public void SpawnDoorCloseIn30() {
        vldoor_t door;

        door = new vldoor_t();

        this.specialdata = door;
        this.special = 0;

        door.function = think_t.T_VerticalDoor;
        TL.AddThinker(door);
        door.sector = this;
        door.direction = 0;
        door.type = vldoor_e.normal;
        door.speed = VDOORSPEED;
        door.topcountdown = 30 * 35;
    }

    //
    // P_SpawnStrobeFlash
    // After the map has been loaded, scan each sector
    // for specials that spawn thinkers
    //
    public void SpawnStrobeFlash(int fastOrSlow, int inSync) {
        strobe_t flash;

        flash = new strobe_t();
        flash.sector = this;
        flash.darktime = fastOrSlow;
        flash.brighttime = STROBEBRIGHT;
        flash.function = think_t.T_StrobeFlash;
        TL.AddThinker(flash);
        flash.maxlight = this.lightlevel;
        flash.minlight = this.FindMinSurroundingLight(this.lightlevel);

        if (flash.minlight == flash.maxlight)
            flash.minlight = 0;

        // nothing special about it during gameplay
        this.special = 0;

        if (inSync == 0)
            flash.count = (RND.P_Random() & 7) + 1;
        else
            flash.count = 1;
    }

    /**
     * P_SpawnLightFlash After the map has been loaded, scan each sector for
     * specials that spawn thinkers
     */

    public void SpawnLightFlash() {
        lightflash_t flash;

        // nothing special about it during gameplay
        special = 0;

        flash = new lightflash_t(RND);
        flash.function = think_t.T_LightFlash;
        TL.AddThinker((thinker_t) flash);
        flash.sector = this;
        flash.maxlight = lightlevel;

        flash.minlight = FindMinSurroundingLight(lightlevel);
        flash.maxtime = 64;
        flash.mintime = 7;
        flash.count = (RND.P_Random() & flash.maxtime) + 1;
    }

    public void SpawnGlowingLight() {
        glow_t g;

        g = new glow_t();
        g.sector = this;
        g.minlight = FindMinSurroundingLight(this.lightlevel);
        g.maxlight = lightlevel;
        g.function = think_t.T_Glow;
        TL.AddThinker(g);
        g.direction = -1;

        this.special = 0;
    }

    @Override
    public void read(DataInputStream f)
            throws IOException {

        // ACHTUNG: the only situation where we'd
        // like to read memory-format sector_t's is from
        // savegames, and in vanilla savegames, not all info
        // is saved (or read) from disk.

        this.floorheight = DoomIO.readLEShort(f) << FRACBITS;
        this.ceilingheight = DoomIO.readLEShort(f) << FRACBITS;
        // MAES: it may be necessary to apply a hack in order to
        // read vanilla savegames.
        this.floorpic = (short) DoomIO.readLEShort(f);
        this.ceilingpic = (short) DoomIO.readLEShort(f);
        // f.skipBytes(4);
        this.lightlevel = DoomIO.readLEShort(f);
        this.special = DoomIO.readLEShort(f); // needed?
        this.tag = DoomIO.readLEShort(f); // needed?
    }

    @Override
    public void pack(ByteBuffer b) {

        b.putShort((short) (floorheight >> FRACBITS));
        b.putShort((short) (ceilingheight >> FRACBITS));
        // MAES: it may be necessary to apply a hack in order to
        // read vanilla savegames.
        b.putShort(floorpic);
        b.putShort(ceilingpic);
        // f.skipBytes(4);
        b.putShort(lightlevel);
        b.putShort(special);
        b.putShort(tag);
    }

    @Override
    public void reset() {
        floorheight = 0;
        ceilingheight = 0;
        floorpic = 0;
        ceilingpic = 0;
        lightlevel = 0;
        special = 0;
        tag = 0;
        soundtraversed = 0;
        soundtarget = null;
        Arrays.fill(blockbox, 0);
        soundorg = null;
        validcount = 0;
        thinglist = null;
        specialdata = null;
        linecount = 0;
        lines = null;
        id = -1;

    }
}

package rr.drawfuns;

public class ColFuncs<T,V> {
    
    public DoomColumnFunction<T, V> main;

    public DoomColumnFunction<T, V> base;

    public DoomColumnFunction<T, V> masked;

    public DoomColumnFunction<T, V> fuzz;

    public DoomColumnFunction<T, V> trans;

    public DoomColumnFunction<T, V> glass;

    public DoomColumnFunction<T, V> player;

    public DoomColumnFunction<T, V> sky;
}

package rr.drawfuns;
import static m.fixed_t.FRACBITS;
import i.IDoomSystem;


public final class R_DrawTLColumn extends DoomColumnFunction<byte[],short[]> {

		public R_DrawTLColumn(int SCREENWIDTH, int SCREENHEIGHT,
	            int[] ylookup, int[] columnofs, ColVars<byte[],short[]> dcvars,
	            short[] screen, IDoomSystem I) {
	        super(SCREENWIDTH, SCREENHEIGHT, ylookup, columnofs, dcvars, screen, I);
	        this.flags=DcFlags.TRANSPARENT;
	    }

        public void invoke() {
			int count;
			int dest; // killough
			int frac; // killough
			final int fracstep;
			final int dc_source_ofs=dcvars.dc_source_ofs;
			final byte[] tranmap=dcvars.tranmap;

			count = dcvars.dc_yh - dcvars.dc_yl + 1;

			if (count <= 0) // Zero length, column does not exceed a pixel.
				return;

			if (RANGECHECK) {
				performRangeCheck();
			}

			// Framebuffer destination address.
			// Use ylookup LUT to avoid multiply with ScreenWidth.
			// Use columnofs LUT for subwindows?

			dest = computeScreenDest();

			// Determine scaling, which is the only mapping to be done.

			fracstep = dcvars.dc_iscale;
			frac = dcvars.dc_texturemid + (dcvars.dc_yl - dcvars.centery) * fracstep;

			// Inner loop that does the actual texture mapping,
			// e.g. a DDA-lile scaling.
			// This is as fast as it gets. (Yeah, right!!! -- killough)
			//
			// killough 2/1/98: more performance tuning

			{
				final byte[] source = dcvars.dc_source;
				final short[] colormap = dcvars.dc_colormap;
				int heightmask = dcvars.dc_texheight - 1;
				if ((dcvars.dc_texheight & heightmask) != 0) // not a power of 2 --
														// killough
				{
					heightmask++;
					heightmask <<= FRACBITS;

					if (frac < 0)
						while ((frac += heightmask) < 0)
							;
					else
						while (frac >= heightmask)
							frac -= heightmask;

					do {
						// Re-map color indices from wall texture column
						// using a lighting/special effects LUT.
						// heightmask is the Tutti-Frutti fix -- killough

						screen[dest] = tranmap[0xFF00
								& (screen[dest] << 8)
								| (0x00FF & colormap[0x00FF & source[dc_source_ofs
										+ ((frac >> FRACBITS) & heightmask)]])];
						dest += SCREENWIDTH;
						if ((frac += fracstep) >= heightmask)
							frac -= heightmask;
					} while (--count > 0);
				} else {
					while ((count -= 4) >= 0) // texture height is a power of 2
												// -- killough
					{
						// screen[dest] =
						// main_tranmap[0xFF00&(screen[dest]<<8)|(0x00FF&colormap[0x00FF&source[dc_source_ofs+((frac>>FRACBITS)
						// & heightmask)]])];
						screen[dest] = tranmap[0xFF00
								& (screen[dest] << 8)
								| (0x00FF & colormap[0x00FF & source[dc_source_ofs
										+ ((frac >> FRACBITS) & heightmask)]])];
						dest += SCREENWIDTH;
						frac += fracstep;
						screen[dest] = tranmap[0xFF00
								& (screen[dest] << 8)
								| (0x00FF & colormap[0x00FF & source[dc_source_ofs
										+ ((frac >> FRACBITS) & heightmask)]])];
						dest += SCREENWIDTH;
						frac += fracstep;
						screen[dest] = tranmap[0xFF00
								& (screen[dest] << 8)
								| (0x00FF & colormap[0x00FF & source[dc_source_ofs
										+ ((frac >> FRACBITS) & heightmask)]])];
						dest += SCREENWIDTH;
						frac += fracstep;
						screen[dest] = tranmap[0xFF00
								& (screen[dest] << 8)
								| (0x00FF & colormap[0x00FF & source[dc_source_ofs
										+ ((frac >> FRACBITS) & heightmask)]])];
						dest += SCREENWIDTH;
						frac += fracstep;
					}
					if ((count & 1) != 0)
						screen[dest] = tranmap[0xFF00
								& (screen[dest] << 8)
								| (0x00FF & colormap[0x00FF & source[dc_source_ofs
										+ ((frac >> FRACBITS) & heightmask)]])];
				}
			}
		}
	}
package rr.drawfuns;

import i.IDoomSystem;

/**
	 * Framebuffer postprocessing. Creates a fuzzy image by copying pixels from
	 * adjacent ones to left and right. Used with an all black colormap, this
	 * could create the SHADOW effect, i.e. spectres and invisible players.
	 */

	public abstract class R_DrawFuzzColumn<T,V> extends DoomColumnFunction<T,V>  {
		
	    public R_DrawFuzzColumn(int SCREENWIDTH, int SCREENHEIGHT,
                int[] ylookup, int[] columnofs, ColVars<T,V> dcvars,
                V screen, IDoomSystem I,T BLURRY_MAP) {
	    	this(SCREENWIDTH, SCREENHEIGHT, ylookup, columnofs, dcvars, screen, I);
	    	this.BLURRY_MAP=BLURRY_MAP;
	    }
		
	    public R_DrawFuzzColumn(int SCREENWIDTH, int SCREENHEIGHT,
                int[] ylookup, int[] columnofs, ColVars<T,V> dcvars,
                V screen, IDoomSystem I) {
            super(SCREENWIDTH, SCREENHEIGHT, ylookup, columnofs, dcvars, screen, I);
        
            this.flags=DcFlags.FUZZY;
        
        FUZZOFF = SCREENWIDTH;
        
        // Recompute fuzz table
        
        fuzzoffset= new int[]{ FUZZOFF, -FUZZOFF, FUZZOFF, -FUZZOFF,
                FUZZOFF, FUZZOFF, -FUZZOFF, FUZZOFF, FUZZOFF, -FUZZOFF, FUZZOFF,
                FUZZOFF, FUZZOFF, -FUZZOFF, FUZZOFF, FUZZOFF, FUZZOFF, -FUZZOFF,
                -FUZZOFF, -FUZZOFF, -FUZZOFF, FUZZOFF, -FUZZOFF, -FUZZOFF, FUZZOFF,
                FUZZOFF, FUZZOFF, FUZZOFF, -FUZZOFF, FUZZOFF, -FUZZOFF, FUZZOFF,
                FUZZOFF, -FUZZOFF, -FUZZOFF, FUZZOFF, FUZZOFF, -FUZZOFF, -FUZZOFF,
                -FUZZOFF, -FUZZOFF, FUZZOFF, FUZZOFF, FUZZOFF, FUZZOFF, -FUZZOFF,
                FUZZOFF, FUZZOFF, -FUZZOFF, FUZZOFF };
        
        FUZZTABLE=fuzzoffset.length;
		}
		
		protected int fuzzpos;
		
		protected final int FUZZTABLE;
		

        //
        // Spectre/Invisibility.
        //

		protected final int FUZZOFF;

		protected final int[] fuzzoffset;

        public static final class HiColor extends R_DrawFuzzColumn<byte[],short[]>{
        	
        	public HiColor(int SCREENWIDTH, int SCREENHEIGHT, int[] ylookup,
					int[] columnofs, ColVars<byte[], short[]> dcvars,
					short[] screen, IDoomSystem I) {
				super(SCREENWIDTH, SCREENHEIGHT, ylookup, columnofs, dcvars, screen, I);
				// TODO Auto-generated constructor stub
			}

			public void invoke() {
    			int count;
    			int dest;

    			// Adjust borders. Low...
    			if (dcvars.dc_yl == 0)
    			    dcvars.dc_yl = 1;

    			// .. and high.
    			if (dcvars.dc_yh == dcvars.viewheight - 1)
    			    dcvars.dc_yh = dcvars.viewheight - 2;

    			count = dcvars.dc_yh - dcvars.dc_yl;

    			// Zero length.
    			if (count < 0)
    				return;

    			if (RANGECHECK) {
    				    super.performRangeCheck();
    			}

    			// Does not work with blocky mode.
    			dest = computeScreenDest();

    			// Looks like an attempt at dithering,
    			// using the colormap #6 (of 0-31, a bit
    			// brighter than average).
    			if (count>4) // MAES: unroll by 4
    			do {
    				// Lookup framebuffer, and retrieve
    				// a pixel that is either one column
    				// left or right of the current one.
    				// Add index from colormap to index.

    				screen[dest] = fuzzMix(screen[dest
    										+ fuzzoffset[fuzzpos]]);

    				// Clamp table lookup index.
    				if (++fuzzpos == FUZZTABLE)
    					fuzzpos = 0;

    				dest += SCREENWIDTH;				
                    
    				screen[dest] = fuzzMix(screen[dest
    										+ fuzzoffset[fuzzpos]]);
                    if (++fuzzpos == FUZZTABLE) fuzzpos = 0;
                    dest += SCREENWIDTH;

    				screen[dest] = fuzzMix(screen[dest
    										+ fuzzoffset[fuzzpos]]);
                    if (++fuzzpos == FUZZTABLE) fuzzpos = 0;
                    dest += SCREENWIDTH;

    				screen[dest] = fuzzMix(screen[dest
    										+ fuzzoffset[fuzzpos]]);
                    if (++fuzzpos == FUZZTABLE) fuzzpos = 0;
                    dest += SCREENWIDTH;
    				
    			} while ((count-=4) > 4);
    			
    			if (count>0)
    	         do {
    	                screen[dest] = fuzzMix(screen[dest
    	                                              + fuzzoffset[fuzzpos]]);

    	                // Clamp table lookup index.
    	                if (++fuzzpos == FUZZTABLE)
    	                    fuzzpos = 0;

    	                dest += SCREENWIDTH;
    	            } while (count-- > 0);
    			
    		}
            
            private final short fuzzMix(short rgb){
                // super-fast half-brite trick
                // 3DEF and >> 1: ok hue, but too dark
                // 7BDE, no shift:  good compromise
                // 739C, no shift: results in too obvious tinting.         
                return (short) (rgb&0x7BDE);        	
            }
        }
        
        public static final class Indexed extends R_DrawFuzzColumn<byte[],byte[]>{
        
        public Indexed(int SCREENWIDTH, int SCREENHEIGHT, int[] ylookup,
					int[] columnofs, ColVars<byte[], byte[]> dcvars,
					byte[] screen, IDoomSystem I,byte[] BLURRY_MAP) {
				super(SCREENWIDTH, SCREENHEIGHT, ylookup, columnofs, dcvars, screen, I,BLURRY_MAP);
			}

		public void invoke() {
			int count;
			int dest;

			// Adjust borders. Low...
			if (dcvars.dc_yl == 0)
			    dcvars.dc_yl = 1;

			// .. and high.
			if (dcvars.dc_yh == dcvars.viewheight - 1)
			    dcvars.dc_yh = dcvars.viewheight - 2;

			count = dcvars.dc_yh - dcvars.dc_yl;

			// Zero length.
			if (count < 0)
				return;

			if (RANGECHECK) {
				    super.performRangeCheck();
			}

			// Does not work with blocky mode.
			dest = computeScreenDest();

			// Looks like an attempt at dithering,
			// using the colormap #6 (of 0-31, a bit
			// brighter than average).
			if (count>4) // MAES: unroll by 4
			do {
				// Lookup framebuffer, and retrieve
				// a pixel that is either one column
				// left or right of the current one.
				// Add index from colormap to index.
				screen[dest] = BLURRY_MAP[0x00FF & screen[dest
						+ fuzzoffset[fuzzpos]]];

				// Clamp table lookup index.
				if (++fuzzpos == FUZZTABLE)
					fuzzpos = 0;

				dest += SCREENWIDTH;				
                
				screen[dest] = 
                        BLURRY_MAP[0x00FF & screen[dest+ fuzzoffset[fuzzpos]]];
                if (++fuzzpos == FUZZTABLE) fuzzpos = 0;
                dest += SCREENWIDTH;
                
                screen[dest] = 
                        BLURRY_MAP[0x00FF & screen[dest+ fuzzoffset[fuzzpos]]];
                if (++fuzzpos == FUZZTABLE) fuzzpos = 0;
                dest += SCREENWIDTH;
                
                screen[dest] = 
                        BLURRY_MAP[0x00FF & screen[dest+ fuzzoffset[fuzzpos]]];
                if (++fuzzpos == FUZZTABLE) fuzzpos = 0;
                dest += SCREENWIDTH;
				
			} while ((count-=4) > 4);
			
			if (count>0)
	         do {
	                // Lookup framebuffer, and retrieve
	                // a pixel that is either one column
	                // left or right of the current one.
	                // Add index from colormap to index.
	                screen[dest] = BLURRY_MAP[0x00FF & screen[dest
	                        + fuzzoffset[fuzzpos]]];

	                // Clamp table lookup index.
	                if (++fuzzpos == FUZZTABLE)
	                    fuzzpos = 0;

	                dest += SCREENWIDTH;
	            } while (count-- > 0);
			
		}
        }

public static final class TrueColor extends R_DrawFuzzColumn<byte[],int[]>{
            
            public TrueColor(int SCREENWIDTH, int SCREENHEIGHT, int[] ylookup,
                    int[] columnofs, ColVars<byte[], int[]> dcvars,
                    int[] screen, IDoomSystem I) {
                super(SCREENWIDTH, SCREENHEIGHT, ylookup, columnofs, dcvars, screen, I);
            }

            public void invoke() {
                int count;
                int dest;

                // Adjust borders. Low...
                if (dcvars.dc_yl == 0)
                    dcvars.dc_yl = 1;

                // .. and high.
                if (dcvars.dc_yh == dcvars.viewheight - 1)
                    dcvars.dc_yh = dcvars.viewheight - 2;

                count = dcvars.dc_yh - dcvars.dc_yl;

                // Zero length.
                if (count < 0)
                    return;

                if (RANGECHECK) {
                        super.performRangeCheck();
                }

                // Does not work with blocky mode.
                dest = computeScreenDest();

                // Looks like an attempt at dithering,
                // using the colormap #6 (of 0-31, a bit
                // brighter than average).
                if (count>4) // MAES: unroll by 4
                do {
                    // Lookup framebuffer, and retrieve
                    // a pixel that is either one column
                    // left or right of the current one.
                    // Add index from colormap to index.

                    screen[dest] = fuzzMix(screen[dest
                                            + fuzzoffset[fuzzpos]]);

                    // Clamp table lookup index.
                    if (++fuzzpos == FUZZTABLE)
                        fuzzpos = 0;

                    dest += SCREENWIDTH;                
                    
                    screen[dest] = fuzzMix(screen[dest
                                            + fuzzoffset[fuzzpos]]);
                    if (++fuzzpos == FUZZTABLE) fuzzpos = 0;
                    dest += SCREENWIDTH;

                    screen[dest] = fuzzMix(screen[dest
                                            + fuzzoffset[fuzzpos]]);
                    if (++fuzzpos == FUZZTABLE) fuzzpos = 0;
                    dest += SCREENWIDTH;

                    screen[dest] = fuzzMix(screen[dest
                                            + fuzzoffset[fuzzpos]]);
                    if (++fuzzpos == FUZZTABLE) fuzzpos = 0;
                    dest += SCREENWIDTH;
                    
                } while ((count-=4) > 4);
                
                if (count>0)
                 do {
                        screen[dest] = fuzzMix(screen[dest
                                                      + fuzzoffset[fuzzpos]]);

                        // Clamp table lookup index.
                        if (++fuzzpos == FUZZTABLE)
                            fuzzpos = 0;

                        dest += SCREENWIDTH;
                    } while (count-- > 0);
                
            }
            
            private final int fuzzMix(int rgb){
                // Proper half-brite alpha!
                return rgb&0x10FFFFFF;
            }
        }
        
	}
package rr.drawfuns;

import i.IDoomSystem;

/** Prototype for 
 * 
 * @author velktron
 *
 * @param <T>
 */

public abstract class DoomColumnFunction<T,V> implements ColumnFunction<T,V>{
    
    protected final boolean RANGECHECK=false;
    protected final int SCREENWIDTH;
    protected final int SCREENHEIGHT;
    protected ColVars<T,V> dcvars;
    protected final V screen;
    protected final IDoomSystem I;
    protected final int[] ylookup;
    protected final int[] columnofs;
    protected T BLURRY_MAP;
    protected int flags;
    
    public DoomColumnFunction(int sCREENWIDTH, int sCREENHEIGHT,int[] ylookup,
            int[] columnofs, ColVars<T,V> dcvars, V screen,IDoomSystem I) {
        SCREENWIDTH = sCREENWIDTH;
        SCREENHEIGHT = sCREENHEIGHT;
        this.ylookup=ylookup;
        this.columnofs=columnofs;
        this.dcvars = dcvars;
        this.screen = screen;
        this.I=I;
        this.BLURRY_MAP=null;
    }
    
    public DoomColumnFunction(int sCREENWIDTH, int sCREENHEIGHT,int[] ylookup,
            int[] columnofs, ColVars<T,V> dcvars, V screen,IDoomSystem I,T BLURRY_MAP) {
        SCREENWIDTH = sCREENWIDTH;
        SCREENHEIGHT = sCREENHEIGHT;
        this.ylookup=ylookup;
        this.columnofs=columnofs;
        this.dcvars = dcvars;
        this.screen = screen;
        this.I=I;
        this.BLURRY_MAP=BLURRY_MAP;
    }

    protected final void performRangeCheck(){
        if (dcvars.dc_x >= SCREENWIDTH || dcvars.dc_yl < 0 || dcvars.dc_yh >= SCREENHEIGHT)
            I.Error("R_DrawColumn: %d to %d at %d", dcvars.dc_yl, dcvars.dc_yh, dcvars.dc_x);
    }
    
    /**
     * 
     * Use ylookup LUT to avoid multiply with ScreenWidth.
     * Use columnofs LUT for subwindows?
     * 
     * @return Framebuffer destination address.
     */
    
    protected final int computeScreenDest() {
        return ylookup[dcvars.dc_yl] + columnofs[dcvars.dc_x];
    }

    protected final int blockyDest1() {
        return ylookup[dcvars.dc_yl] + columnofs[dcvars.dc_x<<1];
    }

    protected final int blockyDest2() {
        return  ylookup[dcvars.dc_yl] + columnofs[(dcvars.dc_x<<1)+1];
    }

    @Override
    public final void invoke(ColVars<T,V> dcvars) {
        this.dcvars=dcvars;
        invoke();
    }
    
    public final int getFlags(){
        return this.flags;
    }
    
}

package rr.drawfuns;
import i.IDoomSystem;

/**
	 * EI VITTU, this gives a clean 25% boost. Da fack...
	 * 
	 * 
	 * @author admin
	 * 
	 */

	public final class R_DrawColumnUnrolled extends DoomColumnFunction<byte[],short[]> {

		/*
		 * That's shit, doesn't help. private final int
		 * SCREENWIDTH2=SCREENWIDTH*2; private final int
		 * SCREENWIDTH3=SCREENWIDTH*3; private final int
		 * SCREENWIDTH4=SCREENWIDTH*4; private final int
		 * SCREENWIDTH5=SCREENWIDTH*5; private final int
		 * SCREENWIDTH6=SCREENWIDTH*6; private final int
		 * SCREENWIDTH7=SCREENWIDTH*7; private final int
		 * SCREENWIDTH8=SCREENWIDTH*8;
		 */

		public R_DrawColumnUnrolled(int SCREENWIDTH, int SCREENHEIGHT,
	            int[] ylookup, int[] columnofs, ColVars<byte[],short[]> dcvars,
	            short[] screen, IDoomSystem I) {
	        super(SCREENWIDTH, SCREENHEIGHT, ylookup, columnofs, dcvars, screen, I);
	    }

        public void invoke() {
			int count,dest;
			final byte[] source;			
			final short[] colormap;
			final int dc_source_ofs=dcvars.dc_source_ofs;

			// These are all "unsigned". Watch out for bit shifts!
			int frac;
			final int fracstep, fracstep2, fracstep3, fracstep4;

			count = dcvars.dc_yh - dcvars.dc_yl + 1;

			source = dcvars.dc_source;
			// dc_source_ofs+=15; // ???? WHY
			colormap = dcvars.dc_colormap;
			dest = computeScreenDest();

			fracstep = dcvars.dc_iscale << 9;
			frac = (dcvars.dc_texturemid + (dcvars.dc_yl - dcvars.centery) * dcvars.dc_iscale) << 9;

			fracstep2 = fracstep + fracstep;
			fracstep3 = fracstep2 + fracstep;
			fracstep4 = fracstep3 + fracstep;

			while (count > 8) {
				screen[dest] = colormap[0x00FF & source[dc_source_ofs + frac >>> 25]];
				screen[dest + SCREENWIDTH] = colormap[0x00FF & source[dc_source_ofs
						+ (frac + fracstep) >>> 25]];
				screen[dest + SCREENWIDTH * 2] = colormap[0x00FF & source[dc_source_ofs
						+ (frac + fracstep2) >>> 25]];
				screen[dest + SCREENWIDTH * 3] = colormap[0x00FF & source[dc_source_ofs
						+ (frac + fracstep3) >>> 25]];

				frac += fracstep4;

				screen[dest + SCREENWIDTH * 4] = colormap[0x00FF & source[dc_source_ofs
						+ frac >>> 25]];
				screen[dest + SCREENWIDTH * 5] = colormap[0x00FF & source[dc_source_ofs
						+ (frac + fracstep) >>> 25]];
				screen[dest + SCREENWIDTH * 6] = colormap[0x00FF & source[dc_source_ofs
						+ (frac + fracstep2) >>> 25]];
				screen[dest + SCREENWIDTH * 7] = colormap[0x00FF & source[dc_source_ofs
						+ (frac + fracstep3) >>> 25]];

				frac += fracstep4;
				dest += SCREENWIDTH * 8;
				count -= 8;
			}

			while (count > 0) {
				screen[dest] = colormap[0x00FF & source[dc_source_ofs + frac >>> 25]];
				dest += SCREENWIDTH;
				frac += fracstep;
				count--;
			}
		}
	}
package rr.drawfuns;

import i.IDoomSystem;

/** An unrolled (4x) rendering loop with full quality */
// public final int dumb=63 * 64;

public final class R_DrawSpanUnrolled2 extends DoomSpanFunction<byte[],short[]> {
    
    
	public R_DrawSpanUnrolled2(int sCREENWIDTH, int sCREENHEIGHT,
            int[] ylookup, int[] columnofs, SpanVars<byte[],short[]> dsvars,
            short[] screen, IDoomSystem I) {
        super(sCREENWIDTH, sCREENHEIGHT, ylookup, columnofs, dsvars, screen, I);
        // TODO Auto-generated constructor stub
    }

    public void invoke() {
	    final byte[] ds_source= dsvars.ds_source;
	    final short[] ds_colormap= dsvars.ds_colormap;
	    final int ds_xstep=dsvars.ds_xstep;
	    final int ds_ystep=dsvars.ds_ystep;
		int f_xfrac; // fixed_t
		int f_yfrac; // fixed_t
		int dest;
		int count;
		int spot;

		// System.out.println("R_DrawSpan: "+ds_x1+" to "+ds_x2+" at "+
		// ds_y);

		if (RANGECHECK) {
		    doRangeCheck();
			// dscount++;
		}

		f_xfrac = dsvars.ds_xfrac;
		f_yfrac = dsvars.ds_yfrac;

		dest = ylookup[dsvars.ds_y] + columnofs[dsvars.ds_x1];

		count = dsvars.ds_x2 - dsvars.ds_x1;
		while (count >= 4) {
			// Current texture index in u,v.
			spot = ((f_yfrac >> (16 - 6)) & (63 * 64))
					+ ((f_xfrac >> 16) & 63);

			// Lookup pixel from flat texture tile,
			// re-index using light/colormap.
			screen[dest++] = ds_colormap[0x00FF & ds_source[spot]];

			// Next step in u,v.
			f_xfrac += ds_xstep;
			f_yfrac += ds_ystep;

			// UNROLL 2
			spot = ((f_yfrac >> (16 - 6)) & (63 * 64))
					+ ((f_xfrac >> 16) & 63);
			screen[dest++] = ds_colormap[0x00FF & ds_source[spot]];
			f_xfrac += ds_xstep;
			f_yfrac += ds_ystep;

			// UNROLL 3
			spot = ((f_yfrac >> (16 - 6)) & (63 * 64))
					+ ((f_xfrac >> 16) & 63);
			screen[dest++] = ds_colormap[0x00FF & ds_source[spot]];
			f_xfrac += ds_xstep;
			f_yfrac += ds_ystep;

			// UNROLL 4
			spot = ((f_yfrac >> (16 - 6)) & (63 * 64))
					+ ((f_xfrac >> 16) & 63);
			screen[dest++] = ds_colormap[0x00FF & ds_source[spot]];
			f_xfrac += ds_xstep;
			f_yfrac += ds_ystep;

			count -= 4;
		}

		while (count > 0) {
			// Current texture index in u,v.
			spot = ((f_yfrac >> (16 - 6)) & (63 * 64))
					+ ((f_xfrac >> 16) & 63);

			// Lookup pixel from flat texture tile,
			// re-index using light/colormap.
			screen[dest++] = ds_colormap[0x00FF & ds_source[spot]];

			// Next step in u,v.
			f_xfrac += ds_xstep;
			f_yfrac += ds_ystep;
			count--;
		}

	}
}

package rr.drawfuns;

import i.IDoomSystem;
import static m.fixed_t.FRACBITS;

/**
 * Adapted from Killough's Boom code. Low-detail variation, no DC_SOURCE
 * optimization.
 * 
 * @author admin
 */

public abstract class R_DrawColumnBoomLow<T, V>
        extends DoomColumnFunction<T, V> {

    public R_DrawColumnBoomLow(int SCREENWIDTH, int SCREENHEIGHT,
            int[] ylookup, int[] columnofs, ColVars<T, V> dcvars, V screen,
            IDoomSystem I) {
        super(SCREENWIDTH, SCREENHEIGHT, ylookup, columnofs, dcvars, screen, I);
        this.flags = DcFlags.LOW_DETAIL;
    }

    public static final class HiColor
            extends R_DrawColumnBoomLow<byte[], short[]> {

        public HiColor(int SCREENWIDTH, int SCREENHEIGHT, int[] ylookup,
                int[] columnofs, ColVars<byte[], short[]> dcvars,
                short[] screen, IDoomSystem I) {
            super(SCREENWIDTH, SCREENHEIGHT, ylookup, columnofs, dcvars,
                    screen, I);
        }

        public void invoke() {
            int count;
            int dest, dest2; // killough
            int frac; // killough
            final int fracstep, dc_source_ofs;

            count = dcvars.dc_yh - dcvars.dc_yl + 1;
            dc_source_ofs = dcvars.dc_source_ofs;

            if (count <= 0) // Zero length, column does not exceed a pixel.
                return;

            if (RANGECHECK) {
                performRangeCheck();
            }

            // Framebuffer destination address.
            // Use ylookup LUT to avoid multiply with ScreenWidth.
            // Use columnofs LUT for subwindows?
            dest = blockyDest1();
            dest2 = blockyDest2();

            // Determine scaling, which is the only mapping to be done.

            fracstep = dcvars.dc_iscale;
            frac =
                dcvars.dc_texturemid + (dcvars.dc_yl - dcvars.centery)
                        * fracstep;

            // Inner loop that does the actual texture mapping,
            // e.g. a DDA-lile scaling.
            // This is as fast as it gets. (Yeah, right!!! -- killough)
            //
            // killough 2/1/98: more performance tuning

            {
                final byte[] source = dcvars.dc_source;
                final short[] colormap = dcvars.dc_colormap;
                int heightmask = dcvars.dc_texheight - 1;
                if ((dcvars.dc_texheight & heightmask) != 0) // not a power of 2
                                                             // --
                // killough
                {
                    heightmask++;
                    heightmask <<= FRACBITS;

                    if (frac < 0)
                        while ((frac += heightmask) < 0)
                            ;
                    else
                        while (frac >= heightmask)
                            frac -= heightmask;

                    do {
                        // Re-map color indices from wall texture column
                        // using a lighting/special effects LUT.

                        // heightmask is the Tutti-Frutti fix -- killough

                        screen[dest] =
                            screen[dest2] =
                                colormap[0x00FF & source[((frac >> FRACBITS))]];
                        dest += SCREENWIDTH;
                        dest2 += SCREENWIDTH;
                        if ((frac += fracstep) >= heightmask)
                            frac -= heightmask;
                    } while (--count > 0);
                } else {
                    while (count >= 4) // texture height is a power of 2 --
                                       // killough
                    {

                        screen[dest] =
                            screen[dest2] =
                                colormap[0x00FF & source[dc_source_ofs
                                        + ((frac >> FRACBITS) & heightmask)]];
                        dest += SCREENWIDTH;
                        dest2 += SCREENWIDTH;
                        frac += fracstep;
                        screen[dest] =
                            screen[dest2] =
                                colormap[0x00FF & source[dc_source_ofs
                                        + ((frac >> FRACBITS) & heightmask)]];
                        dest += SCREENWIDTH;
                        dest2 += SCREENWIDTH;
                        frac += fracstep;
                        screen[dest] =
                            screen[dest2] =
                                colormap[0x00FF & source[dc_source_ofs
                                        + ((frac >> FRACBITS) & heightmask)]];
                        dest += SCREENWIDTH;
                        dest2 += SCREENWIDTH;
                        frac += fracstep;
                        screen[dest] =
                            screen[dest2] =
                                colormap[0x00FF & source[dc_source_ofs
                                        + ((frac >> FRACBITS) & heightmask)]];
                        dest += SCREENWIDTH;
                        dest2 += SCREENWIDTH;
                        frac += fracstep;
                        count -= 4;
                    }

                    while (count > 0) {
                        screen[dest] =
                            screen[dest2] =
                                colormap[0x00FF & source[dc_source_ofs
                                        + ((frac >> FRACBITS) & heightmask)]];
                        dest += SCREENWIDTH;
                        dest2 += SCREENWIDTH;
                        frac += fracstep;
                        count--;
                    }
                }
            }
        }
    }

    public static final class Indexed
            extends R_DrawColumnBoomLow<byte[], byte[]> {

        public Indexed(int SCREENWIDTH, int SCREENHEIGHT, int[] ylookup,
                int[] columnofs, ColVars<byte[], byte[]> dcvars, byte[] screen,
                IDoomSystem I) {
            super(SCREENWIDTH, SCREENHEIGHT, ylookup, columnofs, dcvars,
                    screen, I);
        }

        public void invoke() {
            int count;
            int dest, dest2; // killough
            int frac; // killough
            final int fracstep, dc_source_ofs;

            count = dcvars.dc_yh - dcvars.dc_yl + 1;
            dc_source_ofs = dcvars.dc_source_ofs;

            if (count <= 0) // Zero length, column does not exceed a pixel.
                return;

            if (RANGECHECK) {
                performRangeCheck();
            }

            // Framebuffer destination address.
            // Use ylookup LUT to avoid multiply with ScreenWidth.
            // Use columnofs LUT for subwindows?
            dest = blockyDest1();
            dest2 = blockyDest2();

            // Determine scaling, which is the only mapping to be done.

            fracstep = dcvars.dc_iscale;
            frac =
                dcvars.dc_texturemid + (dcvars.dc_yl - dcvars.centery)
                        * fracstep;

            // Inner loop that does the actual texture mapping,
            // e.g. a DDA-lile scaling.
            // This is as fast as it gets. (Yeah, right!!! -- killough)
            //
            // killough 2/1/98: more performance tuning

            {
                final byte[] source = dcvars.dc_source;
                final byte[] colormap = dcvars.dc_colormap;
                int heightmask = dcvars.dc_texheight - 1;
                if ((dcvars.dc_texheight & heightmask) != 0) // not a power of 2
                                                             // --
                // killough
                {
                    heightmask++;
                    heightmask <<= FRACBITS;

                    if (frac < 0)
                        while ((frac += heightmask) < 0)
                            ;
                    else
                        while (frac >= heightmask)
                            frac -= heightmask;

                    do {
                        // Re-map color indices from wall texture column
                        // using a lighting/special effects LUT.

                        // heightmask is the Tutti-Frutti fix -- killough

                        screen[dest] =
                            screen[dest2] =
                                colormap[0x00FF & source[((frac >> FRACBITS))]];
                        dest += SCREENWIDTH;
                        dest2 += SCREENWIDTH;
                        if ((frac += fracstep) >= heightmask)
                            frac -= heightmask;
                    } while (--count > 0);
                } else {
                    while (count >= 4) // texture height is a power of 2 --
                                       // killough
                    {

                        screen[dest] =
                            screen[dest2] =
                                colormap[0x00FF & source[dc_source_ofs
                                        + ((frac >> FRACBITS) & heightmask)]];
                        dest += SCREENWIDTH;
                        dest2 += SCREENWIDTH;
                        frac += fracstep;
                        screen[dest] =
                            screen[dest2] =
                                colormap[0x00FF & source[dc_source_ofs
                                        + ((frac >> FRACBITS) & heightmask)]];
                        dest += SCREENWIDTH;
                        dest2 += SCREENWIDTH;
                        frac += fracstep;
                        screen[dest] =
                            screen[dest2] =
                                colormap[0x00FF & source[dc_source_ofs
                                        + ((frac >> FRACBITS) & heightmask)]];
                        dest += SCREENWIDTH;
                        dest2 += SCREENWIDTH;
                        frac += fracstep;
                        screen[dest] =
                            screen[dest2] =
                                colormap[0x00FF & source[dc_source_ofs
                                        + ((frac >> FRACBITS) & heightmask)]];
                        dest += SCREENWIDTH;
                        dest2 += SCREENWIDTH;
                        frac += fracstep;
                        count -= 4;
                    }

                    while (count > 0) {
                        screen[dest] =
                            screen[dest2] =
                                colormap[0x00FF & source[dc_source_ofs
                                        + ((frac >> FRACBITS) & heightmask)]];
                        dest += SCREENWIDTH;
                        dest2 += SCREENWIDTH;
                        frac += fracstep;
                        count--;
                    }
                }
            }
        }

    }

            
            public static final class TrueColor
            extends R_DrawColumnBoomLow<byte[], int[]> {

        public TrueColor(int SCREENWIDTH, int SCREENHEIGHT, int[] ylookup,
                int[] columnofs, ColVars<byte[], int[]> dcvars,
                int[] screen, IDoomSystem I) {
            super(SCREENWIDTH, SCREENHEIGHT, ylookup, columnofs, dcvars,
                    screen, I);
        }

        public void invoke() {
            int count;
            int dest, dest2; // killough
            int frac; // killough
            final int fracstep, dc_source_ofs;

            count = dcvars.dc_yh - dcvars.dc_yl + 1;
            dc_source_ofs = dcvars.dc_source_ofs;

            if (count <= 0) // Zero length, column does not exceed a pixel.
                return;

            if (RANGECHECK) {
                performRangeCheck();
            }

            // Framebuffer destination address.
            // Use ylookup LUT to avoid multiply with ScreenWidth.
            // Use columnofs LUT for subwindows?
            dest = blockyDest1();
            dest2 = blockyDest2();

            // Determine scaling, which is the only mapping to be done.

            fracstep = dcvars.dc_iscale;
            frac =
                dcvars.dc_texturemid + (dcvars.dc_yl - dcvars.centery)
                        * fracstep;

            // Inner loop that does the actual texture mapping,
            // e.g. a DDA-lile scaling.
            // This is as fast as it gets. (Yeah, right!!! -- killough)
            //
            // killough 2/1/98: more performance tuning

            {
                final byte[] source = dcvars.dc_source;
                final int[] colormap = dcvars.dc_colormap;
                int heightmask = dcvars.dc_texheight - 1;
                if ((dcvars.dc_texheight & heightmask) != 0) // not a power of 2
                                                             // --
                // killough
                {
                    heightmask++;
                    heightmask <<= FRACBITS;

                    if (frac < 0)
                        while ((frac += heightmask) < 0)
                            ;
                    else
                        while (frac >= heightmask)
                            frac -= heightmask;

                    do {
                        // Re-map color indices from wall texture column
                        // using a lighting/special effects LUT.

                        // heightmask is the Tutti-Frutti fix -- killough

                        screen[dest] =
                            screen[dest2] =
                                colormap[0x00FF & source[((frac >> FRACBITS))]];
                        dest += SCREENWIDTH;
                        dest2 += SCREENWIDTH;
                        if ((frac += fracstep) >= heightmask)
                            frac -= heightmask;
                    } while (--count > 0);
                } else {
                    while (count >= 4) // texture height is a power of 2 --
                                       // killough
                    {

                        screen[dest] =
                            screen[dest2] =
                                colormap[0x00FF & source[dc_source_ofs
                                        + ((frac >> FRACBITS) & heightmask)]];
                        dest += SCREENWIDTH;
                        dest2 += SCREENWIDTH;
                        frac += fracstep;
                        screen[dest] =
                            screen[dest2] =
                                colormap[0x00FF & source[dc_source_ofs
                                        + ((frac >> FRACBITS) & heightmask)]];
                        dest += SCREENWIDTH;
                        dest2 += SCREENWIDTH;
                        frac += fracstep;
                        screen[dest] =
                            screen[dest2] =
                                colormap[0x00FF & source[dc_source_ofs
                                        + ((frac >> FRACBITS) & heightmask)]];
                        dest += SCREENWIDTH;
                        dest2 += SCREENWIDTH;
                        frac += fracstep;
                        screen[dest] =
                            screen[dest2] =
                                colormap[0x00FF & source[dc_source_ofs
                                        + ((frac >> FRACBITS) & heightmask)]];
                        dest += SCREENWIDTH;
                        dest2 += SCREENWIDTH;
                        frac += fracstep;
                        count -= 4;
                    }

                    while (count > 0) {
                        screen[dest] =
                            screen[dest2] =
                                colormap[0x00FF & source[dc_source_ofs
                                        + ((frac >> FRACBITS) & heightmask)]];
                        dest += SCREENWIDTH;
                        dest2 += SCREENWIDTH;
                        frac += fracstep;
                        count--;
                    }
                }
            }
        }
    }
            
}
package rr.drawfuns;

import i.IDoomSystem;

/**
	 * Draws the actual span.
	 * 
	 * ds_frac, ds_yfrac, ds_x2, ds_x1, ds_xstep and ds_ystep must be set.
	 * 
	 */

	public final class R_DrawSpan extends DoomSpanFunction<byte[],short[]> {

        public R_DrawSpan(int sCREENWIDTH, int sCREENHEIGHT, int[] ylookup,
            int[] columnofs, SpanVars<byte[],short[]> dsvars,short[] screen,
            IDoomSystem I) {
        super(sCREENWIDTH, sCREENHEIGHT, ylookup, columnofs, dsvars, screen, I);
        }

        public void invoke() {

			int f_xfrac; // fixed_t
			int f_yfrac; // fixed_t
			int dest,count,spot;
			final short[] ds_colormap=dsvars.ds_colormap;
			final byte[] ds_source=dsvars.ds_source;

			// System.out.println("R_DrawSpan: "+ds_x1+" to "+ds_x2+" at "+
			// ds_y);

			if (RANGECHECK) {
			    doRangeCheck();
				// dscount++;
			}

			f_xfrac = dsvars.ds_xfrac;
			f_yfrac = dsvars.ds_yfrac;

			dest = ylookup[dsvars.ds_y] + columnofs[dsvars.ds_x1];

			// We do not check for zero spans here?
			count = dsvars.ds_x2 - dsvars.ds_x1;

			do {
				// Current texture index in u,v.
				spot = ((f_yfrac >> (16 - 6)) & (63 * 64))
						+ ((f_xfrac >> 16) & 63);

				// Lookup pixel from flat texture tile,
				// re-index using light/colormap.
				screen[dest++] = ds_colormap[0x00FF & ds_source[spot]];

				// Next step in u,v.
				f_xfrac += dsvars.ds_xstep;
				f_yfrac += dsvars.ds_ystep;

			} while (count-- > 0);
		}
	}
package rr.drawfuns;

import static m.fixed_t.FRACBITS;
import i.IDoomSystem;

     /**
	 * Adapted from Killough's Boom code. There are optimized as well as
	 * low-detail versions of it.
	 * 
	 * @author admin
	 * 
	 */

/**
 * A column is a vertical slice/span from a wall texture that, given the
 * DOOM style restrictions on the view orientation, will always have
 * constant z depth. Thus a special case loop for very fast rendering can be
 * used. It has also been used with Wolfenstein 3D. MAES: this is called
 * mostly from inside Draw and from an external "Renderer"
 */

public final class R_DrawColumn extends DoomColumnFunction<byte[],short[]> {
    
    public R_DrawColumn(int SCREENWIDTH, int SCREENHEIGHT,
            int[] ylookup, int[] columnofs, ColVars<byte[],short[]> dcvars,
            short[] screen, IDoomSystem I) {
        super(SCREENWIDTH, SCREENHEIGHT, ylookup, columnofs, dcvars, screen, I);
    }
    
    public void invoke() {
        int count;
        // byte* dest;
        int dest; // As pointer
        // fixed_t
        int frac, fracstep;
        byte colmask = 127;
        count = dcvars.dc_yh - dcvars.dc_yl;
        // How much we should draw
        // count = Math.min(dc_yh - dc_yl,dc_source.length-dc_source_ofs-1);
        // colmask = (byte) Math.min(dc_source.length-dc_source_ofs-1,127);

        // Zero length, column does not exceed a pixel.
        if (count <= 0)
            return;

        if (RANGECHECK) {
            if (dcvars.dc_x >= SCREENWIDTH || dcvars.dc_yl < 0 || dcvars.dc_yh >= SCREENHEIGHT)
                I.Error("R_DrawColumn: %i to %i at %i", dcvars.dc_yl, dcvars.dc_yh, dcvars.dc_x);
        }

        // Trying to draw a masked column? Then something gross will happen.
        /*
         * if (count>=dc_source.length-dc_source_ofs) { int
         * diff=count-(dc_source.length-dc_source_ofs);
         * count=dc_source.length-dc_source_ofs-1; dc_source_ofs=0;
         * //dc_yl=dc_yh-count; gross=true; }
         */
        
        dest = computeScreenDest();

        // Determine scaling,
        // which is the only mapping to be done.
        fracstep = dcvars.dc_iscale;
        frac = dcvars.dc_texturemid + (dcvars.dc_yl - dcvars.centery) * fracstep;

        // Inner loop that does the actual texture mapping,
        // e.g. a DDA-lile scaling.
        // This is as fast as it gets.
        do {
            /*
             * Re-map color indices from wall texture column using a
             * lighting/special effects LUT. 
             * Q: Where is "*dest"supposed to be pointing?
             * A: it's pointing inside screen[0] (set long before we came here)
             * dc_source is a pointer to a decompressed column's data.
             * Oh Woe if it points at non-pixel data.
             */
            // if (gross) System.out.println(frac >> FRACBITS);
            screen[dest] = dcvars.dc_colormap[0x00FF & dcvars.dc_source[(dcvars.dc_source_ofs + (frac >> FRACBITS))
                    & colmask]];

            /*
             * MAES: ok, so we have (from inside out):
             * 
             * frac is a fixed-point number representing a pointer inside a
             * column. It gets shifted to an integer, and AND-ed with 128
             * (this causes vertical column tiling).
             */
            dest += SCREENWIDTH;
            frac += fracstep;

        } while (count-- > 0);
    }
}
package rr.drawfuns;

import i.IDoomSystem;
import static m.fixed_t.FRACBITS;


/**
 * Adapted from Killough's Boom code. Specially super-optimized version assuming
 * that dc_source_ofs is always 0, AND that frac>>FRACBITS can be eliminated by
 * doing fracstep>>FRACBITS a-priori. Experimental/untested.
 * 
 * @author admin
 * 
 */

public final class R_DrawColumnBoomSuperOpt extends DoomColumnFunction<byte[],short[]> {

		public R_DrawColumnBoomSuperOpt(int SCREENWIDTH, int SCREENHEIGHT,
	            int[] ylookup, int[] columnofs, ColVars<byte[],short[]> dcvars,
	            short[] screen, IDoomSystem I) {
	        super(SCREENWIDTH, SCREENHEIGHT, ylookup, columnofs, dcvars, screen, I);
	    }

        public void invoke() {
			int count;
			int dest; // killough
			int frac; // killough
			final int fracstep;

			count = dcvars.dc_yh - dcvars.dc_yl + 1;

			if (count <= 0) // Zero length, column does not exceed a pixel.
				return;

			if (RANGECHECK) {
				performRangeCheck();
			}

			// Framebuffer destination address.
			// Use ylookup LUT to avoid multiply with ScreenWidth.
			// Use columnofs LUT for subwindows?

			dest = computeScreenDest();

			// Determine scaling, which is the only mapping to be done.

			fracstep = dcvars.dc_iscale>>FRACBITS;
			frac = dcvars.dc_texturemid + (dcvars.dc_yl - dcvars.centery) * fracstep;
			frac>>=FRACBITS;

			// Inner loop that does the actual texture mapping,
			// e.g. a DDA-lile scaling.
			// This is as fast as it gets. (Yeah, right!!! -- killough)
			//
			// killough 2/1/98: more performance tuning

			{
				final byte[] source = dcvars.dc_source;
				final short[] colormap = dcvars.dc_colormap;
				int heightmask = dcvars.dc_texheight - 1;
				if ((dcvars.dc_texheight & heightmask) != 0) // not a power of 2 --
														// killough
				{
					heightmask++;
					heightmask <<= FRACBITS;

					if (frac < 0)
						while ((frac += heightmask) < 0)
							;
					else
						while (frac >= heightmask)
							frac -= heightmask;

					do {
						// Re-map color indices from wall texture column
						// using a lighting/special effects LUT.

						// heightmask is the Tutti-Frutti fix -- killough

						screen[dest] = colormap[0x00FF & source[frac]];
						dest += SCREENWIDTH;
						if ((frac += fracstep) >= heightmask)
							frac -= heightmask;
					} while (--count > 0);
				} else {
					while (count >= 4) // texture height is a power of 2 --
										// killough
					{
						// System.err.println(dest);
						screen[dest] = colormap[0x00FF & source[frac & heightmask]];
						dest += SCREENWIDTH;
						frac += fracstep;
						screen[dest] = colormap[0x00FF & source[frac & heightmask]];
						dest += SCREENWIDTH;
						frac += fracstep;
						screen[dest] = colormap[0x00FF & source[frac & heightmask]];
						dest += SCREENWIDTH;
						frac += fracstep;
						screen[dest] = colormap[0x00FF & source[frac & heightmask]];
						dest += SCREENWIDTH;
						frac += fracstep;
						count -= 4;
					}

					while (count > 0) {
						screen[dest] = colormap[0x00FF & source[frac & heightmask]];
						dest += SCREENWIDTH;
						frac += fracstep;
						count--;
					}
				}
			}
		}
	}
package rr.drawfuns;

import i.IDoomSystem;

/**
 * Drawspan loop unrolled by 4. However it has low rendering quality and bad
 * distortion. However it does actually does give a small speed boost (120
 * -> 130 fps with a Mul of 3.0)
 * 
 */

public abstract class R_DrawSpanUnrolled<T,V> extends DoomSpanFunction<T,V> {


    public R_DrawSpanUnrolled(int sCREENWIDTH, int sCREENHEIGHT, int[] ylookup,
            int[] columnofs, SpanVars<T, V> dsvars, V screen, IDoomSystem I) {
        super(sCREENWIDTH, sCREENHEIGHT, ylookup, columnofs, dsvars, screen, I);
        // TODO Auto-generated constructor stub
    }

   public static final class HiColor extends R_DrawSpanUnrolled<byte[],short[]>{
       
       
       public HiColor(int sCREENWIDTH, int sCREENHEIGHT, int[] ylookup,
            int[] columnofs, SpanVars<byte[], short[]> dsvars, short[] screen,
            IDoomSystem I) {
        super(sCREENWIDTH, sCREENHEIGHT, ylookup, columnofs, dsvars, screen, I);
        // TODO Auto-generated constructor stub
    }

    public void invoke() {
           int position, step;
           final byte[] source;
           final short[] colormap;
           int dest;
           int count;
           int spot;
           int xtemp;
           int ytemp;

           position = ((dsvars.ds_xfrac << 10) & 0xffff0000)
                   | ((dsvars.ds_yfrac >> 6) & 0xffff);
           step = ((dsvars.ds_xstep << 10) & 0xffff0000) | ((dsvars.ds_ystep >> 6) & 0xffff);
           source = dsvars.ds_source;
           colormap = dsvars.ds_colormap;
           dest = ylookup[dsvars.ds_y] + columnofs[dsvars.ds_x1];
           count = dsvars.ds_x2 - dsvars.ds_x1 + 1;
           //int rolls = 0;
           while (count >= 4) {
               ytemp = position >> 4;
               ytemp = ytemp & 0xfc0;
               xtemp = position >>> 26;
               spot = xtemp | ytemp;
               position += step;
               screen[dest] = colormap[0x00FF & source[spot]];
               ytemp = position >> 4;
               ytemp = ytemp & 0xfc0;
               xtemp = position >>> 26;
               spot = xtemp | ytemp;
               position += step;
               screen[dest + 1] = colormap[0x00FF & source[spot]];
               ytemp = position >> 4;
               ytemp = ytemp & 0xfc0;
               xtemp = position >>> 26;
               spot = xtemp | ytemp;
               position += step;
               screen[dest + 2] = colormap[0x00FF & source[spot]];
               ytemp = position >> 4;
               ytemp = ytemp & 0xfc0;
               xtemp = position >>> 26;
               spot = xtemp | ytemp;
               position += step;
               screen[dest + 3] = colormap[0x00FF & source[spot]];
               count -= 4;
               dest += 4;

               // Half-assed attempt to fix precision by forced periodic
               // realignment.

               /*
                * if ((rolls++)%64==0){ position =
                * ((((rolls*4)*ds_xstep+ds_xfrac) << 10) & 0xffff0000) |
                * ((((rolls*4)*ds_ystep+ds_yfrac) >> 6) & 0xffff); }
                */

           }

           while (count > 0) {
               ytemp = position >> 4;
               ytemp = ytemp & 4032;
               xtemp = position >>> 26;
               spot = xtemp | ytemp;
               position += step;
               screen[dest++] = colormap[0x00FF & source[spot]];
               count--;
           }
       }
       
   }

   
public static final class Indexed extends R_DrawSpanUnrolled<byte[],byte[]>{
       
       
       public Indexed(int sCREENWIDTH, int sCREENHEIGHT, int[] ylookup,
            int[] columnofs, SpanVars<byte[], byte[]> dsvars, byte[] screen,
            IDoomSystem I) {
        super(sCREENWIDTH, sCREENHEIGHT, ylookup, columnofs, dsvars, screen, I);
        // TODO Auto-generated constructor stub
    }

    public void invoke() {
           int position, step;
           final byte[] source;
           final byte[] colormap;
           int dest;
           int count;
           int spot;
           int xtemp;
           int ytemp;

           position = ((dsvars.ds_xfrac << 10) & 0xffff0000)
                   | ((dsvars.ds_yfrac >> 6) & 0xffff);
           step = ((dsvars.ds_xstep << 10) & 0xffff0000) | ((dsvars.ds_ystep >> 6) & 0xffff);
           source = dsvars.ds_source;
           colormap = dsvars.ds_colormap;
           dest = ylookup[dsvars.ds_y] + columnofs[dsvars.ds_x1];
           count = dsvars.ds_x2 - dsvars.ds_x1 + 1;
           //int rolls = 0;
           while (count >= 4) {
               ytemp = position >> 4;
               ytemp = ytemp & 0xfc0;
               xtemp = position >>> 26;
               spot = xtemp | ytemp;
               position += step;
               screen[dest] = colormap[0x00FF & source[spot]];
               ytemp = position >> 4;
               ytemp = ytemp & 0xfc0;
               xtemp = position >>> 26;
               spot = xtemp | ytemp;
               position += step;
               screen[dest + 1] = colormap[0x00FF & source[spot]];
               ytemp = position >> 4;
               ytemp = ytemp & 0xfc0;
               xtemp = position >>> 26;
               spot = xtemp | ytemp;
               position += step;
               screen[dest + 2] = colormap[0x00FF & source[spot]];
               ytemp = position >> 4;
               ytemp = ytemp & 0xfc0;
               xtemp = position >>> 26;
               spot = xtemp | ytemp;
               position += step;
               screen[dest + 3] = colormap[0x00FF & source[spot]];
               count -= 4;
               dest += 4;

               // Half-assed attempt to fix precision by forced periodic
               // realignment.

               /*
                * if ((rolls++)%64==0){ position =
                * ((((rolls*4)*ds_xstep+ds_xfrac) << 10) & 0xffff0000) |
                * ((((rolls*4)*ds_ystep+ds_yfrac) >> 6) & 0xffff); }
                */

           }

           while (count > 0) {
               ytemp = position >> 4;
               ytemp = ytemp & 4032;
               xtemp = position >>> 26;
               spot = xtemp | ytemp;
               position += step;
               screen[dest++] = colormap[0x00FF & source[spot]];
               count--;
           }
       }
       
   }

public static final class TrueColor extends R_DrawSpanUnrolled<byte[],int[]>{
    
    
    public TrueColor(int sCREENWIDTH, int sCREENHEIGHT, int[] ylookup,
         int[] columnofs, SpanVars<byte[], int[]> dsvars, int[] screen,
         IDoomSystem I) {
     super(sCREENWIDTH, sCREENHEIGHT, ylookup, columnofs, dsvars, screen, I);
 }

 public void invoke() {
        int position, step;
        final byte[] source;
        final int[] colormap;
        int dest;
        int count;
        int spot;
        int xtemp;
        int ytemp;

        position = ((dsvars.ds_xfrac << 10) & 0xffff0000)
                | ((dsvars.ds_yfrac >> 6) & 0xffff);
        step = ((dsvars.ds_xstep << 10) & 0xffff0000) | ((dsvars.ds_ystep >> 6) & 0xffff);
        source = dsvars.ds_source;
        colormap = dsvars.ds_colormap;
        dest = ylookup[dsvars.ds_y] + columnofs[dsvars.ds_x1];
        count = dsvars.ds_x2 - dsvars.ds_x1 + 1;
        //int rolls = 0;
        while (count >= 4) {
            ytemp = position >> 4;
            ytemp = ytemp & 0xfc0;
            xtemp = position >>> 26;
            spot = xtemp | ytemp;
            position += step;
            screen[dest] = colormap[0x00FF & source[spot]];
            ytemp = position >> 4;
            ytemp = ytemp & 0xfc0;
            xtemp = position >>> 26;
            spot = xtemp | ytemp;
            position += step;
            screen[dest + 1] = colormap[0x00FF & source[spot]];
            ytemp = position >> 4;
            ytemp = ytemp & 0xfc0;
            xtemp = position >>> 26;
            spot = xtemp | ytemp;
            position += step;
            screen[dest + 2] = colormap[0x00FF & source[spot]];
            ytemp = position >> 4;
            ytemp = ytemp & 0xfc0;
            xtemp = position >>> 26;
            spot = xtemp | ytemp;
            position += step;
            screen[dest + 3] = colormap[0x00FF & source[spot]];
            count -= 4;
            dest += 4;

            // Half-assed attempt to fix precision by forced periodic
            // realignment.

            /*
             * if ((rolls++)%64==0){ position =
             * ((((rolls*4)*ds_xstep+ds_xfrac) << 10) & 0xffff0000) |
             * ((((rolls*4)*ds_ystep+ds_yfrac) >> 6) & 0xffff); }
             */

        }

        while (count > 0) {
            ytemp = position >> 4;
            ytemp = ytemp & 4032;
            xtemp = position >>> 26;
            spot = xtemp | ytemp;
            position += step;
            screen[dest++] = colormap[0x00FF & source[spot]];
            count--;
        }
    }
    
}
}

package rr.drawfuns;

import i.IDoomSystem;

/**
 * Low detail version. Jesus.
 */

public abstract class R_DrawFuzzColumnLow<T, V> extends
		DoomColumnFunction<T, V> {

	public R_DrawFuzzColumnLow(int SCREENWIDTH, int SCREENHEIGHT,
			int[] ylookup, int[] columnofs, ColVars<T, V> dcvars, V screen,
			IDoomSystem I, T BLURRY_MAP) {
		this(SCREENWIDTH, SCREENHEIGHT, ylookup, columnofs, dcvars, screen, I);
		this.BLURRY_MAP = BLURRY_MAP;
	}

	public R_DrawFuzzColumnLow(int SCREENWIDTH, int SCREENHEIGHT,
			int[] ylookup, int[] columnofs, ColVars<T, V> dcvars, V screen,
			IDoomSystem I) {
		super(SCREENWIDTH, SCREENHEIGHT, ylookup, columnofs, dcvars, screen, I);
		this.flags = DcFlags.LOW_DETAIL | DcFlags.FUZZY;

		FUZZOFF = SCREENWIDTH;

		// Recompute fuzz table

		fuzzoffset = new int[] { FUZZOFF, -FUZZOFF, FUZZOFF, -FUZZOFF, FUZZOFF,
				FUZZOFF, -FUZZOFF, FUZZOFF, FUZZOFF, -FUZZOFF, FUZZOFF,
				FUZZOFF, FUZZOFF, -FUZZOFF, FUZZOFF, FUZZOFF, FUZZOFF,
				-FUZZOFF, -FUZZOFF, -FUZZOFF, -FUZZOFF, FUZZOFF, -FUZZOFF,
				-FUZZOFF, FUZZOFF, FUZZOFF, FUZZOFF, FUZZOFF, -FUZZOFF,
				FUZZOFF, -FUZZOFF, FUZZOFF, FUZZOFF, -FUZZOFF, -FUZZOFF,
				FUZZOFF, FUZZOFF, -FUZZOFF, -FUZZOFF, -FUZZOFF, -FUZZOFF,
				FUZZOFF, FUZZOFF, FUZZOFF, FUZZOFF, -FUZZOFF, FUZZOFF, FUZZOFF,
				-FUZZOFF, FUZZOFF };
	}

	protected int fuzzpos;

	//
	// Spectre/Invisibility.
	//

	protected final int FUZZOFF;

	protected final int[] fuzzoffset;

	public static final class HiColor extends R_DrawFuzzColumn<byte[], short[]> {

		public HiColor(int SCREENWIDTH, int SCREENHEIGHT, int[] ylookup,
				int[] columnofs, ColVars<byte[], short[]> dcvars,
				short[] screen, IDoomSystem I) {
			super(SCREENWIDTH, SCREENHEIGHT, ylookup, columnofs, dcvars,
					screen, I);
			// TODO Auto-generated constructor stub
		}

		public void invoke() {
			int count;
			int dest, dest2;

			// Adjust borders. Low...
			if (dcvars.dc_yl == 0)
				dcvars.dc_yl = 1;

			// .. and high.
			if (dcvars.dc_yh == dcvars.viewheight - 1)
				dcvars.dc_yh = dcvars.viewheight - 2;

			count = dcvars.dc_yh - dcvars.dc_yl;

			// Zero length.
			if (count < 0)
				return;

			if (RANGECHECK) {
				performRangeCheck();
			}

			// The idea is to draw more than one pixel at a time.
			dest = blockyDest1();
			dest2 = blockyDest2();

			// Looks like an attempt at dithering,
			// using the colormap #6 (of 0-31, a bit
			// brighter than average).
			if (count > 4)
				do {
					// Lookup framebuffer, and retrieve
					// a pixel that is either one column
					// left or right of the current one.
					// Add index from colormap to index.
					screen[dest] = screen[dest + fuzzoffset[fuzzpos]];
					screen[dest2] = screen[dest];

					// Ironically, "low detail" fuzziness was not really
					// low-detail,
					// as it normally did full-precision calculations.
					// BLURRY_MAP[0x00FF & screen[dest2+ fuzzoffset[fuzzpos]]];

					// Clamp table lookup index.
					if (++fuzzpos == FUZZTABLE)
						fuzzpos = 0;

					dest += SCREENWIDTH;
					dest2 += SCREENWIDTH;

					screen[dest] = fuzzMix(screen[dest + fuzzoffset[fuzzpos]]);
					screen[dest2] = screen[dest];
					if (++fuzzpos == FUZZTABLE)
						fuzzpos = 0;
					dest += SCREENWIDTH;
					dest2 += SCREENWIDTH;

					screen[dest] = fuzzMix(screen[dest + fuzzoffset[fuzzpos]]);
					screen[dest2] = screen[dest];
					if (++fuzzpos == FUZZTABLE)
						fuzzpos = 0;
					dest += SCREENWIDTH;
					dest2 += SCREENWIDTH;

					screen[dest] = fuzzMix(screen[dest + fuzzoffset[fuzzpos]]);
					screen[dest2] = screen[dest];
					if (++fuzzpos == FUZZTABLE)
						fuzzpos = 0;
					dest += SCREENWIDTH;
					dest2 += SCREENWIDTH;
				} while ((count -= 4) > 4);

			if (count > 0)
				do {
					screen[dest] = fuzzMix(screen[dest + fuzzoffset[fuzzpos]]);
					screen[dest2] = screen[dest];

					if (++fuzzpos == FUZZTABLE)
						fuzzpos = 0;

					dest += SCREENWIDTH;
					dest2 += SCREENWIDTH;
				} while (count-- != 0);

		}

		private final short fuzzMix(short rgb) {
			// super-fast half-brite trick
			// 3DEF and >> 1: ok hue, but too dark
			// 7BDE, no shift: good compromise
			// 739C, no shift: results in too obvious tinting.

			return (short) (rgb & 0x7BDE);

		}
	}

	public static final class Indexed extends R_DrawFuzzColumn<byte[], byte[]> {

		public Indexed(int SCREENWIDTH, int SCREENHEIGHT, int[] ylookup,
				int[] columnofs, ColVars<byte[], byte[]> dcvars, byte[] screen,
				IDoomSystem I, byte[] BLURRY_MAP) {
			super(SCREENWIDTH, SCREENHEIGHT, ylookup, columnofs, dcvars,
					screen, I, BLURRY_MAP);
		}

		public void invoke() {
			int count;
			int dest, dest2;

			// Adjust borders. Low...
			if (dcvars.dc_yl == 0)
				dcvars.dc_yl = 1;

			// .. and high.
			if (dcvars.dc_yh == dcvars.viewheight - 1)
				dcvars.dc_yh = dcvars.viewheight - 2;

			count = dcvars.dc_yh - dcvars.dc_yl;

			// Zero length.
			if (count < 0)
				return;

			if (RANGECHECK) {
				performRangeCheck();
			}

			// The idea is to draw more than one pixel at a time.
			dest = blockyDest1();
			dest2 = blockyDest2();

			// Looks like an attempt at dithering,
			// using the colormap #6 (of 0-31, a bit
			// brighter than average).
			if (count > 4)
				do {
					// Lookup framebuffer, and retrieve
					// a pixel that is either one column
					// left or right of the current one.
					// Add index from colormap to index.
					screen[dest] = BLURRY_MAP[0x00FF & screen[dest
							+ fuzzoffset[fuzzpos]]];
					screen[dest2] = screen[dest];

					// Ironically, "low detail" fuzziness was not really
					// low-detail,
					// as it normally did full-precision calculations.
					// BLURRY_MAP[0x00FF & screen[dest2+ fuzzoffset[fuzzpos]]];

					// Clamp table lookup index.
					if (++fuzzpos == FUZZTABLE)
						fuzzpos = 0;

					dest += SCREENWIDTH;
					dest2 += SCREENWIDTH;

					screen[dest] = BLURRY_MAP[0x00FF & screen[dest
							+ fuzzoffset[fuzzpos]]];
					screen[dest2] = screen[dest];
					if (++fuzzpos == FUZZTABLE)
						fuzzpos = 0;
					dest += SCREENWIDTH;
					dest2 += SCREENWIDTH;

					screen[dest] = BLURRY_MAP[0x00FF & screen[dest
							+ fuzzoffset[fuzzpos]]];
					screen[dest2] = screen[dest];
					if (++fuzzpos == FUZZTABLE)
						fuzzpos = 0;
					dest += SCREENWIDTH;
					dest2 += SCREENWIDTH;

					screen[dest] = BLURRY_MAP[0x00FF & screen[dest
							+ fuzzoffset[fuzzpos]]];
					screen[dest2] = screen[dest];
					if (++fuzzpos == FUZZTABLE)
						fuzzpos = 0;
					dest += SCREENWIDTH;
					dest2 += SCREENWIDTH;
				} while ((count -= 4) > 4);

			if (count > 0)
				do {
					screen[dest] = BLURRY_MAP[0x00FF & screen[dest
							+ fuzzoffset[fuzzpos]]];
					screen[dest2] = screen[dest];

					if (++fuzzpos == FUZZTABLE)
						fuzzpos = 0;

					dest += SCREENWIDTH;
					dest2 += SCREENWIDTH;
				} while (count-- != 0);

		}
	}
	
	public static final class TrueColor extends R_DrawFuzzColumn<byte[], int[]> {

        public TrueColor(int SCREENWIDTH, int SCREENHEIGHT, int[] ylookup,
                int[] columnofs, ColVars<byte[], int[]> dcvars,
                int[] screen, IDoomSystem I) {
            super(SCREENWIDTH, SCREENHEIGHT, ylookup, columnofs, dcvars,
                    screen, I);
        }

        public void invoke() {
            int count;
            int dest, dest2;

            // Adjust borders. Low...
            if (dcvars.dc_yl == 0)
                dcvars.dc_yl = 1;

            // .. and high.
            if (dcvars.dc_yh == dcvars.viewheight - 1)
                dcvars.dc_yh = dcvars.viewheight - 2;

            count = dcvars.dc_yh - dcvars.dc_yl;

            // Zero length.
            if (count < 0)
                return;

            if (RANGECHECK) {
                performRangeCheck();
            }

            // The idea is to draw more than one pixel at a time.
            dest = blockyDest1();
            dest2 = blockyDest2();

            // Looks like an attempt at dithering,
            // using the colormap #6 (of 0-31, a bit
            // brighter than average).
            if (count > 4)
                do {
                    // Lookup framebuffer, and retrieve
                    // a pixel that is either one column
                    // left or right of the current one.
                    // Add index from colormap to index.
                    screen[dest] = screen[dest + fuzzoffset[fuzzpos]];
                    screen[dest2] = screen[dest];

                    // Ironically, "low detail" fuzziness was not really
                    // low-detail,
                    // as it normally did full-precision calculations.
                    // BLURRY_MAP[0x00FF & screen[dest2+ fuzzoffset[fuzzpos]]];

                    // Clamp table lookup index.
                    if (++fuzzpos == FUZZTABLE)
                        fuzzpos = 0;

                    dest += SCREENWIDTH;
                    dest2 += SCREENWIDTH;

                    screen[dest] = fuzzMix(screen[dest + fuzzoffset[fuzzpos]]);
                    screen[dest2] = screen[dest];
                    if (++fuzzpos == FUZZTABLE)
                        fuzzpos = 0;
                    dest += SCREENWIDTH;
                    dest2 += SCREENWIDTH;

                    screen[dest] = fuzzMix(screen[dest + fuzzoffset[fuzzpos]]);
                    screen[dest2] = screen[dest];
                    if (++fuzzpos == FUZZTABLE)
                        fuzzpos = 0;
                    dest += SCREENWIDTH;
                    dest2 += SCREENWIDTH;

                    screen[dest] = fuzzMix(screen[dest + fuzzoffset[fuzzpos]]);
                    screen[dest2] = screen[dest];
                    if (++fuzzpos == FUZZTABLE)
                        fuzzpos = 0;
                    dest += SCREENWIDTH;
                    dest2 += SCREENWIDTH;
                } while ((count -= 4) > 4);

            if (count > 0)
                do {
                    screen[dest] = fuzzMix(screen[dest + fuzzoffset[fuzzpos]]);
                    screen[dest2] = screen[dest];

                    if (++fuzzpos == FUZZTABLE)
                        fuzzpos = 0;

                    dest += SCREENWIDTH;
                    dest2 += SCREENWIDTH;
                } while (count-- != 0);

        }

        private final int fuzzMix(int rgb) {
            // super-fast half-brite trick
            // 3DEF and >> 1: ok hue, but too dark
            // FF7C7C7C, no shift: good compromise
            // FF707070, no shift: results in too obvious tinting.

            return (rgb>>1)&0xFF7F7F7F;

        }
    }
}
package rr.drawfuns;

/** Either draws a column or a span
 * 
 * @author velktron
 *
 */

public interface ColumnFunction<T,V> {
    public void invoke();
    
	public void invoke(ColVars<T,V> dcvars);
	
	/** A set of flags that help identifying the type of function */
	public int getFlags();
}
package rr.drawfuns;

import i.IDoomSystem;
import static m.fixed_t.FRACBITS;

/**
 * Adapted from Killough's Boom code. Specially optimized version assuming that
 * dc_source_ofs is always 0. This eliminates it from expressions.
 * 
 * @author admin
 */

public abstract class R_DrawColumnBoomOpt<T, V>
        extends DoomColumnFunction<T, V> {

    public R_DrawColumnBoomOpt(int sCREENWIDTH, int sCREENHEIGHT,
            int[] ylookup, int[] columnofs, ColVars<T, V> dcvars, V screen,
            IDoomSystem I) {
        super(sCREENWIDTH, sCREENHEIGHT, ylookup, columnofs, dcvars, screen, I);
    }

    public static final class HiColor
            extends R_DrawColumnBoomOpt<byte[], short[]> {
        public HiColor(int sCREENWIDTH, int sCREENHEIGHT, int[] ylookup,
                int[] columnofs, ColVars<byte[], short[]> dcvars,
                short[] screen, IDoomSystem I) {
            super(sCREENWIDTH, sCREENHEIGHT, ylookup, columnofs, dcvars,
                    screen, I);
        }

        public void invoke() {
            int count;
            int dest; // killough
            int frac; // killough
            final int fracstep;

            count = dcvars.dc_yh - dcvars.dc_yl + 1;

            if (count <= 0) // Zero length, column does not exceed a pixel.
                return;

            if (RANGECHECK) {
                performRangeCheck();
            }

            // Framebuffer destination address.
            // Use ylookup LUT to avoid multiply with ScreenWidth.
            // Use columnofs LUT for subwindows?

            dest = computeScreenDest();

            // Determine scaling, which is the only mapping to be done.

            fracstep = dcvars.dc_iscale;
            frac =
                dcvars.dc_texturemid + (dcvars.dc_yl - dcvars.centery)
                        * fracstep;

            // Inner loop that does the actual texture mapping,
            // e.g. a DDA-lile scaling.
            // This is as fast as it gets. (Yeah, right!!! -- killough)
            //
            // killough 2/1/98: more performance tuning

            {
                final byte[] source = dcvars.dc_source;
                final short[] colormap = dcvars.dc_colormap;
                int heightmask = dcvars.dc_texheight - 1;
                if ((dcvars.dc_texheight & heightmask) != 0) // not a power of 2
                                                             // --
                // killough
                {
                    heightmask++;
                    heightmask <<= FRACBITS;

                    if (frac < 0)
                        while ((frac += heightmask) < 0)
                            ;
                    else
                        while (frac >= heightmask)
                            frac -= heightmask;

                    do {
                        // Re-map color indices from wall texture column
                        // using a lighting/special effects LUT.

                        // heightmask is the Tutti-Frutti fix -- killough

                        screen[dest] =
                            colormap[0x00FF & source[((frac >> FRACBITS))]];
                        dest += SCREENWIDTH;
                        if ((frac += fracstep) >= heightmask)
                            frac -= heightmask;
                    } while (--count > 0);
                } else {
                    while (count >= 4) // texture height is a power of 2 --
                                       // killough
                    {
                        // System.err.println(dest);
                        screen[dest] =
                            colormap[0x00FF & source[((frac >> FRACBITS) & heightmask)]];
                        dest += SCREENWIDTH;
                        frac += fracstep;
                        screen[dest] =
                            colormap[0x00FF & source[((frac >> FRACBITS) & heightmask)]];
                        dest += SCREENWIDTH;
                        frac += fracstep;
                        screen[dest] =
                            colormap[0x00FF & source[((frac >> FRACBITS) & heightmask)]];
                        dest += SCREENWIDTH;
                        frac += fracstep;
                        screen[dest] =
                            colormap[0x00FF & source[((frac >> FRACBITS) & heightmask)]];
                        dest += SCREENWIDTH;
                        frac += fracstep;
                        count -= 4;
                    }

                    while (count > 0) {
                        screen[dest] =
                            colormap[0x00FF & source[((frac >> FRACBITS) & heightmask)]];
                        dest += SCREENWIDTH;
                        frac += fracstep;
                        count--;
                    }
                }
            }
        }
    }

    public static final class Indexed
            extends R_DrawColumnBoomOpt<byte[], byte[]> {
        public Indexed(int sCREENWIDTH, int sCREENHEIGHT, int[] ylookup,
                int[] columnofs, ColVars<byte[], byte[]> dcvars, byte[] screen,
                IDoomSystem I) {
            super(sCREENWIDTH, sCREENHEIGHT, ylookup, columnofs, dcvars,
                    screen, I);
        }

        public void invoke() {
            int count;
            int dest; // killough
            int frac; // killough
            final int fracstep;

            count = dcvars.dc_yh - dcvars.dc_yl + 1;

            if (count <= 0) // Zero length, column does not exceed a pixel.
                return;

            if (RANGECHECK) {
                performRangeCheck();
            }

            // Framebuffer destination address.
            // Use ylookup LUT to avoid multiply with ScreenWidth.
            // Use columnofs LUT for subwindows?

            dest = computeScreenDest();

            // Determine scaling, which is the only mapping to be done.

            fracstep = dcvars.dc_iscale;
            frac =
                dcvars.dc_texturemid + (dcvars.dc_yl - dcvars.centery)
                        * fracstep;

            // Inner loop that does the actual texture mapping,
            // e.g. a DDA-lile scaling.
            // This is as fast as it gets. (Yeah, right!!! -- killough)
            //
            // killough 2/1/98: more performance tuning

            {
                final byte[] source = dcvars.dc_source;
                final byte[] colormap = dcvars.dc_colormap;
                int heightmask = dcvars.dc_texheight - 1;
                if ((dcvars.dc_texheight & heightmask) != 0) // not a power of 2
                                                             // --
                // killough
                {
                    heightmask++;
                    heightmask <<= FRACBITS;

                    if (frac < 0)
                        while ((frac += heightmask) < 0)
                            ;
                    else
                        while (frac >= heightmask)
                            frac -= heightmask;

                    do {
                        // Re-map color indices from wall texture column
                        // using a lighting/special effects LUT.

                        // heightmask is the Tutti-Frutti fix -- killough

                        screen[dest] =
                            colormap[0x00FF & source[((frac >> FRACBITS))]];
                        dest += SCREENWIDTH;
                        if ((frac += fracstep) >= heightmask)
                            frac -= heightmask;
                    } while (--count > 0);
                } else {
                    while (count >= 4) // texture height is a power of 2 --
                                       // killough
                    {
                        // System.err.println(dest);
                        screen[dest] =
                            colormap[0x00FF & source[((frac >> FRACBITS) & heightmask)]];
                        dest += SCREENWIDTH;
                        frac += fracstep;
                        screen[dest] =
                            colormap[0x00FF & source[((frac >> FRACBITS) & heightmask)]];
                        dest += SCREENWIDTH;
                        frac += fracstep;
                        screen[dest] =
                            colormap[0x00FF & source[((frac >> FRACBITS) & heightmask)]];
                        dest += SCREENWIDTH;
                        frac += fracstep;
                        screen[dest] =
                            colormap[0x00FF & source[((frac >> FRACBITS) & heightmask)]];
                        dest += SCREENWIDTH;
                        frac += fracstep;
                        count -= 4;
                    }

                    while (count > 0) {
                        screen[dest] =
                            colormap[0x00FF & source[((frac >> FRACBITS) & heightmask)]];
                        dest += SCREENWIDTH;
                        frac += fracstep;
                        count--;
                    }
                }
            }
        }
    }

    public static final class TrueColor
            extends R_DrawColumnBoomOpt<byte[], int[]> {
        public TrueColor(int sCREENWIDTH, int sCREENHEIGHT, int[] ylookup,
                int[] columnofs, ColVars<byte[], int[]> dcvars, int[] screen,
                IDoomSystem I) {
            super(sCREENWIDTH, sCREENHEIGHT, ylookup, columnofs, dcvars,
                    screen, I);
        }

        public void invoke() {
            int count;
            int dest; // killough
            int frac; // killough
            final int fracstep;

            count = dcvars.dc_yh - dcvars.dc_yl + 1;

            if (count <= 0) // Zero length, column does not exceed a pixel.
                return;

            if (RANGECHECK) {
                performRangeCheck();
            }

            // Framebuffer destination address.
            // Use ylookup LUT to avoid multiply with ScreenWidth.
            // Use columnofs LUT for subwindows?

            dest = computeScreenDest();

            // Determine scaling, which is the only mapping to be done.

            fracstep = dcvars.dc_iscale;
            frac =
                dcvars.dc_texturemid + (dcvars.dc_yl - dcvars.centery)
                        * fracstep;

            // Inner loop that does the actual texture mapping,
            // e.g. a DDA-lile scaling.
            // This is as fast as it gets. (Yeah, right!!! -- killough)
            //
            // killough 2/1/98: more performance tuning

            {
                final byte[] source = dcvars.dc_source;
                final int[] colormap = dcvars.dc_colormap;
                int heightmask = dcvars.dc_texheight - 1;
                if ((dcvars.dc_texheight & heightmask) != 0) // not a power of 2
                                                             // --
                // killough
                {
                    heightmask++;
                    heightmask <<= FRACBITS;

                    if (frac < 0)
                        while ((frac += heightmask) < 0)
                            ;
                    else
                        while (frac >= heightmask)
                            frac -= heightmask;

                    do {
                        // Re-map color indices from wall texture column
                        // using a lighting/special effects LUT.

                        // heightmask is the Tutti-Frutti fix -- killough

                        screen[dest] =
                            colormap[0x00FF & source[((frac >> FRACBITS))]];
                        dest += SCREENWIDTH;
                        if ((frac += fracstep) >= heightmask)
                            frac -= heightmask;
                    } while (--count > 0);
                } else {
                    while (count >= 4) // texture height is a power of 2 --
                                       // killough
                    {
                        // System.err.println(dest);
                        screen[dest] =
                            colormap[0x00FF & source[((frac >> FRACBITS) & heightmask)]];
                        dest += SCREENWIDTH;
                        frac += fracstep;
                        screen[dest] =
                            colormap[0x00FF & source[((frac >> FRACBITS) & heightmask)]];
                        dest += SCREENWIDTH;
                        frac += fracstep;
                        screen[dest] =
                            colormap[0x00FF & source[((frac >> FRACBITS) & heightmask)]];
                        dest += SCREENWIDTH;
                        frac += fracstep;
                        screen[dest] =
                            colormap[0x00FF & source[((frac >> FRACBITS) & heightmask)]];
                        dest += SCREENWIDTH;
                        frac += fracstep;
                        count -= 4;
                    }

                    while (count > 0) {
                        screen[dest] =
                            colormap[0x00FF & source[((frac >> FRACBITS) & heightmask)]];
                        dest += SCREENWIDTH;
                        frac += fracstep;
                        count--;
                    }
                }
            }
        }
    }

}
package rr.drawfuns;

import i.IDoomSystem;
import static m.fixed_t.FRACBITS;

/**
 * Adapted from Killough's Boom code. Low-detail variation, with DC SOURCE 
 * optimization.
 * 
 * @author admin
 * 
 */

public abstract class R_DrawColumnBoomOptLow<T,V> extends DoomColumnFunction<T,V> {

		public R_DrawColumnBoomOptLow(int SCREENWIDTH, int SCREENHEIGHT,
	            int[] ylookup, int[] columnofs, ColVars<T,V> dcvars,
	            V screen, IDoomSystem I) {
	        super(SCREENWIDTH, SCREENHEIGHT, ylookup, columnofs, dcvars, screen, I);
	        this.flags=DcFlags.LOW_DETAIL;
	    }

		
		public static final class HiColor extends R_DrawColumnBoomOptLow<byte[],short[]>{
		
		public HiColor(int SCREENWIDTH, int SCREENHEIGHT, int[] ylookup,
                    int[] columnofs, ColVars<byte[], short[]> dcvars,
                    short[] screen, IDoomSystem I) {
                super(SCREENWIDTH, SCREENHEIGHT, ylookup, columnofs, dcvars, screen, I);
            }

        public void invoke() {
            int count;
            int dest,dest2; // killough
            int frac; // killough
            final int fracstep;

            count = dcvars.dc_yh - dcvars.dc_yl + 1;
            // Assumed to be always zero for optimized draws.
            //dc_source_ofs=dcvars.dc_source_ofs;
            
            if (count <= 0) // Zero length, column does not exceed a pixel.
                return;

            if (RANGECHECK) {
                performRangeCheck();
            }

            // Framebuffer destination address.
            // Use ylookup LUT to avoid multiply with ScreenWidth.
            // Use columnofs LUT for subwindows?
            dest = blockyDest1();
            dest2 = blockyDest2();
            
            // Determine scaling, which is the only mapping to be done.

            fracstep = dcvars.dc_iscale;
            frac = dcvars.dc_texturemid + (dcvars.dc_yl - dcvars.centery) * fracstep;

            // Inner loop that does the actual texture mapping,
            // e.g. a DDA-lile scaling.
            // This is as fast as it gets. (Yeah, right!!! -- killough)
            //
            // killough 2/1/98: more performance tuning

            {
                final byte[] source = dcvars.dc_source;
                final short[] colormap = dcvars.dc_colormap;
                int heightmask = dcvars.dc_texheight - 1;
                if ((dcvars.dc_texheight & heightmask) != 0) // not a power of 2 --
                                                        // killough
                {
                    heightmask++;
                    heightmask <<= FRACBITS;

                    if (frac < 0)
                        while ((frac += heightmask) < 0)
                            ;
                    else
                        while (frac >= heightmask)
                            frac -= heightmask;

                    do {
                        // Re-map color indices from wall texture column
                        // using a lighting/special effects LUT.

                        // heightmask is the Tutti-Frutti fix -- killough

                        screen[dest] = screen[dest2]=colormap[0x00FF & source[((frac >> FRACBITS))]];
                        dest += SCREENWIDTH;
                        dest2 += SCREENWIDTH;
                        if ((frac += fracstep) >= heightmask)
                            frac -= heightmask;
                    } while (--count > 0);
                } else {
                    while (count >= 4) // texture height is a power of 2 --
                                        // killough
                    {

                        screen[dest] = screen[dest2]= colormap[0x00FF & source[((frac >> FRACBITS) & heightmask)]];
                        dest += SCREENWIDTH;
                        dest2 += SCREENWIDTH;
                        frac += fracstep;
                        screen[dest] = screen[dest2]= colormap[0x00FF & source[((frac >> FRACBITS) & heightmask)]];
                        dest += SCREENWIDTH;
                        dest2 += SCREENWIDTH;
                        frac += fracstep;
                        screen[dest] = screen[dest2]= colormap[0x00FF & source[((frac >> FRACBITS) & heightmask)]];
                        dest += SCREENWIDTH;
                        dest2 += SCREENWIDTH;
                        frac += fracstep;
                        screen[dest] = screen[dest2]= colormap[0x00FF & source[((frac >> FRACBITS) & heightmask)]];
                        dest += SCREENWIDTH;
                        dest2 += SCREENWIDTH;
                        frac += fracstep;
                        count -= 4;
                    }

                    while (count > 0) {
                        screen[dest] = screen[dest2]= colormap[0x00FF & source[((frac >> FRACBITS) & heightmask)]];
                        dest += SCREENWIDTH;
                        dest2 += SCREENWIDTH;
                        frac += fracstep;
                        count--;
                    }
                }
            }
        }
		}
		
		public static final class Indexed extends R_DrawColumnBoomOptLow<byte[],byte[]>{
	        
	        public Indexed(int SCREENWIDTH, int SCREENHEIGHT, int[] ylookup,
	                    int[] columnofs, ColVars<byte[], byte[]> dcvars,
	                    byte[] screen, IDoomSystem I) {
	                super(SCREENWIDTH, SCREENHEIGHT, ylookup, columnofs, dcvars, screen, I);
	            }

	        public void invoke() {
	            int count;
	            int dest,dest2; // killough
	            int frac; // killough
	            final int fracstep;

	            count = dcvars.dc_yh - dcvars.dc_yl + 1;
	            // Assumed to be always zero for optimized draws.
	            //dc_source_ofs=dcvars.dc_source_ofs;
	            
	            if (count <= 0) // Zero length, column does not exceed a pixel.
	                return;

	            if (RANGECHECK) {
	                performRangeCheck();
	            }

	            // Framebuffer destination address.
	            // Use ylookup LUT to avoid multiply with ScreenWidth.
	            // Use columnofs LUT for subwindows?
	            dest = blockyDest1();
	            dest2 = blockyDest2();
	            
	            // Determine scaling, which is the only mapping to be done.

	            fracstep = dcvars.dc_iscale;
	            frac = dcvars.dc_texturemid + (dcvars.dc_yl - dcvars.centery) * fracstep;

	            // Inner loop that does the actual texture mapping,
	            // e.g. a DDA-lile scaling.
	            // This is as fast as it gets. (Yeah, right!!! -- killough)
	            //
	            // killough 2/1/98: more performance tuning

	            {
	                final byte[] source = dcvars.dc_source;
	                final byte[] colormap = dcvars.dc_colormap;
	                int heightmask = dcvars.dc_texheight - 1;
	                if ((dcvars.dc_texheight & heightmask) != 0) // not a power of 2 --
	                                                        // killough
	                {
	                    heightmask++;
	                    heightmask <<= FRACBITS;

	                    if (frac < 0)
	                        while ((frac += heightmask) < 0)
	                            ;
	                    else
	                        while (frac >= heightmask)
	                            frac -= heightmask;

	                    do {
	                        // Re-map color indices from wall texture column
	                        // using a lighting/special effects LUT.

	                        // heightmask is the Tutti-Frutti fix -- killough

	                        screen[dest] = screen[dest2]=colormap[0x00FF & source[((frac >> FRACBITS))]];
	                        dest += SCREENWIDTH;
	                        dest2 += SCREENWIDTH;
	                        if ((frac += fracstep) >= heightmask)
	                            frac -= heightmask;
	                    } while (--count > 0);
	                } else {
	                    while (count >= 4) // texture height is a power of 2 --
	                                        // killough
	                    {

	                        screen[dest] = screen[dest2]= colormap[0x00FF & source[((frac >> FRACBITS) & heightmask)]];
	                        dest += SCREENWIDTH;
	                        dest2 += SCREENWIDTH;
	                        frac += fracstep;
	                        screen[dest] = screen[dest2]= colormap[0x00FF & source[((frac >> FRACBITS) & heightmask)]];
	                        dest += SCREENWIDTH;
	                        dest2 += SCREENWIDTH;
	                        frac += fracstep;
	                        screen[dest] = screen[dest2]= colormap[0x00FF & source[((frac >> FRACBITS) & heightmask)]];
	                        dest += SCREENWIDTH;
	                        dest2 += SCREENWIDTH;
	                        frac += fracstep;
	                        screen[dest] = screen[dest2]= colormap[0x00FF & source[((frac >> FRACBITS) & heightmask)]];
	                        dest += SCREENWIDTH;
	                        dest2 += SCREENWIDTH;
	                        frac += fracstep;
	                        count -= 4;
	                    }

	                    while (count > 0) {
	                        screen[dest] = screen[dest2]= colormap[0x00FF & source[((frac >> FRACBITS) & heightmask)]];
	                        dest += SCREENWIDTH;
	                        dest2 += SCREENWIDTH;
	                        frac += fracstep;
	                        count--;
	                    }
	                }
	            }
	        }
	        }
		
		public static final class TrueColor extends R_DrawColumnBoomOptLow<byte[],int[]>{
	        
	        public TrueColor(int SCREENWIDTH, int SCREENHEIGHT, int[] ylookup,
	                    int[] columnofs, ColVars<byte[], int[]> dcvars,
	                    int[] screen, IDoomSystem I) {
	                super(SCREENWIDTH, SCREENHEIGHT, ylookup, columnofs, dcvars, screen, I);
	            }

	        public void invoke() {
	            int count;
	            int dest,dest2; // killough
	            int frac; // killough
	            final int fracstep;

	            count = dcvars.dc_yh - dcvars.dc_yl + 1;
	            // Assumed to be always zero for optimized draws.
	            //dc_source_ofs=dcvars.dc_source_ofs;
	            
	            if (count <= 0) // Zero length, column does not exceed a pixel.
	                return;

	            if (RANGECHECK) {
	                performRangeCheck();
	            }

	            // Framebuffer destination address.
	            // Use ylookup LUT to avoid multiply with ScreenWidth.
	            // Use columnofs LUT for subwindows?
	            dest = blockyDest1();
	            dest2 = blockyDest2();
	            
	            // Determine scaling, which is the only mapping to be done.

	            fracstep = dcvars.dc_iscale;
	            frac = dcvars.dc_texturemid + (dcvars.dc_yl - dcvars.centery) * fracstep;

	            // Inner loop that does the actual texture mapping,
	            // e.g. a DDA-lile scaling.
	            // This is as fast as it gets. (Yeah, right!!! -- killough)
	            //
	            // killough 2/1/98: more performance tuning

	            {
	                final byte[] source = dcvars.dc_source;
	                final int[] colormap = dcvars.dc_colormap;
	                int heightmask = dcvars.dc_texheight - 1;
	                if ((dcvars.dc_texheight & heightmask) != 0) // not a power of 2 --
	                                                        // killough
	                {
	                    heightmask++;
	                    heightmask <<= FRACBITS;

	                    if (frac < 0)
	                        while ((frac += heightmask) < 0)
	                            ;
	                    else
	                        while (frac >= heightmask)
	                            frac -= heightmask;

	                    do {
	                        // Re-map color indices from wall texture column
	                        // using a lighting/special effects LUT.

	                        // heightmask is the Tutti-Frutti fix -- killough

	                        screen[dest] = screen[dest2]=colormap[0x00FF & source[((frac >> FRACBITS))]];
	                        dest += SCREENWIDTH;
	                        dest2 += SCREENWIDTH;
	                        if ((frac += fracstep) >= heightmask)
	                            frac -= heightmask;
	                    } while (--count > 0);
	                } else {
	                    while (count >= 4) // texture height is a power of 2 --
	                                        // killough
	                    {

	                        screen[dest] = screen[dest2]= colormap[0x00FF & source[((frac >> FRACBITS) & heightmask)]];
	                        dest += SCREENWIDTH;
	                        dest2 += SCREENWIDTH;
	                        frac += fracstep;
	                        screen[dest] = screen[dest2]= colormap[0x00FF & source[((frac >> FRACBITS) & heightmask)]];
	                        dest += SCREENWIDTH;
	                        dest2 += SCREENWIDTH;
	                        frac += fracstep;
	                        screen[dest] = screen[dest2]= colormap[0x00FF & source[((frac >> FRACBITS) & heightmask)]];
	                        dest += SCREENWIDTH;
	                        dest2 += SCREENWIDTH;
	                        frac += fracstep;
	                        screen[dest] = screen[dest2]= colormap[0x00FF & source[((frac >> FRACBITS) & heightmask)]];
	                        dest += SCREENWIDTH;
	                        dest2 += SCREENWIDTH;
	                        frac += fracstep;
	                        count -= 4;
	                    }

	                    while (count > 0) {
	                        screen[dest] = screen[dest2]= colormap[0x00FF & source[((frac >> FRACBITS) & heightmask)]];
	                        dest += SCREENWIDTH;
	                        dest2 += SCREENWIDTH;
	                        frac += fracstep;
	                        count--;
	                    }
	                }
	            }
	        }
	        }
		
		
	}
package rr.drawfuns;

import i.IDoomSystem;
import static m.fixed_t.FRACBITS;

public abstract class R_DrawTranslatedColumn<T, V>
        extends DoomColumnFunction<T, V> {

    public R_DrawTranslatedColumn(int SCREENWIDTH, int SCREENHEIGHT,
            int[] ylookup, int[] columnofs, ColVars<T, V> dcvars, V screen,
            IDoomSystem I) {
        super(SCREENWIDTH, SCREENHEIGHT, ylookup, columnofs, dcvars, screen, I);
        this.flags = DcFlags.TRANSLATED;
    }

    public static final class HiColor
            extends R_DrawTranslatedColumn<byte[], short[]> {
        public HiColor(int SCREENWIDTH, int SCREENHEIGHT, int[] ylookup,
                int[] columnofs, ColVars<byte[], short[]> dcvars,
                short[] screen, IDoomSystem I) {
            super(SCREENWIDTH, SCREENHEIGHT, ylookup, columnofs, dcvars,
                    screen, I);
        }

        public void invoke() {
            int count;
            // MAES: you know the deal by now...
            int dest;
            int frac;
            final int fracstep;
            final int dc_source_ofs = dcvars.dc_source_ofs;
            final byte[] dc_source = dcvars.dc_source;
            final short[] dc_colormap = dcvars.dc_colormap;
            final byte[] dc_translation = dcvars.dc_translation;

            count = dcvars.dc_yh - dcvars.dc_yl;
            if (count < 0)
                return;

            if (RANGECHECK) {
                super.performRangeCheck();
            }

            // WATCOM VGA specific.
            /*
             * Keep for fixing. if (detailshift) { if (dc_x & 1) outp
             * (SC_INDEX+1,12); else outp (SC_INDEX+1,3); dest = destview +
             * dc_yl*80 + (dc_x>>1); } else { outp (SC_INDEX+1,1<<(dc_x&3));
             * dest = destview + dc_yl*80 + (dc_x>>2); }
             */

            // FIXME. As above.
            dest = computeScreenDest();

            // Looks familiar.
            fracstep = dcvars.dc_iscale;
            frac =
                dcvars.dc_texturemid + (dcvars.dc_yl - dcvars.centery)
                        * fracstep;

            // Here we do an additional index re-mapping.
            // Maes: Unroll by 4
            if (count >= 4)
                do {
                    // Translation tables are used
                    // to map certain colorramps to other ones,
                    // used with PLAY sprites.
                    // Thus the "green" ramp of the player 0 sprite
                    // is mapped to gray, red, black/indigo.
                    screen[dest] =
                        dc_colormap[0x00FF & dc_translation[0xFF & dc_source[dc_source_ofs
                                + (frac >> FRACBITS)]]];
                    dest += SCREENWIDTH;
                    frac += fracstep;

                    screen[dest] =
                        dc_colormap[0x00FF & dc_translation[0xFF & dc_source[dc_source_ofs
                                + (frac >> FRACBITS)]]];
                    dest += SCREENWIDTH;
                    frac += fracstep;

                    screen[dest] =
                        dc_colormap[0x00FF & dc_translation[0xFF & dc_source[dc_source_ofs
                                + (frac >> FRACBITS)]]];
                    dest += SCREENWIDTH;
                    frac += fracstep;

                    screen[dest] =
                        dc_colormap[0x00FF & dc_translation[0xFF & dc_source[dc_source_ofs
                                + (frac >> FRACBITS)]]];
                    dest += SCREENWIDTH;
                    frac += fracstep;

                } while ((count -= 4) > 4);

            if (count > 0)
                do {
                    // Translation tables are used
                    // to map certain colorramps to other ones,
                    // used with PLAY sprites.
                    // Thus the "green" ramp of the player 0 sprite
                    // is mapped to gray, red, black/indigo.
                    screen[dest] =
                        dc_colormap[0x00FF & dc_translation[0xFF & dc_source[dc_source_ofs
                                + (frac >> FRACBITS)]]];
                    dest += SCREENWIDTH;

                    frac += fracstep;
                } while (count-- != 0);
        }
    }

    public static final class Indexed
            extends R_DrawTranslatedColumn<byte[], byte[]> {

        public Indexed(int SCREENWIDTH, int SCREENHEIGHT, int[] ylookup,
                int[] columnofs, ColVars<byte[], byte[]> dcvars, byte[] screen,
                IDoomSystem I) {
            super(SCREENWIDTH, SCREENHEIGHT, ylookup, columnofs, dcvars,
                    screen, I);
        }

        public void invoke() {
            int count;
            // MAES: you know the deal by now...
            int dest;
            int frac;
            final int fracstep;
            final int dc_source_ofs = dcvars.dc_source_ofs;
            final byte[] dc_source = dcvars.dc_source;
            final byte[] dc_colormap = dcvars.dc_colormap;
            final byte[] dc_translation = dcvars.dc_translation;

            count = dcvars.dc_yh - dcvars.dc_yl;
            if (count < 0)
                return;

            if (RANGECHECK) {
                super.performRangeCheck();
            }

            // WATCOM VGA specific.
            /*
             * Keep for fixing. if (detailshift) { if (dc_x & 1) outp
             * (SC_INDEX+1,12); else outp (SC_INDEX+1,3); dest = destview +
             * dc_yl*80 + (dc_x>>1); } else { outp (SC_INDEX+1,1<<(dc_x&3));
             * dest = destview + dc_yl*80 + (dc_x>>2); }
             */

            // FIXME. As above.
            dest = computeScreenDest();

            // Looks familiar.
            fracstep = dcvars.dc_iscale;
            frac =
                dcvars.dc_texturemid + (dcvars.dc_yl - dcvars.centery)
                        * fracstep;

            // Here we do an additional index re-mapping.
            // Maes: Unroll by 4
            if (count >= 4)
                do {
                    // Translation tables are used
                    // to map certain colorramps to other ones,
                    // used with PLAY sprites.
                    // Thus the "green" ramp of the player 0 sprite
                    // is mapped to gray, red, black/indigo.
                    screen[dest] =
                        dc_colormap[0x00FF & dc_translation[0xFF & dc_source[dc_source_ofs
                                + (frac >> FRACBITS)]]];
                    dest += SCREENWIDTH;
                    frac += fracstep;

                    screen[dest] =
                        dc_colormap[0x00FF & dc_translation[0xFF & dc_source[dc_source_ofs
                                + (frac >> FRACBITS)]]];
                    dest += SCREENWIDTH;
                    frac += fracstep;

                    screen[dest] =
                        dc_colormap[0x00FF & dc_translation[0xFF & dc_source[dc_source_ofs
                                + (frac >> FRACBITS)]]];
                    dest += SCREENWIDTH;
                    frac += fracstep;

                    screen[dest] =
                        dc_colormap[0x00FF & dc_translation[0xFF & dc_source[dc_source_ofs
                                + (frac >> FRACBITS)]]];
                    dest += SCREENWIDTH;
                    frac += fracstep;

                } while ((count -= 4) > 4);

            if (count > 0)
                do {
                    // Translation tables are used
                    // to map certain colorramps to other ones,
                    // used with PLAY sprites.
                    // Thus the "green" ramp of the player 0 sprite
                    // is mapped to gray, red, black/indigo.
                    screen[dest] =
                        dc_colormap[0x00FF & dc_translation[0xFF & dc_source[dc_source_ofs
                                + (frac >> FRACBITS)]]];
                    dest += SCREENWIDTH;

                    frac += fracstep;
                } while (count-- != 0);
        }
    }

    public static final class TrueColor
            extends R_DrawTranslatedColumn<byte[], int[]> {
        public TrueColor(int SCREENWIDTH, int SCREENHEIGHT, int[] ylookup,
                int[] columnofs, ColVars<byte[], int[]> dcvars, int[] screen,
                IDoomSystem I) {
            super(SCREENWIDTH, SCREENHEIGHT, ylookup, columnofs, dcvars,
                    screen, I);
        }

        public void invoke() {
            int count;
            // MAES: you know the deal by now...
            int dest;
            int frac;
            final int fracstep;
            final int dc_source_ofs = dcvars.dc_source_ofs;
            final byte[] dc_source = dcvars.dc_source;
            final int[] dc_colormap = dcvars.dc_colormap;
            final byte[] dc_translation = dcvars.dc_translation;

            count = dcvars.dc_yh - dcvars.dc_yl;
            if (count < 0)
                return;

            if (RANGECHECK) {
                super.performRangeCheck();
            }

            // WATCOM VGA specific.
            /*
             * Keep for fixing. if (detailshift) { if (dc_x & 1) outp
             * (SC_INDEX+1,12); else outp (SC_INDEX+1,3); dest = destview +
             * dc_yl*80 + (dc_x>>1); } else { outp (SC_INDEX+1,1<<(dc_x&3));
             * dest = destview + dc_yl*80 + (dc_x>>2); }
             */

            // FIXME. As above.
            dest = computeScreenDest();

            // Looks familiar.
            fracstep = dcvars.dc_iscale;
            frac =
                dcvars.dc_texturemid + (dcvars.dc_yl - dcvars.centery)
                        * fracstep;

            // Here we do an additional index re-mapping.
            // Maes: Unroll by 4
            if (count >= 4)
                do {
                    // Translation tables are used
                    // to map certain colorramps to other ones,
                    // used with PLAY sprites.
                    // Thus the "green" ramp of the player 0 sprite
                    // is mapped to gray, red, black/indigo.
                    screen[dest] =
                        dc_colormap[0x00FF & dc_translation[0xFF & dc_source[dc_source_ofs
                                + (frac >> FRACBITS)]]];
                    dest += SCREENWIDTH;
                    frac += fracstep;

                    screen[dest] =
                        dc_colormap[0x00FF & dc_translation[0xFF & dc_source[dc_source_ofs
                                + (frac >> FRACBITS)]]];
                    dest += SCREENWIDTH;
                    frac += fracstep;

                    screen[dest] =
                        dc_colormap[0x00FF & dc_translation[0xFF & dc_source[dc_source_ofs
                                + (frac >> FRACBITS)]]];
                    dest += SCREENWIDTH;
                    frac += fracstep;

                    screen[dest] =
                        dc_colormap[0x00FF & dc_translation[0xFF & dc_source[dc_source_ofs
                                + (frac >> FRACBITS)]]];
                    dest += SCREENWIDTH;
                    frac += fracstep;

                } while ((count -= 4) > 4);

            if (count > 0)
                do {
                    // Translation tables are used
                    // to map certain colorramps to other ones,
                    // used with PLAY sprites.
                    // Thus the "green" ramp of the player 0 sprite
                    // is mapped to gray, red, black/indigo.
                    screen[dest] =
                        dc_colormap[0x00FF & dc_translation[0xFF & dc_source[dc_source_ofs
                                + (frac >> FRACBITS)]]];
                    dest += SCREENWIDTH;

                    frac += fracstep;
                } while (count-- != 0);
        }
    }
}
package rr.drawfuns;

import i.IDoomSystem;
import static m.fixed_t.FRACBITS;

public abstract class R_DrawTranslatedColumnLow<T, V>
        extends DoomColumnFunction<T, V> {

    public R_DrawTranslatedColumnLow(int SCREENWIDTH, int SCREENHEIGHT,
            int[] ylookup, int[] columnofs, ColVars<T, V> dcvars, V screen,
            IDoomSystem I) {
        super(SCREENWIDTH, SCREENHEIGHT, ylookup, columnofs, dcvars, screen, I);
        this.flags = DcFlags.TRANSLATED | DcFlags.LOW_DETAIL;
    }

    public static final class HiColor
            extends R_DrawTranslatedColumnLow<byte[], short[]> {
        public HiColor(int SCREENWIDTH, int SCREENHEIGHT, int[] ylookup,
                int[] columnofs, ColVars<byte[], short[]> dcvars,
                short[] screen, IDoomSystem I) {
            super(SCREENWIDTH, SCREENHEIGHT, ylookup, columnofs, dcvars,
                    screen, I);
        }

        public void invoke() {
            int count;
            // MAES: you know the deal by now...
            int dest, dest2;
            int frac;
            final int fracstep;
            final int dc_source_ofs = dcvars.dc_source_ofs;
            final byte[] dc_source = dcvars.dc_source;
            final short[] dc_colormap = dcvars.dc_colormap;
            final byte[] dc_translation = dcvars.dc_translation;

            count = dcvars.dc_yh - dcvars.dc_yl;
            if (count < 0)
                return;

            if (RANGECHECK) {
                super.performRangeCheck();
            }

            // The idea is to draw more than one pixel at a time.
            dest = blockyDest1();
            dest2 = blockyDest2();

            // Looks familiar.
            fracstep = dcvars.dc_iscale;
            frac =
                dcvars.dc_texturemid + (dcvars.dc_yl - dcvars.centery)
                        * fracstep;

            // Here we do an additional index re-mapping.
            // Maes: Unroll by 4
            if (count >= 4)
                do {
                    // Translation tables are used
                    // to map certain colorramps to other ones,
                    // used with PLAY sprites.
                    // Thus the "green" ramp of the player 0 sprite
                    // is mapped to gray, red, black/indigo.
                    screen[dest] =
                        screen[dest2] =
                            dc_colormap[0x00FF & dc_translation[0xFF & dc_source[dc_source_ofs
                                    + (frac >> FRACBITS)]]];
                    dest += SCREENWIDTH;
                    dest2 += SCREENWIDTH;
                    frac += fracstep;

                    screen[dest] =
                        screen[dest2] =
                            dc_colormap[0x00FF & dc_translation[0xFF & dc_source[dc_source_ofs
                                    + (frac >> FRACBITS)]]];
                    dest += SCREENWIDTH;
                    dest2 += SCREENWIDTH;
                    frac += fracstep;

                    screen[dest] =
                        screen[dest2] =
                            dc_colormap[0x00FF & dc_translation[0xFF & dc_source[dc_source_ofs
                                    + (frac >> FRACBITS)]]];
                    dest += SCREENWIDTH;
                    dest2 += SCREENWIDTH;
                    frac += fracstep;

                    screen[dest] =
                        screen[dest2] =
                            dc_colormap[0x00FF & dc_translation[0xFF & dc_source[dc_source_ofs
                                    + (frac >> FRACBITS)]]];
                    dest += SCREENWIDTH;
                    dest2 += SCREENWIDTH;
                    frac += fracstep;

                } while ((count -= 4) > 4);

            if (count > 0)
                do {
                    // Translation tables are used
                    // to map certain colorramps to other ones,
                    // used with PLAY sprites.
                    // Thus the "green" ramp of the player 0 sprite
                    // is mapped to gray, red, black/indigo.
                    screen[dest] =
                        screen[dest2] =
                            dc_colormap[0x00FF & dc_translation[0xFF & dc_source[dc_source_ofs
                                    + (frac >> FRACBITS)]]];
                    dest += SCREENWIDTH;
                    dest2 += SCREENWIDTH;

                    frac += fracstep;
                } while (count-- != 0);
        }

    }

    public static final class Indexed
            extends R_DrawTranslatedColumnLow<byte[], byte[]> {

        public Indexed(int SCREENWIDTH, int SCREENHEIGHT, int[] ylookup,
                int[] columnofs, ColVars<byte[], byte[]> dcvars, byte[] screen,
                IDoomSystem I) {
            super(SCREENWIDTH, SCREENHEIGHT, ylookup, columnofs, dcvars,
                    screen, I);
        }

        public void invoke() {
            int count;
            // MAES: you know the deal by now...
            int dest, dest2;
            int frac;
            final int fracstep;
            final int dc_source_ofs = dcvars.dc_source_ofs;
            final byte[] dc_source = dcvars.dc_source;
            final byte[] dc_colormap = dcvars.dc_colormap;
            final byte[] dc_translation = dcvars.dc_translation;

            count = dcvars.dc_yh - dcvars.dc_yl;
            if (count < 0)
                return;

            if (RANGECHECK) {
                super.performRangeCheck();
            }

            // The idea is to draw more than one pixel at a time.
            dest = blockyDest1();
            dest2 = blockyDest2();

            // Looks familiar.
            fracstep = dcvars.dc_iscale;
            frac =
                dcvars.dc_texturemid + (dcvars.dc_yl - dcvars.centery)
                        * fracstep;

            // Here we do an additional index re-mapping.
            // Maes: Unroll by 4
            if (count >= 4)
                do {
                    // Translation tables are used
                    // to map certain colorramps to other ones,
                    // used with PLAY sprites.
                    // Thus the "green" ramp of the player 0 sprite
                    // is mapped to gray, red, black/indigo.
                    screen[dest] =
                        screen[dest2] =
                            dc_colormap[0x00FF & dc_translation[0xFF & dc_source[dc_source_ofs
                                    + (frac >> FRACBITS)]]];
                    dest += SCREENWIDTH;
                    dest2 += SCREENWIDTH;
                    frac += fracstep;

                    screen[dest] =
                        screen[dest2] =
                            dc_colormap[0x00FF & dc_translation[0xFF & dc_source[dc_source_ofs
                                    + (frac >> FRACBITS)]]];
                    dest += SCREENWIDTH;
                    dest2 += SCREENWIDTH;
                    frac += fracstep;

                    screen[dest] =
                        screen[dest2] =
                            dc_colormap[0x00FF & dc_translation[0xFF & dc_source[dc_source_ofs
                                    + (frac >> FRACBITS)]]];
                    dest += SCREENWIDTH;
                    dest2 += SCREENWIDTH;
                    frac += fracstep;

                    screen[dest] =
                        screen[dest2] =
                            dc_colormap[0x00FF & dc_translation[0xFF & dc_source[dc_source_ofs
                                    + (frac >> FRACBITS)]]];
                    dest += SCREENWIDTH;
                    dest2 += SCREENWIDTH;
                    frac += fracstep;

                } while ((count -= 4) > 4);

            if (count > 0)
                do {
                    // Translation tables are used
                    // to map certain colorramps to other ones,
                    // used with PLAY sprites.
                    // Thus the "green" ramp of the player 0 sprite
                    // is mapped to gray, red, black/indigo.
                    screen[dest] =
                        screen[dest2] =
                            dc_colormap[0x00FF & dc_translation[0xFF & dc_source[dc_source_ofs
                                    + (frac >> FRACBITS)]]];
                    dest += SCREENWIDTH;
                    dest2 += SCREENWIDTH;

                    frac += fracstep;
                } while (count-- != 0);
        }
    }

    public static final class TrueColor
            extends R_DrawTranslatedColumnLow<byte[], int[]> {

        public TrueColor(int SCREENWIDTH, int SCREENHEIGHT, int[] ylookup,
                int[] columnofs, ColVars<byte[], int[]> dcvars, int[] screen,
                IDoomSystem I) {
            super(SCREENWIDTH, SCREENHEIGHT, ylookup, columnofs, dcvars,
                    screen, I);
        }

        public void invoke() {
            int count;
            // MAES: you know the deal by now...
            int dest, dest2;
            int frac;
            final int fracstep;
            final int dc_source_ofs = dcvars.dc_source_ofs;
            final byte[] dc_source = dcvars.dc_source;
            final int[] dc_colormap = dcvars.dc_colormap;
            final byte[] dc_translation = dcvars.dc_translation;

            count = dcvars.dc_yh - dcvars.dc_yl;
            if (count < 0)
                return;

            if (RANGECHECK) {
                super.performRangeCheck();
            }

            // The idea is to draw more than one pixel at a time.
            dest = blockyDest1();
            dest2 = blockyDest2();

            // Looks familiar.
            fracstep = dcvars.dc_iscale;
            frac =
                dcvars.dc_texturemid + (dcvars.dc_yl - dcvars.centery)
                        * fracstep;

            // Here we do an additional index re-mapping.
            // Maes: Unroll by 4
            if (count >= 4)
                do {
                    // Translation tables are used
                    // to map certain colorramps to other ones,
                    // used with PLAY sprites.
                    // Thus the "green" ramp of the player 0 sprite
                    // is mapped to gray, red, black/indigo.
                    screen[dest] =
                        screen[dest2] =
                            dc_colormap[0x00FF & dc_translation[0xFF & dc_source[dc_source_ofs
                                    + (frac >> FRACBITS)]]];
                    dest += SCREENWIDTH;
                    dest2 += SCREENWIDTH;
                    frac += fracstep;

                    screen[dest] =
                        screen[dest2] =
                            dc_colormap[0x00FF & dc_translation[0xFF & dc_source[dc_source_ofs
                                    + (frac >> FRACBITS)]]];
                    dest += SCREENWIDTH;
                    dest2 += SCREENWIDTH;
                    frac += fracstep;

                    screen[dest] =
                        screen[dest2] =
                            dc_colormap[0x00FF & dc_translation[0xFF & dc_source[dc_source_ofs
                                    + (frac >> FRACBITS)]]];
                    dest += SCREENWIDTH;
                    dest2 += SCREENWIDTH;
                    frac += fracstep;

                    screen[dest] =
                        screen[dest2] =
                            dc_colormap[0x00FF & dc_translation[0xFF & dc_source[dc_source_ofs
                                    + (frac >> FRACBITS)]]];
                    dest += SCREENWIDTH;
                    dest2 += SCREENWIDTH;
                    frac += fracstep;

                } while ((count -= 4) > 4);

            if (count > 0)
                do {
                    // Translation tables are used
                    // to map certain colorramps to other ones,
                    // used with PLAY sprites.
                    // Thus the "green" ramp of the player 0 sprite
                    // is mapped to gray, red, black/indigo.
                    screen[dest] =
                        screen[dest2] =
                            dc_colormap[0x00FF & dc_translation[0xFF & dc_source[dc_source_ofs
                                    + (frac >> FRACBITS)]]];
                    dest += SCREENWIDTH;
                    dest2 += SCREENWIDTH;

                    frac += fracstep;
                } while (count-- != 0);
        }
    }

}
package rr.drawfuns;

/** Flags used to mark column functions according to type, 
 *  for quick type identification.
 * 
 * @author velktron
 *
 */

public final class DcFlags {
    public static final int FUZZY=0x1;
    public static final int TRANSLATED=0x2;
    public static final int TRANSPARENT=0x4;
    public static final int LOW_DETAIL=0x8;
}

package rr.drawfuns;

import i.IDoomSystem;

public abstract class DoomSpanFunction<T,V> implements SpanFunction<T,V> {
    
    protected final boolean RANGECHECK=false;
    protected final int SCREENWIDTH;
    protected final int SCREENHEIGHT;
    protected SpanVars<T,V> dsvars;
    protected final int[] ylookup;
    protected final int[] columnofs;
    protected final V screen;
    protected final IDoomSystem I;
    
    public DoomSpanFunction(int sCREENWIDTH, int sCREENHEIGHT,
            int[] ylookup, int[] columnofs,SpanVars<T,V> dsvars, V screen,IDoomSystem I) {
        SCREENWIDTH = sCREENWIDTH;
        SCREENHEIGHT = sCREENHEIGHT;
        this.ylookup=ylookup;
        this.columnofs=columnofs;
        this.dsvars = dsvars;
        this.screen = screen;
        this.I=I;
    }
    
    protected final void doRangeCheck(){
        if (dsvars.ds_x2 < dsvars.ds_x1 || dsvars.ds_x1 < 0 || dsvars.ds_x2 >= SCREENWIDTH
                || dsvars.ds_y > SCREENHEIGHT) {
            I.Error("R_DrawSpan: %d to %d at %d", dsvars.ds_x1, dsvars.ds_x2, dsvars.ds_y);
        }
    }

    @Override
    public final void invoke(SpanVars<T,V> dsvars) {
        this.dsvars=dsvars;
        invoke();
    }
    
}

package rr.drawfuns;

import static m.fixed_t.FRACBITS;
import i.IDoomSystem;

/**
 * Adapted from Killough's Boom code. There are optimized as well as low-detail
 * versions of it.
 * 
 * @author admin
 */

public abstract class R_DrawColumnBoom<T, V>
        extends DoomColumnFunction<T, V> {

    public R_DrawColumnBoom(int SCREENWIDTH, int SCREENHEIGHT, int[] ylookup,
            int[] columnofs, ColVars<T, V> dcvars, V screen, IDoomSystem I) {
        super(SCREENWIDTH, SCREENHEIGHT, ylookup, columnofs, dcvars, screen, I);
    }

    public static final class HiColor
            extends R_DrawColumnBoom<byte[], short[]> {

        public HiColor(int SCREENWIDTH, int SCREENHEIGHT, int[] ylookup,
                int[] columnofs, ColVars<byte[], short[]> dcvars,
                short[] screen, IDoomSystem I) {
            super(SCREENWIDTH, SCREENHEIGHT, ylookup, columnofs, dcvars,
                    screen, I);
        }

        public void invoke() {
            int count;
            int dest; // killough
            int frac; // killough
            int fracstep;
            final int dc_source_ofs = dcvars.dc_source_ofs;
            count = dcvars.dc_yh - dcvars.dc_yl + 1;

            if (count <= 0) // Zero length, column does not exceed a pixel.
                return;

            if (RANGECHECK) {
                performRangeCheck();
            }

            dest = computeScreenDest();

            // Determine scaling, which is the only mapping to be done.

            fracstep = dcvars.dc_iscale;
            frac =
                dcvars.dc_texturemid + (dcvars.dc_yl - dcvars.centery)
                        * fracstep;

            // Inner loop that does the actual texture mapping,
            // e.g. a DDA-lile scaling.
            // This is as fast as it gets. (Yeah, right!!! -- killough)
            //
            // killough 2/1/98: more performance tuning

            {
                final byte[] source = dcvars.dc_source;
                final short[] colormap = dcvars.dc_colormap;
                int heightmask = dcvars.dc_texheight - 1;
                if ((dcvars.dc_texheight & heightmask) != 0) // not a power of 2
                                                             // --
                // killough
                {
                    heightmask++;
                    heightmask <<= FRACBITS;

                    if (frac < 0)
                        while ((frac += heightmask) < 0)
                            ;
                    else
                        while (frac >= heightmask)
                            frac -= heightmask;

                    do {
                        // Re-map color indices from wall texture column
                        // using a lighting/special effects LUT.

                        // heightmask is the Tutti-Frutti fix -- killoughdcvars

                        screen[dest] =
                            colormap[0x00FF & source[((frac >> FRACBITS))]];
                        dest += SCREENWIDTH;
                        if ((frac += fracstep) >= heightmask)
                            frac -= heightmask;
                    } while (--count > 0);
                } else {
                    while (count >= 4) // texture height is a power of 2 --
                                       // killough
                    {
                        // System.err.println(dest);
                        screen[dest] =
                            colormap[0x00FF & source[dc_source_ofs
                                    + ((frac >> FRACBITS) & heightmask)]];
                        dest += SCREENWIDTH;
                        frac += fracstep;
                        screen[dest] =
                            colormap[0x00FF & source[dc_source_ofs
                                    + ((frac >> FRACBITS) & heightmask)]];
                        dest += SCREENWIDTH;
                        frac += fracstep;
                        screen[dest] =
                            colormap[0x00FF & source[dc_source_ofs
                                    + ((frac >> FRACBITS) & heightmask)]];
                        dest += SCREENWIDTH;
                        frac += fracstep;
                        screen[dest] =
                            colormap[0x00FF & source[dc_source_ofs
                                    + ((frac >> FRACBITS) & heightmask)]];
                        dest += SCREENWIDTH;
                        frac += fracstep;
                        count -= 4;
                    }

                    while (count > 0) {
                        try {
                            screen[dest] =
                                colormap[0x00FF & source[dc_source_ofs
                                        + ((frac >> FRACBITS) & heightmask)]];
                        } catch (Exception e) {
                            System.err.printf("%s %s %x %x %x\n", colormap,
                                source, dc_source_ofs, frac, heightmask);
                        }
                        dest += SCREENWIDTH;
                        frac += fracstep;
                        count--;
                    }
                }
            }
        }
    }

    public static final class Indexed
            extends R_DrawColumnBoom<byte[], byte[]> {

        public Indexed(int SCREENWIDTH, int SCREENHEIGHT, int[] ylookup,
                int[] columnofs, ColVars<byte[], byte[]> dcvars, byte[] screen,
                IDoomSystem I) {
            super(SCREENWIDTH, SCREENHEIGHT, ylookup, columnofs, dcvars,
                    screen, I);
        }

        public void invoke() {
            int count;
            int dest; // killough
            int frac; // killough
            int fracstep;
            final int dc_source_ofs = dcvars.dc_source_ofs;
            count = dcvars.dc_yh - dcvars.dc_yl + 1;

            if (count <= 0) // Zero length, column does not exceed a pixel.
                return;

            if (RANGECHECK) {
                performRangeCheck();
            }

            dest = computeScreenDest();

            // Determine scaling, which is the only mapping to be done.

            fracstep = dcvars.dc_iscale;
            frac =
                dcvars.dc_texturemid + (dcvars.dc_yl - dcvars.centery)
                        * fracstep;

            // Inner loop that does the actual texture mapping,
            // e.g. a DDA-lile scaling.
            // This is as fast as it gets. (Yeah, right!!! -- killough)
            //
            // killough 2/1/98: more performance tuning

            {
                final byte[] source = dcvars.dc_source;
                final byte[] colormap = dcvars.dc_colormap;
                int heightmask = dcvars.dc_texheight - 1;
                if ((dcvars.dc_texheight & heightmask) != 0) // not a power of 2
                                                             // --
                // killough
                {
                    heightmask++;
                    heightmask <<= FRACBITS;

                    if (frac < 0)
                        while ((frac += heightmask) < 0)
                            ;
                    else
                        while (frac >= heightmask)
                            frac -= heightmask;

                    do {
                        // Re-map color indices from wall texture column
                        // using a lighting/special effects LUT.

                        // heightmask is the Tutti-Frutti fix -- killoughdcvars

                        screen[dest] =
                            colormap[0x00FF & source[((frac >> FRACBITS))]];
                        dest += SCREENWIDTH;
                        if ((frac += fracstep) >= heightmask)
                            frac -= heightmask;
                    } while (--count > 0);
                } else {
                    while (count >= 4) // texture height is a power of 2 --
                                       // killough
                    {
                        // System.err.println(dest);
                        screen[dest] =
                            colormap[0x00FF & source[dc_source_ofs
                                    + ((frac >> FRACBITS) & heightmask)]];
                        dest += SCREENWIDTH;
                        frac += fracstep;
                        screen[dest] =
                            colormap[0x00FF & source[dc_source_ofs
                                    + ((frac >> FRACBITS) & heightmask)]];
                        dest += SCREENWIDTH;
                        frac += fracstep;
                        screen[dest] =
                            colormap[0x00FF & source[dc_source_ofs
                                    + ((frac >> FRACBITS) & heightmask)]];
                        dest += SCREENWIDTH;
                        frac += fracstep;
                        screen[dest] =
                            colormap[0x00FF & source[dc_source_ofs
                                    + ((frac >> FRACBITS) & heightmask)]];
                        dest += SCREENWIDTH;
                        frac += fracstep;
                        count -= 4;
                    }

                    while (count > 0) {
                        try {
                            screen[dest] =
                                colormap[0x00FF & source[dc_source_ofs
                                        + ((frac >> FRACBITS) & heightmask)]];
                        } catch (Exception e) {
                            System.err.printf("%s %s %x %x %x\n", colormap,
                                source, dc_source_ofs, frac, heightmask);
                        }
                        dest += SCREENWIDTH;
                        frac += fracstep;
                        count--;
                    }
                }
            }
        }
    }

    public static final class TrueColor
            extends R_DrawColumnBoom<byte[], int[]> {

        public TrueColor(int SCREENWIDTH, int SCREENHEIGHT, int[] ylookup,
                int[] columnofs, ColVars<byte[], int[]> dcvars, int[] screen,
                IDoomSystem I) {
            super(SCREENWIDTH, SCREENHEIGHT, ylookup, columnofs, dcvars,
                    screen, I);
        }

        public void invoke() {
            int count;
            int dest; // killough
            int frac; // killough
            int fracstep;
            final int dc_source_ofs = dcvars.dc_source_ofs;
            count = dcvars.dc_yh - dcvars.dc_yl + 1;

            if (count <= 0) // Zero length, column does not exceed a pixel.
                return;

            if (RANGECHECK) {
                performRangeCheck();
            }

            dest = computeScreenDest();

            // Determine scaling, which is the only mapping to be done.

            fracstep = dcvars.dc_iscale;
            frac =
                dcvars.dc_texturemid + (dcvars.dc_yl - dcvars.centery)
                        * fracstep;

            // Inner loop that does the actual texture mapping,
            // e.g. a DDA-lile scaling.
            // This is as fast as it gets. (Yeah, right!!! -- killough)
            //
            // killough 2/1/98: more performance tuning

            {
                final byte[] source = dcvars.dc_source;
                final int[] colormap = dcvars.dc_colormap;
                int heightmask = dcvars.dc_texheight - 1;
                if ((dcvars.dc_texheight & heightmask) != 0) // not a power of 2
                                                             // --
                // killough
                {
                    heightmask++;
                    heightmask <<= FRACBITS;

                    if (frac < 0)
                        while ((frac += heightmask) < 0)
                            ;
                    else
                        while (frac >= heightmask)
                            frac -= heightmask;

                    do {
                        // Re-map color indices from wall texture column
                        // using a lighting/special effects LUT.

                        // heightmask is the Tutti-Frutti fix -- killoughdcvars

                        screen[dest] =
                            colormap[0x00FF & source[((frac >> FRACBITS))]];
                        dest += SCREENWIDTH;
                        if ((frac += fracstep) >= heightmask)
                            frac -= heightmask;
                    } while (--count > 0);
                } else {
                    while (count >= 4) // texture height is a power of 2 --
                                       // killough
                    {
                        // System.err.println(dest);
                        screen[dest] =
                            colormap[0x00FF & source[dc_source_ofs
                                    + ((frac >> FRACBITS) & heightmask)]];
                        dest += SCREENWIDTH;
                        frac += fracstep;
                        screen[dest] =
                            colormap[0x00FF & source[dc_source_ofs
                                    + ((frac >> FRACBITS) & heightmask)]];
                        dest += SCREENWIDTH;
                        frac += fracstep;
                        screen[dest] =
                            colormap[0x00FF & source[dc_source_ofs
                                    + ((frac >> FRACBITS) & heightmask)]];
                        dest += SCREENWIDTH;
                        frac += fracstep;
                        screen[dest] =
                            colormap[0x00FF & source[dc_source_ofs
                                    + ((frac >> FRACBITS) & heightmask)]];
                        dest += SCREENWIDTH;
                        frac += fracstep;
                        count -= 4;
                    }

                    while (count > 0) {
                        try {
                            screen[dest] =
                                colormap[0x00FF & source[dc_source_ofs
                                        + ((frac >> FRACBITS) & heightmask)]];
                        } catch (Exception e) {
                            System.err.printf("%s %s %x %x %x\n", colormap,
                                source, dc_source_ofs, frac, heightmask);
                        }
                        dest += SCREENWIDTH;
                        frac += fracstep;
                        count--;
                    }
                }
            }
        }
    }
}
package rr.drawfuns;

import i.IDoomSystem;

public abstract class R_DrawSpanLow<T, V>
        extends DoomSpanFunction<T, V> {

    public R_DrawSpanLow(int SCREENWIDTH, int SCREENHEIGHT, int[] ylookup,
            int[] columnofs, SpanVars<T, V> dsvars, V screen, IDoomSystem I) {
        super(SCREENWIDTH, SCREENHEIGHT, ylookup, columnofs, dsvars, screen, I);
    }

    public static final class HiColor
            extends R_DrawSpanLow<byte[], short[]> {

        public HiColor(int SCREENWIDTH, int SCREENHEIGHT, int[] ylookup,
                int[] columnofs, SpanVars<byte[], short[]> dsvars,
                short[] screen, IDoomSystem I) {
            super(SCREENWIDTH, SCREENHEIGHT, ylookup, columnofs, dsvars,
                    screen, I);
        }

        @Override
        public void invoke() {
            final byte[] ds_source = dsvars.ds_source;
            final short[] ds_colormap = dsvars.ds_colormap;
            final int ds_xstep = dsvars.ds_xstep;
            final int ds_ystep = dsvars.ds_ystep;
            int f_xfrac = dsvars.ds_xfrac;
            int f_yfrac = dsvars.ds_yfrac;
            int dest;
            int count;
            int spot;

            if (RANGECHECK) {
                doRangeCheck();
                // dscount++;
            }

            // MAES: count must be performed before shifting.
            count = dsvars.ds_x2 - dsvars.ds_x1;

            // Blocky mode, need to multiply by 2.
            dsvars.ds_x1 <<= 1;
            dsvars.ds_x2 <<= 1;

            dest = ylookup[dsvars.ds_y] + columnofs[dsvars.ds_x1];

            do {
                spot =
                    ((f_yfrac >> (16 - 6)) & (63 * 64))
                            + ((f_xfrac >> 16) & 63);
                // Lowres/blocky mode does it twice,
                // while scale is adjusted appropriately.

                screen[dest++] = ds_colormap[0x00FF & ds_source[spot]];
                screen[dest++] = ds_colormap[0x00FF & ds_source[spot]];

                f_xfrac += ds_xstep;
                f_yfrac += ds_ystep;

            } while (count-- > 0);

        }
    }

    public static final class Indexed
            extends R_DrawSpanLow<byte[], byte[]> {

        public Indexed(int SCREENWIDTH, int SCREENHEIGHT, int[] ylookup,
                int[] columnofs, SpanVars<byte[], byte[]> dsvars,
                byte[] screen, IDoomSystem I) {
            super(SCREENWIDTH, SCREENHEIGHT, ylookup, columnofs, dsvars,
                    screen, I);
        }

        public void invoke() {
            final byte[] ds_source = dsvars.ds_source;
            final byte[] ds_colormap = dsvars.ds_colormap;
            final int ds_xstep = dsvars.ds_xstep;
            final int ds_ystep = dsvars.ds_ystep;
            int f_xfrac = dsvars.ds_xfrac;
            int f_yfrac = dsvars.ds_yfrac;
            int dest;
            int count;
            int spot;

            if (RANGECHECK) {
                doRangeCheck();
                // dscount++;
            }

            // MAES: count must be performed before shifting.
            count = dsvars.ds_x2 - dsvars.ds_x1;

            // Blocky mode, need to multiply by 2.
            dsvars.ds_x1 <<= 1;
            dsvars.ds_x2 <<= 1;

            dest = ylookup[dsvars.ds_y] + columnofs[dsvars.ds_x1];

            do {
                spot =
                    ((f_yfrac >> (16 - 6)) & (63 * 64))
                            + ((f_xfrac >> 16) & 63);
                // Lowres/blocky mode does it twice,
                // while scale is adjusted appropriately.

                screen[dest++] = ds_colormap[0x00FF & ds_source[spot]];
                screen[dest++] = ds_colormap[0x00FF & ds_source[spot]];

                f_xfrac += ds_xstep;
                f_yfrac += ds_ystep;

            } while (count-- > 0);

        }
    }

    public static final class TrueColor
            extends R_DrawSpanLow<byte[], int[]> {

        public TrueColor(int SCREENWIDTH, int SCREENHEIGHT, int[] ylookup,
                int[] columnofs, SpanVars<byte[], int[]> dsvars, int[] screen,
                IDoomSystem I) {
            super(SCREENWIDTH, SCREENHEIGHT, ylookup, columnofs, dsvars,
                    screen, I);
        }

        @Override
        public void invoke() {
            final byte[] ds_source = dsvars.ds_source;
            final int[] ds_colormap = dsvars.ds_colormap;
            final int ds_xstep = dsvars.ds_xstep;
            final int ds_ystep = dsvars.ds_ystep;
            int f_xfrac = dsvars.ds_xfrac;
            int f_yfrac = dsvars.ds_yfrac;
            int dest;
            int count;
            int spot;

            if (RANGECHECK) {
                doRangeCheck();
                // dscount++;
            }

            // MAES: count must be performed before shifting.
            count = dsvars.ds_x2 - dsvars.ds_x1;

            // Blocky mode, need to multiply by 2.
            dsvars.ds_x1 <<= 1;
            dsvars.ds_x2 <<= 1;

            dest = ylookup[dsvars.ds_y] + columnofs[dsvars.ds_x1];

            do {
                spot =
                    ((f_yfrac >> (16 - 6)) & (63 * 64))
                            + ((f_xfrac >> 16) & 63);
                // Lowres/blocky mode does it twice,
                // while scale is adjusted appropriately.

                screen[dest++] = ds_colormap[0x00FF & ds_source[spot]];
                screen[dest++] = ds_colormap[0x00FF & ds_source[spot]];

                f_xfrac += ds_xstep;
                f_yfrac += ds_ystep;

            } while (count-- > 0);

        }
    }
}
package rr.drawfuns;

import i.IDoomSystem;
import static m.fixed_t.FRACBITS;

public final class R_DrawColumnLow extends DoomColumnFunction<byte[],short[]> {
    
		public R_DrawColumnLow(int SCREENWIDTH, int SCREENHEIGHT,
	            int[] ylookup, int[] columnofs, ColVars<byte[],short[]> dcvars,
	            short[] screen, IDoomSystem I) {
	        super(SCREENWIDTH, SCREENHEIGHT, ylookup, columnofs, dcvars, screen, I);
	        this.flags=DcFlags.LOW_DETAIL;
	    }

        public void invoke() {
			int count;
			// MAES: were pointers. Of course...
			int dest, dest2;
			final byte[] dc_source=dcvars.dc_source;
			final short[] dc_colormap=dcvars.dc_colormap;
			final int dc_source_ofs=dcvars.dc_source_ofs;
			// Maes: fixed_t never used as such.
			int frac;
			final int fracstep;

			count = dcvars.dc_yh - dcvars.dc_yl;

			// Zero length.
			if (count < 0)
				return;

			if (RANGECHECK) {
			    performRangeCheck();
			}

			// The idea is to draw more than one pixel at a time.
			dest = blockyDest1();
			dest2 = blockyDest2();

			fracstep = dcvars.dc_iscale;
			frac = dcvars.dc_texturemid + (dcvars.dc_yl - dcvars.centery) * fracstep;
			// int spot=(frac >>> FRACBITS) & 127;
			do {

				// Hack. Does not work correctly.
				// MAES: that's good to know.
				screen[dest] = screen[dest2] = dc_colormap[0x00FF & dc_source[dc_source_ofs
						+ ((frac >>> FRACBITS) & 127)]];

				dest += SCREENWIDTH;
				dest2 += SCREENWIDTH;
				frac += fracstep;
			} while (count-- != 0);
		}
	}
package rr.drawfuns;

public class SpanVars<T,V> {

    public int ds_xfrac;
    public int ds_yfrac;
    public int ds_xstep;
    public T ds_source;
    
    /** DrawSpan colormap. */
    public V ds_colormap;
    public int ds_y;
    public int ds_x2;
    public int ds_x1;
    public int ds_ystep;
    
    public DoomSpanFunction<T,V>  spanfunc;
}

package rr.drawfuns;

/** This is all the information needed to draw a particular column. Really.
 *  So if we store all of this crap somewhere instead of drawing, we can do the
 *  drawing when it's more convenient, and since they are non-overlapping we can 
 *  parallelize them. Any questions?
 *  
 */

public class ColVars<T,V> {    
    /** when passing dc_source around, also set this */
    public T dc_source;
    public int dc_source_ofs;
    
    public T dc_translation;
    public int viewheight;
    
    /** Used by functions that accept transparency or other special 
     *  remapping tables.
     * 
     */
    public T tranmap;
    
    public int centery;
    public int dc_iscale;

    public int dc_texturemid;
    public int dc_texheight; // Boom enhancement
    public int dc_x;
    public int dc_yh;
    public int dc_yl;
    
    public int dc_flags;

    /**
     * MAES: this was a typedef for unsigned bytes, called "lighttable_t". It
     * makes more sense to make it generic and parametrize it to an array of 
     * primitives since it's performance-critical in the renderer. 
     * Now, whether this should be made bytes or shorts or chars or even ints 
     * is debatable.
     */
    
    public V dc_colormap;

    /** Copies all BUT flags */
    
    public final void copyFrom(ColVars<T,V> dcvars) {
        this.dc_source=dcvars.dc_source;
        this.dc_colormap=dcvars.dc_colormap;
        this.dc_source_ofs=dcvars.dc_source_ofs;
        this.viewheight=dcvars.viewheight;
        this.centery=dcvars.centery;
        this.dc_x=dcvars.dc_x;
        this.dc_yh=dcvars.dc_yh;
        this.dc_yl=dcvars.dc_yl;
        this.dc_texturemid=dcvars.dc_texturemid;
        this.dc_iscale=dcvars.dc_iscale;
        this.dc_texheight=dcvars.dc_texheight;        
    }
    
    /** Assigns specific flags */
    public void copyFrom(ColVars<T,V> dcvars,int flags) {
        this.copyFrom(dcvars);
        this.dc_flags=flags;
    }
    

}

package rr.drawfuns;

/** Either draws a column or a span
 * 
 * @author velktron
 *
 */

public interface SpanFunction<T,V> {
    public void invoke();
    
	public void invoke(SpanVars<T,V> dsvars);

}
package rr;

import java.io.IOException;
import java.nio.ByteBuffer;

import utils.C2JUtils;
import w.CacheableDoomObject;

/** column_t is a list of 0 or more post_t, (byte)-1 terminated
 * typedef post_t  column_t;
 * For the sake of efficiency, "column_t" will store raw data, however I added
 * some stuff to make my life easier.
 * 
 */

public class column_t implements CacheableDoomObject{
    
    /** Static buffers used during I/O. 
     *  There's ABSO-FUCKING-LUTELY no reason to manipulate them externally!!!
     *  I'M NOT KIDDING!!!11!!
     */
    
    private static final int[] guesspostofs=new int[256];
    private static final short[] guesspostlens=new short[256];
    private static final short[] guesspostdeltas=new short[256];
    
    
    // MAES: there are useless, since the renderer is using raw byte data anyway, and the per-post
    // data is available in the special arrays.
    // public short        topdelta;   // -1 is the last post in a column (actually 0xFF, since this was unsigned???)
    // public short        length;     // length data bytes follows (actually add +2)
	//public column_t[]      posts;    // This is quite tricky to read.
    /** The RAW data (includes initial header and padding, because no post gets preferential treatment). */
    public byte[] data; 
    /** Actual number of posts inside this column. All guesswork is done while loading */
	public int posts;
	/** Positions of posts inside the raw data (point at headers) */
	public int[] postofs; 
	/** Posts lengths, intended as actual drawable pixels.  Add +4 to get the whole post length */
	public short[] postlen;
	/** Vertical offset of each post. In theory it should be possible to quickly
	 *  clip to the next visible post when drawing a column */
	public short[] postdeltas;
	
    @Override
		public void unpack(ByteBuffer buf) throws IOException {
	        // Mark current position.
	        buf.mark();
	        int skipped=0;
	        short postlen=0;
	        int colheight=0;	        
	        int len=0; // How long is the WHOLE column, until the final FF?
	        int postno=0; // Actual number of posts.
	        int topdelta=0;
	        int prevdelta=-1; // HACK for DeepSea tall patches.
	        
	        // Scan every byte until we encounter an 0xFF which definitively marks the end of a column.
	        while((topdelta=C2JUtils.toUnsignedByte(buf.get()))!=0xFF){
	        
	        // From the wiki:
	        // A column's topdelta is compared to the previous column's topdelta 
	        // (or to -1 if there is no previous column in this row). If the new 
	        //  topdelta is lesser than the previous, it is interpreted as a tall
	        // patch and the two values are added together, the sum serving as the 
	        // current column's actual offset.
	            
	         int tmp=topdelta;    
	            
	        if (topdelta<prevdelta) {	            
	            topdelta+=prevdelta;
	            }

            prevdelta=tmp;
	        
	        // First byte of a post should be its "topdelta"
            guesspostdeltas[postno]=(short)topdelta;
            
	        guesspostofs[postno]=skipped+3; // 0 for first post

	        // Read one more byte...this should be the post length.
	        postlen=(short)C2JUtils.toUnsignedByte(buf.get());
	        guesspostlens[postno++]=(short) (postlen);
	        
	        // So, we already read 2 bytes (topdelta + length)
	        // Two further bytes are padding so we can safely skip 2+2+postlen bytes until the next post
	        skipped+=4+postlen;
	        buf.position(buf.position()+2+postlen);
	        
	        // Obviously, this adds to the height of the column, which might not be equal to the patch that
	        // contains it.
	        colheight+=postlen;
	        }
	        
	        // Skip final padding byte ?
	        skipped++;
	        
	        len = finalizeStatus(skipped, colheight, postno);
	        
	        // Go back...and read the raw data. That's what will actually be used in the renderer.
	        buf.reset();
	        buf.get(data, 0, len);
	    }


    /** This -almost- completes reading, by filling in the header information
     *  before the raw column data is read in.
     *  
     * @param skipped
     * @param colheight
     * @param postno
     * @return
     */
    
    private int finalizeStatus(int skipped, int colheight, int postno) {
        int len;
        // That's the TOTAL length including all padding.
        // This means we redundantly read some data
        len=(int) (skipped);
        this.data=new byte[len];
        
        this.postofs=new int[postno];
        this.postlen=new short[postno];
        // this.length=(short) colheight;
        this.postdeltas=new short[postno];
        
        System.arraycopy(guesspostofs, 0,this.postofs, 0, postno);
        System.arraycopy(guesspostlens, 0,this.postlen, 0, postno);
        System.arraycopy(guesspostdeltas, 0,this.postdeltas, 0, postno);
        
        this.posts=postno;
        return len;
    }
    
    /** based on raw data */
    public int getTopDelta(){
        return C2JUtils.toUnsignedByte(data[0]);
    }
    
    /** based on raw data */
    public int getLength(){
        return C2JUtils.toUnsignedByte(data[1]);
    }
   
}


// $Log: column_t.java,v $
// Revision 1.18  2011/10/04 14:34:08  velktron
// Removed length and topdelta members (unused).
//
// Revision 1.17  2011/07/25 14:37:20  velktron
// Added support for DeePSea tall patches.
//
// Revision 1.16  2011/07/12 16:32:05  velktron
// 4 extra padding bytes uneeded.
//
// Revision 1.15  2011/06/23 17:01:24  velktron
// column_t no longer needs to support IReadableDoomObject, since patch_t doesn't. If you really need it back, it can be done through buffering and using unpack(), without duplicate code. Also added setData() method for synthesized columns.
//
// Revision 1.14  2011/06/08 16:11:13  velktron
// IMPORTANT: postofs now skip delta,height and padding ENTIRELY (added +3). This eliminates the need to add +3 before accessing the data, saving some CPU cycles for each column. Of course, anything using column_t must take this into account.
//
package rr;

/** An interface used to ease the use of the GetCachedColumn by part
 *  of parallelized renderers.
 *  
 * @author Maes
 *
 */

public interface IGetColumn<T> {

	T GetColumn(int tex, int col);

}

package rr;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

import w.CacheableDoomObject;

/**
 * Texture definition.
 * Each texture is composed of one or more patches,
 * with patches being lumps stored in the WAD.
 * The lumps are referenced by number, and patched
 * into the rectangular texture space using origin
 * and possibly other attributes. 
 */

public class mappatch_t implements CacheableDoomObject {
     public short   originx;
     public short   originy;
     public short   patch;
     public short   stepdir;
     public short   colormap;
     
    @Override
    public void unpack(ByteBuffer buf)
            throws IOException {
        buf.order(ByteOrder.LITTLE_ENDIAN);
        originx=buf.getShort();
        originy=buf.getShort();
        patch=buf.getShort();
        stepdir=buf.getShort();
        colormap=buf.getShort();        
    }

    public static final int size() {
        return 10;
    }
     
     
 };

package rr;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

public class z_vertex_t
        extends vertex_t {

    public z_vertex_t(){
        super();
    }
    
    /** Notice how we auto-expand to fixed_t */
    @Override
    public void unpack(ByteBuffer buf)
            throws IOException {
        buf.order(ByteOrder.LITTLE_ENDIAN);
        this.x=buf.getInt();
        this.y=buf.getInt();
        
    }
    
    public final static int sizeOf() {
        return 8;
    }
    
}

package rr;

import static data.Defines.FF_FRAMEMASK;
import static data.Defines.FF_FULLBRIGHT;
import static data.Defines.SIL_BOTTOM;
import static data.Defines.SIL_TOP;
import static data.Defines.pw_invisibility;
import static doom.player_t.NUMPSPRITES;
import static m.fixed_t.FRACBITS;
import static m.fixed_t.FRACUNIT;
import static m.fixed_t.FixedMul;
import static p.mobj_t.MF_TRANSLATION;
import static p.mobj_t.MF_TRANSSHIFT;
import static rr.LightsAndColors.LIGHTLEVELS;
import static rr.LightsAndColors.LIGHTSCALESHIFT;
import static rr.LightsAndColors.LIGHTSEGSHIFT;
import static rr.LightsAndColors.MAXLIGHTSCALE;
import static rr.line_t.ML_DONTPEGBOTTOM;
import i.IDoomSystem;
import p.pspdef_t;
import rr.drawfuns.ColFuncs;
import rr.drawfuns.ColVars;
import rr.drawfuns.ColumnFunction;
import v.IVideoScale;
import w.IWadLoader;

/**
 * Refresh of things, i.e. objects represented by sprites. This abstract
 * class is the base for all implementations, and contains the gory clipping
 * and priority stuff. It can terminate by drawing directly, or by buffering
 * into a pipeline for parallelized drawing.
 * 
 * It need to be aware of almost everything in the renderer, which means that
 * it's a PITA to keep "disembodied". Then again, this probably means it's more 
 * extensible...
 * 
 * 
 * 
 */

public abstract class AbstractThings<T,V>
        implements IMaskedDrawer<T,V> {

    private final static boolean RANGECHECK=false;
    
    protected short[] maskedtexturecol;
    protected int pmaskedtexturecol = 0;
    
    // Cache those you get from the sprite manager
    protected int[] spritewidth, spriteoffset, spritetopoffset;

    /** fixed_t */
    protected int pspritescale, pspriteiscale, pspritexscale,
            pspriteyscale, skyscale;

    // Used for masked segs
    protected int rw_scalestep;

    protected int spryscale;

    protected int sprtopscreen;
    
    protected short[] mfloorclip;

    protected int p_mfloorclip;

    protected short[] mceilingclip;

    protected int p_mceilingclip;

    protected sector_t frontsector;

    protected sector_t backsector;

    // This must be "pegged" to the one used by the default renderer.
    protected ColVars<T, V> maskedcvars;
    
    protected ColumnFunction<T,V> colfunc;
    protected ColFuncs<T,V> colfuncs;
    protected ColFuncs<T,V> colfuncshi;
    protected ColFuncs<T,V> colfuncslow;    
    protected final LightsAndColors<V> colormaps;
    protected final ViewVars view;
    protected final SegVars seg_vars;
    protected final TextureManager<T> TexMan;
    protected final IDoomSystem I;
    protected final ISpriteManager SM;
    protected final BSPVars MyBSP;
    protected final IVisSpriteManagement<V> VIS;
    protected final IWadLoader W;
    protected final vissprite_t<V> avis;
    
    public AbstractThings(Renderer<T,V> R) {
        this.colfuncshi=R.getColFuncsHi();
        this.colfuncslow=R.getColFuncsLow();
        this.colormaps=R.getColorMap();
        this.view=R.getView();
        this.seg_vars=R.getSegVars();
        this.TexMan=R.getTextureManager();
        this.I=R.getDoomSystem();
        this.SM=R.getSpriteManager();
        this.MyBSP=R.getBSPVars();
        this.VIS=R.getVisSpriteManager();
        this.W=R.getWadLoader();
        this.avis=new vissprite_t<V>();
        this.maskedcvars=R.getMaskedDCVars();
        
    }

    @Override
    public void cacheSpriteManager(ISpriteManager SM) {
        this.spritewidth = SM.getSpriteWidth();
        this.spriteoffset = SM.getSpriteOffset();
        this.spritetopoffset = SM.getSpriteTopOffset();
    }

    // ///////////// VIDEO SCALE STUFF /////////////////////

    protected int SCREENWIDTH;

    protected int SCREENHEIGHT;

    protected IVideoScale vs;

    @Override
    public void setVideoScale(IVideoScale vs) {
        this.vs = vs;
    }

    @Override
    public void initScaling() {
        this.SCREENHEIGHT = vs.getScreenHeight();
        this.SCREENWIDTH = vs.getScreenWidth();

        // Pre-scale stuff.

        clipbot = new short[SCREENWIDTH];
        cliptop = new short[SCREENWIDTH];
    }

    /**
     * R_DrawVisSprite mfloorclip and mceilingclip should also be set.
     * Sprites are actually drawn here. MAES: Optimized. No longer needed to
     * pass x1 and x2 parameters (useless) +2 fps on nuts.wad timedemo.
     */
    protected void DrawVisSprite(vissprite_t<V> vis) {
        column_t column;
        int texturecolumn;
        int frac; // fixed_t
        patch_t patch;

        // At this point, the view angle (and patch) has already been
        // chosen. Go back.
        patch = W.CachePatchNum(vis.patch + SM.getFirstSpriteLump());

        maskedcvars.dc_colormap = vis.colormap;
        // colfunc=glasscolfunc;
        if (maskedcvars.dc_colormap == null) {
            // NULL colormap = shadow draw
            colfunc = colfuncs.fuzz;
        } else if ((vis.mobjflags & MF_TRANSLATION) != 0) {
            colfunc = colfuncs.trans;
            maskedcvars.dc_translation =
                (T) colormaps.getTranslationTable(vis.mobjflags);
        }

        maskedcvars.dc_iscale = Math.abs(vis.xiscale) >> view.detailshift;
        maskedcvars.dc_texturemid = vis.texturemid;
        frac = vis.startfrac;
        spryscale = vis.scale;
        sprtopscreen =
            view.centeryfrac
                    - FixedMul(maskedcvars.dc_texturemid, spryscale);

        // A texture height of 0 means "not tiling" and holds for
        // all sprite/masked renders.
        maskedcvars.dc_texheight = 0;

        for (maskedcvars.dc_x = vis.x1; maskedcvars.dc_x <= vis.x2; maskedcvars.dc_x++, frac +=
            vis.xiscale) {
            texturecolumn = frac >> FRACBITS;
            if (RANGECHECK) {
                if (texturecolumn < 0 || texturecolumn >= patch.width)
                    I.Error("R_DrawSpriteRange: bad texturecolumn");
            }
            column = patch.columns[texturecolumn];
            DrawMaskedColumn(column);
        }

        colfunc = colfuncs.masked;
    }

    /**
     * R_RenderMaskedSegRange
     * 
     * @param ds
     * @param x1
     * @param x2
     */
    protected void RenderMaskedSegRange(drawseg_t ds, int x1, int x2) {
        int index;

        int lightnum;
        int texnum;

        // System.out.printf("RenderMaskedSegRange from %d to %d\n",x1,x2);

        // Calculate light table.
        // Use different light tables
        // for horizontal / vertical / diagonal. Diagonal?
        // OPTIMIZE: get rid of LIGHTSEGSHIFT globally
        MyBSP.curline = ds.curline;
        frontsector = MyBSP.curline.frontsector;
        backsector = MyBSP.curline.backsector;
        texnum = TexMan.getTextureTranslation(MyBSP.curline.sidedef.midtexture);
        // System.out.print(" for texture "+textures[texnum].name+"\n:");
        lightnum =
            (frontsector.lightlevel >> LIGHTSEGSHIFT) + colormaps.extralight;

        if (MyBSP.curline.v1y == MyBSP.curline.v2y)
            lightnum--;
        else if (MyBSP.curline.v1x == MyBSP.curline.v2x)
            lightnum++;

        // Killough code.
        colormaps.walllights =
            lightnum >= LIGHTLEVELS ? colormaps.scalelight[LIGHTLEVELS - 1]
                    : lightnum < 0 ? colormaps.scalelight[0]
                            : colormaps.scalelight[lightnum];

        // Get the list
        maskedtexturecol = ds.getMaskedTextureColList();
        // And this is the pointer.
        pmaskedtexturecol = ds.getMaskedTextureColPointer();

        rw_scalestep = ds.scalestep;
        spryscale = ds.scale1 + (x1 - ds.x1) * rw_scalestep;

        // HACK to get "pointers" inside clipping lists
        mfloorclip = ds.getSprBottomClipList();
        p_mfloorclip = ds.getSprBottomClipPointer();
        mceilingclip = ds.getSprTopClipList();
        p_mceilingclip = ds.getSprTopClipPointer();
        // find positioning
        if ((MyBSP.curline.linedef.flags & ML_DONTPEGBOTTOM) != 0) {
            maskedcvars.dc_texturemid =
                frontsector.floorheight > backsector.floorheight ? frontsector.floorheight
                        : backsector.floorheight;
            maskedcvars.dc_texturemid =
                maskedcvars.dc_texturemid + TexMan.getTextureheight(texnum)
                        - view.z;
        } else {
            maskedcvars.dc_texturemid =
                frontsector.ceilingheight < backsector.ceilingheight ? frontsector.ceilingheight
                        : backsector.ceilingheight;
            maskedcvars.dc_texturemid = maskedcvars.dc_texturemid - view.z;
        }
        maskedcvars.dc_texturemid += MyBSP.curline.sidedef.rowoffset;

        if (colormaps.fixedcolormap != null)
            maskedcvars.dc_colormap = colormaps.fixedcolormap;

        // Texture height must be set at this point. This will trigger
        // tiling. For sprites, it should be set to 0.
        maskedcvars.dc_texheight =
            TexMan.getTextureheight(texnum) >> FRACBITS;

        // draw the columns
        for (maskedcvars.dc_x = x1; maskedcvars.dc_x <= x2; maskedcvars.dc_x++) {
            // calculate lighting
            if (maskedtexturecol[pmaskedtexturecol + maskedcvars.dc_x] != Short.MAX_VALUE) {
                if (colormaps.fixedcolormap == null) {
                    index = spryscale >>> LIGHTSCALESHIFT;

                    if (index >= MAXLIGHTSCALE)
                        index = MAXLIGHTSCALE - 1;

                    maskedcvars.dc_colormap = colormaps.walllights[index];
                }

                sprtopscreen =
                    view.centeryfrac
                            - FixedMul(maskedcvars.dc_texturemid, spryscale);
                maskedcvars.dc_iscale = (int) (0xffffffffL / spryscale);

                // draw the texture
                column_t data = TexMan.GetColumnStruct(texnum,
                    (int) maskedtexturecol[pmaskedtexturecol
                            + maskedcvars.dc_x]);// -3);

                DrawMaskedColumn(data);

                maskedtexturecol[pmaskedtexturecol + maskedcvars.dc_x] =
                    Short.MAX_VALUE;
            }
            spryscale += rw_scalestep;
        }

    }

    /**
     * R_DrawPSprite Draws a "player sprite" with slighly different rules
     * than normal sprites. This is actually a PITA, at best :-/
     */

    protected void DrawPSprite(pspdef_t psp) {

        int tx;
        int x1;
        int x2;
        spritedef_t sprdef;
        spriteframe_t sprframe;
        vissprite_t<V> vis;
        int lump;
        boolean flip;

        // decide which patch to use (in terms of angle?)
        if (RANGECHECK) {
            if (psp.state.sprite.ordinal() >= SM.getNumSprites())
                I.Error("R_ProjectSprite: invalid sprite number %d ",
                    psp.state.sprite);
        }

        sprdef = SM.getSprite(psp.state.sprite.ordinal());

        if (RANGECHECK) {
            if ((psp.state.frame & FF_FRAMEMASK) >= sprdef.numframes)
                I.Error("R_ProjectSprite: invalid sprite frame %d : %d ",
                    psp.state.sprite, psp.state.frame);
        }

        sprframe = sprdef.spriteframes[psp.state.frame & FF_FRAMEMASK];

        // Base frame for "angle 0" aka viewed from dead-front.
        lump = sprframe.lump[0];
        // Q: where can this be set? A: at sprite loadtime.
        flip = (boolean) (sprframe.flip[0] != 0);

        // calculate edges of the shape. tx is expressed in "view units".
        tx = (int) (FixedMul(psp.sx, view.BOBADJUST) - view.WEAPONADJUST);

        tx -= spriteoffset[lump];

        // So...centerxfrac is the center of the screen (pixel coords in
        // fixed point).
        x1 = (view.centerxfrac + FixedMul(tx, pspritescale)) >> FRACBITS;

        // off the right side
        if (x1 > view.width)
            return;

        tx += spritewidth[lump];
        x2 =
            ((view.centerxfrac + FixedMul(tx, pspritescale)) >> FRACBITS) - 1;

        // off the left side
        if (x2 < 0)
            return;

        // store information in a vissprite ?
        vis = avis;
        vis.mobjflags = 0;
        vis.texturemid =
            ((BASEYCENTER + view.lookdir) << FRACBITS) + FRACUNIT / 2
                    - (psp.sy - spritetopoffset[lump]);
        vis.x1 = x1 < 0 ? 0 : x1;
        vis.x2 = x2 >= view.width ? view.width - 1 : x2;
        vis.scale = (pspritescale) << view.detailshift;

        if (flip) {
            vis.xiscale = -pspriteiscale;
            vis.startfrac = spritewidth[lump] - 1;
        } else {
            vis.xiscale = pspriteiscale;
            vis.startfrac = 0;
        }

        if (vis.x1 > x1)
            vis.startfrac += vis.xiscale * (vis.x1 - x1);

        vis.patch = lump;

        if ((view.player.powers[pw_invisibility] > 4 * 32)
                || (view.player.powers[pw_invisibility] & 8) != 0) {
            // shadow draw
            vis.colormap = null;

        } else if (colormaps.fixedcolormap != null) {
            // fixed color
            vis.colormap = colormaps.fixedcolormap;
            // vis.pcolormap=0;
        } else if ((psp.state.frame & FF_FULLBRIGHT) != 0) {
            // full bright
            vis.colormap = colormaps.colormaps[0];
            // vis.pcolormap=0;
        } else {
            // local light
            vis.colormap = colormaps.spritelights[MAXLIGHTSCALE - 1];
        }

        // System.out.println("Weapon draw "+vis);
        DrawVisSprite(vis);
    }

    protected int PSpriteSY[] = { 0, // staff
            5 * FRACUNIT, // goldwand
            15 * FRACUNIT, // crossbow
            15 * FRACUNIT, // blaster
            15 * FRACUNIT, // skullrod
            15 * FRACUNIT, // phoenix rod
            15 * FRACUNIT, // mace
            15 * FRACUNIT, // gauntlets
            15 * FRACUNIT // beak
        };

    /**
     * R_DrawPlayerSprites This is where stuff like guns is drawn...right?
     */

    protected final void DrawPlayerSprites() {
        int i;
        int lightnum;
        pspdef_t psp;

        // get light level
        lightnum =
            (view.player.mo.subsector.sector.lightlevel >> LIGHTSEGSHIFT)
                    + colormaps.extralight;

        if (lightnum < 0)
            colormaps.spritelights = colormaps.scalelight[0];
        else if (lightnum >= LIGHTLEVELS)
            colormaps.spritelights = colormaps.scalelight[LIGHTLEVELS - 1];
        else
            colormaps.spritelights = colormaps.scalelight[lightnum];

        // clip to screen bounds
        mfloorclip = view.screenheightarray;
        p_mfloorclip = 0;
        mceilingclip = view.negonearray;
        p_mceilingclip = 0;

        // add all active psprites
        // MAES 25/5/2011 Fixed another stupid bug that prevented
        // PSP from actually being updated. This in turn uncovered
        // other bugs in the way psp and state were treated, and the way
        // flash states were set. It should be OK now.
        for (i = 0; i < NUMPSPRITES; i++) {
            psp = view.player.psprites[i];
            if (psp.state != null && psp.state.id != 0) {
                DrawPSprite(psp);
            }
        }
    }

    // MAES: Scale to SCREENWIDTH
    protected short[] clipbot;

    protected short[] cliptop;

    /**
     * R_DrawSprite
     */

    protected final void DrawSprite(vissprite_t<V> spr) {
        int ds;
        drawseg_t dss;

        int x;
        int r1;
        int r2;
        int scale; // fixed
        int lowscale; // fixed
        int silhouette;

        for (x = spr.x1; x <= spr.x2; x++)
            clipbot[x] = cliptop[x] = -2;

        // Scan drawsegs from end to start for obscuring segs.
        // The first drawseg that has a greater scale
        // is the clip seg.
        for (ds = seg_vars.ds_p - 1; ds >= 0; ds--) {
            // determine if the drawseg obscures the sprite
            // System.out.println("Drawseg "+ds+"of "+(ds_p-1));
            dss = seg_vars.drawsegs[ds];
            if (dss.x1 > spr.x2
                    || dss.x2 < spr.x1
                    || ((dss.silhouette == 0) && (dss
                            .nullMaskedTextureCol()))) {
                // does not cover sprite
                continue;
            }

            r1 = dss.x1 < spr.x1 ? spr.x1 : dss.x1;
            r2 = dss.x2 > spr.x2 ? spr.x2 : dss.x2;

            if (dss.scale1 > dss.scale2) {
                lowscale = dss.scale2;
                scale = dss.scale1;
            } else {
                lowscale = dss.scale1;
                scale = dss.scale2;
            }

            if (scale < spr.scale
                    || (lowscale < spr.scale && (dss.curline
                            .PointOnSegSide(spr.gx, spr.gy) == 0))) {
                // masked mid texture?
                if (!dss.nullMaskedTextureCol())
                    RenderMaskedSegRange(dss, r1, r2);
                // seg is behind sprite
                continue;
            }

            // clip this piece of the sprite
            silhouette = dss.silhouette;

            if (spr.gz >= dss.bsilheight)
                silhouette &= ~SIL_BOTTOM;

            if (spr.gzt <= dss.tsilheight)
                silhouette &= ~SIL_TOP;

            // BOTTOM clipping
            if (silhouette == 1) {
                // bottom sil
                for (x = r1; x <= r2; x++)
                    if (clipbot[x] == -2)
                        clipbot[x] = dss.getSprBottomClip(x);

            } else if (silhouette == 2) {
                // top sil
                for (x = r1; x <= r2; x++)
                    if (cliptop[x] == -2)
                        cliptop[x] = dss.getSprTopClip(x);
            } else if (silhouette == 3) {
                // both
                for (x = r1; x <= r2; x++) {
                    if (clipbot[x] == -2)
                        clipbot[x] = dss.getSprBottomClip(x);
                    if (cliptop[x] == -2)
                        cliptop[x] = dss.getSprTopClip(x);
                }
            }

        }

        // all clipping has been performed, so draw the sprite

        // check for unclipped columns
        for (x = spr.x1; x <= spr.x2; x++) {
            if (clipbot[x] == -2)
                clipbot[x] = (short) view.height;
            // ?? What's this bullshit?
            if (cliptop[x] == -2)
                cliptop[x] = -1;
        }

        mfloorclip = clipbot;
        p_mfloorclip = 0;
        mceilingclip = cliptop;
        p_mceilingclip = 0;
        DrawVisSprite(spr);
    }

    /**
     * R_DrawMasked Sorts and draws vissprites (room for optimization in
     * sorting func.) Draws masked textures. Draws player weapons and
     * overlays (psprites). Sorting function can be swapped for almost
     * anything, and it will work better, in-place and be simpler to draw,
     * too.
     */

    @Override
    public void DrawMasked() {
        // vissprite_t spr;
        int ds;
        drawseg_t dss;

        // Well, it sorts visspite objects.
        // It actually IS faster to sort with comparators, but you need to
        // go into NUTS.WAD-like wads.
        // numbers. The built-in sort if about as good as it gets. In fact,
        // it's hardly slower
        // to draw sprites without sorting them when using the built-in
        // modified mergesort, while
        // the original algorithm is so dreadful it actually does slow
        // things down.

        VIS.SortVisSprites();

        // If you are feeling adventurous, try these ones. They *might*
        // perform
        // better in very extreme situations where all sprites are always on
        // one side
        // of your view, but I hardly see any benefits in that. They are
        // both
        // much better than the original anyway.

        // combSort(vissprites,vissprite_p);
        // shellsort(vissprites,vissprite_p);

        // pQuickSprite.sort(vissprites);

        // The original sort. It's incredibly bad on so many levels (uses a
        // separate
        // linked list for the sorted sequence, which is pointless since the
        // vissprite_t
        // array is gonna be changed all over in the next frame anyway, it's
        // not like
        // it helps preseving or anything. It does work in Java too, but I'd
        // say to Keep Away. No srsly.

        /*
         * SortVisSprites (); // Sprite "0" not visible? /*if (vissprite_p >
         * 0) { // draw all vissprites back to front for (spr =
         * vsprsortedhead.next ; spr != vsprsortedhead ; spr=spr.next) {
         * DrawSprite (spr); } }
         */

        // After using in-place sorts, sprites can be drawn as simply as
        // that.

        colfunc = colfuncs.masked; // Sprites use fully-masked capable
                                 // function.

        final vissprite_t<V>[] vissprites = VIS.getVisSprites();
        final int numvissprites = VIS.getNumVisSprites();

        for (int i = 0; i < numvissprites; i++) {
            DrawSprite(vissprites[i]);
        }

        // render any remaining masked mid textures
        for (ds = seg_vars.ds_p - 1; ds >= 0; ds--) {
            dss = seg_vars.drawsegs[ds];
            if (!dss.nullMaskedTextureCol())
                RenderMaskedSegRange(dss, dss.x1, dss.x2);
        }
        // draw the psprites on top of everything
        // but does not draw on side views
        // if (viewangleoffset==0)

        colfunc = colfuncs.player;
        DrawPlayerSprites();
        colfunc = colfuncs.masked;
    }

    /**
     * R_DrawMaskedColumn Used for sprites and masked mid textures. Masked
     * means: partly transparent, i.e. stored in posts/runs of opaque
     * pixels. NOTE: this version accepts raw bytes, in case you know what
     * you're doing.
     */

   /* protected final void DrawMaskedColumn(T column) {
        int topscreen;
        int bottomscreen;
        int basetexturemid; // fixed_t
        int topdelta;
        int length;

        basetexturemid = maskedcvars.dc_texturemid;
        // That's true for the whole column.
        maskedcvars.dc_source = (T) column;
        int pointer = 0;

        // for each post...
        while ((topdelta = 0xFF & column[pointer]) != 0xFF) {
            // calculate unclipped screen coordinates
            // for post
            topscreen = sprtopscreen + spryscale * topdelta;
            length = 0xff & column[pointer + 1];
            bottomscreen = topscreen + spryscale * length;

            maskedcvars.dc_yl = (topscreen + FRACUNIT - 1) >> FRACBITS;
            maskedcvars.dc_yh = (bottomscreen - 1) >> FRACBITS;

            if (maskedcvars.dc_yh >= mfloorclip[p_mfloorclip
                    + maskedcvars.dc_x])
                maskedcvars.dc_yh =
                    mfloorclip[p_mfloorclip + maskedcvars.dc_x] - 1;

            if (maskedcvars.dc_yl <= mceilingclip[p_mceilingclip
                    + maskedcvars.dc_x])
                maskedcvars.dc_yl =
                    mceilingclip[p_mceilingclip + maskedcvars.dc_x] + 1;

            // killough 3/2/98, 3/27/98: Failsafe against overflow/crash:
            if (maskedcvars.dc_yl <= maskedcvars.dc_yh
                    && maskedcvars.dc_yh < view.height) {
                // Set pointer inside column to current post's data
                // Rremember, it goes {postlen}{postdelta}{pad}[data]{pad}
                maskedcvars.dc_source_ofs = pointer + 3;
                maskedcvars.dc_texturemid =
                    basetexturemid - (topdelta << FRACBITS);

                // Drawn by either R_DrawColumn
                // or (SHADOW) R_DrawFuzzColumn.
                maskedcvars.dc_texheight = 0; // Killough

                completeColumn();
            }
            pointer += length + 4;
        }

        maskedcvars.dc_texturemid = basetexturemid;
    }
    */


    /**
     * R_DrawMaskedColumn Used for sprites and masked mid textures. Masked
     * means: partly transparent, i.e. stored in posts/runs of opaque
     * pixels. FIXME: while it does work with "raw columns", if the initial
     * post is drawn outside of the screen the rest appear screwed up.
     * SOLUTION: use the version taking raw byte[] arguments.
     */

    protected final void DrawMaskedColumn(column_t column) {
        int topscreen;
        int bottomscreen;
        int basetexturemid; // fixed_t

        basetexturemid = maskedcvars.dc_texturemid;
        // That's true for the whole column.
        maskedcvars.dc_source = (T) column.data;
        // dc_source_ofs=0;

        // for each post...
        for (int i = 0; i < column.posts; i++) {
            maskedcvars.dc_source_ofs = column.postofs[i];
            // calculate unclipped screen coordinates
            // for post
            topscreen = sprtopscreen + spryscale * column.postdeltas[i];
            bottomscreen = topscreen + spryscale * column.postlen[i];

            maskedcvars.dc_yl = (topscreen + FRACUNIT - 1) >> FRACBITS;
            maskedcvars.dc_yh = (bottomscreen - 1) >> FRACBITS;

            if (maskedcvars.dc_yh >= mfloorclip[p_mfloorclip
                    + maskedcvars.dc_x])
                maskedcvars.dc_yh =
                    mfloorclip[p_mfloorclip + maskedcvars.dc_x] - 1;
            if (maskedcvars.dc_yl <= mceilingclip[p_mceilingclip
                    + maskedcvars.dc_x])
                maskedcvars.dc_yl =
                    mceilingclip[p_mceilingclip + maskedcvars.dc_x] + 1;

            // killough 3/2/98, 3/27/98: Failsafe against overflow/crash:
            if (maskedcvars.dc_yl <= maskedcvars.dc_yh
                    && maskedcvars.dc_yh < maskedcvars.viewheight) {

                // Set pointer inside column to current post's data
                // Remember, it goes {postlen}{postdelta}{pad}[data]{pad}

                maskedcvars.dc_texturemid =
                    basetexturemid - (column.postdeltas[i] << FRACBITS);

                // Drawn by either R_DrawColumn or (SHADOW)
                // R_DrawFuzzColumn.
                // MAES: when something goes bad here, it means that the
                // following:
                //
                // fracstep = dc_iscale;
                // frac = dc_texturemid + (dc_yl - centery) * fracstep;
                //
                // results in a negative initial frac number.

                // Drawn by either R_DrawColumn
                //  or (SHADOW) R_DrawFuzzColumn.
                
                // FUN FACT: this was missing and fucked my shit up.
                maskedcvars.dc_texheight=0; // Killough
                
                completeColumn();
                 
            }
        }

        maskedcvars.dc_texturemid = basetexturemid;
    }
    
    /*
     * R_DrawMaskedColumn
     * Used for sprites and masked mid textures.
     * Masked means: partly transparent, i.e. stored
     *  in posts/runs of opaque pixels.
     *  
     *  NOTE: this version accepts raw bytes, in case you  know what you're doing.
     *  NOTE: this is a legacy function. Do not reactivate unless
     *  REALLY needed.
     *
     */
/*
    protected final  void DrawMaskedColumn (byte[] column)
    {
        int topscreen;
        int bottomscreen;
        int basetexturemid; // fixed_t
        int topdelta;
        int length;
        
        basetexturemid = dc_texturemid;
        // That's true for the whole column.
        dc_source = column;
        int pointer=0;
        
        // for each post...
        while((topdelta=0xFF&column[pointer])!=0xFF)
        {
        // calculate unclipped screen coordinates
        //  for post
        topscreen = sprtopscreen + spryscale*topdelta;
        length=0xff&column[pointer+1];
        bottomscreen = topscreen + spryscale*length;

        dc_yl = (topscreen+FRACUNIT-1)>>FRACBITS;
        dc_yh = (bottomscreen-1)>>FRACBITS;
            
        if (dc_yh >= mfloorclip[p_mfloorclip+dc_x])
            dc_yh = mfloorclip[p_mfloorclip+dc_x]-1;
        
        if (dc_yl <= mceilingclip[p_mceilingclip+dc_x])
            dc_yl = mceilingclip[p_mceilingclip+dc_x]+1;

        // killough 3/2/98, 3/27/98: Failsafe against overflow/crash:
        if (dc_yl <= dc_yh && dc_yh < viewheight)
        {
            // Set pointer inside column to current post's data
            // Rremember, it goes {postlen}{postdelta}{pad}[data]{pad} 
            dc_source_ofs = pointer+3;
            dc_texturemid = basetexturemid - (topdelta<<FRACBITS);

            // Drawn by either R_DrawColumn
            //  or (SHADOW) R_DrawFuzzColumn.
            dc_texheight=0; // Killough
                
            maskedcolfunc.invoke();
        }
        pointer+=length + 4;
        }
        
        dc_texturemid = basetexturemid;
    }
      */

    @Override
    public void setPspriteIscale(int i) {
        pspriteiscale = i;

    }

    @Override
    public void setPspriteScale(int i) {
        pspritescale = i;
    }

    @Override
    public void setDetail(int detailshift){
        switch (detailshift){
        case HIGH_DETAIL:
            colfuncs=colfuncshi;
            break;
        case LOW_DETAIL:
            colfuncs=colfuncslow;
            break;
        }
    }
    
}

package rr;

//
// ?
//

public class drawseg_t {
	
	public drawseg_t(){
		
	}

     /** MAES: was pointer. Not array? */
     public  seg_t      curline;
     public int         x1, x2;

     /** fixed_t */
     public int     scale1,  scale2,  scalestep;

     /** 0=none, 1=bottom, 2=top, 3=both */
     public  int         silhouette;

     /** do not clip sprites above this (fixed_t) */
     public int     bsilheight;

     /** do not clip sprites below this (fixed_t) */
     public int     tsilheight;
     
     /** Indexes to lists for sprite clipping, 
        all three adjusted so [x1] is first value. */
     private int      psprtopclip, psprbottomclip, pmaskedtexturecol;

     /** Pointers to the actual lists  */

     private short[]      sprtopclip, sprbottomclip, maskedtexturecol;
     
     ///////////////// Accessor methods to simulate mid-array pointers ///////////
     
     public void setSprTopClip(short[] array, int index){
         this.sprtopclip=array;
         this.psprtopclip=index;         
     }
     
     public void setSprBottomClip(short[] array, int index){
         this.sprbottomclip=array;
         this.psprbottomclip=index;         
     }
     
     public void setMaskedTextureCol(short[] array, int index){
         this.maskedtexturecol=array;
         this.pmaskedtexturecol=index;         
     }
     
     public short getSprTopClip(int index){
         return this.sprtopclip[this.psprtopclip+index];
     }
     
     public short getSprBottomClip( int index){
         return this.sprbottomclip[this.psprbottomclip+index];     
         }
     
     public short getMaskedTextureCol(int index){
         return this.maskedtexturecol[this.pmaskedtexturecol+index];         
     }
     
     public short[] getSprTopClipList(){
         return this.sprtopclip;
     }
     
     public short[] getSprBottomClipList(){
         return this.sprbottomclip;
     }
     
     public short[] getMaskedTextureColList(){
         return this.maskedtexturecol;
     }
     
     public int getSprTopClipPointer(){
         return this.psprtopclip;
     }
     
     public int getSprBottomClipPointer(){
         return this.psprbottomclip;
     }
     
     public int getMaskedTextureColPointer(){
         return this.pmaskedtexturecol;
     }
     
     public void setSprTopClipPointer(int index){
         this.psprtopclip=index;
     }
     
     public void setSprBottomClipPointer(int index){
         this.psprbottomclip=index;
     }
     
     public void setMaskedTextureColPointer(int index){
         this.pmaskedtexturecol=index;
     }     
     
     public boolean nullSprTopClip(){
         return this.sprtopclip==null;
     }
     
     public boolean nullSprBottomClip(){
         return this.sprbottomclip==null;
     }
     
     public boolean nullMaskedTextureCol(){
         return this.maskedtexturecol==null;
     }
     
}

package rr;

import java.io.DataInputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Arrays;

import p.Interceptable;
import p.Resettable;
import s.degenmobj_t;
import w.DoomIO;
import w.IPackableDoomObject;
import w.IReadableDoomObject;
import static m.BBox.BOXBOTTOM;
import static m.BBox.BOXLEFT;
import static m.BBox.BOXRIGHT;
import static m.BBox.BOXTOP;
import static m.fixed_t.*;
import static utils.C2JUtils.eval;
import defines.slopetype_t;
import doom.thinker_t;

/** This is the actual linedef */

public class line_t
        implements Interceptable, IReadableDoomObject, IPackableDoomObject,
        Resettable {

    public static final char NO_INDEX=0xFFFF;
    
    public line_t() {
        sidenum = new char[2];
        bbox = new int[4];
        slopetype = slopetype_t.ST_HORIZONTAL;
    }

    /**
     * Vertices, from v1 to v2. NOTE: these are almost never passed as-such, nor
     * linked to Maybe we can get rid of them and only use the value semantics?
     */
    public vertex_t v1, v2;

    /** remapped vertex coords, for quick lookup with value semantics */
    public int v1x, v1y, v2x, v2y;

    /** (fixed_t) Precalculated v2 - v1 for side checking. */
    public int dx, dy;

    /** Animation related. */
    public short flags, special, tag;

    /**
     * Visual appearance: SideDefs. sidenum[1] will be 0xFFFF if one sided
     */
    public char[] sidenum;

    /**
     * Neat. Another bounding box, for the extent of the LineDef. MAES: make
     * this a proper bbox? fixed_t bbox[4];
     */
    public int[] bbox;

    /** To aid move clipping. */
    public slopetype_t slopetype;

    /**
     * Front and back sector. Note: redundant? Can be retrieved from SideDefs.
     * MAES: pointers
     */
    public sector_t frontsector, backsector;

    public int frontsectorid, backsectorid;

    /** if == validcount, already checked */
    public int validcount;

    /** thinker_t for reversable actions MAES: (void*) */
    public thinker_t specialdata;

    public int specialdataid;

    public degenmobj_t soundorg;

    // From Boom
    public int tranlump;
    
    public int id;
    
    /** killough 4/17/98: improves searches for tags. */
    public int firsttag,nexttag;    

    /** For Boom stuff, interprets sidenum specially */
    public int getSpecialSidenum() {
        return (sidenum[0] << 16) & (0x0000ffff & sidenum[1]);
    }

    public void assignVertexValues() {
        this.v1x = v1.x;
        this.v1y = v1.y;
        this.v2x = v2.x;
        this.v2y = v2.y;

    }

    /**
     * P_PointOnLineSide
     * 
     * @param x
     *        fixed_t
     * @param y
     *        fixed_t
     * @return 0 or 1 (false, true) - (front, back)
     */
    public boolean PointOnLineSide(int x, int y)

    {
    	

    	  return
    			    (dx==0) ? x <= this.v1x ? this.dy > 0 : this.dy < 0 :
    			    (dy==0) ? y <= this.v1y ? this.dx < 0 : this.dx > 0 :
    			    FixedMul(y-this.v1y, this.dx>>FRACBITS) >=
    			    FixedMul(this.dy>>FRACBITS, x-this.v1x);
    	/*
        int dx, dy, left, right;
        if (this.dx == 0) {
            if (x <= this.v1x)
                return this.dy > 0;

            return this.dy < 0;
        }
        if (this.dy == 0) {
            if (y <= this.v1y)
                return this.dx < 0;

            return this.dx > 0;
        }

        dx = (x - this.v1x);
        dy = (y - this.v1y);

        left = FixedMul(this.dy >> FRACBITS, dx);
        right = FixedMul(dy, this.dx >> FRACBITS);

        if (right < left)
            return false; // front side
        return true; // back side*/
    }

    /**
     * P_BoxOnLineSide Considers the line to be infinite Returns side 0 or 1, -1
     * if box crosses the line. Doubles as a convenient check for whether a
     * bounding box crosses a line at all
     * 
     * @param tmbox
     *        fixed_t[]
     */
    public int BoxOnLineSide(int[] tmbox) {
        boolean p1 = false;
        boolean p2 = false;

        switch (this.slopetype) {
        // Line perfectly horizontal, box floating "north" of line
        case ST_HORIZONTAL:
            p1 = tmbox[BOXTOP] > v1y;
            p2 = tmbox[BOXBOTTOM] > v1y;
            if (dx < 0) {
                p1 ^= true;
                p2 ^= true;
            }
            break;

        // Line perfectly vertical, box floating "west" of line
        case ST_VERTICAL:

            p1 = tmbox[BOXRIGHT] < v1x;
            p2 = tmbox[BOXLEFT] < v1x;
            if (dy < 0) {
                p1 ^= true;
                p2 ^= true;
            }
            break;

        case ST_POSITIVE:
            // Positive slope, both points on one side.
            p1 = PointOnLineSide(tmbox[BOXLEFT], tmbox[BOXTOP]);
            p2 = PointOnLineSide(tmbox[BOXRIGHT], tmbox[BOXBOTTOM]);
            break;

        case ST_NEGATIVE:
            // Negative slope, both points (mirrored horizontally) on one side.
            p1 = PointOnLineSide(tmbox[BOXRIGHT], tmbox[BOXTOP]);
            p2 = PointOnLineSide(tmbox[BOXLEFT], tmbox[BOXBOTTOM]);
            break;
        }

        if (p1 == p2)
            return p1 ? 1 : 0;
        // Any other result means non-inclusive crossing.
        return -1;
    }

    /**
     * Variant of P_BoxOnLineSide. Uses inclusive checks, so that even lines on
     * the border of a box will be considered crossing. This is more useful for
     * building blockmaps.
     * 
     * @param tmbox
     *        fixed_t[]
     */
    public int BoxOnLineSideInclusive(int[] tmbox) {
        boolean p1 = false;
        boolean p2 = false;

        switch (this.slopetype) {
        // Line perfectly horizontal, box floating "north" of line
        case ST_HORIZONTAL:
            p1 = tmbox[BOXTOP] >= v1y;
            p2 = tmbox[BOXBOTTOM] >= v1y;
            if (dx < 0) {
                p1 ^= true;
                p2 ^= true;
            }
            break;

        // Line perfectly vertical, box floating "west" of line
        case ST_VERTICAL:

            p1 = tmbox[BOXRIGHT] <= v1x;
            p2 = tmbox[BOXLEFT] <= v1x;
            if (dy < 0) {
                p1 ^= true;
                p2 ^= true;
            }
            break;

        case ST_POSITIVE:
            // Positive slope, both points on one side.
            p1 = PointOnLineSide(tmbox[BOXLEFT], tmbox[BOXTOP]);
            p2 = PointOnLineSide(tmbox[BOXRIGHT], tmbox[BOXBOTTOM]);
            break;

        case ST_NEGATIVE:
            // Negative slope, both points (mirrored horizontally) on one side.
            p1 = PointOnLineSide(tmbox[BOXRIGHT], tmbox[BOXTOP]);
            p2 = PointOnLineSide(tmbox[BOXLEFT], tmbox[BOXBOTTOM]);
            break;
        }

        if (p1 == p2)
            return p1 ? 1 : 0;
        // Any other result means non-inclusive crossing.
        return -1;
    }

    /**
     * getNextSector() Return sector_t * of sector next to current. NULL if not
     * two-sided line
     */

    public sector_t getNextSector(sector_t sec) {
        if (!eval(flags& ML_TWOSIDED))
            return null;

        if (frontsector == sec)
            return backsector;

        return frontsector;
    }

    public String toString() {
        return (String.format("Line %d Flags: %x Special %d Tag: %d ", this.id,this.flags,
            this.special, this.tag));
    }

    @Override
    public void read(DataInputStream f)
            throws IOException {

        // For histerical reasons, these are the only parts of line_t that
        // are archived in vanilla savegames. Go figure.
        this.flags = DoomIO.readLEShort(f);
        this.special = DoomIO.readLEShort(f);
        this.tag = DoomIO.readLEShort(f);
    }

    @Override
    public void pack(ByteBuffer buffer) {
        buffer.putShort(flags);
        buffer.putShort(special);
        buffer.putShort(tag);
        // buffer.putShort((short) 0XDEAD);
        // buffer.putShort((short) 0XBABE);
        // buffer.putShort((short) 0XBEEF);
    }

    @Override
    public void reset() {
        v1 = v2 = null;
        v1x = v1y = v2x = v2y = 0;
        dx = dy = 0;
        flags = special = tag = 0;
        Arrays.fill(sidenum, (char) 0);
        Arrays.fill(bbox, 0);
        slopetype = slopetype_t.ST_HORIZONTAL;
        frontsector = backsector = null;
        frontsectorid = backsectorid = 0;
        validcount = 0;
        specialdata = null;
        specialdataid = 0;
        soundorg = null;
        tranlump = 0;
    }
    
    /**
     * LUT, motion clipping, walls/grid element // // LineDef attributes. // /**
     * Solid, is an obstacle.
     */
    public static final int ML_BLOCKING = 1;

    /** Blocks monsters only. */
    public static final int ML_BLOCKMONSTERS = 2;

    /** Backside will not be present at all if not two sided. */
    public static final int ML_TWOSIDED = 4;

    // If a texture is pegged, the texture will have
    // the end exposed to air held constant at the
    // top or bottom of the texture (stairs or pulled
    // down things) and will move with a height change
    // of one of the neighbor sectors.
    // Unpegged textures allways have the first row of
    // the texture at the top pixel of the line for both
    // top and bottom textures (use next to windows).

    /** upper texture unpegged */
    public static final int ML_DONTPEGTOP = 8;

    /** lower texture unpegged */
    public static final int ML_DONTPEGBOTTOM = 16;

    /** In AutoMap: don't map as two sided: IT'S A SECRET! */
    public static final int ML_SECRET = 32;

    /** Sound rendering: don't let sound cross two of these. */
    public static final int ML_SOUNDBLOCK = 64;

    /** Don't draw on the automap at all. */
    public static final int ML_DONTDRAW = 128;

    /** Set if already seen, thus drawn in automap. */
    public static final int ML_MAPPED = 256;

}

package rr;

import data.Limits;
import utils.C2JUtils;

public class SegVars {
	// /// FROM BSP /////////
	
    public int MAXDRAWSEGS = Limits.MAXDRAWSEGS;

	/** pointer to drawsegs */
    public int ds_p;

    public drawseg_t[] drawsegs;

    public short[] maskedtexturecol;
    public int pmaskedtexturecol = 0;


	/**
	 * R_ClearDrawSegs
	 * 
	 * The drawseg list is reset by pointing back at 0.
	 * 
	 */
	public void ClearDrawSegs() {
		ds_p = 0;
	}

	public final void ResizeDrawsegs() {
		drawsegs = C2JUtils.resize(drawsegs[0], drawsegs, drawsegs.length*2);
	}
}

package rr;
public class pQuickSprite{

    public static final void sort(vissprite_t[] c){
        int i,j,left = 0,right = c.length - 1,stack_pointer = -1;
        int[] stack = new int[128];
        vissprite_t swap,temp;
        while(true){
            if(right - left <= 7){
                for(j=left+1;j<=right;j++){
                    swap = c[j];
                    i = j-1;
                    while(i>=left && c[i].scale>swap.scale)
                        c[i+1] = c[i--];
                    c[i+1] = swap;
                }
                if(stack_pointer == -1)
                    break;
                right = stack[stack_pointer--];
                left = stack[stack_pointer--];
            }else{
                int median = (left + right) >> 1;
                i = left + 1;
                j = right;
                swap = c[median]; c[median] = c[i]; c[i] = swap;
                /* make sure: c[left] <= c[left+1] <= c[right] */
                if(c[left].scale > c[right].scale){
                    swap = c[left]; c[left] = c[right]; c[right] = swap;
                }if(c[i].scale>c[right].scale ){
                    swap = c[i]; c[i] = c[right]; c[right] = swap;
                }if(c[left].scale>c[i].scale){
                    swap = c[left]; c[left] = c[i]; c[i] = swap;
                }
                temp = c[i];
                while(true){
                    do i++; while(c[i].scale<temp.scale);
                    do j--; while(c[j].scale>temp.scale);
                    if(j < i)
                        break;
                    swap = c[i]; c[i] = c[j]; c[j] = swap;
                }
                c[left + 1] = c[j];
                c[j] = temp;
                if(right-i+1 >= j-left){
                    stack[++stack_pointer] = i;
                    stack[++stack_pointer] = right;
                    right = j-1;
                }else{
                    stack[++stack_pointer] = left;
                    stack[++stack_pointer] = j-1;
                    left = i;
                }
            }
        }
    }
}
    
package rr;

/** Sprites are patches with a special naming convention
 * so they can be recognized by R_InitSprites.
 * The base name is NNNNFx or NNNNFxFx, with
 * x indicating the rotation, x = 0, 1-7.
 * The sprite and frame specified by a thing_t
 * is range checked at run time.
 * A sprite is a patch_t that is assumed to represent
 * a three dimensional object and may have multiple
 * rotations pre drawn.
 * Horizontal flipping is used to save space,
 * thus NNNNF2F5 defines a mirrored patch.
 * Some sprites will only have one picture used
 * for all views: NNNNF0
 */
public class spriteframe_t implements Cloneable{

 public spriteframe_t(){
     lump=new int[8];
     flip=new byte[8];
 }

 /** If false use 0 for any position.
  * Note: as eight entries are available,
  * we might as well insert the same name eight times. 
  * 
  * FIXME: this is used as a tri-state.
  * 0= false
  * 1= true
  * -1= cleared/indeterminate, which should not evaluate to either true or false.
  * */
 public int rotate;

 /** Lump to use for view angles 0-7. */
 public int[]    lump;

 /** Flip bit (1 = flip) to use for view angles 0-7. */
 public byte[]    flip;
 
 public spriteframe_t clone(){
     spriteframe_t response=new spriteframe_t();
     response.rotate=rotate;     
     System.arraycopy(this.lump, 0, response.lump, 0, lump.length);
     System.arraycopy(this.flip, 0, response.flip, 0, flip.length);
     return response;
     
 }
 

}

package rr;

import static p.MobjFlags.MF_TRANSLATION;
import static p.MobjFlags.MF_TRANSSHIFT;
import i.Main;

/**
 *   Combined colormap and light LUTs.
 *   Used for z-depth cuing per column/row,
 *   and other lighting effects (sector ambient, flash).
 *   
 * @author velktron
 *
 * @param <V> The data type of the SCREEN
 */

public class LightsAndColors<V> {
    
    /** For HiColor, these are, effectively, a bunch of 555 RGB palettes,
     *  for TrueColor they are a bunch of 32-bit ARGB palettes etc.
     *  Only for indexed they represent index remappings.  
     */

    /** "peg" this to the one from RendererData */
    public V[] colormaps;

    /** lighttable_t** */
    public V[] walllights;

    /** Use in conjunction with pfixedcolormap */
    public V fixedcolormap;
    /** Use in conjunction with fixedcolormap[] */
    public int pfixedcolormap;
	
	/**
	 * Color tables for different players, translate a limited part to another
	 * (color ramps used for suit colors).
	 */

	public byte[][] translationtables;
	
	
	/** Bits representing color levels. 5 for 32. */
	public static final int LBITS;
	
    /**
     * These two are tied by an inverse relationship. E.g. 256 levels, 0 shift
     * 128 levels, 1 shift ...etc... 16 levels, 4 shift (default). Or even less,
     * if you want.
     * 
     * By setting it to the max however you get smoother light and get rid of
     * lightsegshift globally, too. Of course, by increasing the number of light
     * levels, you also put more memory pressure, and due to their being only
     * 256 colors to begin with, visually, there won't be many differences.
     */
	
	 
    public static final int LIGHTLEVELS;
    public static final int LIGHTSEGSHIFT;
    
    
  /** Number of diminishing brightness levels.
     There a 0-31, i.e. 32 LUT in the COLORMAP lump. 
     TODO: how can those be distinct from the light levels???
     */    
    
  public static final int  NUMCOLORMAPS;

    
    // These are a bit more tricky to figure out though.

    /** Maximum index used for light levels of sprites. In practice,
     *  it's capped by the number of light levels???
     *  
     *  Normally set to 48 (32 +16???)
     */
    
    public static final int MAXLIGHTSCALE;
    
    /** Used to scale brightness of walls and sprites. Their "scale" is shifted by
     *  this amount, and this results in an index, which is capped by MAXLIGHTSCALE.
     *  Normally it's 12 for 32 levels, so 11 for 64, 10 for 128, ans 9 for 256.
     *  
     */
    public static final int LIGHTSCALESHIFT;
    
    /** This one seems arbitrary. Will auto-fit to 128 possible levels? */
    public static final int MAXLIGHTZ;
    
    
    public static final int LIGHTBRIGHT;
    
    /** Normally 20 for 32 colormaps, applied to distance.
     * Formula: 25-LBITS
     *  
     */
    public static final int LIGHTZSHIFT;

    public V[][] scalelight;
    public V[] scalelightfixed;
    public V[][] zlight;
    public V[] spritelights;

    // bumped light from gun blasts
    public int extralight;
    
    static {

        // Horrible hack.
        
        switch (Main.bpp){
        case Indexed:
        case TrueColor32:
        case HiColor:
            LBITS=5;
            break;
        case TrueColor:
            LBITS=8;
            break;
        default:
            LBITS=5;            
            break;
        }
        
        LIGHTLEVELS = 1<<LBITS;
        MAXLIGHTZ=Math.max(LIGHTLEVELS*4,256);
        LIGHTBRIGHT= 2;
        LIGHTSEGSHIFT = 8-LBITS;
        NUMCOLORMAPS=     LIGHTLEVELS;
        MAXLIGHTSCALE = 3*LIGHTLEVELS/2;
        LIGHTSCALESHIFT = 17        -LBITS;
        LIGHTZSHIFT=25-LBITS;
    }

    public final byte[] getTranslationTable(long mobjflags) {
        return translationtables[(int) ((mobjflags & MF_TRANSLATION)>>MF_TRANSSHIFT)];
    }
    
}

package rr;


/** A maptexturedef_t describes a rectangular texture,
 *  which is composed of one or more mappatch_t structures
 *  that arrange graphic patches.
 *  
 *  This is the in-memory format, which is similar to maptexture_t (which is on-disk).
 *  
 * @author Maes
 *
 */

public class texture_t {
    /** Keep name for switch changing, etc. */
    public String    name;        
    public short   width;
    public short   height;
    
    // All the patches[patchcount]
    //  are drawn back to front into the cached texture.
    public short   patchcount;
    public texpatch_t[]  patches;     
    
    /** Unmarshalling at its best! */
    
    public void copyFromMapTexture(maptexture_t mt){
        this.name=new String(mt.name);
        this.width=mt.width;
        this.height=mt.height;
        this.patchcount=mt.patchcount;
        this.patches=new texpatch_t[patchcount];
        
        for (int i=0;i<patchcount;i++){
            patches[i]=new texpatch_t();
            patches[i].copyFromMapPatch(mt.patches[i]);
        }
    }
    
    @Override
    public String toString(){
        StringBuilder sb=new StringBuilder();
        sb.append(name);
        sb.append(" Height ");
        sb.append(height);
        sb.append(" Width ");
        sb.append(width);
        sb.append(" Patchcount ");
        sb.append(patchcount);
        return sb.toString();
            
        }    
}
package rr;

/** Resets limit-removing stuff back to their initial values,
 *  either for initialization reasons or to regain memory 
 *  e.g. playing MAP02 after nuts.wad should free up some vissprite buffers. 
 *  
 * @author admin
 *
 */

public interface ILimitResettable {
    public void resetLimits();   
}

package rr;

import doom.thinker_t;

/** Used for special sector-based function for doors, ceilings 
 *  etc. that are treated as a thinker by the engine. The sector
 *  is part of the spec, so extending classes don't need to override 
 *  it. Also, it extends thinker so futher extensions are thinkers too.
 * 
 */
 
public abstract class SectorAction extends thinker_t {
  
    public sector_t sector;
    
    /** Special, only used when (un)archiving in order to re-link stuff
     *  to their proper sector.
     */
    public int sectorid;
}

package rr;

import static m.fixed_t.FRACBITS;
import static m.fixed_t.FixedMul;
import static utils.C2JUtils.eval;

import java.util.Arrays;

import p.Resettable;
import utils.C2JUtils;
import m.BBox;
import m.ISyncLogger;

/** BSP node.
 * 
 * @author Maes
 *
 */
public class node_t implements Resettable{

    
    public node_t(){
        bbox=new BBox[2];
        children= new int[2];
        C2JUtils.initArrayOfObjects(bbox, BBox.class);
    }
    
     public node_t(int x, int y, int dx, int dy, BBox[] bbox,
            int[] children) {
        this.x = x;
        this.y = y;
        this.dx = dx;
        this.dy = dy;
        this.bbox = bbox;
        this.children = children;
    }


    /** (fixed_t) Partition line. */
     public int x, y, dx, dy;

     /** Bounding box for each child. */
     //public fixed_t bbox[2][4];
     // Maes: make this into two proper bboxes?
          
     public BBox[] bbox;
     

     /** If NF_SUBSECTOR its a subsector. 
      * 
      * e6y: support for extented nodes
      */
     public int[] children;
     
     /**
      * R_PointOnSide
      * Traverse BSP (sub) tree,
      *  check point against partition plane.
      * Returns side 0 (front) or 1 (back).
      * @param x fixed
      * @param y fixed
      * 
      */

     public static int PointOnSide
     ( int   x,
       int   y,
       node_t    node )
     {
         // MAES: These are used mainly as ints, no need to use fixed_t internally.
         // fixed_t will only be used as a "pass type", but calculations will be done with ints, preferably.
         int dx; 
         int dy;
         int left;
         int right;
         
         if (node.dx==0)
         {
         if (x <= node.x)
             return (node.dy > 0)?1:0;
         
         return (node.dy < 0)?1:0;
         }
         if (node.dy==0)
         {
         if (y <= node.y)
             return (node.dx < 0)?1:0;
         
         return (node.dx > 0)?1:0;
         }
         
         dx = (x - node.x);
         dy = (y - node.y);
         
         // Try to quickly decide by looking at sign bits.
         if ( ((node.dy ^ node.dx ^ dx ^ dy)&0x80000000 )!=0)
         {
         if  ( ((node.dy ^ dx) & 0x80000000 )!=0)
         {
             // (left is negative)
             return 1;
         }
         return 0;
         }

         left = FixedMul ( node.dy>>FRACBITS , dx );
         right = FixedMul ( dy , node.dx>>FRACBITS );
         
         if (right < left)
         {
         // front side
         return 0;
         }
         // back side
         return 1;           
     }
     
   /** Since no context is needed, this is perfect for an instance method 
    *      
    * @param x fixed
    * @param y fixed
    * @return
    */
     public int PointOnSide
     ( int   x,
       int   y
       )
     {
         // MAES: These are used mainly as ints, no need to use fixed_t internally.
         // fixed_t will only be used as a "pass type", but calculations will be done with ints, preferably.
         int dx; 
         int dy;
         int left;
         int right;
         
         if (this.dx==0)
         {
         if (x <= this.x)
             return (this.dy > 0)?1:0;
         
         return (this.dy < 0)?1:0;
         }
         if (this.dy==0)
         {
         if (y <= this.y)
             return (this.dx < 0)?1:0;
         
         return (this.dx > 0)?1:0;
         }
         
         dx = (x - this.x);
         dy = (y - this.y);
         
         // Try to quickly decide by looking at sign bits.
         if ( ((this.dy ^ this.dx ^ dx ^ dy)&0x80000000 )!=0)
         {
         if  ( ((this.dy ^ dx) & 0x80000000 )!=0)
         {
             // (left is negative)
             return 1;
         }
         return 0;
         }

         left = FixedMul ( this.dy>>FRACBITS , dx );
         right = FixedMul ( dy , this.dx>>FRACBITS );
         
         if (right < left)
         {
         // front side
         return 0;
         }
         // back side
         return 1;           
     }

  /**
   * Clone of divline_t's method. Same contract, but working on node_t's to avoid casts.
   * P_DivlineSide
   * Returns side 0 (front), 1 (back), or 2 (on).
   */
 	public int
 	DivlineSide
 	( int	x,
 	  int	y)
 	{
 	    
 	      int left,right;
 	        // Boom-style code. Da fack.
 	      // [Maes]: using it leads to very different DEMO4 UD behavior.
 	        
 	       return
 	      (this.dx==0) ? x == this.x ? 2 : x <= this.x ? eval(this.dy > 0) : eval(this.dy < 0) :
 	      (this.dy==0) ? (olddemo ? x : y) == this.y ? 2 : y <= this.y ? eval(this.dx < 0) : eval(this.dx > 0) :
 	      (this.dy==0) ? y == this.y ? 2 : y <= this.y ? eval(this.dx < 0) : eval(this.dx > 0) :
 	      (right = ((y - this.y) >> FRACBITS) * (this.dx >> FRACBITS)) <
 	      (left  = ((x - this.x) >> FRACBITS) * (this.dy >> FRACBITS)) ? 0 :
 	      right == left ? 2 : 1;
 	      
 	    
 	      /*
 	    int	dx; // fixed_t
 	    int	dy;

 	    if (this.dx==0)
 	    {
 		if (x==this.x)
 		    return 2;
 		
 		if (x <= this.x)
 		    return (this.dy > 0)?1:0;

 		return (this.dy < 0)?1:0;
 	    }
 	    
 	    if (this.dy==0)
 	    {
 		if (x==this.y)
 		    return 2;

 		if (y <= this.y)
 		    return (this.dx < 0)?1:0;

 		return (this.dx > 0)?1:0;
 	    }
 		
 	    dx = (x - this.x);
 	    dy = (y - this.y);

 	    left =  (this.dy>>FRACBITS) * (dx>>FRACBITS);
 	    right = (dy>>FRACBITS) * (this.dx>>FRACBITS);
 		
 	    if (right < left)
 		return 0;	// front side
 	    
 	    if (left == right)
 		return 2;
 	    return 1;		// back side
 	    */
 	    
 	}
 	
 	private static final boolean olddemo = true;
 	
 	  public int
 	    DivlineSide
 	    ( int   x,
 	      int   y,
 	      ISyncLogger SL,
 	      boolean sync)
 	    {
 	      int result=DivlineSide(x,y);
 	      if (sync){
 	         SL.sync("DLS %d\n",
 	            result);
 	      }
 	      return result;
 	      
 	    }
@Override
    public void reset() {
    x=y=dx = dy = 0;
    for (int i=0;i<2;i++){
        bbox[i].ClearBox();
        }
    Arrays.fill(children, 0);
    
    }
     
 }


package rr;

/**
 * A very "simple" things class which just does serial rendering and uses all
 * the base methods from AbstractThings.
 * 
 * @author velktron
 * @param <T>
 * @param <V>
 */


public final class SimpleThings<T,V>
        extends AbstractThings<T,V> {

    public SimpleThings(Renderer<T, V> R) {
        super(R);
    }

    @Override
    public void completeColumn() {
        colfunc.invoke();
        }
}

package rr;

/** An interface used to ease the use of the GetCachedColumn by part
 *  of parallelized renderers.
 *  
 * @author Maes
 *
 */

public interface IGetCachedColumn<T> {

	T GetCachedColumn(int tex, int col);

}

package rr.parallel;

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.Executor;

import rr.AbstractThings;
import rr.IMaskedDrawer;
import rr.ISpriteManager;
import rr.IVisSpriteManagement;
import rr.Renderer;
import v.IVideoScale;

/**  Alternate parallel sprite renderer using a split-screen strategy.
 *  For N threads, each thread gets to render only the sprites that are entirely
 *  in its own 1/Nth portion of the screen.
 *  
 *  Sprites that span more than one section, are drawn partially. Each thread
 *  only has to worry with the priority of its own sprites. Similar to the 
 *  split-seg parallel drawer.
 * 
 *  Uses the "masked workers" subsystem, there is no column pipeline: workers
 *  "tap" directly in the sprite sorted table and act accordingly (draw entirely,
 *  draw nothing, draw partially).
 *  
 *  It uses masked workers to perform the actual work, each of which is a complete
 *  Thing Drawer. 
 * 
 * @author velktron
 *
 */

public final class ParallelThings2<T,V>
        implements IMaskedDrawer<T,V> {

    MaskedWorker<T,V>[] maskedworkers;
    CyclicBarrier maskedbarrier;
    Executor tp;
    protected final IVisSpriteManagement<V> VIS;
    
    public ParallelThings2(Renderer<T,V> R) {
        this.VIS=R.getVisSpriteManager();

    }

    @Override
    public void DrawMasked() {

        VIS.SortVisSprites();

        for (int i = 0; i < maskedworkers.length; i++) {
            tp.execute(maskedworkers[i]);
        }

        try {
            maskedbarrier.await();
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (BrokenBarrierException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }

    @Override
    public void completeColumn() {
        // Does nothing. Dummy.
    }

    @Override
    public void setPspriteScale(int scale) {
        for (int i = 0; i < maskedworkers.length; i++)
            maskedworkers[i].setPspriteScale(scale);
    }

    @Override
    public void setPspriteIscale(int scale) {
        for (int i = 0; i < maskedworkers.length; i++)
            maskedworkers[i].setPspriteIscale(scale);
    }

    @Override
    public void setVideoScale(IVideoScale vs) {
        for (int i = 0; i < maskedworkers.length; i++)
            maskedworkers[i].setVideoScale(vs);
    }

    @Override
    public void initScaling() {
        for (int i = 0; i < maskedworkers.length; i++)
            maskedworkers[i].initScaling();
        
    }

    @Override
    public void setDetail(int detailshift) {
        for (int i = 0; i < maskedworkers.length; i++)
            maskedworkers[i].setDetail(detailshift);
        
    }

    @Override
    public void cacheSpriteManager(ISpriteManager SM) {
        for (int i = 0; i < maskedworkers.length; i++)
            maskedworkers[i].cacheSpriteManager(SM);
        
    }

}

package rr.parallel;

/** This is all the information needed to draw a particular SEG.
 * It's quite a lot, actually, but much better than in testing
 * versions.
 *  
 */

public class RenderSegInstruction<V> {    
	public int     rw_x,rw_stopx;
	public int toptexture,midtexture,bottomtexture;
	public int  pixhigh,pixlow,pixhighstep,pixlowstep,
	topfrac,    topstep,bottomfrac, bottomstep;	
	public boolean segtextured,markfloor,markceiling;
	public long     rw_centerangle; // angle_t
	/** fixed_t */
	public int     rw_offset,rw_distance,rw_scale,
	rw_scalestep,rw_midtexturemid,rw_toptexturemid,rw_bottomtexturemid;
	public int viewheight;
	V[] walllights;
	public int centery;

}

package rr.parallel;

import i.IDoomSystem;

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

import rr.IDetailAware;
import rr.drawfuns.ColVars;
import rr.drawfuns.DcFlags;
import rr.drawfuns.DoomColumnFunction;
import rr.drawfuns.R_DrawColumnBoom;
import rr.drawfuns.R_DrawColumnBoomLow;
import rr.drawfuns.R_DrawFuzzColumn;
import rr.drawfuns.R_DrawFuzzColumnLow;
import rr.drawfuns.R_DrawTranslatedColumn;
import rr.drawfuns.R_DrawTranslatedColumnLow;

/**
 * This is what actual executes the RenderWallInstruction. Essentially it's a
 * self-contained column rendering function.
  * 
 * @author velktron
 */

public abstract class RenderMaskedExecutor<T,V>
        implements Runnable,IDetailAware {

    protected CyclicBarrier barrier;

    protected ColVars<T,V>[] RMI;
   
    protected int rmiend;

    protected boolean lowdetail=false;
    
    protected int start, end;

    protected DoomColumnFunction<T,V> colfunchi, colfunclow;
    protected DoomColumnFunction<T,V> fuzzfunchi, fuzzfunclow;
    protected DoomColumnFunction<T,V> transfunchi, transfunclow;
    
    protected DoomColumnFunction<T,V> colfunc;

    public RenderMaskedExecutor(int SCREENWIDTH, int SCREENHEIGHT,            
            ColVars<T,V>[] RMI, CyclicBarrier barrier
            ) {
        this.RMI = RMI;
        this.barrier = barrier;
        this.SCREENWIDTH = SCREENWIDTH;
        this.SCREENHEIGHT = SCREENHEIGHT;
    }

    public void setRange(int start, int end) {
        this.end = end;
        this.start = start;
    }

    public void setDetail(int detailshift) {
        if (detailshift == 0)
            lowdetail=false;
        else
            lowdetail=true;
    }

    public void run() {

        // System.out.println("Wall executor from "+start +" to "+ end);
        int dc_flags=0;
        
        // Check out ALL valid RMIs, but only draw those on YOUR side of the screen.
        for (int i = 0; i < rmiend; i++) {
            
            if (RMI[i].dc_x>=start && RMI[i].dc_x<=end){
            // Change function type according to flags.
            // No flag change means reusing the last used type
            dc_flags=RMI[i].dc_flags;
                //System.err.printf("Flags transition %d\n",dc_flags);
                if (lowdetail){
                    if ((dc_flags&DcFlags.FUZZY)!=0)                        
                        colfunc=fuzzfunclow;
                    else
                    if ((dc_flags&DcFlags.TRANSLATED)!=0)
                            colfunc=transfunclow;
                    else
                        colfunc=colfunclow;
                } else {
                    if ((dc_flags&DcFlags.FUZZY)!=0)
                        colfunc=fuzzfunchi;
                    else
                    if ((dc_flags&DcFlags.TRANSLATED)!=0)
                        colfunc=transfunchi;
                    else
                        colfunc=colfunchi;
                    }
            
            // No need to set shared DCvars, because it's passed with the arg.
            colfunc.invoke(RMI[i]);
            }
        }

        try {
            barrier.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (BrokenBarrierException e) {
            e.printStackTrace();
        }
    }
        
    public void setRMIEnd(int rmiend){
        this.rmiend=rmiend;
    }         

    public void updateRMI(ColVars<T,V>[] RMI) {
        this.RMI = RMI;

    }

    /////////////// VIDEO SCALE STUFF//////////////////////

    protected final int SCREENWIDTH;

    protected final int SCREENHEIGHT;
    /*
     * protected IVideoScale vs;
     * @Override public void setVideoScale(IVideoScale vs) { this.vs=vs; }
     * @Override public void initScaling() {
     * this.SCREENHEIGHT=vs.getScreenHeight();
     * this.SCREENWIDTH=vs.getScreenWidth(); }
     */
    
    public static final class HiColor extends RenderMaskedExecutor<byte[],short[]>{

        public HiColor(int SCREENWIDTH, int SCREENHEIGHT, int[] columnofs,
                int[] ylookup, short[] screen, ColVars<byte[], short[]>[] RMI,
                CyclicBarrier barrier,IDoomSystem I) {
            super(SCREENWIDTH, SCREENHEIGHT,RMI, barrier);
            
            // Regular masked columns
            this.colfunc = new R_DrawColumnBoom.HiColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,null,screen,I);
            this.colfunclow = new R_DrawColumnBoomLow.HiColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,null,screen,I);
            
            // Fuzzy columns
            this.fuzzfunchi= new R_DrawFuzzColumn.HiColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,null,screen,I);
            this.fuzzfunclow =new R_DrawFuzzColumnLow.HiColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,null,screen,I);

            // Translated columns
            this.transfunchi=new R_DrawTranslatedColumn.HiColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,null,screen,I);
            this.transfunclow= new R_DrawTranslatedColumnLow.HiColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,null,screen,I);

        }
        
    }
    
    public static final class Indexed extends RenderMaskedExecutor<byte[],byte[]>{

        public Indexed(int SCREENWIDTH, int SCREENHEIGHT, int[] columnofs,
                int[] ylookup, byte[] screen, ColVars<byte[], byte[]>[] RMI,
                CyclicBarrier barrier,IDoomSystem I,byte[] BLURRY_MAP) {
            super(SCREENWIDTH, SCREENHEIGHT,RMI, barrier);
            
            // Regular masked columns
            this.colfunc = new R_DrawColumnBoom.Indexed(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,null,screen,I);
            this.colfunclow = new R_DrawColumnBoomLow.Indexed(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,null,screen,I);
            
            // Fuzzy columns
            this.fuzzfunchi= new R_DrawFuzzColumn.Indexed(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,null,screen,I,BLURRY_MAP);
            this.fuzzfunclow =new R_DrawFuzzColumnLow.Indexed(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,null,screen,I,BLURRY_MAP);

            // Translated columns
            this.transfunchi=new R_DrawTranslatedColumn.Indexed(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,null,screen,I);
            this.transfunclow= new R_DrawTranslatedColumnLow.Indexed(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,null,screen,I);

        }
        
    }
    
    public static final class TrueColor extends RenderMaskedExecutor<byte[],int[]>{

        public TrueColor(int SCREENWIDTH, int SCREENHEIGHT, int[] columnofs,
                int[] ylookup, int[] screen, ColVars<byte[], int[]>[] RMI,
                CyclicBarrier barrier,IDoomSystem I) {
            super(SCREENWIDTH, SCREENHEIGHT,RMI, barrier);
            
            // Regular masked columns
            this.colfunc = new R_DrawColumnBoom.TrueColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,null,screen,I);
            this.colfunclow = new R_DrawColumnBoomLow.TrueColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,null,screen,I);
            
            // Fuzzy columns
            this.fuzzfunchi= new R_DrawFuzzColumn.TrueColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,null,screen,I);
            this.fuzzfunclow =new R_DrawFuzzColumnLow.TrueColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,null,screen,I);

            // Translated columns
            this.transfunchi=new R_DrawTranslatedColumn.TrueColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,null,screen,I);
            this.transfunclow= new R_DrawTranslatedColumnLow.TrueColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,null,screen,I);


        }
        
    }
    
}

package rr.parallel;

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

import p.pspdef_t;
import static rr.LightsAndColors.*;
import rr.AbstractThings;
import rr.IDetailAware;
import rr.Renderer;
import rr.column_t;
import rr.drawseg_t;
import rr.patch_t;
import rr.spritedef_t;
import rr.spriteframe_t;
import rr.vissprite_t;
import rr.drawfuns.ColFuncs;
import rr.drawfuns.ColVars;
import rr.drawfuns.R_DrawColumnBoom;
import rr.drawfuns.R_DrawColumnBoomLow;
import rr.drawfuns.R_DrawFuzzColumn;
import rr.drawfuns.R_DrawFuzzColumnLow;
import rr.drawfuns.R_DrawTranslatedColumn;
import rr.drawfuns.R_DrawTranslatedColumnLow;
import static p.mobj_t.MF_TRANSLATION;
import static p.mobj_t.MF_TRANSSHIFT;
import static m.fixed_t.*;
import static rr.line_t.*;
import static data.Defines.FF_FRAMEMASK;
import static data.Defines.FF_FULLBRIGHT;
import static data.Defines.pw_invisibility;

/** A "Masked Worker" draws sprites in a split-screen strategy. Used by 
 * ParallelRenderer2. Each Masked Worker is essentially a complete Things
 * drawer, and reuses much of the serial methods.
 * 
 * @author velktron
 *
 * @param <T>
 * @param <V>
 */

public abstract class MaskedWorker<T,V> extends AbstractThings<T,V> implements Runnable, IDetailAware{
    
    private final static boolean DEBUG=false;
    private final static boolean RANGECHECK=false;
	
    protected final CyclicBarrier barrier;
    protected final int id;
    protected final int numthreads;
    
    protected ColVars<T,V> maskedcvars;
   
	public MaskedWorker(Renderer<T,V> R,int id,int SCREENWIDTH, int SCREENHEIGHT,int numthreads,CyclicBarrier barrier){
	    super(R);
	    // Workers have their own set, not a "pegged" one.
	    this.colfuncshi=new ColFuncs<T,V>();
	    this.colfuncslow=new ColFuncs<T,V>();
	    this.maskedcvars=new ColVars<T,V>();
	    this.id=id;
        this.numthreads=numthreads;
        this.barrier=barrier;        
        }
	
	public final void completeColumn(){
	    // Does nothing. Shuts up inheritance
	}
    
    public static final class HiColor extends MaskedWorker<byte[],short[]>{

		public HiColor(Renderer<byte[],short[]> R,int id, int SCREENWIDTH, int SCREENHEIGHT,
				int[] ylookup, int[] columnofs, int numthreads, short[] screen,
				CyclicBarrier barrier) {
			super(R,id, SCREENWIDTH, SCREENHEIGHT,numthreads, barrier);

	        // Non-optimized stuff for masked.
			colfuncshi.base=colfuncshi.main=colfuncshi.masked=new R_DrawColumnBoom.HiColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I);
	        colfuncslow.masked=new R_DrawColumnBoomLow.HiColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I);

	        // Fuzzy columns. These are also masked.
	        colfuncshi.fuzz=new R_DrawFuzzColumn.HiColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I);
	        colfuncslow.fuzz=new R_DrawFuzzColumnLow.HiColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I);

	        // Translated columns are usually sprites-only.
	        colfuncshi.trans=new R_DrawTranslatedColumn.HiColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I);
	        colfuncslow.trans=new R_DrawTranslatedColumnLow.HiColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I);
	        
	        colfuncs=colfuncshi;

		}
    	
    }
    
    public static final class Indexed extends MaskedWorker<byte[],byte[]>{

        public Indexed(Renderer<byte[],byte[]> R,int id, int SCREENWIDTH, int SCREENHEIGHT,
                int[] ylookup, int[] columnofs, int numthreads, byte[] screen,
                CyclicBarrier barrier,byte[] BLURRY_MAP) {
            super(R,id, SCREENWIDTH, SCREENHEIGHT,numthreads, barrier);
            colfuncshi.base=colfuncshi.main=colfuncshi.masked=new R_DrawColumnBoom.Indexed(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I);
            colfuncslow.masked=new R_DrawColumnBoomLow.Indexed(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I);

            // Fuzzy columns. These are also masked.
            colfuncshi.fuzz=new R_DrawFuzzColumn.Indexed(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I,BLURRY_MAP);
            colfuncslow.fuzz=new R_DrawFuzzColumnLow.Indexed(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I,BLURRY_MAP);

            // Translated columns are usually sprites-only.
            colfuncshi.trans=new R_DrawTranslatedColumn.Indexed(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I);
            colfuncslow.trans=new R_DrawTranslatedColumnLow.Indexed(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I);
            
            colfuncs=colfuncshi;
        }
        
    }
    
    public static final class TrueColor extends MaskedWorker<byte[],int[]>{

        public TrueColor(Renderer<byte[],int[]> R,int id, int SCREENWIDTH, int SCREENHEIGHT,
                int[] ylookup, int[] columnofs, int numthreads, int[] screen,
                CyclicBarrier barrier) {
            super(R,id, SCREENWIDTH, SCREENHEIGHT,numthreads, barrier);

            // Non-optimized stuff for masked.
            colfuncshi.base=colfuncshi.main=colfuncshi.masked=new R_DrawColumnBoom.TrueColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I);
            colfuncslow.masked=new R_DrawColumnBoomLow.TrueColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I);

            // Fuzzy columns. These are also masked.
            colfuncshi.fuzz=new R_DrawFuzzColumn.TrueColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I);
            colfuncslow.fuzz=new R_DrawFuzzColumnLow.TrueColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I);

            // Translated columns are usually sprites-only.
            colfuncshi.trans=new R_DrawTranslatedColumn.TrueColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I);
            colfuncslow.trans=new R_DrawTranslatedColumnLow.TrueColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I);
            
            colfuncs=colfuncshi;

        }
        
    }
    
    protected int startx, endx;
    
    /**
     * R_DrawVisSprite mfloorclip and mceilingclip should also be set.
     * 
     * Sprites are actually drawn here. Obviously overrides the serial
     * method, and only draws a portion of the sprite.
     * 
     * 
     */
    protected final void DrawVisSprite(vissprite_t<V> vis) {
        column_t column;
        int texturecolumn;
        int frac; // fixed_t
        patch_t patch;
        // The sprite may have been partially drawn on another portion of the
        // screen.
        int bias=startx-vis.x1;
            if (bias<0) bias=0; // nope, it ain't.

        // Trim bounds to zone NOW
        int x1=Math.max(startx, vis.x1);
        int x2=Math.min(endx,vis.x2);
            
        // At this point, the view angle (and patch) has already been
        // chosen. Go back.
        patch = W.CachePatchNum(vis.patch + SM.getFirstSpriteLump());
        
        maskedcvars.dc_colormap = vis.colormap;
        // colfunc=glasscolfunc;
        if (maskedcvars.dc_colormap == null) {
            // NULL colormap = shadow draw
            colfunc = colfuncs.fuzz;
        } else if ((vis.mobjflags & MF_TRANSLATION) != 0) {
            colfunc = colfuncs.trans;
            maskedcvars.dc_translation = (T) colormaps.getTranslationTable(vis.mobjflags);

        }

        maskedcvars.dc_iscale = Math.abs(vis.xiscale) >> view.detailshift;
        maskedcvars.dc_texturemid = vis.texturemid;
        // Add bias to compensate for partially drawn sprite which has not been rejected.
        frac = vis.startfrac+vis.xiscale*bias;
        spryscale = vis.scale;
        sprtopscreen = view.centeryfrac - FixedMul(maskedcvars.dc_texturemid, spryscale);

        // A texture height of 0 means "not tiling" and holds for
        // all sprite/masked renders.
        maskedcvars.dc_texheight=0;
        
        for (maskedcvars.dc_x = x1; maskedcvars.dc_x <= x2; maskedcvars.dc_x++, frac += vis.xiscale) {
            texturecolumn = frac >> FRACBITS;
            if (true) {
                if (texturecolumn < 0 || texturecolumn >= patch.width)
                    I.Error("R_DrawSpriteRange: bad texturecolumn %d vs %d %d %d",texturecolumn,patch.width,x1,x2);
            }
            column = patch.columns[texturecolumn];
            
            if (column==null)
                System.err.printf("Null column for texturecolumn %d\n",texturecolumn,x1,x2);
            else
            DrawMaskedColumn(column);
        }

        colfunc = colfuncs.masked;
    }

    /**
     * R_RenderMaskedSegRange
     * 
     * @param ds
     * @param x1
     * @param x2
     */
    
    protected final void RenderMaskedSegRange(drawseg_t ds, int x1, int x2) {
    	
    	// Trivial rejection
        if (ds.x1>endx || ds.x2<startx) return;
        
        // Trim bounds to zone NOW
        x1=Math.max(startx, x1);
        x2=Math.min(endx,x2);
    	
        int index;

        int lightnum;
        int texnum;
        int bias=startx-ds.x1; // Correct for starting outside
        if (bias<0) bias=0; // nope, it ain't.
        
        // System.out.printf("RenderMaskedSegRange from %d to %d\n",x1,x2);

        // Calculate light table.
        // Use different light tables
        // for horizontal / vertical / diagonal. Diagonal?
        // OPTIMIZE: get rid of LIGHTSEGSHIFT globally
        MyBSP.curline = ds.curline;
        frontsector = MyBSP.curline.frontsector;
        backsector = MyBSP.curline.backsector;
        texnum = TexMan.getTextureTranslation(MyBSP.curline.sidedef.midtexture);
        // System.out.print(" for texture "+textures[texnum].name+"\n:");
        lightnum = (frontsector.lightlevel >> LIGHTSEGSHIFT) + colormaps.extralight;

        if (MyBSP.curline.v1y == MyBSP.curline.v2y)
            lightnum--;
        else if (MyBSP.curline.v1x == MyBSP.curline.v2x)
            lightnum++;

        // Killough code.
        colormaps.walllights = lightnum >= LIGHTLEVELS ? colormaps.scalelight[LIGHTLEVELS - 1]
                : lightnum < 0 ? colormaps.scalelight[0] : colormaps.scalelight[lightnum];

        // Get the list
        maskedtexturecol = ds.getMaskedTextureColList();
        // And this is the pointer.
        pmaskedtexturecol = ds.getMaskedTextureColPointer();

        rw_scalestep = ds.scalestep;
        spryscale = ds.scale1 + (x1 - ds.x1) * rw_scalestep;

        // HACK to get "pointers" inside clipping lists
        mfloorclip = ds.getSprBottomClipList();
        p_mfloorclip = ds.getSprBottomClipPointer();
        mceilingclip = ds.getSprTopClipList();
        p_mceilingclip = ds.getSprTopClipPointer();
        // find positioning
        if ((MyBSP.curline.linedef.flags & ML_DONTPEGBOTTOM) != 0) {
            maskedcvars.dc_texturemid = frontsector.floorheight > backsector.floorheight ? frontsector.floorheight
                    : backsector.floorheight;
            maskedcvars.dc_texturemid = maskedcvars.dc_texturemid + TexMan.getTextureheight(texnum)
                    - view.z;
        } else {
            maskedcvars.dc_texturemid = frontsector.ceilingheight < backsector.ceilingheight ? frontsector.ceilingheight
                    : backsector.ceilingheight;
            maskedcvars.dc_texturemid = maskedcvars.dc_texturemid - view.z;
        }
        maskedcvars.dc_texturemid += MyBSP.curline.sidedef.rowoffset;

        if (colormaps.fixedcolormap != null)
            maskedcvars.dc_colormap = colormaps.fixedcolormap;

        // Texture height must be set at this point. This will trigger
        // tiling. For sprites, it should be set to 0.
        maskedcvars.dc_texheight = TexMan.getTextureheight(texnum) >> FRACBITS;

        // draw the columns
        for (maskedcvars.dc_x = x1; maskedcvars.dc_x <= x2; maskedcvars.dc_x++) {
            // calculate lighting
            if (maskedtexturecol[pmaskedtexturecol + maskedcvars.dc_x] != Short.MAX_VALUE) {
                if (colormaps.fixedcolormap == null) {
                    index = spryscale >>> LIGHTSCALESHIFT;

                    if (index >= MAXLIGHTSCALE)
                        index = MAXLIGHTSCALE - 1;

                    maskedcvars.dc_colormap = colormaps.walllights[index];
                }

                sprtopscreen = view.centeryfrac
                        - FixedMul(maskedcvars.dc_texturemid, spryscale);
                maskedcvars.dc_iscale = (int) (0xffffffffL / spryscale);

                // draw the texture
                column_t data = TexMan.GetSmpColumn(texnum,
                        maskedtexturecol[pmaskedtexturecol + maskedcvars.dc_x],id);
                
                DrawMaskedColumn(data);
                maskedtexturecol[pmaskedtexturecol + maskedcvars.dc_x] = Short.MAX_VALUE;
            }
            spryscale += rw_scalestep;
        }

    }		
    
    /**
     * R_DrawPSprite
     * 
     * Draws a "player sprite" with slighly different rules than normal
     * sprites. This is actually a PITA, at best :-/
     * 
     * Also different than normal implementation.
     * 
     */

    protected final void DrawPSprite(pspdef_t psp) {

        int tx;
        int x1;
        int x2;
        spritedef_t sprdef;
        spriteframe_t sprframe;
        vissprite_t<V> vis;
        int lump;
        boolean flip;

        //

        // decide which patch to use (in terms of angle?)
        if (RANGECHECK) {
            if (psp.state.sprite.ordinal() >= SM.getNumSprites())
                I.Error("R_ProjectSprite: invalid sprite number %d ",
                        psp.state.sprite);
        }

        sprdef = SM.getSprite(psp.state.sprite.ordinal());
        
        if (RANGECHECK) {
            if ((psp.state.frame & FF_FRAMEMASK) >= sprdef.numframes)
                I.Error("R_ProjectSprite: invalid sprite frame %d : %d ",
                        psp.state.sprite, psp.state.frame);
        }
        
        sprframe = sprdef.spriteframes[psp.state.frame & FF_FRAMEMASK];

        // Base frame for "angle 0" aka viewed from dead-front.
        lump = sprframe.lump[0];
        // Q: where can this be set? A: at sprite loadtime.
        flip = (boolean) (sprframe.flip[0] != 0);

        // calculate edges of the shape. tx is expressed in "view units".
        tx = (int) (FixedMul(psp.sx, view.BOBADJUST) - view.WEAPONADJUST);

        tx -= spriteoffset[lump];

        // So...centerxfrac is the center of the screen (pixel coords in
        // fixed point).
        x1 = (view.centerxfrac + FixedMul(tx, pspritescale)) >> FRACBITS;

        // off the right side
        if (x1 > endx)
            return;

        tx += spritewidth[lump];
        x2 = ((view.centerxfrac + FixedMul(tx, pspritescale)) >> FRACBITS) - 1;

        // off the left side
        if (x2 < startx)
            return;

        // store information in a vissprite ?
        vis = avis;
        vis.mobjflags = 0;
        vis.texturemid = ((BASEYCENTER+view.lookdir) << FRACBITS) + FRACUNIT / 2
                - (psp.sy - spritetopoffset[lump]);
        vis.x1 = x1 < startx ? startx : x1;
        vis.x2 = x2 >= endx ? endx - 1 : x2;
        vis.scale = (pspritescale) << view.detailshift;

        if (flip) {
            vis.xiscale = -pspriteiscale;
            vis.startfrac = spritewidth[lump] - 1;
        } else {
            vis.xiscale = pspriteiscale;
            vis.startfrac = 0;
        }

        if (vis.x1 > x1)
            vis.startfrac += vis.xiscale * (vis.x1 - x1);

        vis.patch = lump;

        if ((view.player.powers[pw_invisibility] > 4 * 32)
                || (view.player.powers[pw_invisibility] & 8) != 0) {
            // shadow draw
            vis.colormap = null;

        } else if (colormaps.fixedcolormap != null) {
            // fixed color
            vis.colormap = colormaps.fixedcolormap;
            // vis.pcolormap=0;
        } else if ((psp.state.frame & FF_FULLBRIGHT) != 0) {
            // full bright
            vis.colormap = colormaps.colormaps[0];
            // vis.pcolormap=0;
        } else {
            // local light
            vis.colormap = colormaps.spritelights[MAXLIGHTSCALE - 1];
        }

        //System.out.printf("Weapon draw from %d to %d\n",vis.x1,vis.x2);
        DrawVisSprite(vis);
    }
    
    
    /**
     * R_DrawMasked
     * 
     * Sorts and draws vissprites (room for optimization in sorting func.)
     * Draws masked textures. Draws player weapons and overlays (psprites).
     * 
     * Sorting function can be swapped for almost anything, and it will work
     * better, in-place and be simpler to draw, too.
     * 
     * 
     */
    
    @Override
    public void run() {
        // vissprite_t spr;
        int ds;
        drawseg_t dss;

        // Sprites should already be sorted for distance 

        colfunc = colfuncs.masked; // Sprites use fully-masked capable
                                 // function.

        // Update view height
        
        this.maskedcvars.viewheight=view.height;
        this.maskedcvars.centery=view.centery;
        this.startx=((id*view.width)/numthreads);
        this.endx=(((id+1)*view.width)/numthreads);
        
        // Update thread's own vissprites
        
        final vissprite_t<V>[] vissprites=VIS.getVisSprites();
        final int numvissprites=VIS.getNumVisSprites();
        
        //System.out.printf("Sprites to render: %d\n",numvissprites);
        
        // Try drawing all sprites that are on your side of
        // the screen. Limit by x1 and x2, if you have to.
        for (int i = 0; i < numvissprites; i++) {
            DrawSprite(vissprites[i]);
        }
        
        //System.out.printf("Segs to render: %d\n",ds_p);

        // render any remaining masked mid textures
        for (ds = seg_vars.ds_p - 1; ds >= 0; ds--) {
            dss = seg_vars.drawsegs[ds];
            if (!(dss.x1>endx || dss.x2<startx)&&!dss.nullMaskedTextureCol())
                RenderMaskedSegRange(dss, dss.x1,dss.x2);
        }
        // draw the psprites on top of everything
        // but does not draw on side views
        // if (viewangleoffset==0)

        colfunc = colfuncs.player;
        DrawPlayerSprites();
        colfunc = colfuncs.masked;
        
        try {
            barrier.await();
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (BrokenBarrierException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        
    }
    
}

package rr.parallel;

import static rr.LightsAndColors.LIGHTLEVELS;
import static rr.LightsAndColors.MAXLIGHTSCALE;
import static rr.LightsAndColors.MAXLIGHTZ;

import java.io.IOException;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

import rr.IDetailAware;
import rr.PlaneDrawer;
import rr.SimpleThings;
import rr.patch_t;
import rr.drawfuns.ColVars;
import rr.drawfuns.DcFlags;
import rr.drawfuns.R_DrawColumnBoom;
import rr.drawfuns.R_DrawColumnBoomLow;
import rr.drawfuns.R_DrawColumnBoomOpt;
import rr.drawfuns.R_DrawColumnBoomOptLow;
import rr.drawfuns.R_DrawFuzzColumn;
import rr.drawfuns.R_DrawFuzzColumnLow;
import rr.drawfuns.R_DrawSpanLow;
import rr.drawfuns.R_DrawSpanUnrolled;
import rr.drawfuns.R_DrawTLColumn;
import rr.drawfuns.R_DrawTranslatedColumn;
import rr.drawfuns.R_DrawTranslatedColumnLow;
import utils.C2JUtils;
import v.DoomVideoRenderer;
import doom.DoomMain;
import doom.player_t;

/**
 * This is Mocha Doom's famous parallel software renderer. It builds on the
 * basic software renderer, but adds specialized handling for drawing segs
 * (walls) and spans (floors) in parallel. There's inherent parallelism between
 * walls and floor, and internal parallelism between walls and between floors.
 * However, visplane limits and openings need to be pre-computed before any
 * actual drawing starts, that's why rendering of walls is stored in "RWI"s or
 * "Render Wall Instructions", and then rendered once they are all in place and
 * the can be parallelized between rendering threads. Rendering of sprites is
 * NOT parallelized yet (and probably not worth it, at this point).
 * 
 * @author admin
 */

public abstract class ParallelRenderer<T, V>
        extends AbstractParallelRenderer<T, V> {

    public ParallelRenderer(DoomMain<T, V> DM, int wallthread,
            int floorthreads, int nummaskedthreads) {
        super(DM, wallthread, floorthreads, nummaskedthreads);
        
        // Register parallel seg drawer with list of RWI subsystems.
        ParallelSegs tmp= new ParallelSegs(this);
        this.MySegs = tmp;
        RWIs= tmp;
        
        this.MyThings =
                new SimpleThings<T,V>(this);
        //this.MyPlanes = new Planes(this);// new ParallelPlanes<T, V>(DM.R);

    }

    /**
     * Default constructor, 1 seg, 1 span and two masked threads.
     * 
     * @param DM
     */
    public ParallelRenderer(DoomMain<T, V> DM) {
        this(DM, 1, 1, 2);
    }



    /**
     * R_RenderView As you can guess, this renders the player view of a
     * particular player object. In practice, it could render the view of any
     * mobj too, provided you adapt the SetupFrame method (where the viewing
     * variables are set).
     * 
     * @throws IOException
     */

    public void RenderPlayerView(player_t player) {

        // Viewing variables are set according to the player's mobj. Interesting
        // hacks like
        // free cameras or monster views can be done.
        SetupFrame(player);

        /*
         * Uncommenting this will result in a very existential experience if
         * (Math.random()>0.999){ thinker_t shit=P.getRandomThinker(); try {
         * mobj_t crap=(mobj_t)shit; player.mo=crap; } catch (ClassCastException
         * e){ } }
         */

        // Clear buffers.
        MyBSP.ClearClipSegs();
        seg_vars.ClearDrawSegs();
        vp_vars.ClearPlanes();
        MySegs.ClearClips();
        VIS.ClearSprites();
        // Check for new console commands.
        DGN.NetUpdate();

        // The head node is the last node output.
        MyBSP.RenderBSPNode(LL.numnodes - 1);

        // System.out.printf("Submitted %d RWIs\n",RWIcount);

        MySegs.CompleteRendering();

        // Check for new console commands.
        DGN.NetUpdate();

        // "Warped floor" fixed, same-height visplane merging fixed.
        MyPlanes.DrawPlanes();

        // Check for new console commands.
        DGN.NetUpdate();

        MySegs.sync();
        MyPlanes.sync();

//            drawsegsbarrier.await();
//            visplanebarrier.await();


        MyThings.DrawMasked();

        // RenderRMIPipeline();
        /*
         * try { maskedbarrier.await(); } catch (Exception e) {
         * e.printStackTrace(); }
         */

        // Check for new console commands.
        DGN.NetUpdate();
    }

    public static final class Indexed
            extends ParallelRenderer<byte[], byte[]> {

        public Indexed(DoomMain<byte[], byte[]> DM, int wallthread,
                int floorthreads, int nummaskedthreads) {
            super(DM, wallthread, floorthreads, nummaskedthreads);

            // Init light levels
            colormaps.scalelight = new byte[LIGHTLEVELS][MAXLIGHTSCALE][];
            colormaps.scalelightfixed = new byte[MAXLIGHTSCALE][];
            colormaps.zlight = new byte[LIGHTLEVELS][MAXLIGHTZ][];
            
            completeInit();
        
        }
        
        /**
         * R_InitColormaps
         * 
         * @throws IOException
         */
        protected void InitColormaps() throws IOException {
            int lump, length;

            // Load in the light tables,
            // 256 byte align tables.
            lump = W.GetNumForName("COLORMAP");
            length = W.LumpLength(lump) + 256;
            colormaps.colormaps = new byte[(length / 256)][256];
            System.out.println("Colormaps: " + colormaps.colormaps.length);

            byte[] tmp = new byte[length];
            W.ReadLump(lump,tmp);

            for (int i = 0; i < colormaps.colormaps.length; i++) {
                System.arraycopy(tmp, i * 256, colormaps.colormaps[i], 0, 256);
            }
            
            // MAES: blurry effect is hardcoded to this colormap.
            BLURRY_MAP=colormaps.colormaps[6];
            // colormaps = (byte *)( ((int)colormaps + 255)&~0xff);     

            
        }

 protected void R_InitDrawingFunctions(){            
            
            // Span functions. Common to all renderers unless overriden
            // or unused e.g. parallel renderers ignore them.
            DrawSpan=new R_DrawSpanUnrolled.Indexed(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,dsvars,screen,I);
            DrawSpanLow=new R_DrawSpanLow.Indexed(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,dsvars,screen,I);
            
            
            // Translated columns are usually sprites-only.
            DrawTranslatedColumn=new R_DrawTranslatedColumn.Indexed(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I);
            DrawTranslatedColumnLow=new R_DrawTranslatedColumnLow.Indexed(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I);
          //  DrawTLColumn=new R_DrawTLColumn(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I);
            
            // Fuzzy columns. These are also masked.
            DrawFuzzColumn=new R_DrawFuzzColumn.Indexed(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I,BLURRY_MAP);
            DrawFuzzColumnLow=new R_DrawFuzzColumnLow.Indexed(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I,BLURRY_MAP);
            
            // Regular draw for solid columns/walls. Full optimizations.
            DrawColumn=new R_DrawColumnBoomOpt.Indexed(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,dcvars,screen,I);
            DrawColumnLow=new R_DrawColumnBoomOptLow.Indexed(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,dcvars,screen,I);
            
            // Non-optimized stuff for masked.
            DrawColumnMasked=new R_DrawColumnBoom.Indexed(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I);
            DrawColumnMaskedLow=new R_DrawColumnBoomLow.Indexed(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I);
            
            // Player uses masked
            DrawColumnPlayer=DrawColumnMasked; // Player normally uses masked.
            
            // Skies use their own. This is done in order not to stomp parallel threads.
            
            DrawColumnSkies=new R_DrawColumnBoomOpt.Indexed(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,skydcvars,screen,I);
            DrawColumnSkiesLow=new R_DrawColumnBoomOptLow.Indexed(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,skydcvars,screen,I);
            
            super.R_InitDrawingFunctions();
        }
        
        protected void InitMaskedWorkers() {
            for (int i = 0; i < NUMMASKEDTHREADS; i++) {
                maskedworkers[i] =
                    new MaskedWorker.Indexed(this, i, SCREENWIDTH,
                            SCREENHEIGHT, ylookup, columnofs, NUMMASKEDTHREADS,
                            screen, maskedbarrier, BLURRY_MAP);
                detailaware.add(maskedworkers[i]);
                // "Peg" to sprite manager.
                maskedworkers[i].cacheSpriteManager(SM);
            }
        }

		@Override
		public RenderWallExecutor<byte[], byte[]>[] InitRWIExecutors(
				int num,ColVars<byte[], byte[]>[] RWI) {
				RenderWallExecutor<byte[],byte[]>[] tmp=
						new RenderWallExecutor.Indexed[num];
				
				for (int i=0;i<num;i++)
					tmp[i]=new RenderWallExecutor.Indexed(SCREENWIDTH, SCREENHEIGHT, columnofs, ylookup, screen, RWI, drawsegsbarrier);
			
				return tmp;
		}

    }

    @Override
    protected void InitParallelStuff() {


    	// ...yeah, it works.
    	if (!(RWIs==null)){ 
    		ColVars<T,V>[] RWI=RWIs.getRWI();
    		RenderWallExecutor<T,V>[] RWIExec=InitRWIExecutors(NUMWALLTHREADS,RWI);
    		RWIs.setExecutors(RWIExec);
    		
            for (int i = 0; i < NUMWALLTHREADS; i++) {
            	
                detailaware.add(RWIExec[i]);
            }
    		}
    		
    	
        // CATCH: this must be executed AFTER screen is set, and
        // AFTER we initialize the RWI themselves,
        // before V is set (right?)

    	
        // This actually only creates the necessary arrays and
        // barriers. Things aren't "wired" yet.

        // Using "render wall instruction" subsystem

        // Using masked sprites
       // RMIExec = new RenderMaskedExecutor[NUMMASKEDTHREADS];

        // Using
        //vpw = new Runnable[NUMFLOORTHREADS];
        //maskedworkers = new MaskedWorker.Indexed[NUMMASKEDTHREADS];






        // RWIcount = 0;

        // InitRWISubsystem();
        // InitRMISubsystem();
        // InitPlaneWorkers();
        // InitMaskedWorkers();

        // If using masked threads, set these too.
        TexMan.setSMPVars(NUMMASKEDTHREADS);

    }

    /*
     * private void InitPlaneWorkers(){ for (int i = 0; i < NUMFLOORTHREADS;
     * i++) { vpw[i] = new VisplaneWorker2(i,SCREENWIDTH, SCREENHEIGHT,
     * columnofs, ylookup, screen, visplanebarrier, NUMFLOORTHREADS);
     * //vpw[i].id = i; detailaware.add((IDetailAware) vpw[i]); } }
     */

    @Override
    public void initScaling() {
    	
    	super.initScaling();
    	

        /*
         * TODO: relay to dependent objects. super.initScaling();
         * ColVars<byte[],byte[]> fake = new ColVars<byte[],byte[]>(); RWI =
         * C2JUtils.createArrayOfObjects(fake, SCREENWIDTH * 3); // Be MUCH more
         * generous with this one. RMI = C2JUtils.createArrayOfObjects(fake,
         * SCREENWIDTH * 6);
         */

    }

    protected abstract void InitMaskedWorkers();

    public static final class HiColor
            extends ParallelRenderer<byte[], short[]> {

        public HiColor(DoomMain<byte[], short[]> DM, int wallthread,
                int floorthreads, int nummaskedthreads) {
            super(DM, wallthread, floorthreads, nummaskedthreads);
            
            // Init light levels
            colormaps.scalelight = new short[LIGHTLEVELS][MAXLIGHTSCALE][];
            colormaps.scalelightfixed = new short[MAXLIGHTSCALE][];
            colormaps.zlight = new short[LIGHTLEVELS][MAXLIGHTZ][];
        }

        protected void InitMaskedWorkers() {
            for (int i = 0; i < NUMMASKEDTHREADS; i++) {
                maskedworkers[i] =
                    new MaskedWorker.HiColor(this, i, SCREENWIDTH,
                            SCREENHEIGHT, ylookup, columnofs, NUMMASKEDTHREADS,
                            screen, maskedbarrier);
                detailaware.add(maskedworkers[i]);
                // "Peg" to sprite manager.
                maskedworkers[i].cacheSpriteManager(SM);
            }
        }

        /**
         * R_InitColormaps This is VERY different for hicolor.
         * 
         * @throws IOException
         */
        protected void InitColormaps()
                throws IOException {

            colormaps.colormaps = V.getColorMaps();
            System.out.println("COLORS15 Colormaps: "
                    + colormaps.colormaps.length);

            // MAES: blurry effect is hardcoded to this colormap.
            // Pointless, since we don't use indexes. Instead, a half-brite
            // processing works just fine.
            BLURRY_MAP = null;// colormaps[0];
        }
        
        protected void R_InitDrawingFunctions(){            
            
            // Span functions. Common to all renderers unless overriden
            // or unused e.g. parallel renderers ignore them.
            DrawSpan=new R_DrawSpanUnrolled.HiColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,dsvars,screen,I);
            DrawSpanLow=new R_DrawSpanLow.HiColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,dsvars,screen,I);
            
            
            // Translated columns are usually sprites-only.
            DrawTranslatedColumn=new R_DrawTranslatedColumn.HiColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I);
            DrawTranslatedColumnLow=new R_DrawTranslatedColumnLow.HiColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I);
            DrawTLColumn=new R_DrawTLColumn(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I);
            
            // Fuzzy columns. These are also masked.
            DrawFuzzColumn=new R_DrawFuzzColumn.HiColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I);
            DrawFuzzColumnLow=new R_DrawFuzzColumnLow.HiColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I);
            
            // Regular draw for solid columns/walls. Full optimizations.
            DrawColumn=new R_DrawColumnBoomOpt.HiColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,dcvars,screen,I);
            DrawColumnLow=new R_DrawColumnBoomOptLow.HiColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,dcvars,screen,I);
            
            // Non-optimized stuff for masked.
            DrawColumnMasked=new R_DrawColumnBoom.HiColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I);
            DrawColumnMaskedLow=new R_DrawColumnBoomLow.HiColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I);
            
            // Player uses masked
            DrawColumnPlayer=DrawColumnMasked; // Player normally uses masked.
            
            // Skies use their own. This is done in order not to stomp parallel threads.
            
            DrawColumnSkies=new R_DrawColumnBoomOpt.HiColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,skydcvars,screen,I);
            DrawColumnSkiesLow=new R_DrawColumnBoomOptLow.HiColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,skydcvars,screen,I);
            
            super.R_InitDrawingFunctions();
        }

		@Override
		public RenderWallExecutor<byte[], short[]>[] InitRWIExecutors(
				int num,ColVars<byte[], short[]>[] RWI) {
				RenderWallExecutor<byte[],short[]>[] tmp=
						new RenderWallExecutor.HiColor[num];
				
				for (int i=0;i<num;i++)
					tmp[i]=new RenderWallExecutor.HiColor(SCREENWIDTH, SCREENHEIGHT, columnofs, ylookup, screen, RWI, drawsegsbarrier);
			
				return tmp;
		}

    }

    public static final class TrueColor
            extends ParallelRenderer<byte[], int[]> {

        public TrueColor(DoomMain<byte[], int[]> DM, int wallthread,
                int floorthreads, int nummaskedthreads) {
            super(DM, wallthread, floorthreads, nummaskedthreads);
            
            // Init light levels
            colormaps.scalelight = new int[LIGHTLEVELS][MAXLIGHTSCALE][];
            colormaps.scalelightfixed = new int[MAXLIGHTSCALE][];
            colormaps.zlight = new int[LIGHTLEVELS][MAXLIGHTZ][];
        }
        
  protected void R_InitDrawingFunctions(){            
            
            // Span functions. Common to all renderers unless overriden
            // or unused e.g. parallel renderers ignore them.
            DrawSpan=new R_DrawSpanUnrolled.TrueColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,dsvars,screen,I);
            DrawSpanLow=new R_DrawSpanLow.TrueColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,dsvars,screen,I);
            
            
            // Translated columns are usually sprites-only.
            DrawTranslatedColumn=new R_DrawTranslatedColumn.TrueColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I);
            DrawTranslatedColumnLow=new R_DrawTranslatedColumnLow.TrueColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I);
            //DrawTLColumn=new R_DrawTLColumn(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I);
            
            // Fuzzy columns. These are also masked.
            DrawFuzzColumn=new R_DrawFuzzColumn.TrueColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I);
            DrawFuzzColumnLow=new R_DrawFuzzColumnLow.TrueColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I);
            
            // Regular draw for solid columns/walls. Full optimizations.
            DrawColumn=new R_DrawColumnBoomOpt.TrueColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,dcvars,screen,I);
            DrawColumnLow=new R_DrawColumnBoomOptLow.TrueColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,dcvars,screen,I);
            
            // Non-optimized stuff for masked.
            DrawColumnMasked=new R_DrawColumnBoom.TrueColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I);
            DrawColumnMaskedLow=new R_DrawColumnBoomLow.TrueColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I);
            
            // Player uses masked
            DrawColumnPlayer=DrawColumnMasked; // Player normally uses masked.
            
            // Skies use their own. This is done in order not to stomp parallel threads.
            
            DrawColumnSkies=new R_DrawColumnBoomOpt.TrueColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,skydcvars,screen,I);
            DrawColumnSkiesLow=new R_DrawColumnBoomOptLow.TrueColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,skydcvars,screen,I);
            
            super.R_InitDrawingFunctions();
        }

        /**
         * R_InitColormaps This is VERY different for hicolor.
         * 
         * @throws IOException
         */
        protected void InitColormaps()
                throws IOException {

            colormaps.colormaps = V.getColorMaps();
            System.out.println("COLORS15 Colormaps: "
                    + colormaps.colormaps.length);

            // MAES: blurry effect is hardcoded to this colormap.
            // Pointless, since we don't use indexes. Instead, a half-brite
            // processing works just fine.
            BLURRY_MAP = null;// colormaps[0];
        }
        
        protected void InitMaskedWorkers() {
            for (int i = 0; i < NUMMASKEDTHREADS; i++) {
                maskedworkers[i] =
                    new MaskedWorker.TrueColor(this,i, SCREENWIDTH, SCREENHEIGHT,
                            ylookup, columnofs, NUMMASKEDTHREADS, screen,
                            maskedbarrier);
                detailaware.add(maskedworkers[i]);
                // "Peg" to sprite manager.
                maskedworkers[i].cacheSpriteManager(SM);
            }
        }
        
		@Override
		public RenderWallExecutor<byte[], int[]>[] InitRWIExecutors(
				int num,ColVars<byte[], int[]>[] RWI) {
			RenderWallExecutor<byte[],int[]>[] tmp=
					new RenderWallExecutor.TrueColor[num];
			
			for (int i=0;i<num;i++)
				tmp[i]=new RenderWallExecutor.TrueColor(SCREENWIDTH, SCREENHEIGHT, columnofs, ylookup, screen, RWI, drawsegsbarrier);
		
			return tmp;
		}

    }

}

package rr.parallel;

import static data.Limits.*;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.Executors;

import rr.patch_t;
import utils.C2JUtils;
import v.DoomVideoRenderer;
import doom.DoomMain;
import doom.DoomStatus;
import doom.player_t;

/** This is a second attempt at building a seg-focused parallel renderer, instead of
 * column-based. It does function, but is broken and has unsolved data dependencies.
 * It's therefore not used in official releases, and I chose to deprecate it.
 * If you still want to develop it, be my guest.
 * 
 * @author velktron
 *
 */

public class ParallelRenderer2 extends AbstractParallelRenderer {
    
    public ParallelRenderer2(DoomStatus DS, int wallthread, int floorthreads,int nummaskedthreads) {

        super(DS,wallthread,floorthreads,nummaskedthreads);
        System.out.println("Parallel Renderer 2 (Seg-based)");
        
        this.MySegs=new ParallelSegs2();
        this.MyPlanes=new ParallelPlanes();
        this.MyThings=new ParallelThings2(); 

    }

	public ParallelRenderer2(DoomStatus DS, int wallthread, int floorthreads) {

		super(DS,wallthread,floorthreads);
		System.out.println("Parallel Renderer 2 (Seg-based)");
		
		this.MySegs=new ParallelSegs2();
		this.MyPlanes=new ParallelPlanes();
		this.MyThings=new ParallelThings2(); 

	}

	/** Default constructor, 2 wall threads and one floor thread.
	 * 
	 * @param DM
	 */
	public ParallelRenderer2(DoomMain DM) {
		this(DM,2,1);
	}

	@Override
	protected void InitParallelStuff() {
		// Prepare parallel stuff
		RSIExec=new RenderSegExecutor[NUMWALLTHREADS];
		tp=   Executors.newFixedThreadPool(NUMWALLTHREADS+NUMFLOORTHREADS);
		// Prepare the barrier for MAXTHREADS + main thread.
		//wallbarrier=new CyclicBarrier(NUMWALLTHREADS+1);
		visplanebarrier=new CyclicBarrier(NUMFLOORTHREADS+NUMWALLTHREADS+1);

		vpw=new VisplaneWorker[NUMFLOORTHREADS];
		
		// Uses "seg" parallel drawer, so RSI.
        InitRSISubsystem();
        
        // Masked workers.
        maskedworkers=new MaskedWorker[NUMMASKEDTHREADS];

        maskedbarrier =
            new CyclicBarrier(NUMMASKEDTHREADS + 1);
        
        InitMaskedWorkers();
        
        // If using masked threads, set these too.
        
        smp_composite=new boolean[NUMMASKEDTHREADS];// = false;
        smp_lasttex=new int[NUMMASKEDTHREADS];// = -1;
        smp_lastlump=new int[NUMMASKEDTHREADS];// = -1;
        smp_lastpatch=new patch_t[NUMMASKEDTHREADS];// = null;
        
	}

	///////////////////////// The actual rendering calls ///////////////////////

	/**
	 * R_RenderView
	 * 
	 * As you can guess, this renders the player view of a particular player object.
	 * In practice, it could render the view of any mobj too, provided you adapt the
	 * SetupFrame method (where the viewing variables are set).
	 * 
	 */

	public void RenderPlayerView (player_t player)
	{   

		// Viewing variables are set according to the player's mobj. Interesting hacks like
		// free cameras or monster views can be done.
		SetupFrame (player);



		/* Uncommenting this will result in a very existential experience
  if (Math.random()>0.999){
	  thinker_t shit=P.getRandomThinker();
	  try {
	  mobj_t crap=(mobj_t)shit;
	  player.mo=crap;
	  } catch (ClassCastException e){

	  }
  	}*/

		// Clear buffers. 
		MyBSP.ClearClipSegs ();
		MyBSP.ClearDrawSegs ();
		MyPlanes.ClearPlanes ();
		MySegs.ClearClips();
		VIS.ClearSprites ();

		// Check for new console commands.
		DGN.NetUpdate ();

		// The head node is the last node output.
		MyBSP.RenderBSPNode (LL.numnodes-1);
		
		RenderRSIPipeline();
		// Check for new console commands.
		DGN.NetUpdate ();

		// "Warped floor" fixed, same-height visplane merging fixed.
		MyPlanes.DrawPlanes ();

		try {
			visplanebarrier.await();
		} catch (Exception e){
			e.printStackTrace();
		}



		// Check for new console commands.
		DGN.NetUpdate ();

		MyThings.DrawMasked ();

		colfunc=basecolfunc;

		// Check for new console commands.
		DGN.NetUpdate ();           
	}

	/**
	 * R_Init
	 */

	//public int  detailLevel;
	//public int  screenblocks=9; // has defa7ult

	public void Init ()

	{
		// Any good reason for this to be here?
		//drawsegs=new drawseg_t[MAXDRAWSEGS];
		//C2JUtils.initArrayOfObjects(drawsegs);

		// DON'T FORGET ABOUT MEEEEEE!!!11!!!
		this.screen=V.getScreen(DoomVideoRenderer.SCREEN_FG);

		System.out.print("\nR_InitData");
		InitData ();
		//InitPointToAngle ();
		System.out.print("\nR_InitPointToAngle");

		// ds.DM.viewwidth / ds.viewheight / detailLevel are set by the defaults
		System.out.print ("\nR_InitTables");
		InitTables ();

		SetViewSize (DM.M.getScreenBlocks(), DM.M.getDetailLevel());

		System.out.print ("\nR_InitPlanes");
		MyPlanes.InitPlanes ();

		System.out.print("\nR_InitLightTables");
		InitLightTables ();

		System.out.print("\nR_InitSkyMap: "+TexMan.InitSkyMap ());

		System.out.print("\nR_InitTranslationsTables");
		InitTranslationTables ();
		
        System.out.print("\nR_InitParallelStuff: ");
        InitParallelStuff();

		System.out.print("\nR_InitTranMap: ");
		R_InitTranMap(0);
		
		System.out.print("\nR_InitDrawingFunctions: ");
        R_InitDrawingFunctions();       		

		framecount = 0;
	}

	@Override
	public void initScaling(){
		super.initScaling();
		this.RSI=new RenderSegInstruction[MAXSEGS*3];
		C2JUtils.initArrayOfObjects(RSI);
		}


}

package rr.parallel;

import static data.Tables.finetangent;
import static m.fixed_t.FRACBITS;
import static m.fixed_t.FixedMul;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

import data.Tables;
import doom.DoomStatus;

import rr.PlaneDrawer;
import rr.Renderer;
import rr.RendererState;
import rr.drawfuns.ColVars;
import utils.C2JUtils;

/**
 * Features and functionality which is common among parallel renderers
 * 
 * @author velktron
 */

public abstract class AbstractParallelRenderer<T, V>
        extends RendererState<T, V> implements RWI.Init<T, V>{

    public AbstractParallelRenderer(DoomStatus<T, V> DS, int wallthread,
            int floorthreads, int nummaskedthreads) {
        super(DS);
        this.NUMWALLTHREADS = wallthread;
        this.NUMFLOORTHREADS = floorthreads;
        this.NUMMASKEDTHREADS = nummaskedthreads;
        // Prepare the barriers for MAXTHREADS + main thread.
        drawsegsbarrier = new CyclicBarrier(NUMWALLTHREADS + 1);
        visplanebarrier = new CyclicBarrier(NUMFLOORTHREADS + 1);        
        maskedbarrier = new CyclicBarrier(NUMMASKEDTHREADS + 1);
        tp = Executors.newCachedThreadPool();

    }

    public AbstractParallelRenderer(DoomStatus<T, V> DS, int wallthread,
            int floorthreads) {
        super(DS);
        this.NUMWALLTHREADS = wallthread;
        this.NUMFLOORTHREADS = floorthreads;
        this.NUMMASKEDTHREADS = 1;
        // Prepare the barriers for MAXTHREADS + main thread.
        drawsegsbarrier = new CyclicBarrier(NUMWALLTHREADS + 1);
        visplanebarrier = new CyclicBarrier(NUMFLOORTHREADS + 1);        
        maskedbarrier = new CyclicBarrier(NUMMASKEDTHREADS + 1);
        tp = Executors.newCachedThreadPool();
    }

    // //////// PARALLEL OBJECTS /////////////
    protected final int NUMWALLTHREADS;

    protected final int NUMMASKEDTHREADS;

    protected final int NUMFLOORTHREADS;

    protected Executor tp;

    protected Runnable[] vpw;

    protected MaskedWorker<T, V>[] maskedworkers;

    protected CyclicBarrier drawsegsbarrier;

    protected CyclicBarrier visplanebarrier;

    protected CyclicBarrier maskedbarrier;

    protected static final boolean DEBUG = false;

    protected final class ParallelSegs
            extends SegDrawer implements RWI.Get<T,V>{

        public ParallelSegs(Renderer<?, ?> R) {
            super(R);
        }

        /**
         * Parallel version. Since there's so much crap to take into account
         * when rendering, the number of walls to render is unknown a-priori and
         * the BSP trasversal itself is not worth parallelizing, it makes more
         * sense to store "rendering instructions" as quickly as the BSP can be
         * transversed, and then execute those in parallel. Also saves on having
         * to duplicate way too much status.
         */

        @Override
        protected final void CompleteColumn() {

            // Don't wait to go over
            if (RWIcount >= RWI.length) {
                ResizeRWIBuffer();
            }

            // A deep copy is still necessary, as dc
            RWI[RWIcount].copyFrom(dcvars);

            // We only need to point to the next one in the list.
            RWIcount++;
        }

        /**
         * Starts the RenderWallExecutors. Sync is EXTERNAL, however.
         */

        @Override
        public void CompleteRendering() {

            for (int i = 0; i < NUMWALLTHREADS; i++) {

                RWIExec[i].setRange((i * RWIcount) / NUMWALLTHREADS,
                    ((i + 1) * RWIcount) / NUMWALLTHREADS);
                // RWIExec[i].setRange(i%NUMWALLTHREADS,RWIcount,NUMWALLTHREADS);
                tp.execute(RWIExec[i]);
            }

            // System.out.println("RWI count"+RWIcount);
            RWIcount = 0;
        }

        /*
         * Just what are "RWIs"? Stored wall rendering instructions. They can be
         * at most 3*SCREENWIDTH (if there are low, mid and high textures on
         * every column of the screen) Remember to init them and set screen and
         * ylookup for all of them. Their max number is static and work
         * partitioning can be done in any way, as long as you keep track of how
         * many there are in any given frame. This value is stored inside
         * RWIcount. TODO: there are cases when more than 3*SCREENWIDTH
         * instructions need to be stored. therefore we really need a resizeable
         * array here, but ArrayList is way too slow for our needs. Storing a
         * whole wall is not an option, as, once again, a wall may have a
         * variable number of columns and an irregular height profile -> we'd
         * need to look into visplanes ugh...
         */

        protected RenderWallExecutor<T, V>[] RWIExec;

        /** Array of "wall" (actually, column) instructions */

        protected ColVars<T, V>[] RWI;

        /**
         * Increment this as you submit RWIs to the "queue". Remember to reset
         * to 0 when you have drawn everything!
         */

        protected int RWIcount = 0;

        /**
         * Resizes RWI buffer, updates executors. Sorry for the hackish
         * implementation but ArrayList and pretty much everything in
         * Collections is way too slow for what we're trying to accomplish.
         */

        protected final void ResizeRWIBuffer() {
            ColVars<T, V> fake = new ColVars<T, V>();

            // Bye bye, old RWI.
            RWI = C2JUtils.resize(fake, RWI, RWI.length * 2);

            for (int i = 0; i < NUMWALLTHREADS; i++) {
                RWIExec[i].updateRWI(RWI);
            }
            // System.err.println("RWI Buffer resized. Actual capacity " +
            // RWI.length);
        }
        
        /**
         * R_InitRWISubsystem Initialize RWIs and RWI Executors. Pegs them to
         * the RWI, ylookup and screen[0].
         */

        public void initScaling() {
        	super.initScaling();
        	ColVars<T,V> fake=new ColVars<T,V>();
        	RWI=C2JUtils.createArrayOfObjects(fake, 3*SCREENWIDTH);

        }

		@Override
		public ColVars<T, V>[] getRWI() {
			return RWI;
		}

		@Override
		public void setExecutors(RenderWallExecutor<T, V>[] RWIExec) {
			this.RWIExec=RWIExec;
			
		}
		
		public void sync(){
			try {
				drawsegsbarrier.await();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (BrokenBarrierException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
    }

    protected final class ParallelPlanes<T, V>
            extends PlaneDrawer<T, V> {

        protected ParallelPlanes(Renderer<T, V> R) {
            super(R);
        }

        /**
         * R_DrawPlanes At the end of each frame. This also means that visplanes
         * must have been set BEFORE we called this function. Therefore, look
         * for errors behind.
         * 
         * @throws IOException
         */
        public void DrawPlanes() {

            if (RANGECHECK) {
                rangeCheckErrors();
            }

            // vpw[0].setRange(0,lastvisplane/2);
            // vpw[1].setRange(lastvisplane/2,lastvisplane);

            for (int i = 0; i < NUMFLOORTHREADS; i++)
                tp.execute(vpw[i]);
        }

    } // End Plane class

    protected final class ParallelSegs2
            extends SegDrawer {

        /**
         * RenderSeg subsystem. Similar concept to RWI, but stores
         * "Render Seg Instructions" instead. More complex to build, but
         * potentially faster in some situations, as it allows distributing load
         * per-wall, rather than per-screen portion. Requires careful
         * concurrency considerations.
         */
        protected RenderSegInstruction<V>[] RSI;

        /**
         * Increment this as you submit RSIs to the "queue". Remember to reset
         * to 0 when you have drawn everything!
         */
        protected int RSIcount = 0;

        protected RenderSegExecutor<byte[], V>[] RSIExec;

        public ParallelSegs2(Renderer<?, ?> R) {
            super(R);
        }

        @Override
        protected void RenderSegLoop() {
            int angle;
            int yl, top, bottom, yh, mid, texturecolumn = 0;

            // Generate Seg rendering instruction BEFORE the looping start
            // and anything is modified. The loop will be repeated in the
            // threads, but without marking ceilings/floors etc.
            GenerateRSI();

            for (; rw_x < rw_stopx; rw_x++) {
                // mark floor / ceiling areas
                yl = (topfrac + HEIGHTUNIT - 1) >> HEIGHTBITS;

                // no space above wall?
                if (yl < ceilingclip[rw_x] + 1)
                    yl = ceilingclip[rw_x] + 1;

                if (markceiling) {
                    top = ceilingclip[rw_x] + 1;
                    bottom = yl - 1;

                    if (bottom >= floorclip[rw_x])
                        bottom = floorclip[rw_x] - 1;

                    if (top <= bottom) {
                        vp_vars.visplanes[vp_vars.ceilingplane].setTop(rw_x,
                            (char) top);
                        vp_vars.visplanes[vp_vars.ceilingplane].setBottom(rw_x,
                            (char) bottom);
                    }
                }

                yh = bottomfrac >> HEIGHTBITS;

                if (yh >= floorclip[rw_x])
                    yh = floorclip[rw_x] - 1;

                // System.out.printf("Precompute: rw %d yl %d yh %d\n",rw_x,yl,yh);

                // A particular seg has been identified as a floor marker.

                if (markfloor) {
                    top = yh + 1;
                    bottom = floorclip[rw_x] - 1;
                    if (top <= ceilingclip[rw_x])
                        top = ceilingclip[rw_x] + 1;
                    if (top <= bottom) {
                        vp_vars.visplanes[vp_vars.floorplane].setTop(rw_x,
                            (char) top);
                        vp_vars.visplanes[vp_vars.floorplane].setBottom(rw_x,
                            (char) bottom);
                    }
                }

                // texturecolumn and lighting are independent of wall tiers
                if (segtextured) {
                    // calculate texture offset. Still important to do because
                    // of masked

                    angle =
                        Tables.toBAMIndex(rw_centerangle
                                + (int) xtoviewangle[rw_x]);
                    texturecolumn =
                        rw_offset - FixedMul(finetangent[angle], rw_distance);
                    texturecolumn >>= FRACBITS;
                }

                // Don't to any drawing, only compute bounds.
                if (midtexture != 0) {

                    dcvars.dc_source =
                        TexMan.GetCachedColumn(midtexture, texturecolumn);
                    // dc_m=dcvars.dc_source_ofs;
                    // single sided line
                    ceilingclip[rw_x] = (short) view.height;
                    floorclip[rw_x] = -1;
                } else {
                    // two sided line
                    if (toptexture != 0) {
                        // top wall
                        mid = pixhigh >> HEIGHTBITS;
                        pixhigh += pixhighstep;

                        if (mid >= floorclip[rw_x])
                            mid = floorclip[rw_x] - 1;

                        if (mid >= yl) {
                            dcvars.dc_source =
                                TexMan.GetCachedColumn(toptexture,
                                    texturecolumn);
                            ceilingclip[rw_x] = (short) mid;
                        } else
                            ceilingclip[rw_x] = (short) (yl - 1);
                    } else {
                        // no top wall
                        if (markceiling)
                            ceilingclip[rw_x] = (short) (yl - 1);
                    }

                    if (bottomtexture != 0) {
                        // bottom wall
                        mid = (pixlow + HEIGHTUNIT - 1) >> HEIGHTBITS;
                        pixlow += pixlowstep;

                        // no space above wall?
                        if (mid <= ceilingclip[rw_x])
                            mid = ceilingclip[rw_x] + 1;

                        if (mid <= yh) {
                            dcvars.dc_source =
                                TexMan.GetCachedColumn(bottomtexture,
                                    texturecolumn);
                            floorclip[rw_x] = (short) mid;
                        } else
                            floorclip[rw_x] = (short) (yh + 1);
                    } else {
                        // no bottom wall
                        if (markfloor)
                            floorclip[rw_x] = (short) (yh + 1);
                    }

                    if (maskedtexture) {
                        // save texturecol
                        // for backdrawing of masked mid texture
                        seg_vars.maskedtexturecol[seg_vars.pmaskedtexturecol
                                + rw_x] = (short) texturecolumn;
                    }
                }

                rw_scale += rw_scalestep;
                topfrac += topstep;
                bottomfrac += bottomstep;
            }
        }

        protected final void GenerateRSI() {

            if (RSIcount >= RSI.length) {
                ResizeRSIBuffer();
            }

            RenderSegInstruction<V> rsi = RSI[RSIcount];
            rsi.centery = view.centery;
            rsi.bottomfrac = bottomfrac;
            rsi.bottomstep = bottomstep;
            rsi.bottomtexture = bottomtexture;
            rsi.markceiling = markceiling;
            rsi.markfloor = markfloor;
            rsi.midtexture = midtexture;
            rsi.pixhigh = pixhigh;
            rsi.pixhighstep = pixhighstep;
            rsi.pixlow = pixlow;
            rsi.pixlowstep = pixlowstep;
            rsi.rw_bottomtexturemid = rw_bottomtexturemid;
            rsi.rw_centerangle = rw_centerangle;
            rsi.rw_distance = rw_distance;
            rsi.rw_midtexturemid = rw_midtexturemid;
            rsi.rw_offset = rw_offset;
            rsi.rw_scale = rw_scale;
            rsi.rw_scalestep = rw_scalestep;
            rsi.rw_stopx = rw_stopx;
            rsi.rw_toptexturemid = rw_toptexturemid;
            rsi.rw_x = rw_x;
            rsi.segtextured = segtextured;
            rsi.topfrac = topfrac;
            rsi.topstep = topstep;
            rsi.toptexture = toptexture;
            rsi.walllights = colormaps.walllights;
            rsi.viewheight = view.height;
            // rsi.floorplane=floorplane;
            // rsi.ceilingplane=ceilingplane;
            RSIcount++;
        }

        @Override
        protected void CompleteColumn() {
            // TODO Auto-generated method stub

        }

        protected void RenderRSIPipeline() {

            for (int i = 0; i < NUMWALLTHREADS; i++) {
                RSIExec[i].setRSIEnd(RSIcount);
                // RWIExec[i].setRange(i%NUMWALLTHREADS,RWIcount,NUMWALLTHREADS);
                tp.execute(RSIExec[i]);
            }

            // System.out.println("RWI count"+RWIcount);
            RSIcount = 0;
        }

        /**
         * Resizes RWI buffer, updates executors. Sorry for the hackish
         * implementation but ArrayList and pretty much everything in
         * Collections is way too slow for what we're trying to accomplish.
         */

        protected void ResizeRSIBuffer() {

            RenderSegInstruction<V> fake = new RenderSegInstruction<V>();
            // Bye bye, old RSI.
            RSI = C2JUtils.resize(fake, RSI, RSI.length * 2);

            for (int i = 0; i < NUMWALLTHREADS; i++) {
                RSIExec[i].updateRSI(RSI);
            }

            System.out.println("RWI Buffer resized. Actual capacity "
                    + RSI.length);
        }

    }

    protected final class ParallelPlanes2<T, V>
            extends PlaneDrawer<T, V> {

        protected ParallelPlanes2(Renderer<T, V> R) {
            super(R);
        }

        /**
         * R_DrawPlanes At the end of each frame. This also means that visplanes
         * must have been set BEFORE we called this function. Therefore, look
         * for errors behind.
         * 
         * @throws IOException
         */
        public void DrawPlanes() {

            if (RANGECHECK) {
                rangeCheckErrors();
            }

            // vpw[0].setRange(0,lastvisplane/2);
            // vpw[1].setRange(lastvisplane/2,lastvisplane);

            for (int i = 0; i < NUMFLOORTHREADS; i++)
                tp.execute(vpw[i]);
        }

    } // End Plane class

    // / RWI SUBSYSTEM
    // / AKA "Render Wall Instruction": instructions that store only a single
    // column
    // from a wall

    /**
     * R_InitRSISubsystem Initialize RSIs and RSI Executors. Pegs them to the
     * RSI, ylookup and screen[0].
     */

    // protected abstract void InitRSISubsystem();

    /*
     * { // CATCH: this must be executed AFTER screen is set, and // AFTER we
     * initialize the RWI themselves, // before V is set (right?) //offsets=new
     * int[NUMWALLTHREADS]; for (int i=0;i<NUMWALLTHREADS;i++){ RSIExec[i]=new
     * RenderSegExecutor.HiColor( SCREENWIDTH, SCREENHEIGHT, i, screen, this,
     * TexMan, RSI, MySegs.getBLANKCEILINGCLIP(), MySegs.getBLANKFLOORCLIP(),
     * MySegs.getCeilingClip(), MySegs.getFloorClip(), columnofs, xtoviewangle,
     * ylookup, this.visplanes, this.visplanebarrier);
     * RSIExec[i].setVideoScale(this.vs); RSIExec[i].initScaling(); // Each
     * SegExecutor sticks to its own half (or 1/nth) of the screen.
     * RSIExec[i].setScreenRange
     * (i*(SCREENWIDTH/NUMWALLTHREADS),(i+1)*(SCREENWIDTH/NUMWALLTHREADS));
     * detailaware.add(RSIExec[i]); } for (int i=0;i<NUMFLOORTHREADS;i++){
     * vpw[i]=new VisplaneWorker(i,SCREENWIDTH,SCREENHEIGHT,columnofs,ylookup,
     * screen,visplanebarrier,NUMFLOORTHREADS); detailaware.add((IDetailAware)
     * vpw[i]); } }
     */

    /** Creates RMI Executors */

    // protected abstract void InitRMISubsystem();

    /*
     * { for (int i = 0; i < NUMMASKEDTHREADS; i++) { // Each masked executor
     * gets its own set of column functions. RMIExec[i] = new
     * RenderMaskedExecutor(SCREENWIDTH, SCREENHEIGHT, columnofs, ylookup,
     * screen, RMI, maskedbarrier, // Regular masked columns new
     * R_DrawColumnBoom
     * (SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I), new
     * R_DrawColumnBoomLow
     * (SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I), //
     * Fuzzy columns new
     * R_DrawFuzzColumn.HiColor(SCREENWIDTH,SCREENHEIGHT,ylookup
     * ,columnofs,maskedcvars,screen,I), new
     * R_DrawFuzzColumnLow.HiColor(SCREENWIDTH
     * ,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I), // Translated
     * columns new
     * R_DrawTranslatedColumn(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs
     * ,maskedcvars,screen,I), new
     * R_DrawTranslatedColumnLow(SCREENWIDTH,SCREENHEIGHT
     * ,ylookup,columnofs,maskedcvars,screen,I) ); detailaware.add(RMIExec[i]);
     * } }
     */

    public void Init(){
    	super.Init();
    	InitParallelStuff();
    }
    
    /**
     * Any scaling and implementation-specific stuff to do for parallel stuff
     * should go here. This method is called internally by the public Init().
     * The idea is that the renderer should be up & running after you finally
     * called this.
     */
    protected abstract void InitParallelStuff();
    
    /** Override this in one of the final implementors, if you want it to work */
    
    public RenderWallExecutor<T,V>[] InitRWIExecutors(int num,ColVars<T,V>[] RWI){
    	return null;
    }
    RWI.Get<T,V> RWIs;
    
}

package rr.parallel;

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

import rr.IDetailAware;
import rr.drawfuns.ColVars;
import rr.drawfuns.DoomColumnFunction;

import rr.drawfuns.R_DrawColumnBoomOpt;
import rr.drawfuns.R_DrawColumnBoomOptLow;

/**
 * This is what actual executes the RenderWallInstruction. Essentially it's a
 * self-contained column rendering function.
 * 
 * @author admin
 */

public class RenderWallExecutor<T,V>
        implements Runnable,IDetailAware {

    protected CyclicBarrier barrier;

    protected ColVars<T,V>[] RWI;

    protected int start, end;

    protected DoomColumnFunction<T,V> colfunchi, colfunclow;

    protected DoomColumnFunction<T,V> colfunc;

    public RenderWallExecutor(int SCREENWIDTH, int SCREENHEIGHT,
            int[] columnofs, int[] ylookup, V screen,
            ColVars<T,V>[] RWI, CyclicBarrier barrier) {
        this.RWI = RWI;
        this.barrier = barrier;
        this.SCREENWIDTH = SCREENWIDTH;
        this.SCREENHEIGHT = SCREENHEIGHT;

    }

    public void setRange(int start, int end) {
        this.end = end;
        this.start = start;
    }

    public void setDetail(int detailshift) {
        if (detailshift == 0)
            colfunc = colfunchi;
        else
            colfunc = colfunclow;
    }

    public void run() {

        // System.out.println("Wall executor from "+start +" to "+ end);

        for (int i = start; i < end; i++) {
            colfunc.invoke(RWI[i]);
        }

        try {
            barrier.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (BrokenBarrierException e) {
            e.printStackTrace();
        }
    }

    public void updateRWI(ColVars<T,V>[] RWI) {
        this.RWI = RWI;

    }

    /////////////// VIDEO SCALE STUFF//////////////////////

    protected final int SCREENWIDTH;

    protected final int SCREENHEIGHT;
    /*
     * protected IVideoScale vs;
     * @Override public void setVideoScale(IVideoScale vs) { this.vs=vs; }
     * @Override public void initScaling() {
     * this.SCREENHEIGHT=vs.getScreenHeight();
     * this.SCREENWIDTH=vs.getScreenWidth(); }
     */

    public static final class HiColor extends RenderWallExecutor<byte[],short[]> {

        public HiColor(int SCREENWIDTH, int SCREENHEIGHT, int[] columnofs,
                int[] ylookup, short[] screen,
                ColVars<byte[], short[]>[] RWI, CyclicBarrier barrier) {
            super(SCREENWIDTH, SCREENHEIGHT, columnofs, ylookup, screen, RWI, barrier);
            colfunc =
                colfunchi =
                    new R_DrawColumnBoomOpt.HiColor(SCREENWIDTH, SCREENHEIGHT, ylookup,
                            columnofs, null, screen, null);
            colfunclow =
                new R_DrawColumnBoomOptLow.HiColor(SCREENWIDTH, SCREENHEIGHT, ylookup,
                        columnofs, null, screen, null);
        }
        
    }
    
    public static final class Indexed extends RenderWallExecutor<byte[],byte[]> {

        public Indexed(int SCREENWIDTH, int SCREENHEIGHT, int[] columnofs,
                int[] ylookup, byte[] screen,
                ColVars<byte[], byte[]>[] RWI, CyclicBarrier barrier) {
            super(SCREENWIDTH, SCREENHEIGHT, columnofs, ylookup, screen, RWI, barrier);
            colfunc =
                colfunchi =
                    new R_DrawColumnBoomOpt.Indexed(SCREENWIDTH, SCREENHEIGHT, ylookup,
                            columnofs, null, screen, null);
            colfunclow =
                new R_DrawColumnBoomOptLow.Indexed(SCREENWIDTH, SCREENHEIGHT, ylookup,
                        columnofs, null, screen, null);
        }
        
    }
    
    public static final class TrueColor extends RenderWallExecutor<byte[],int[]> {

        public TrueColor(int SCREENWIDTH, int SCREENHEIGHT, int[] columnofs,
                int[] ylookup, int[] screen,
                ColVars<byte[], int[]>[] RWI, CyclicBarrier barrier) {
            super(SCREENWIDTH, SCREENHEIGHT, columnofs, ylookup, screen, RWI, barrier);
            colfunc =
                colfunchi =
                    new R_DrawColumnBoomOpt.TrueColor(SCREENWIDTH, SCREENHEIGHT, ylookup,
                            columnofs, null, screen, null);
            colfunclow =
                new R_DrawColumnBoomOptLow.TrueColor(SCREENWIDTH, SCREENHEIGHT, ylookup,
                        columnofs, null, screen, null);
        }
        
    }
    
}

package rr.parallel;

import rr.column_t;

/** An interface used to ease the use of the GetCachedColumn by part
 *  of parallelized renderers.
 *  
 * @author Maes
 *
 */

/**
 * Special version of GetColumn meant to be called concurrently by different
 * seg rendering threads, identfiex by index. This serves to avoid stomping
 * on mutual cached textures and causing crashes.
 * 
 */

public interface IGetSmpColumn {

	column_t GetSmpColumn(int tex, int col,int id);

}

package rr.parallel;

import java.util.List;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.Executor;

import rr.AbstractThings;
import rr.IDetailAware;
import rr.Renderer;
import rr.drawfuns.ColVars;
import rr.drawfuns.DcFlags;
import utils.C2JUtils;

/**
 * Parallel Things drawing class, column based, using RMI pipeline.
 * For N threads, each thread only draws those columns of sprites that
 * are in its own 1/N portion of the screen.
 * 
 * Overrides only the terminal drawing methods from things, using a
 * mechanism very similar to column-based wall threading. It's not very
 * efficient, since some of the really heavy parts (such as visibility
 * priority) are still done serially, and actually do take up a lot of the
 * actual rendering time, and the number of columns generated is REALLY
 * enormous (100K+ for something like nuts.wad), and the thing chokes on
 * synchronization, more than anything. The only appropriate thing to do
 * would be to have a per-vissprite renderer, which would actually move much
 * of the brunt work away from the main thread. Some interesting benchmarks
 * on nuts.wad timedemo: Normal things serial renderer: 60-62 fps "Dummy"
 * completeColumns: 72 fps "Dummy" things renderer without final drawing: 80
 * fps "Dummy" things renderer without ANY calculations: 90 fps. This means
 * that even a complete parallelization will likely have a quite limited
 * impact.
 * 
 * @author velktron
 */

public abstract class ParallelThings<T,V>
        extends AbstractThings<T,V> {
    
    // stuff to get from container
    
    
    /** Render Masked Instuction subsystem. Essentially, a way to split sprite work
     *  between threads on a column-basis.
     */

    protected ColVars<T, V>[] RMI;

    /**
     * Increment this as you submit RMIs to the "queue". Remember to reset to 0
     * when you have drawn everything!
     */
    protected int RMIcount = 0;

    protected RenderMaskedExecutor<T,V>[] RMIExec;
    
    protected final int NUMMASKEDTHREADS;
    protected final CyclicBarrier maskedbarrier;
    protected final Executor tp;
    
    public ParallelThings(Renderer<T,V> R,Executor tp, int numthreads){
        super(R);
        this.tp=tp;
        this.NUMMASKEDTHREADS=numthreads;
        this.maskedbarrier=new CyclicBarrier(NUMMASKEDTHREADS+1);
    }

    @Override
    public void DrawMasked() {
        
        // This just generates the RMI instructions.
        super.DrawMasked();
        // This splits the work among threads and fires them up
        RenderRMIPipeline();

        try {
            // Wait for them to be done.
            maskedbarrier.await();
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (BrokenBarrierException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    @Override
    public void completeColumn() {

        if (view.detailshift == 1)
            flags = DcFlags.LOW_DETAIL;
        // Don't wait to go over
        if (RMIcount >= RMI.length) {
            ResizeRMIBuffer();
        }

        // A deep copy is still necessary, as well as setting dc_flags
        RMI[RMIcount].copyFrom(maskedcvars, colfunc.getFlags());

        // We only need to point to the next one in the list.
        RMIcount++;
    }

    int flags;
    
   
    protected void RenderRMIPipeline() {

        for (int i = 0; i < NUMMASKEDTHREADS; i++) {

            RMIExec[i].setRange((i * this.SCREENWIDTH) / NUMMASKEDTHREADS,
                ((i + 1) * this.SCREENWIDTH) / NUMMASKEDTHREADS);
            RMIExec[i].setRMIEnd(RMIcount);
            // RWIExec[i].setRange(i%NUMWALLTHREADS,RWIcount,NUMWALLTHREADS);
            tp.execute(RMIExec[i]);
        }

        // System.out.println("RWI count"+RWIcount);
        RMIcount = 0;
    }
    
    protected void ResizeRMIBuffer() {
        ColVars<T, V> fake = new ColVars<T, V>();
        ColVars<T, V>[] tmp =            // TODO Auto-generated constructor stub
            C2JUtils.createArrayOfObjects(fake, RMI.length * 2);
        System.arraycopy(RMI, 0, tmp, 0, RMI.length);

        // Bye bye, old RMI.
        RMI = tmp;

        for (int i = 0; i < NUMMASKEDTHREADS; i++) {
            RMIExec[i].updateRMI(RMI);
        }
        
        System.err.println("RMI Buffer resized. Actual capacity " + RMI.length);
    }
    
    protected abstract void InitRMISubsystem(int[] columnofs, int[] ylookup,V screen, CyclicBarrier
            maskedbarrier,V BLURRY_MAP, List<IDetailAware> detailaware);
    
    public static class Indexed extends ParallelThings<byte[],byte[]>{

    public Indexed(Renderer<byte[], byte[]> R, Executor tp, int numthreads) {
            super(R, tp, numthreads);
        }

    protected void InitRMISubsystem(int[] columnofs, int[] ylookup,byte[] screen, CyclicBarrier
            maskedbarrier,byte[] BLURRY_MAP, List<IDetailAware> detailaware)    {
        for (int i = 0; i < NUMMASKEDTHREADS; i++) {
            RMIExec[i] =
                new RenderMaskedExecutor.Indexed(SCREENWIDTH, SCREENHEIGHT, columnofs,
                        ylookup, screen, RMI, maskedbarrier,I,BLURRY_MAP);
                
            detailaware.add(RMIExec[i]);
            }
        }
    }
    
    public static class HiColor extends ParallelThings<byte[],short[]>{
        

        public HiColor(Renderer<byte[], short[]> R, Executor tp, int numthreads) {
            super(R, tp, numthreads);
        }

        protected void InitRMISubsystem(int[] columnofs, int[] ylookup,short[] screen, CyclicBarrier
                maskedbarrier,short[] BLURRY_MAP, List<IDetailAware> detailaware)    {
            for (int i = 0; i < NUMMASKEDTHREADS; i++) {
                RMIExec[i] =
                    new RenderMaskedExecutor.HiColor(SCREENWIDTH, SCREENHEIGHT, columnofs,
                            ylookup, screen, RMI, maskedbarrier,I);
                    
                detailaware.add(RMIExec[i]);
                }
            }
        }
    
public static class TrueColor extends ParallelThings<byte[],int[]>{


        public TrueColor(Renderer<byte[], int[]> R, Executor tp, int numthreads) {
        super(R, tp, numthreads);
    }

        protected void InitRMISubsystem(int[] columnofs, int[] ylookup,int[] screen, CyclicBarrier
                maskedbarrier,int[] BLURRY_MAP, List<IDetailAware> detailaware)    {
            for (int i = 0; i < NUMMASKEDTHREADS; i++) {
                RMIExec[i] =
                    new RenderMaskedExecutor.TrueColor(SCREENWIDTH, SCREENHEIGHT, columnofs,
                            ylookup, screen, RMI, maskedbarrier,I);
                    
                detailaware.add(RMIExec[i]);
                }
            }
        }

}

package rr.parallel;

import rr.drawfuns.ColVars;

public interface RWI<T,V> {
	public interface Init<T,V>{
		RenderWallExecutor<T,V>[] InitRWIExecutors(int num,ColVars<T,V>[] RWI);
	}
	
	public interface Get<T,V>{
		ColVars<T,V>[] getRWI();
		void setExecutors(RenderWallExecutor<T,V>[] RWIExec);
	}
}

package rr.parallel;

import static data.Defines.ANGLETOSKYSHIFT;
import static data.Tables.addAngles;
import static m.fixed_t.FRACBITS;
import static rr.LightsAndColors.LIGHTLEVELS;
import static rr.LightsAndColors.LIGHTSEGSHIFT;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

import rr.IDetailAware;
import rr.PlaneDrawer;
import rr.Renderer;
import rr.visplane_t;
import rr.drawfuns.ColVars;
import rr.drawfuns.DoomColumnFunction;
import rr.drawfuns.DoomSpanFunction;
import rr.drawfuns.R_DrawColumnBoomOpt;
import rr.drawfuns.R_DrawColumnBoomOptLow;
import rr.drawfuns.R_DrawSpanLow;
import rr.drawfuns.R_DrawSpanUnrolled;
import rr.drawfuns.SpanVars;

/** Visplane worker which shares work in an equal-visplane number strategy
 *  with other workers. Might be unbalanced if one worker gets too large
 *  visplanes and others get smaller ones. Balancing strategy is applied in 
 *  run(), otherwise it's practically similar to a PlaneDrwer.
 *  
 *  
 * @author velktron
 *
 */

public abstract class VisplaneWorker<T,V> extends PlaneDrawer<T,V> implements Runnable,IDetailAware{

    // Private to each thread.
    protected final int id;
    protected final int NUMFLOORTHREADS;
    protected final CyclicBarrier barrier;
    
    protected int vpw_planeheight;
    protected V[] vpw_planezlight;
    protected int vpw_basexscale,vpw_baseyscale;

    protected SpanVars<T,V> vpw_dsvars;
    protected ColVars<T,V> vpw_dcvars;
    
    // OBVIOUSLY each thread must have its own span functions.
    protected DoomSpanFunction<T,V> vpw_spanfunc;
    protected DoomColumnFunction<T,V> vpw_skyfunc;
    protected DoomSpanFunction<T,V> vpw_spanfunchi;
    protected DoomSpanFunction<T,V> vpw_spanfunclow;
    protected DoomColumnFunction<T,V> vpw_skyfunchi;
    protected DoomColumnFunction<T,V> vpw_skyfunclow;
        
    public VisplaneWorker(int id,int SCREENWIDTH, int SCREENHEIGHT, Renderer<T,V> R,CyclicBarrier visplanebarrier,int NUMFLOORTHREADS) {
        super(R); 
        this.barrier=visplanebarrier;
        this.id=id;
        this.NUMFLOORTHREADS=NUMFLOORTHREADS;
    }

    public static final class HiColor extends VisplaneWorker<byte[],short[]>{

        public HiColor(int id, int SCREENWIDTH, int SCREENHEIGHT,Renderer<byte[],short[]> R,
                int[] columnofs, int[] ylookup, short[] screen,
                CyclicBarrier visplanebarrier, int NUMFLOORTHREADS) {
            super(id, SCREENWIDTH, SCREENHEIGHT, R,  visplanebarrier, NUMFLOORTHREADS);
            // Alias to those of Planes.


            vpw_dsvars=new SpanVars<byte[],short[]>();
            vpw_dcvars=new ColVars<byte[],short[]>();
            vpw_spanfunc=vpw_spanfunchi=new R_DrawSpanUnrolled.HiColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,vpw_dsvars,screen,I);
            vpw_spanfunclow=new R_DrawSpanLow.HiColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,vpw_dsvars,screen,I);
            vpw_skyfunc=vpw_skyfunchi=new R_DrawColumnBoomOpt.HiColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,vpw_dcvars,screen,I);
            vpw_skyfunclow=new R_DrawColumnBoomOptLow.HiColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,vpw_dcvars,screen,I);
            
        }
        
    }
    
    public void setDetail(int detailshift) {
        if (detailshift == 0){
            vpw_spanfunc = vpw_spanfunchi;
            vpw_skyfunc= vpw_skyfunchi;
        }
        else{
            vpw_spanfunc = vpw_spanfunclow;
            vpw_skyfunc =vpw_skyfunclow;
        }
    }
    
    @Override
    public void run() {
        visplane_t      pln=null; //visplane_t
        // These must override the global ones
        int         light;
        int         x;
        int         stop;
        int         angle;
      
        // Now it's a good moment to set them.
        vpw_basexscale=vpvars.getBaseXScale();
        vpw_baseyscale=vpvars.getBaseYScale();
        
        // TODO: find a better way to split work. As it is, it's very uneven
        // and merged visplanes in particular are utterly dire.
        
        for (int pl= this.id; pl <vpvars.lastvisplane; pl+=NUMFLOORTHREADS) {
             pln=vpvars.visplanes[pl];
            // System.out.println(id +" : "+ pl);
             
         if (pln.minx > pln.maxx)
             continue;

         
         // sky flat
         if (pln.picnum == TexMan.getSkyFlatNum() )
         {
             // Cache skytexture stuff here. They aren't going to change while
             // being drawn, after all, are they?
             int skytexture=TexMan.getSkyTexture();
             // MAES: these must be updated to keep up with screen size changes.
             vpw_dcvars.viewheight=view.height;
             vpw_dcvars.centery=view.centery;
             vpw_dcvars.dc_texheight=TexMan.getTextureheight(skytexture)>>FRACBITS;                 
             vpw_dcvars.dc_iscale = vpvars.getSkyScale()>>view.detailshift;
             
             vpw_dcvars.dc_colormap = colormap.colormaps[0];
             vpw_dcvars.dc_texturemid = TexMan.getSkyTextureMid();
             for (x=pln.minx ; x <= pln.maxx ; x++)
             {
           
                 vpw_dcvars.dc_yl = pln.getTop(x);
                 vpw_dcvars.dc_yh = pln.getBottom(x);
             
             if (vpw_dcvars.dc_yl <= vpw_dcvars.dc_yh)
             {
                 angle = (int) (addAngles(view.angle, view.xtoviewangle[x])>>>ANGLETOSKYSHIFT);
                 vpw_dcvars.dc_x = x;
                 // Optimized: texheight is going to be the same during normal skies drawing...right?
                 vpw_dcvars.dc_source = TexMan.GetCachedColumn(TexMan.getSkyTexture(), angle);
                 vpw_skyfunc.invoke();
             }
             }
             continue;
         }
         
         // regular flat
         vpw_dsvars.ds_source = TexMan.getSafeFlat(pln.picnum);

         vpw_planeheight = Math.abs(pln.height-view.z);
         light = (pln.lightlevel >>> LIGHTSEGSHIFT)+colormap.extralight;

         if (light >= LIGHTLEVELS)
             light = LIGHTLEVELS-1;

         if (light < 0)
             light = 0;

         vpw_planezlight = colormap.zlight[light];

         // We set those values at the border of a plane's top to a "sentinel" value...ok.
         pln.setTop(pln.maxx+1,(char) 0xffff);
         pln.setTop(pln.minx-1, (char) 0xffff);
         
         stop = pln.maxx + 1;

         
         for (x=pln.minx ; x<= stop ; x++) {
          MakeSpans(x,pln.getTop(x-1),
             pln.getBottom(x-1),
             pln.getTop(x),
             pln.getBottom(x));
            }
         
         }
         // We're done, wait.

            try {
                barrier.await();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (BrokenBarrierException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

     }
  
      
          
      
      
  }
package rr.parallel;

import static data.Defines.ANGLETOSKYSHIFT;
import static data.Defines.PU_STATIC;
import static data.Tables.ANGLETOFINESHIFT;
import static data.Tables.BITS32;
import static data.Tables.addAngles;
import static data.Tables.finecosine;
import static data.Tables.finesine;
import static m.fixed_t.FRACBITS;
import static m.fixed_t.FixedMul;
import static rr.LightsAndColors.*;

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

import rr.PlaneDrawer;
import rr.Renderer;
import rr.flat_t;
import rr.visplane_t;
import rr.drawfuns.ColVars;
import rr.drawfuns.DoomColumnFunction;
import rr.drawfuns.DoomSpanFunction;
import rr.drawfuns.R_DrawColumnBoomOpt;
import rr.drawfuns.R_DrawColumnBoomOptLow;
import rr.drawfuns.R_DrawSpanLow;
import rr.drawfuns.R_DrawSpanUnrolled;
import rr.drawfuns.SpanVars;

/** Visplane worker which shares work in an equal screen-portions strategy.
 * 
 * More balanced, but requires careful synchronization to avoid overdrawing and
 * stomping.
 *  
 *  
 * @author vepitrop.
 *
 */

public abstract class VisplaneWorker2<T,V> extends PlaneDrawer<T,V> implements Runnable{

    protected final int id;
    protected final int NUMFLOORTHREADS;
    protected int startvp;  
    protected int endvp;
    protected int vpw_planeheight;
    protected V[] vpw_planezlight;
    protected int vpw_basexscale,vpw_baseyscale;
    protected final SpanVars<T,V> vpw_dsvars;
    protected final ColVars<T,V> vpw_dcvars;
    protected DoomSpanFunction<T,V> vpw_spanfunc;
    protected DoomColumnFunction<T,V> vpw_skyfunc;
    protected DoomSpanFunction<T,V> vpw_spanfunchi;
    protected DoomSpanFunction<T,V> vpw_spanfunclow;
    protected DoomColumnFunction<T,V> vpw_skyfunchi;
    protected DoomColumnFunction<T,V> vpw_skyfunclow;
    protected visplane_t pln;
    
    public VisplaneWorker2(Renderer<T,V> R,int id,int sCREENWIDTH, int sCREENHEIGHT, CyclicBarrier visplanebarrier,int NUMFLOORTHREADS) {
        super(R);
        this.barrier=visplanebarrier;
        this.id=id;
        // Alias to those of Planes.
        vpw_dsvars=new SpanVars<T,V>();
        vpw_dcvars=new ColVars<T,V>();
        this.NUMFLOORTHREADS=NUMFLOORTHREADS;
    }

    public static class HiColor extends VisplaneWorker2<byte[],short[]>{

        public HiColor(Renderer<byte[],short[]> R,int id, int sCREENWIDTH, int sCREENHEIGHT,
                int[] columnofs, int[] ylookup, short[] screen,
                CyclicBarrier visplanebarrier, int NUMFLOORTHREADS) {
            super(R,id, sCREENWIDTH, sCREENHEIGHT, visplanebarrier, NUMFLOORTHREADS);
            vpw_spanfunc=vpw_spanfunchi=new R_DrawSpanUnrolled.HiColor(sCREENWIDTH,sCREENHEIGHT,ylookup,columnofs,vpw_dsvars,screen,I);
            vpw_spanfunclow=new R_DrawSpanLow.HiColor(sCREENWIDTH,sCREENHEIGHT,ylookup,columnofs,vpw_dsvars,screen,I);
            vpw_skyfunc=vpw_skyfunchi=new R_DrawColumnBoomOpt.HiColor(sCREENWIDTH,sCREENHEIGHT,ylookup,columnofs,vpw_dcvars,screen,I);
            vpw_skyfunclow=new R_DrawColumnBoomOptLow.HiColor(sCREENWIDTH,sCREENHEIGHT,ylookup,columnofs,vpw_dcvars,screen,I);

        }
        
    }
    
    public static class Indexed extends VisplaneWorker2<byte[],byte[]>{

        public Indexed(Renderer<byte[],byte[]> R,int id, int sCREENWIDTH, int sCREENHEIGHT,
                int[] columnofs, int[] ylookup, byte[] screen,
                CyclicBarrier visplanebarrier, int NUMFLOORTHREADS) {
            super(R,id, sCREENWIDTH, sCREENHEIGHT, visplanebarrier, NUMFLOORTHREADS);
            vpw_spanfunc=vpw_spanfunchi=new R_DrawSpanUnrolled.Indexed(sCREENWIDTH,sCREENHEIGHT,ylookup,columnofs,vpw_dsvars,screen,I);
            vpw_spanfunclow=new R_DrawSpanLow.Indexed(sCREENWIDTH,sCREENHEIGHT,ylookup,columnofs,vpw_dsvars,screen,I);
            vpw_skyfunc=vpw_skyfunchi=new R_DrawColumnBoomOpt.Indexed(sCREENWIDTH,sCREENHEIGHT,ylookup,columnofs,vpw_dcvars,screen,I);
            vpw_skyfunclow=new R_DrawColumnBoomOptLow.Indexed(sCREENWIDTH,sCREENHEIGHT,ylookup,columnofs,vpw_dcvars,screen,I);

        }
        
    }
    
    public static class TrueColor extends VisplaneWorker2<byte[],int[]>{

        public TrueColor(Renderer<byte[],int[]> R,int id, int sCREENWIDTH, int sCREENHEIGHT,
                int[] columnofs, int[] ylookup, int[] screen,
                CyclicBarrier visplanebarrier, int NUMFLOORTHREADS) {
            super(R,id, sCREENWIDTH, sCREENHEIGHT, visplanebarrier, NUMFLOORTHREADS);
            vpw_spanfunc=vpw_spanfunchi=new R_DrawSpanUnrolled.TrueColor(sCREENWIDTH,sCREENHEIGHT,ylookup,columnofs,vpw_dsvars,screen,I);
            vpw_spanfunclow=new R_DrawSpanLow.TrueColor(sCREENWIDTH,sCREENHEIGHT,ylookup,columnofs,vpw_dsvars,screen,I);
            vpw_skyfunc=vpw_skyfunchi=new R_DrawColumnBoomOpt.TrueColor(sCREENWIDTH,sCREENHEIGHT,ylookup,columnofs,vpw_dcvars,screen,I);
            vpw_skyfunclow=new R_DrawColumnBoomOptLow.TrueColor(sCREENWIDTH,sCREENHEIGHT,ylookup,columnofs,vpw_dcvars,screen,I);

        }
        
    }
    
    @Override
    public void run() {
        pln=null; //visplane_t
        // These must override the global ones

        int         light;
        int         x;
        int         stop;
        int         angle;
        int minx,maxx;
        
        // Now it's a good moment to set them.
        vpw_basexscale=vpvars.getBaseXScale();
        vpw_baseyscale=vpvars.getBaseYScale();
        
        startvp=((id*view.width)/NUMFLOORTHREADS);
        endvp=(((id+1)*view.width)/NUMFLOORTHREADS);
        
        // TODO: find a better way to split work. As it is, it's very uneven
        // and merged visplanes in particular are utterly dire.
        
         for (int pl= 0; pl <vpvars.lastvisplane; pl++) {
             pln=vpvars.visplanes[pl];
            // System.out.println(id +" : "+ pl);

        // Trivial rejection.
         if ((pln.minx > endvp) || (pln.maxx <startvp)) 
                 continue;

         // Reject non-visible  
         if (pln.minx > pln.maxx)
             continue;

         // Trim to zone
         minx=Math.max(pln.minx,startvp);
         maxx=Math.min(pln.maxx,endvp);
         
         // sky flat
         if (pln.picnum == TexMan.getSkyFlatNum() )
         {
             // Cache skytexture stuff here. They aren't going to change while
             // being drawn, after all, are they?
             int skytexture=TexMan.getSkyTexture();
             // MAES: these must be updated to keep up with screen size changes.
             vpw_dcvars.viewheight=view.height;
             vpw_dcvars.centery=view.centery;
             vpw_dcvars.dc_texheight=TexMan.getTextureheight(skytexture)>>FRACBITS;                 
             vpw_dcvars.dc_iscale = vpvars.getSkyScale()>>view.detailshift;
             
             vpw_dcvars.dc_colormap = colormap.colormaps[0];
             vpw_dcvars.dc_texturemid = TexMan.getSkyTextureMid();
             for (x=minx ; x <= maxx ; x++)
             {
           
                 vpw_dcvars.dc_yl = pln.getTop(x);
                 vpw_dcvars.dc_yh = pln.getBottom(x);
             
             if (vpw_dcvars.dc_yl <= vpw_dcvars.dc_yh)
             {
                 angle = (int) (addAngles(view.angle, view.xtoviewangle[x])>>>ANGLETOSKYSHIFT);
                 vpw_dcvars.dc_x = x;
                 vpw_dcvars.dc_texheight=TexMan.getTextureheight(TexMan.getSkyTexture())>>FRACBITS;
                 vpw_dcvars.dc_source = TexMan.GetCachedColumn(TexMan.getSkyTexture(), angle);
                 vpw_skyfunc.invoke();
             }
             }
             continue;
         }
         
         // regular flat
         vpw_dsvars.ds_source = TexMan.getSafeFlat(pln.picnum);
         vpw_planeheight = Math.abs(pln.height-view.z);
         light = (pln.lightlevel >>> LIGHTSEGSHIFT)+colormap.extralight;

         if (light >= LIGHTLEVELS)
             light = LIGHTLEVELS-1;

         if (light < 0)
             light = 0;

         vpw_planezlight = colormap.zlight[light];

         // Some tinkering required to make sure visplanes
         // don't end prematurely on each other's stop markers
         
         char value=pln.getTop(maxx+1);
         if (!isMarker(value)) { // is it a marker?
                value|=visplane_t.SENTINEL; // Mark it so.
                value&=visplane_t.THREADIDCLEAR; //clear id bits
                value|=(id<<visplane_t.THREADIDSHIFT); // set our own id.
                } // Otherwise, it was set by another thread.
                  // Leave it be.
         
         pln.setTop(maxx+1,value);
         
         value=pln.getTop(minx-1);
         if (!isMarker(value)) { // is it a marker?
                value|=visplane_t.SENTINEL; // Mark it so.
                value&=visplane_t.THREADIDCLEAR; //clear id bits
                value|=(id<<visplane_t.THREADIDSHIFT); // set our own id.
                } // Otherwise, it was set by another thread.
                  // Leave it be.
         
         pln.setTop(minx-1, value);
         
         stop = maxx+1;
         
         for (x=minx ; x<= stop ; x++) {
          MakeSpans(x,pln.getTop(x-1),
             pln.getBottom(x-1),
             pln.getTop(x),
             pln.getBottom(x));
            }
         
         }
         // We're done, wait.

            try {
                barrier.await();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (BrokenBarrierException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

     }
        
    private final boolean isMarker(int t1){
        return ((t1&visplane_t.SENTINEL)!=0);
        }
    
    private final int decodeID(int t1){
        return (t1&visplane_t.THREADIDBITS)>>visplane_t.THREADIDSHIFT;
        }
    
    private final int decodeValue(int t1){
        return t1&visplane_t.THREADVALUEBITS;
        }
    
    public void setDetail(int detailshift) {
        if (detailshift == 0){
            vpw_spanfunc = vpw_spanfunchi;
            vpw_skyfunc= vpw_skyfunchi;
        }
        else{
            vpw_spanfunc = vpw_spanfunclow;
            vpw_skyfunc =vpw_skyfunclow;
        }
    }
    
    /**
     * R_MakeSpans
     * 
     * Called only by DrawPlanes.
     * If you wondered where the actual boundaries for the visplane
     * flood-fill are laid out, this is it.
     * 
     * The system of coords seems to be defining a sort of cone.          
     *          
     * 
     * @param x Horizontal position
     * @param t1 Top-left y coord?
     * @param b1 Bottom-left y coord?
     * @param t2 Top-right y coord ?
     * @param b2 Bottom-right y coord ?
     * 
     */

      protected final void MakeSpans(int x, int t1, int b1, int t2, int b2) {
      
          // Top 1 sentinel encountered.
          if (isMarker(t1))
              if (decodeID(t1)!=id) // We didn't put it here.
                  t1=decodeValue(t1);

        // Top 2 sentinel encountered.
          if (isMarker(t2))
              if (decodeID(t2)!=id)
                  t2=decodeValue(t2);
              
          // If t1 = [sentinel value] then this part won't be executed.
          while (t1 < t2 && t1 <= b1) {
              this.MapPlane(t1, spanstart[t1], x - 1);
              t1++;
          }
          while (b1 > b2 && b1 >= t1) {
              this.MapPlane(b1, spanstart[b1], x - 1);
              b1--;
          }

          // So...if t1 for some reason is < t2, we increase t2 AND store the current x
          // at spanstart [t2] :-S
          while (t2 < t1 && t2 <= b2) {
              spanstart[t2] = x;
              t2++;
          }

          // So...if t1 for some reason b2 > b1, we decrease b2 AND store the current x
          // at spanstart [t2] :-S

          while (b2 > b1 && b2 >= t2) {
              spanstart[b2] = x;
              b2--;
          }
      }
      
      /**
       * R_MapPlane
       *
       * Called only by R_MakeSpans.
       * 
       * This is where the actual span drawing function is called.
       * 
       * Uses global vars:
       * planeheight
       *  ds_source -> flat data has already been set.
       *  basexscale -> actual drawing angle and position is computed from these
       *  baseyscale
       *  viewx
       *  viewy
       *
       * BASIC PRIMITIVE
       */
      /* TODO: entirely similar to serial version?
      private void
      MapPlane
      ( int       y,
        int       x1,
        int       x2 )
      {
          // MAES: angle_t
          int angle;
          // fixed_t
          int distance;
          int length;
          int index;
          
      if (RANGECHECK){
          if (x2 < x1
          || x1<0
          || x2>=view.width
          || y>view.height)
          {
          I.Error ("R_MapPlane: %d, %d at %d",x1,x2,y);
          }
      }

          if (vpw_planeheight != cachedheight[y])
          {
          cachedheight[y] = vpw_planeheight;
          distance = cacheddistance[y] = FixedMul (vpw_planeheight , yslope[y]);
          vpw_dsvars.ds_xstep = cachedxstep[y] = FixedMul (distance,vpw_basexscale);
          vpw_dsvars.ds_ystep = cachedystep[y] = FixedMul (distance,vpw_baseyscale);
          }
          else
          {
          distance = cacheddistance[y];
          vpw_dsvars.ds_xstep = cachedxstep[y];
          vpw_dsvars.ds_ystep = cachedystep[y];
          }
          
          length = FixedMul (distance,distscale[x1]);
          angle = (int)(((view.angle +xtoviewangle[x1])&BITS32)>>>ANGLETOFINESHIFT);
          vpw_dsvars.ds_xfrac = view.x + FixedMul(finecosine[angle], length);
          vpw_dsvars.ds_yfrac = -view.y - FixedMul(finesine[angle], length);

          if (colormap.fixedcolormap!=null)
              vpw_dsvars.ds_colormap = colormap.fixedcolormap;
          else
          {
          index = distance >>> LIGHTZSHIFT;
          
          if (index >= MAXLIGHTZ )
              index = MAXLIGHTZ-1;

          vpw_dsvars.ds_colormap = vpw_planezlight[index];
          }
          
          vpw_dsvars.ds_y = y;
          vpw_dsvars.ds_x1 = x1;
          vpw_dsvars.ds_x2 = x2;

          // high or low detail
          if (view.detailshift==0)
              vpw_spanfunc.invoke();
          else
              vpw_spanfunclow.invoke();         
      }
      */
      
      // Private to each thread.
      int[]           spanstart;
      int[]           spanstop;
      CyclicBarrier barrier;
      
  }

package rr.parallel;

import static data.Tables.finetangent;
import static m.fixed_t.*;
import static rr.LightsAndColors.*;

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

import rr.IDetailAware;
import rr.IGetColumn;
import rr.TextureManager;
import rr.visplane_t;
import rr.drawfuns.ColVars;
import rr.drawfuns.DoomColumnFunction;
import rr.drawfuns.R_DrawColumnBoomOpt;
import rr.drawfuns.R_DrawColumnBoomOptLow;

import data.Tables;

import v.IVideoScale;
import v.IVideoScaleAware;

/** This is what actual executes the RenderSegInstructions.
 *   *  
 *  Each thread actually operates on a FIXED PORTION OF THE SCREEN
 *  (e.g. half-width, third-width etc.) and only renders the portions
 *  of the RenderSegInstructions that are completely contained
 *  within its own screen area. For this reason, all threads
 *  check out all RenderSegInstructions of the list, and render any 
 *  and all portions that are within their responsability domain, so
 *  to speak.
 *  
 *  FIXME there's a complex data dependency with ceilingclip/floorclip 
 *  I was not quite able to fix yet. Practically, in the serial renderer,
 *  calls to RenderSegLoop are done in a correct, non-overlapping order,
 *  and certain parts are drawn before others in order to set current
 *  floor/ceiling markers and visibility e.g. think of a wall visible
 *  through windows.
 *  
 *  FIXME 7/6/2011 Data dependencies and per-thread clipping are now 
 *  fixed, however there is still visible "jitter" or "noise" on some 
 *  of the walls, probably related to column offsets.
 * 
 * @author velktron
 *
 */

public abstract class RenderSegExecutor<T,V> implements Runnable, IVideoScaleAware,IDetailAware {

	// This needs to be set by the partitioner.
    protected int rw_start, rw_end,rsiend;
	// These need to be set on creation, and are unchangeable.
	protected final TextureManager<T> TexMan;
	protected final CyclicBarrier barrier;
	protected RenderSegInstruction<V>[] RSI;
	protected final long[] xtoviewangle;
	protected final short[] ceilingclip, floorclip;

	// Each thread should do its own ceiling/floor blanking
	protected final short[] BLANKFLOORCLIP;
	protected final short[] BLANKCEILINGCLIP;

    protected static final int HEIGHTBITS   =   12;
	protected static final int HEIGHTUNIT   =   (1<<HEIGHTBITS);
	protected final int id;
	
	protected DoomColumnFunction<T,V> colfunchi,colfunclow;
	protected DoomColumnFunction<T,V> colfunc;
	protected ColVars<T,V> dcvars;
	
	public RenderSegExecutor(int SCREENWIDTH, int SCREENHEIGHT,int id,V screen, 
			TextureManager<T> texman,
			RenderSegInstruction<V>[] RSI,
			short[] BLANKCEILINGCLIP,
			short[] BLANKFLOORCLIP,
			short[] ceilingclip,
			short[] floorclip,
			int[] columnofs,
			long[] xtoviewangle,
			int[] ylookup,
			visplane_t[] visplanes,
			CyclicBarrier barrier){
		this.id=id;
		this.TexMan=texman;
		this.RSI=RSI;
		this.barrier=barrier;		
		this.ceilingclip=ceilingclip;
		this.floorclip=floorclip;
		this.xtoviewangle=xtoviewangle;
		this.BLANKCEILINGCLIP=BLANKCEILINGCLIP;
		this.BLANKFLOORCLIP=BLANKFLOORCLIP;
        
	}

	 protected final void ProcessRSI(RenderSegInstruction<V> rsi, int startx,int endx,boolean contained){
         int     angle; // angle_t
         int     index;
         int     yl; // low
         int     yh; // hight
         int     mid;
         int pixlow,pixhigh,pixhighstep,pixlowstep;
         int rw_scale,topfrac,bottomfrac,bottomstep;
         // These are going to be modified A LOT, so we cache them here.
         pixhighstep=rsi.pixhighstep;
         pixlowstep=rsi.pixlowstep;
         bottomstep=rsi.bottomstep;
         //  We must re-scale it.
         int rw_scalestep= rsi.rw_scalestep; 
         int topstep=rsi.topstep;
         int     texturecolumn=0; // fixed_t
         final int bias;
         // Well is entirely contained in our screen zone 
         // (or the very least it starts in it).
         if (contained) bias=0; 
             // We are continuing a wall that started in another 
             // screen zone.
             else bias=(startx-rsi.rw_x);
         // PROBLEM: these must be pre-biased when multithreading.
             rw_scale=rsi.rw_scale+bias*rw_scalestep;
             topfrac = rsi.topfrac+bias*topstep;
             bottomfrac = rsi.bottomfrac+ bias*bottomstep;
             pixlow=rsi.pixlow+bias*pixlowstep;
             pixhigh=rsi.pixhigh+bias*pixhighstep;

         {
            
             for ( int rw_x=startx; rw_x < endx ; rw_x++)
             {
             // mark floor / ceiling areas
             yl = (topfrac+HEIGHTUNIT-1)>>HEIGHTBITS;

             // no space above wall?
             if (yl < ceilingclip[rw_x]+1)
                 yl = ceilingclip[rw_x]+1;
                 
             yh = bottomfrac>>HEIGHTBITS;

             if (yh >= floorclip[rw_x])
                 yh = floorclip[rw_x]-1;
             
           //  System.out.printf("Thread: rw %d yl %d yh %d\n",rw_x,yl,yh);

             // A particular seg has been identified as a floor marker.
             
             
             // texturecolumn and lighting are independent of wall tiers
             if (rsi.segtextured)
             {
                 // calculate texture offset
                
                 
               // CAREFUL: a VERY anomalous point in the code. Their sum is supposed
               // to give an angle not exceeding 45 degrees (or 0x0FFF after shifting).
               // If added with pure unsigned rules, this doesn't hold anymore,
               // not even if accounting for overflow.
                 angle = Tables.toBAMIndex(rsi.rw_centerangle + (int)xtoviewangle[rw_x]);
               //angle = (int) (((rw_centerangle + xtoviewangle[rw_x])&BITS31)>>>ANGLETOFINESHIFT);
               //angle&=0x1FFF;
                 
               // FIXME: We are accessing finetangent here, the code seems pretty confident
               // in that angle won't exceed 4K no matter what. But xtoviewangle
               // alone can yield 8K when shifted.
               // This usually only overflows if we idclip and look at certain directions 
               // (probably angles get fucked up), however it seems rare enough to just 
               // "swallow" the exception. You can eliminate it by anding with 0x1FFF
               // if you're so inclined. 
               
               texturecolumn = rsi.rw_offset-FixedMul(finetangent[angle],rsi.rw_distance);
                texturecolumn >>= FRACBITS;
               // calculate lighting
               index = rw_scale>>LIGHTSCALESHIFT;
       

                 if (index >=  MAXLIGHTSCALE )
                 index = MAXLIGHTSCALE-1;

                 dcvars.dc_colormap = rsi.walllights[index];
                 dcvars.dc_x = rw_x;
                 dcvars.dc_iscale = (int) (0xffffffffL / rw_scale);
             }
             
             // draw the wall tiers
             if (rsi.midtexture!=0)
             {
                 // single sided line
                 dcvars.dc_yl = yl;
                 dcvars.dc_yh = yh;
                 dcvars.dc_texheight = TexMan.getTextureheight(rsi.midtexture)>>FRACBITS; // killough
                 dcvars.dc_texturemid = rsi.rw_midtexturemid;    
                 dcvars.dc_source = TexMan.GetCachedColumn(rsi.midtexture,texturecolumn);
                 dcvars.dc_source_ofs=0;
                 colfunc.invoke();
                 ceilingclip[rw_x] = (short) rsi.viewheight;
                 floorclip[rw_x] = -1;
             }
             else
             {
                 // two sided line
                 if (rsi.toptexture!=0)
                 {
                     // top wall
                     mid = pixhigh>>HEIGHTBITS;
                     pixhigh += pixhighstep;

                     if (mid >= floorclip[rw_x])
                         mid = floorclip[rw_x]-1;

                 if (mid >= yl)
                 {
                     dcvars.dc_yl = yl;
                     dcvars.dc_yh = mid;
                     dcvars.dc_texturemid = rsi.rw_toptexturemid;
                     dcvars.dc_texheight=TexMan.getTextureheight(rsi.toptexture)>>FRACBITS;
                     dcvars.dc_source = TexMan.GetCachedColumn(rsi.toptexture,texturecolumn);
                     //dc_source_ofs=0;
                     colfunc.invoke();
                     ceilingclip[rw_x] = (short) mid;
                 }
                 else
                     ceilingclip[rw_x] = (short) (yl-1);
                 }  // if toptexture
                 else
                 {
                     // no top wall
                     if (rsi.markceiling)
                         ceilingclip[rw_x] = (short) (yl-1);
                 } 
                     
                 if (rsi.bottomtexture!=0)
                 {
                 // bottom wall
                 mid = (pixlow+HEIGHTUNIT-1)>>HEIGHTBITS;
                 pixlow += pixlowstep;

                 // no space above wall?
                 if (mid <= ceilingclip[rw_x])
                     mid = ceilingclip[rw_x]+1;
                 
                 if (mid <= yh)
                 {
                     dcvars.dc_yl = mid;
                     dcvars.dc_yh = yh;
                     dcvars.dc_texturemid = rsi.rw_bottomtexturemid;
                     dcvars.dc_texheight=TexMan.getTextureheight(rsi.bottomtexture)>>FRACBITS;
                     dcvars.dc_source = TexMan.GetCachedColumn(rsi.bottomtexture,texturecolumn);
                     // dc_source_ofs=0;
                     colfunc.invoke();
                     floorclip[rw_x] = (short) mid;
                 }
                 else
                      floorclip[rw_x] = (short) (yh+1);

             } // end-bottomtexture
             else
             {
                 // no bottom wall
                 if (rsi.markfloor)
                     floorclip[rw_x] = (short) (yh+1);
             }
                 
            } // end-else (two-sided line)
                 rw_scale += rw_scalestep;
                 topfrac += topstep;
                 bottomfrac += bottomstep;
             } // end-rw 
         } // end-block
     }
	
	@Override
    public void setDetail(int detailshift) {
        if (detailshift == 0)
            colfunc = colfunchi;
        else
            colfunc = colfunclow;
    }
	
	/** Only called once per screen width change */
	public void setScreenRange(int rwstart, int rwend){
		this.rw_end=rwend;
		this.rw_start=rwstart;
	}

	
	/** How many instructions TOTAL are there to wade through.
	 *  Not all will be executed on one thread, except in some rare
	 *  circumstances. 
	 *  
	 * @param rsiend
	 */
	public void setRSIEnd(int rsiend){
		this.rsiend=rsiend;
	}

	public void run()
	{

		RenderSegInstruction<V> rsi;

		// Each worker blanks its own portion of the floor/ceiling clippers.
		System.arraycopy(BLANKFLOORCLIP,rw_start,floorclip, rw_start,rw_end-rw_start);
		System.arraycopy(BLANKCEILINGCLIP,rw_start,ceilingclip, rw_start,rw_end-rw_start);

		// For each "SegDraw" instruction...
		for (int i=0;i<rsiend;i++){
			rsi=RSI[i];
			dcvars.centery=RSI[i].centery;
			int startx,endx;
			// Does a wall actually start in our screen zone?
			// If yes, we need no bias, since it was meant for it.
			// If the wall started BEFORE our zone, then we
			// will need to add a bias to it (see ProcessRSI).
			// If its entirely non-contained, ProcessRSI won't be
			// called anyway, so we don't need to check for the end.
			
			boolean contained=(rsi.rw_x>=rw_start);
			// Keep to your part of the screen. It's possible that several
			// threads will process the same RSI, but different parts of it.
			
				// Trim stuff that starts before our rw_start position.
				startx=Math.max(rsi.rw_x,rw_start);
				// Similarly, trim stuff after our rw_end position.
				endx=Math.min(rsi.rw_stopx,rw_end);
				// Is there anything to actually draw?
				if ((endx-startx)>0) {
					ProcessRSI(rsi,startx,endx,contained);
					}
		} // end-instruction
	
		try {
			barrier.await();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (BrokenBarrierException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}
	
	//protected abstract void ProcessRSI(RenderSegInstruction<V> rsi, int startx,int endx,boolean contained);

	

	////////////////////////////VIDEO SCALE STUFF ////////////////////////////////

	protected int SCREENWIDTH;
	protected int SCREENHEIGHT;
	protected IVideoScale vs;

	@Override
	public void setVideoScale(IVideoScale vs) {
		this.vs=vs;
	}

	@Override
	public void initScaling() {
		this.SCREENHEIGHT=vs.getScreenHeight();
		this.SCREENWIDTH=vs.getScreenWidth();
	}

	public void updateRSI(RenderSegInstruction<V>[] rsi) {
		this.RSI=rsi;
		}
	
	public static final class HiColor extends RenderSegExecutor<byte[],short[]>{

        public HiColor(int SCREENWIDTH, int SCREENHEIGHT, int id,
                short[] screen, TextureManager<byte[]> texman,
                RenderSegInstruction<short[]>[] RSI, short[] BLANKCEILINGCLIP,
                short[] BLANKFLOORCLIP, short[] ceilingclip, short[] floorclip,
                int[] columnofs, long[] xtoviewangle, int[] ylookup,
                visplane_t[] visplanes, CyclicBarrier barrier) {
            super(SCREENWIDTH, SCREENHEIGHT, id, screen, texman, RSI, BLANKCEILINGCLIP,
                    BLANKFLOORCLIP, ceilingclip, floorclip, columnofs, xtoviewangle,
                    ylookup, visplanes, barrier);
            dcvars=new ColVars<byte[],short[]>();
            colfunc=colfunchi=new R_DrawColumnBoomOpt.HiColor(SCREENWIDTH, SCREENHEIGHT,ylookup,columnofs,dcvars,screen,null );
            colfunclow=new R_DrawColumnBoomOptLow.HiColor(SCREENWIDTH, SCREENHEIGHT,ylookup,columnofs,dcvars,screen,null );
        }
	}
	
	public static final class Indexed extends RenderSegExecutor<byte[],byte[]>{

        public Indexed(int SCREENWIDTH, int SCREENHEIGHT, int id,
                byte[] screen, IGetColumn<byte[]> gc, TextureManager<byte[]> texman,
                RenderSegInstruction<byte[]>[] RSI, short[] BLANKCEILINGCLIP,
                short[] BLANKFLOORCLIP, short[] ceilingclip, short[] floorclip,
                int[] columnofs, long[] xtoviewangle, int[] ylookup,
                visplane_t[] visplanes, CyclicBarrier barrier) {
            super(SCREENWIDTH, SCREENHEIGHT, id, screen, texman, RSI, BLANKCEILINGCLIP,
                    BLANKFLOORCLIP, ceilingclip, floorclip, columnofs, xtoviewangle,
                    ylookup, visplanes, barrier);
            dcvars=new ColVars<byte[],byte[]>();
            colfunc=colfunchi=new R_DrawColumnBoomOpt.Indexed(SCREENWIDTH, SCREENHEIGHT,ylookup,columnofs,dcvars,screen,null );
            colfunclow=new R_DrawColumnBoomOptLow.Indexed(SCREENWIDTH, SCREENHEIGHT,ylookup,columnofs,dcvars,screen,null );
        }
    }
	
}

// $Log: RenderSegExecutor.java,v $
// Revision 1.2  2012/09/24 17:16:22  velktron
// Massive merge between HiColor and HEAD. There's no difference from now on, and development continues on HEAD.
//
// Revision 1.1.2.2  2012/09/21 16:18:29  velktron
// More progress...but no cigar, yet.
//
// Revision 1.1.2.1  2012/09/20 14:20:10  velktron
// Parallel stuff in their own package (STILL BROKEN)
//
// Revision 1.12.2.4  2012/09/19 21:45:41  velktron
// More extensions...
//
// Revision 1.12.2.3  2012/09/18 16:11:50  velktron
// Started new "all in one" approach for unifying Indexed, HiColor and (future) TrueColor branches.
//
// Revision 1.12.2.2  2012/06/15 14:44:09  velktron
// Doesn't need walllights?
//
// Revision 1.12.2.1  2011/11/14 00:27:11  velktron
// A barely functional HiColor branch. Most stuff broken. DO NOT USE
//
// Revision 1.12  2011/11/01 19:04:06  velktron
// Cleaned up, using IDetailAware for subsystems.
//
// Revision 1.11  2011/10/31 18:33:56  velktron
// Much cleaner implementation using the new rendering model. Now it's quite faster, too.
//
// Revision 1.10  2011/07/25 11:39:10  velktron
// Optimized to work without dc_source_ofs (uses only cached, solid textures)
//
// Revision 1.9  2011/07/12 16:29:35  velktron
// Now using GetCachedColumn
//
// Revision 1.8  2011/07/12 16:25:02  velktron
// Removed dependency on per-thread column pointers.
//
// Revision 1.7  2011/06/07 21:21:15  velktron
// Definitively fixed jitter bug, which was due to dc_offset_contention. Now the alternative parallel renderer is just as good as the original one.
//
// Revision 1.6  2011/06/07 13:35:38  velktron
// Definitively fixed drawing priority/zones. Now to solve the jitter :-/
//
// Revision 1.5  2011/06/07 01:32:32  velktron
// Parallel Renderer 2 still buggy :-(
//
// Revision 1.4  2011/06/07 00:50:47  velktron
// Alternate Parallel Renderer fixed.
//
// Revision 1.3  2011/06/07 00:11:11  velktron
// Fixed alternate parallel renderer (seg based). No longer deprecated.
//
package rr;


/** A vissprite_t is a thing
 * that will be drawn during a refresh.
 * I.e. a sprite object that is partly visible.
 */

public class vissprite_t<V> implements Comparable<vissprite_t<V>>{

// Doubly linked list.
public vissprite_t<V> prev;
public vissprite_t<V> next;

public int         x1;
public int         x2;

// for line side calculation
public int     gx;
public int     gy;     

// global bottom / top for silhouette clipping
public int     gz;
public int     gzt;

// horizontal position of x1
public int     startfrac;

public int     scale;

// negative if flipped
public int     xiscale;    

public int     texturemid;
public int         patch;

/** for color translation and shadow draw,
 * maxbright frames as well.
 * 
 * Use paired with pcolormap;
 */ 
public V colormap;

/* pointer into colormap
public int pcolormap; */

public long mobjflags;

/** visspites are sorted by scale */

@Override
public final int compareTo(vissprite_t<V> o) {
    // We only really care if it's drawn before. 
    if (this.scale> o.scale) return 1;
    if (this.scale< o.scale) return -1;
    return 0;
}

public String toString(){    
    return ("Effective drawing position x1: "+x1 + " x2: "+ x2 +" scale "+(scale/65535.0) +" iscale "+(xiscale/65535.0));
}

}
package rr;
import java.util.Arrays;
import v.IVideoScale;

/** Now what is a visplane, anyway?
 *  Basically, it's a bunch of arrays buffer representing
 *  a top and a bottom boundary of a region to be filled with a
 *  specific kind of flat. They are as wide as the screen, 
 *  and actually store height bounding values or sentinel valuesThink of it as an arbitrary boundary.
 *  
 *  These are refreshed continuously during rendering, and mark 
 *  the limits between flat regions. Special values mean "do not 
 *  render this column at all", while clipping out of the map bounds
 *  results in well-known bleeding effects.
 * 
 * @author admin
 *
 */

public class visplane_t{
    
    public static final int TOPOFFSET=1;
    public static final int MIDDLEPADDING=2;
    public static int BOTTOMOFFSET;
    
    // Multithreading trickery (for strictly x-bounded drawers)
    // The thread if is encoded in the upper 3 bits (puts an upper limit 
    // of 8 floor threads), and the stomped value is encoded in the next 12 
    // bits (this puts an upper height limit of 4096 pixels). 
    // Not the cleanest system possible, but it's backwards compatible
    // TODO: expand visplane buffers to full-fledged ints?
    
    public static final char SENTINEL=0x8000;
    public static final char THREADIDSHIFT=12;
    public static final char THREADIDCLEAR=0x8FFF;
    public static final char THREADIDBITS=0XFFFF-THREADIDCLEAR;
    public static final char THREADVALUEBITS=THREADIDCLEAR-SENTINEL;
    
    public visplane_t(){
        this.data=new char[4+2*SCREENWIDTH];
        this.updateHashCode();
    }
    
    
    public visplane_t(int height, int picnum, int lightlevel){
        this.height=height;
        this.picnum=picnum;
        this.lightlevel=lightlevel;
        this.updateHashCode();
        this.data=new char[4+2*SCREENWIDTH];
        }
    
    
/** (fixed_t) */
public int       height;
public int           picnum;
public int           lightlevel;
public int           minx;
public int           maxx;

// leave pads for [minx-1]/[maxx+1]

/*
public byte      pad1;
// Here lies the rub for all
//  dynamic resize/change of resolution.
public byte[]      top=new byte[SCREENWIDTH];
public byte      pad2;
public byte      pad3;
// See above.
public byte[]      bottom=new byte [SCREENWIDTH];
public byte      pad4;*/

char data[];

// Hack to allow quick clearing of visplanes.
protected static char[] clearvisplane;


/** "Clear" the top with FF's.  */
public void clearTop(){
    System.arraycopy(clearvisplane, 0, this.data, TOPOFFSET, SCREENWIDTH);
      
}

/** "Clear" the bottom with FF's.  */
public void clearBottom(){
    System.arraycopy(clearvisplane, 0, this.data, BOTTOMOFFSET, SCREENWIDTH);      
}

public void setTop(int index, char value){
    this.data[TOPOFFSET+index]=value;    
}

public char getTop(int index){
    return this.data[TOPOFFSET+index];
    
}

public void setBottom(int index, char value){
    this.data[BOTTOMOFFSET+index]=value;
    
}

public int getBottom(int index){
    return this.data[BOTTOMOFFSET+index];
    
}

public String toString(){
    sb.setLength(0);
    sb.append("Visplane\n");
    sb.append('\t');
    sb.append("Height: ");
    sb.append(this.height);
    sb.append('\t');
    sb.append("Min-Max: ");
    sb.append(this.minx);
    sb.append('-');
    sb.append(this.maxx);
    sb.append('\t');
    sb.append("Picnum: ");    
    sb.append(this.picnum);
    sb.append('\t');
    sb.append("Lightlevel: ");
    sb.append(this.lightlevel);

    return sb.toString();
    
    
}

protected int hash;

/** Call this upon any changed in height, picnum or lightlevel */

public void updateHashCode(){
    this.hash=height^picnum^lightlevel;
}

public int hashCode(){
    return this.hash;
}

public static int visplaneHash(int height, int picnum, int lightlevel){
    return height^picnum^lightlevel;
    
}

protected static StringBuilder sb=new StringBuilder();


// HACK: the resolution awareness is shared between all visplanes.
// Change this if you ever plan on running multiple renderers with
// different resolution or something.
protected static int SCREENWIDTH;
protected static int SCREENHEIGHT;
protected static IVideoScale vs;

public static void setVideoScale(IVideoScale vs) {
    visplane_t.vs=vs;
}

public static void initScaling() {
    SCREENHEIGHT=vs.getScreenHeight();
    SCREENWIDTH=vs.getScreenWidth();

    // Pre-scale stuff.
    BOTTOMOFFSET=SCREENWIDTH+TOPOFFSET+MIDDLEPADDING;

    if (clearvisplane==null || clearvisplane.length<SCREENWIDTH) {
        clearvisplane=new char[SCREENWIDTH];
        Arrays.fill(clearvisplane,Character.MAX_VALUE);
        }
}


};

package rr;

import static data.Tables.ANG90;
import static data.Tables.finecosine;
import static data.Tables.finesine;
import static m.fixed_t.FixedDiv;


import utils.C2JUtils;
import v.IVideoScale;
import v.IVideoScaleAware;
import data.Limits;
import data.Tables;

/** Actual visplane data and methods are isolate here.
 *  This allows more encapsulation and some neat hacks like sharing 
 *  visplane data among parallel renderers, without duplicating them.
 */

public class Visplanes implements IVideoScaleAware{

    private static final boolean DEBUG2=false;

    protected final ViewVars view;
    protected final TextureManager<?> TexMan;
    
    public Visplanes(ViewVars view,TextureManager<?> TexMan){
        this.view=view;
        this.TexMan=TexMan;
    }

    protected int SCREENHEIGHT;
    protected int SCREENWIDTH;
    

    // HACK: An all zeroes array used for fast clearing of certain visplanes.
    public int[] cachedheight,BLANKCACHEDHEIGHT;

    
    /** To treat as fixed_t */
    public int basexscale, baseyscale;
    
    /** To treat at fixed_t */
    protected int[] yslope;
    
    // initially.
    public int MAXVISPLANES = Limits.MAXVISPLANES;
    public int MAXOPENINGS;
    
    /** visplane_t*, treat as indexes into visplanes */
    public int lastvisplane, floorplane, ceilingplane;
    public visplane_t[] visplanes = new visplane_t[MAXVISPLANES];

    /**
     * openings is supposed to show where "openings" in visplanes start and end
     * e.g. due to sprites, windows etc.
     */
    public short[] openings;
    /** Maes: this is supposed to be a pointer inside openings */
    public int lastopening;
    
    protected int skyscale;

    
    /**
     * Call only after visplanes have been properly resized for resolution.
     * In case of dynamic resolution changes, the old ones should just be
     * discarded, as they would be nonsensical.
     */

    public void initVisplanes() {
        cachedheight = new int[SCREENHEIGHT];
        C2JUtils.initArrayOfObjects(visplanes);
        
        }
    
    public int getBaseXScale(){
        return basexscale;
    }

    public int getBaseYScale(){
        return baseyscale;
    }

    public int getSkyScale(){
        return skyscale;
    }
    
    public void setSkyScale(int i) {
        skyscale=i;        
    }
    
    public int getLength(){
        return visplanes.length;
    }
    
    /** Return the last of visplanes, allocating a new one if needed */
    
    public visplane_t allocate(){
        if (lastvisplane == visplanes.length) {
            //  visplane overflows could occur at this point.
            resizeVisplanes();
        }
        
        return visplanes[lastvisplane++];
    }
    
    public final void resizeVisplanes() {
        // Bye bye, old visplanes.
        visplanes = C2JUtils.resize(visplanes[0], visplanes, visplanes.length*2);
    }
    
    /**
     * R_FindPlane
     * 
     * Checks whether a visplane with the specified height, picnum and light
     * level exists among those already created. This looks like a half-assed
     * attempt at reusing already existing visplanes, rather than creating new
     * ones. The tricky part is understanding what happens if one DOESN'T exist.
     * Called only from within R_Subsector (so while we're still trasversing
     * stuff).
     * 
     * @param height
     *            (fixed_t)
     * @param picnum
     * @param lightlevel
     * @return was visplane_t*, returns index into visplanes[]
     */

    public final int FindPlane(int height, int picnum, int lightlevel) {
        // System.out.println("\tChecking for visplane merging...");
        int check = 0; // visplane_t*
        visplane_t chk = null;

        if (picnum == TexMan.getSkyFlatNum()) {
            height = 0; // all skys map together
            lightlevel = 0;
        }

        chk = visplanes[0];

        // Find visplane with the desired attributes
        for (check = 0; check < lastvisplane; check++) {

            chk = visplanes[check];
            if (height == chk.height && picnum == chk.picnum
                    && lightlevel == chk.lightlevel) {
                // Found a visplane with the desired specs.
                break;
            }
        }

        if (check < lastvisplane) {
            return check;
        }

        // This should return the next available visplane and resize if needed,
        // no need to hack with lastvisplane++
        chk = allocate();
        // Add a visplane
        chk.height = height;
        chk.picnum = picnum;
        chk.lightlevel = lightlevel;
        chk.minx = SCREENWIDTH;
        chk.maxx = -1;
        // memset (chk.top,0xff,sizeof(chk.top));
        chk.clearTop();

        return check;
    }
    
    /**
     * R_ClearPlanes At begining of frame.
     * 
     */

    public void ClearPlanes() {
        int angle;

        /*
         * View planes are cleared at the beginning of every plane, by
         * setting them "just outside" the borders of the screen (-1 and
         * viewheight).
         */

        // Point to #1 in visplane list? OK... ?!
        lastvisplane = 0;

        // We point back to the first opening of the list openings[0],
        // again.
        lastopening = 0;

        // texture calculation
        System.arraycopy(BLANKCACHEDHEIGHT, 0, cachedheight, 0,
                BLANKCACHEDHEIGHT.length);

        // left to right mapping
        // FIXME: If viewangle is ever < ANG90, you're fucked. How can this
        // be prevented?
        // Answer: 32-bit unsigned are supposed to roll over. You can & with
        // 0xFFFFFFFFL.
        angle = (int) Tables.toBAMIndex(view.angle - ANG90);

        // scale will be unit scale at SCREENWIDTH/2 distance
        basexscale = FixedDiv(finecosine[angle], view.centerxfrac);
        baseyscale = -FixedDiv(finesine[angle], view.centerxfrac);
    }
    
    /**
     * R_CheckPlane
     * 
     * Called from within StoreWallRange
     * 
     * Presumably decides if a visplane should be split or not?
     * 
     */

    public int CheckPlane(int index, int start, int stop) {

        if (DEBUG2)
            System.out.println("Checkplane " + index + " between " + start
                    + " and " + stop);

        // Interval ?
        int intrl;
        int intrh;

        // Union?
        int unionl;
        int unionh;
        // OK, so we check out ONE particular visplane.
        visplane_t pl = visplanes[index];

        if (DEBUG2)
            System.out.println("Checking out plane " + pl);

        int x;

        // If start is smaller than the plane's min...
        //
        // start minx maxx stop
        // | | | |
        // --------PPPPPPPPPPPPPP-----------
        //
        //
        if (start < pl.minx) {
            intrl = pl.minx;
            unionl = start;
            // Then we will have this:
            //
            // unionl intrl maxx stop
            // | | | |
            // --------PPPPPPPPPPPPPP-----------
            //

        } else {
            unionl = pl.minx;
            intrl = start;

            // else we will have this:
            //
            // union1 intrl maxx stop
            // | | | |
            // --------PPPPPPPPPPPPPP-----------
            //
            // unionl comes before intrl in any case.
            //
            //
        }

        // Same as before, for for stop and maxx.
        // This time, intrh comes before unionh.
        //

        if (stop > pl.maxx) {
            intrh = pl.maxx;
            unionh = stop;
        } else {
            unionh = pl.maxx;
            intrh = stop;
        }

        // An interval is now defined, which is entirely contained in the
        // visplane.
        //

        // If the value FF is NOT stored ANYWWHERE inside it, we bail out
        // early
        for (x = intrl; x <= intrh; x++)
            if (pl.getTop(x) != Character.MAX_VALUE)
                break;

        // This can only occur if the loop above completes,
        // else the visplane we were checking has non-visible/clipped
        // portions within that range: we must split.

        if (x > intrh) {
            // Merge the visplane
            pl.minx = unionl;
            pl.maxx = unionh;
            // System.out.println("Plane modified as follows "+pl);
            // use the same one
            return index;
        }

        // SPLIT: make a new visplane at "last" position, copying materials
        // and light.

        visplane_t last=allocate();
        last.height = pl.height;
        last.picnum = pl.picnum;
        last.lightlevel = pl.lightlevel;

        pl = last;
        pl.minx = start;
        pl.maxx = stop;

        // memset (pl.top,0xff,sizeof(pl.top));
        pl.clearTop();

        // return pl;

        // System.out.println("New plane created: "+pl);
        return lastvisplane - 1;
    }

    public void initScaling() {        
        MAXOPENINGS = SCREENWIDTH * 64;
        openings = new short[MAXOPENINGS];
        BLANKCACHEDHEIGHT = new int[SCREENHEIGHT];
        yslope = new int[SCREENHEIGHT];
    }

    @Override
    public void setVideoScale(IVideoScale vs) {
        this.SCREENHEIGHT=vs.getScreenHeight();
        this.SCREENWIDTH=vs.getScreenWidth();
        
    }
    
    /*
     
       /**
     * A hashtable used to retrieve planes with particular attributes faster
     * -hopefully-. The planes are still stored in the visplane array for
     * convenience, but we can search them in the hashtable too -as a bonus, we
     * can reuse previously created planes that match newer ones-.
     */
    /*
    Hashtable<visplane_t, Integer> planehash = new Hashtable<visplane_t, Integer>(
            128);
    visplane_t check = new visplane_t();
    */

    /*
    protected final int FindPlane2(int height, int picnum, int lightlevel) {
        // System.out.println("\tChecking for visplane merging...");
        // int check=0; // visplane_t*
        visplane_t chk = null;
        Integer checknum;

        if (picnum == TexMan.getSkyFlatNum()) {
            height = 0; // all skys map together
            lightlevel = 0;
        }

        // Try and find this.
        check.lightlevel = lightlevel;
        check.picnum = picnum;
        check.height = height;
        check.updateHashCode();
        checknum = planehash.get(check);

        // Something found, get it.

        if (!(checknum == null)) {

            // Visplane exists and is within those allocated in the current tic.
            if (checknum < lastvisplane) {
                return checknum;
            }

            // Found a visplane, but we can't add anymore.
            // Resize right away. This shouldn't take too long.
            if (lastvisplane == MAXVISPLANES) {
                // I.Error ("R_FindPlane: no more visplanes");
                ResizeVisplanes();
            }
        }

        // We found a visplane (possibly one allocated on a previous tic)
        // but we can't link directly to it, we need to copy its data
        // around.

        checknum = new Integer(Math.max(0, lastvisplane));

        chk = visplanes[checknum];
        // Add a visplane
        lastvisplane++;
        chk.height = height;
        chk.picnum = picnum;
        chk.lightlevel = lightlevel;
        chk.minx = SCREENWIDTH;
        chk.maxx = -1;
        chk.updateHashCode();
        planehash.put(chk, checknum);
        // memset (chk.top,0xff,sizeof(chk.top));
        chk.clearTop();

        return checknum;
    }
    */
    
}

package rr;

public class base_ratio_t {

  public base_ratio_t(int base_width, int base_height, int psprite_offset,
            int multiplier, float gl_ratio) {
        this.base_width = base_width;
        this.base_height = base_height;
        this.psprite_offset = psprite_offset;
        this.multiplier = multiplier;
        this.gl_ratio = (float) (RMUL*gl_ratio);
    }

public int base_width;      // Base width (unused)
  public int base_height;     // Base height (used for wall visibility multiplier)
  public  int psprite_offset;  // Psprite offset (needed for "tallscreen" modes)
  public  int multiplier;      // Width or height multiplier
  public  float gl_ratio;
  
  public static final double RMUL =1.6d/1.333333d;
  
}
package awt;

import static g.Keys.*;
import i.DoomEventInterface;

import java.awt.AWTEvent;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Event;
import java.awt.KeyEventDispatcher;
import java.awt.MouseInfo;
import java.awt.Point;
import java.awt.Robot;
import java.awt.Toolkit;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.LinkedList;

import doom.DoomMain;
import doom.event_t;
import doom.evtype_t;

public class AWTEvents2 implements WindowListener,KeyEventDispatcher,KeyListener,MouseListener,MouseMotionListener,DoomEventInterface {

    // modifications of eventQueue must be thread safe!
    public LinkedList<DoomEvent> eventQueue = new LinkedList<DoomEvent>();

    public DoomMain DM;
    public Component canvas;
    Robot robby;

    public AWTEvents2(DoomMain DM, Component canvas) {
        this.DM = DM;
        this.canvas = canvas;

        // AWT: create cursors.
        this.normal=canvas.getCursor();
        this.hidden=this.createInvisibleCursor();
        
        // Create AWT Robot for forcing mouse
        try {
        robby=new Robot();
        } catch (Exception e){
          System.out.println("AWT Robot could not be created, mouse input focus will be loose!");
        }

    }
    
    //////////// LISTENERS //////////
    
    @Override
    public void mouseClicked(MouseEvent mouseevent) {
        addEvent(mouseevent);
    }

    @Override
    public void mouseEntered(MouseEvent mouseevent) {
        addEvent(mouseevent);
    }

    @Override
    public void mouseExited(MouseEvent mouseevent) {
        addEvent(mouseevent);

        relockMouse(mouseevent);
    }

    @Override
    public void mousePressed(MouseEvent mouseevent) {
        addEvent(mouseevent);
        
    }

    @Override
    public void mouseReleased(MouseEvent mouseevent) {
        addEvent(mouseevent);
        
    }

    @Override
    public void mouseDragged(MouseEvent mouseevent) {
        //addEvent(mouseevent);
        mouseMoved(mouseevent);
        
    }

    ArrayList<Point[]> cancelMoves = new ArrayList<Point[]>();
    
    Point lastMousePos = new Point(0,0);
    
    @Override
    public void mouseMoved(MouseEvent mouseevent) {
        DoomEvent dEv = new DoomEvent(mouseevent);
        Point srcPos = lastMousePos;
        Point destPos = mouseevent.getLocationOnScreen();
        Point deltaPoint = new Point(destPos.x-srcPos.x, destPos.y-srcPos.y);
        dEv.deltaPoint = deltaPoint;
        addEvent(dEv);

        relockMouse(mouseevent);
    }
    
    public void relockMouse(MouseEvent mouseevent) {
        if (canvas.hasFocus()) {
            Point destPoint = new Point(canvas.getX()+canvas.getWidth()/2, canvas.getY()+canvas.getHeight()/2);

            canvas.removeMouseListener(this);
            robby.mouseMove(destPoint.x, destPoint.y);
            canvas.addMouseListener(this);
        }
        
        lastMousePos = MouseInfo.getPointerInfo().getLocation();
    }
    
    
    @Override
    public boolean dispatchKeyEvent(KeyEvent e) {
        return false;
    }

    @Override
    public void windowActivated(WindowEvent windowevent) {
        canvas.getInputContext().selectInputMethod(java.util.Locale.US);
        
    }

    @Override
    public void windowClosed(WindowEvent windowevent) {
        // TODO Auto-generated method stub
        
    }

    @Override
    public void windowClosing(WindowEvent windowevent) {
        // TODO Auto-generated method stub
        
    }

    @Override
    public void windowDeactivated(WindowEvent windowevent) {
        // TODO Auto-generated method stub
        
    }

    @Override
    public void windowDeiconified(WindowEvent windowevent) {
        canvas.getInputContext().selectInputMethod(java.util.Locale.US);
        
    }

    @Override
    public void windowIconified(WindowEvent windowevent) {
        // TODO Auto-generated method stub
        
    }

    @Override
    public void windowOpened(WindowEvent windowevent) {
        canvas.getInputContext().selectInputMethod(java.util.Locale.US);
        
    }

    
    public void keyPressed(KeyEvent e) {
        //if ((e.getModifiersEx() & UNACCEPTABLE_MODIFIERS) ==0) {
        if (isAcceptableKey(new DoomEvent(e))) {
            addEvent(e);
        }
        
        
        e.consume();
    }

    public void keyReleased(KeyEvent e) {
        //if ((e.getModifiersEx() & UNACCEPTABLE_MODIFIERS) ==0) {
        if (isAcceptableKey(new DoomEvent(e))) {
            addEvent(e);
        }
        
        e.consume();
    }

    public void keyTyped(KeyEvent e) {
    //  if ((e.getModifiersEx() & UNACCEPTABLE_MODIFIERS) ==0){
        if (isAcceptableKey(new DoomEvent(e))) {
            addEvent(e);
        }
    }
    
    
    int AWTMouseToKey(DoomEvent ev) {
        int buttonIdx = ev.getButton() - MouseEvent.BUTTON1;
        return KEY_MOUSE1+buttonIdx;
    }
    
    // number of total 'button' inputs, include keyboard keys, plus virtual
    // keys (mousebuttons and joybuttons becomes keys)
    public static final int NUMKEYS         = 256;

    public static final int MOUSEBUTTONS    = 8;
    public static final int JOYBUTTONS      = 14;  // 10 bases + 4 hat
    
    //
    // mouse and joystick buttons are handled as 'virtual' keys
    //
    public static final int KEY_MOUSE1        = NUMKEYS;                  
    public static final int KEY_JOY1          = KEY_MOUSE1+MOUSEBUTTONS;  
    public static final int KEY_DBLMOUSE1     = KEY_JOY1+JOYBUTTONS;        // double clicks
    public static final int KEY_DBLJOY1       = KEY_DBLMOUSE1+MOUSEBUTTONS;
    public static final int KEY_2MOUSE1       = KEY_DBLJOY1+JOYBUTTONS;
    public static final int KEY_DBL2MOUSE1    = KEY_2MOUSE1+MOUSEBUTTONS;
    public static final int KEY_MOUSEWHEELUP  = KEY_DBL2MOUSE1+MOUSEBUTTONS;
    public static final int KEY_MOUSEWHEELDOWN= KEY_MOUSEWHEELUP+1;
    public static final int KEY_2MOUSEWHEELUP = KEY_MOUSEWHEELUP+2;
    public static final int KEY_2MOUSEWHEELDOWN= KEY_MOUSEWHEELUP+3;
    public static final int NUMINPUTS = KEY_MOUSEWHEELUP+4;
    
    int currentMouseButtons = 0;
    
    @Override
    public void GetEvent() {
        DoomEvent X_event;
        event_t event=new event_t();;
        // put event-grabbing stuff in here
        if (eventQueue.isEmpty()) return;   
        X_event=nextEvent();
        //System.out.println("Event type:"+X_event.getID());

        // Check for Event.??? shit
        if (!ignorebutton){
            switch (X_event.getID())
            {
            case Event.KEY_PRESS:
                event=new event_t(evtype_t.ev_keydown, xlatekey(/*(KeyEvent)*/X_event));
                DM.PostEvent(event);
                //System.err.println("k");
                break;
            case Event.KEY_RELEASE:
                event=new event_t(evtype_t.ev_keyup, xlatekey(/*(KeyEvent)*/X_event));
                DM.PostEvent(event);
                //System.err.println( "ku");
                break;
            }
        }

        switch (X_event.getID()){
        // ButtonPress
        case Event.MOUSE_DOWN:
            /*
            currentMouseButtons = (X_event.getButton() == MouseEvent.BUTTON1 ? 1 : 0)
            | ((X_event.getButton() == MouseEvent.BUTTON3) ? 2 : 0)
            | ((X_event.getButton() == MouseEvent.BUTTON2) ? 4 : 0);*/


            event.data1 = AWTMouseToKey(X_event);
            event.data2 = event.data3 = 0;
            event.type=evtype_t.ev_keydown;//ev_mouse;
            DM.PostEvent(event);
            break;
            // ButtonRelease
        case Event.MOUSE_UP:
            event.type = evtype_t.ev_keyup;// ev_mouse;
/*
            currentMouseButtons = currentMouseButtons
            & ~(X_event.getButton() == MouseEvent.BUTTON1 ? 1 : 0)
            & ~(X_event.getButton() == MouseEvent.BUTTON3 ? 2 : 0)
            & ~(X_event.getButton() == MouseEvent.BUTTON2 ? 4 : 0);*/

            event.data1 = AWTMouseToKey(X_event);
            event.data2 = event.data3 = 0;
            DM.PostEvent(event);
            break;
            // MotionNotify:
        case Event.MOUSE_MOVE:
        case Event.MOUSE_DRAG:
            event.type = evtype_t.ev_mouse;
            // Get buttons, as usual.
            event.data1 = currentMouseButtons;
            event.data2 = X_event.deltaPoint.x << 2;
            event.data3 = -X_event.deltaPoint.y << 2;

            if ((event.data2 | event.data3)!=0) {
                DM.PostEvent(event);
            }
            break;
        case Event.MOUSE_ENTER:
            System.err.println("ACCEPTING keyboard input");
            canvas.requestFocus();
            canvas.setCursor(hidden);
            ignorebutton=false;
            break;
        case Event.MOUSE_EXIT:
            System.err.println("IGNORING keyboard input");
            canvas.setCursor(normal);

            ignorebutton=true;
            break;
        case Event.WINDOW_EXPOSE:
            // No real single equivalent for "ConfigureNotify"
        case Event.WINDOW_MOVED:
        case Event.WINDOW_DESTROY:
            break;
        default:
            // NOT NEEDED in AWT if (doShm && X_event.type == X_shmeventtype) shmFinished = true;
            break;

        }

    }

    
    boolean isAcceptableKey(DoomEvent e) {
        return e.getKeyCode()<=KeyEvent.VK_F12 || e.getKeyChar()=='#';
    }
    

    void addEvent(AWTEvent ev) {
        addEvent(new DoomEvent(ev));

        if (ev instanceof InputEvent) {
            ((InputEvent)ev).consume();
        }
    }

    void addEvent(DoomEvent ev) {
        synchronized (eventQueue) {
            eventQueue.addLast(ev);
        }
    }

    public DoomEvent nextEvent() {
        DoomEvent ev;
        synchronized (eventQueue) {
            ev = (!eventQueue.isEmpty())?(DoomEvent)eventQueue.removeFirst():null;
        }
        return ev;
    }
    
    public static class DoomEvent {
        int keyCode;
        char keyChar;
        int ID;
        int button;
        int x;
        int y;
        Point deltaPoint;
        
        public DoomEvent(AWTEvent evt) {
            this.ID = evt.getID();
            if (evt instanceof KeyEvent) {
                this.keyChar = ((KeyEvent)evt).getKeyChar();
                this.keyCode = ((KeyEvent)evt).getKeyCode();
            }
            if (evt instanceof MouseEvent) {
                this.button = ((MouseEvent)evt).getButton();
                this.x = ((MouseEvent)evt).getX();
                this.y = ((MouseEvent)evt).getY();
            }
        }

        public int getKeyCode() {
            return keyCode;
        }

        public char getKeyChar() {
            return keyChar;
        }

        public int getID() {
            return ID;
        }

        public int getButton() {
            return button;
        }
        
        public int getX() {
            return x;
        }

        public int getY() {
            return y;
        }
    }

    
    /** FIXME: input must be made scancode dependent rather than VK_Dependent,
     *  else a lot of things just don't work. 
     * 
     * @param e
     * @return
     */

    public int xlatekey(DoomEvent e)
    {

      int rc;

      switch(rc =     e.getKeyCode())
      
          //Event.XKeycodeToKeysym(X_display, X_event.xkey.keycode, 0))

      {
      case KeyEvent.VK_LEFT:    rc = KEY_LEFTARROW; break;
      case KeyEvent.VK_RIGHT:   rc = KEY_RIGHTARROW;    break;
      case KeyEvent.VK_DOWN:    rc = KEY_DOWNARROW; break;
      case KeyEvent.VK_UP:  rc = KEY_UPARROW;   break;
        case KeyEvent.VK_ESCAPE:  rc = KEY_ESCAPE;    break;
        case KeyEvent.VK_ENTER:   rc = KEY_ENTER;     break;
        case KeyEvent.VK_CONTROL: rc= KEY_CTRL; break;
        case KeyEvent.VK_ALT: rc=KEY_ALT; break;
        case KeyEvent.VK_SHIFT: rc=KEY_SHIFT; break;
        // Added handling of pgup/pgdown/home etc.
        case KeyEvent.VK_PAGE_DOWN: rc= KEY_PGDN;	break;
        case KeyEvent.VK_PAGE_UP: rc= KEY_PGUP;	break;
        case KeyEvent.VK_HOME: rc= KEY_HOME;	break;
        case KeyEvent.VK_F1:  rc = KEY_F1;        break;
        case KeyEvent.VK_F2:  rc = KEY_F2;        break;
        case KeyEvent.VK_F3:  rc = KEY_F3;        break;
        case KeyEvent.VK_F4:  rc = KEY_F4;        break;
        case KeyEvent.VK_F5:  rc = KEY_F5;        break;
        case KeyEvent.VK_F6:  rc = KEY_F6;        break;
        case KeyEvent.VK_F7:  rc = KEY_F7;        break;
        case KeyEvent.VK_F8:  rc = KEY_F8;        break;
        case KeyEvent.VK_F9:  rc = KEY_F9;        break;
        case KeyEvent.VK_F10: rc = KEY_F10;       break;
        case KeyEvent.VK_F11: rc = KEY_F11;       break;
        case KeyEvent.VK_F12: rc = KEY_F12;       break;
        
        case KeyEvent.VK_BACK_SPACE:
        case KeyEvent.VK_DELETE:  rc = KEY_BACKSPACE; break;

        case KeyEvent.VK_PAUSE:   rc = KEY_PAUSE;     break;

        case KeyEvent.KEY_RELEASED:
        case KeyEvent.VK_TAB: rc = KEY_TAB;       break;
        
        case KeyEvent.KEY_PRESSED:
            switch(e.getKeyCode()){
            
        case KeyEvent.VK_PLUS:
        case KeyEvent.VK_EQUALS: 
            rc = KEY_EQUALS;    
            break;
        
        case (13):
        case KeyEvent.VK_SUBTRACT: 
        case KeyEvent.VK_MINUS:   
            rc = KEY_MINUS;     
            break;

        case KeyEvent.VK_SHIFT:
        rc = KEY_SHIFT;
        break;
        
        case KeyEvent.VK_CONTROL:
        rc = KEY_CTRL;
        break;                           
       
        case KeyEvent.VK_ALT:
        rc = KEY_ALT;
        break;

        case KeyEvent.VK_SPACE:
            rc = ' ';
            break;

            }
            
        default:
            
       /*if (rc >= KeyEvent.VK_SPACE && rc <= KeyEvent.VK_DEAD_TILDE)
            {
            rc = (int) (rc - KeyEvent.FOCUS_EVENT_MASK + ' ');
            break;
            } */
        if (rc >= KeyEvent.VK_A && rc <= KeyEvent.VK_Z){
            rc = rc-KeyEvent.VK_A +'a';
            break;
            }
        // Unknown. Probably fucking up with the keyboard locale. Switch to be sure.
        // Sorry for this horrible hack, but Java can't read locale-free keycodes -_-
        // this.getInputContext().selectInputMethod(java.util.Locale.US);
        //if (rc>KEY_F12) rc=KEY_RSHIFT;
        break;
      }
      
     //System.out.println("Typed "+e.getKeyCode()+" char "+e.getKeyChar()+" mapped to "+Integer.toHexString(rc));

      // Sanitize. Nothing beyond F12 must pass through, else you will
      // get the "all cheats" bug.
      return rc;//Math.min(rc,KEY_F12);

    }
    
    // This stuff should NOT get through in keyboard events.
    protected final int UNACCEPTABLE_MODIFIERS=(int) (InputEvent.ALT_GRAPH_DOWN_MASK+
                                             InputEvent.META_DOWN_MASK+
                                             InputEvent.META_MASK+
                                             InputEvent.WINDOW_EVENT_MASK+
                                             InputEvent.WINDOW_FOCUS_EVENT_MASK);

    protected boolean ignorebutton;

      /** Returns true if flags are included in arg.
       * Synonymous with (flags & arg)!=0
       * 
       * @param flags
       * @param arg
       * @return
       */
      public static final boolean flags(int flags, int arg){
          return ((flags & arg)!=0);
      }
      

      Cursor hidden;
      Cursor normal;

    /**
    * NASTY hack to hide the cursor.
    * 
    * Create a 'hidden' cursor by using a transparent image
    * ...return the invisible cursor
    */

    protected Cursor createInvisibleCursor()

    {
    Dimension bestCursorDim = Toolkit.getDefaultToolkit().getBestCursorSize(2, 2);
    BufferedImage transparentImage = new BufferedImage(bestCursorDim.width, bestCursorDim.height, BufferedImage.TYPE_INT_ARGB);
    Cursor hiddenCursor = Toolkit.getDefaultToolkit( ).createCustomCursor(transparentImage, new Point(1, 1),    "HiddenCursor");
    return hiddenCursor;
    }
}

package awt;

import java.awt.*;

import java.awt.event.*;

import javax.swing.JLabel;

/** A convenient message box class to pop up here and there.
 *  
 * @author zyklon
 *
 */

public class MsgBox extends Dialog implements ActionListener {
	/**
	 * 
	 */
	private static final long serialVersionUID = -872019680203708495L;
	private Button ok, can;
	private boolean isOk = false;

	/*  
	 * * @param frame parent frame
	 * 
	 * @param msg message to be displayed
	 * 
	 * @param okcan true : ok cancel buttons, false : ok button only
	 */

	public boolean isOk() {
		return isOk;
	}

	public MsgBox(Frame frame, String title, String msg, boolean okcan) {
		super(frame, title, true);
		setLayout(new BorderLayout());
		add("Center", new JLabel(msg));
		addOKCancelPanel(okcan);
		createFrame();
		pack();
		setVisible(true);
		this.can.requestFocus();
	}

	public MsgBox(Frame frame, String msg) {
		this(frame, "Message", msg, false);
	}

	private void addOKCancelPanel(boolean okcan) {
		Panel p = new Panel();
		p.setLayout(new FlowLayout());
		createOKButton(p);
		if (okcan == true)
			createCancelButton(p);
		add("South", p);
	}

	private void createOKButton(Panel p) {
		p.add(ok = new Button("OK"));
		ok.addActionListener(this);
	}

	private void createCancelButton(Panel p) {
		p.add(can = new Button("Cancel"));
		can.addActionListener(this);
	}

	private void createFrame() {
		Dimension d = getToolkit().getScreenSize();
		setLocation(d.width / 3, d.height / 3);		
	}

	public void actionPerformed(ActionEvent ae) {
		if (ae.getSource() == ok) {
			isOk = true;
			setVisible(false);
		} else if (ae.getSource() == can) {
			setVisible(false);
		}
	}

	/*
	 * public static void main(String args[]) { //Frame f = new Frame();
	 * //f.setSize(200,200); //f.setVisible(true); MsgBox message = new MsgBox
	 * (null , "Hey you user, are you sure ?", true); if (message.isOk)
	 * System.out.println("Ok pressed"); if (!message.isOk)
	 * System.out.println("Cancel pressed"); message.dispose(); }
	 */
}
package awt;

import java.awt.Canvas;
import java.awt.Graphics;
import java.awt.Graphics2D;
import v.DoomVideoRenderer;
import doom.DoomMain;

/** A simple Doom display & keyboard driver for AWT.
 *  Uses a Canvas for painting and implements some
 *  of the IVideo methods.
 * 
 *  Uses 8-bit buffered images and switchable IndexColorModels.
 *  
 *  It's really basic, but allows testing and user interaction.
 *  Heavily based on the original LinuxDoom X11 implementation, and
 *  is similar in goals: just a functional, reference implementation
 *  to build upon whatever fancy extensions you like.
 *  
 *  The only "hitch" is that this implementation expects to be 
 *  initialized upon a BufferedRenderer with multiple images per 
 *  screen buffer in order to perform the palette switching trick.
 *  
 *  The images themselves don't have to be "BufferedImage",
 *  and in theory it could be possible to use e.g. MemoryImageSource
 *  for the same purpose . Oh well.
 *    
 *  
 * 
 * @author Velktron
 *
 */
public abstract class AWTDoom<V> extends DoomFrame<V> {


		/**
     * 
     */
    private static final long serialVersionUID = 1L;

        /** Gimme some raw palette RGB data.
		 *  I will do the rest
		 *  
		 *  (hint: read this from the PLAYPAL
		 *   lump in the IWAD!!!).
		 * 
		 */
     
        public AWTDoom(DoomMain<?,V> DM, DoomVideoRenderer<?,V> V) {
      		super(DM, V);
      		drawhere=new Canvas();
      		gelatine=new Canvas();
        // Don't do anything yet until InitGraphics is called.
        }
        
    	public void SetGamma(int level){
    		if (D) System.err.println("Setting gamma "+level);
    		V.setUsegamma(level);
    		screen=V.getCurrentScreen(); // Refresh screen after change.
    		RAWSCREEN=V.getScreen(DoomVideoRenderer.SCREEN_FG);
    	}
        
public static final class HiColor extends AWTDoom<short[]>{
    
    /**
     * 
     */
    private static final long serialVersionUID = 1L;

    public HiColor(DoomMain<?, short[]> DM, DoomVideoRenderer<?,short[]> V) {
        super(DM, V);
    }

    @Override
    public void ReadScreen(short[] scr) {
        System.arraycopy(this.RAWSCREEN, 0, scr, 0, RAWSCREEN.length);
        }
    
    @Override
    public void FinishUpdate() {
        int     tics;
        int     i;
        
        // draws little dots on the bottom of the screen
        /*if (true)
        {

        i = I.GetTime();
        tics = i - lasttic;
        lasttic = i;
        if (tics > 20) tics = 20;
        if (tics < 1) tics = 1;

        for (i=0 ; i<tics*2 ; i+=2)
            RAWSCREEN[ (SCREENHEIGHT-1)*SCREENWIDTH + i] = (byte) 0xff;
        for ( ; i<20*2 ; i+=2)
            RAWSCREEN[ (SCREENHEIGHT-1)*SCREENWIDTH + i] = 0x0;
        
        } */

        if (true)
        {

        i = TICK.GetTime();
        tics = i - lasttic;
        lasttic = i;
        if (tics<1) 
            frames++;
        else
        {
        //frames*=35;
        for (i=0 ; i<frames*2 ; i+=2)
            RAWSCREEN[ (height-1)*width + i] = (short) 0xffff;
        for ( ; i<20*2 ; i+=2)
            RAWSCREEN[ (height-1)*width + i] = 0x0;
        frames=0;
        }
        }

        this.update(null);
        //this.getInputContext().selectInputMethod(java.util.Locale.US);
        
    }
}

public static final class Indexed extends AWTDoom<byte[]>{
    
    /**
     * 
     */
    private static final long serialVersionUID = 1L;

    public Indexed(DoomMain<?,byte[]> DM, DoomVideoRenderer<?,byte[]> V) {
        super(DM, V);
    }

    @Override
    public void ReadScreen(byte[] scr) {
        System.arraycopy(this.RAWSCREEN, 0, scr, 0, RAWSCREEN.length);
        }
    
    @Override
    public void FinishUpdate() {
        int     tics;
        int     i;
        
        // draws little dots on the bottom of the screen
        /*if (true)
        {

        i = I.GetTime();
        tics = i - lasttic;
        lasttic = i;
        if (tics > 20) tics = 20;
        if (tics < 1) tics = 1;

        for (i=0 ; i<tics*2 ; i+=2)
            RAWSCREEN[ (SCREENHEIGHT-1)*SCREENWIDTH + i] = (byte) 0xff;
        for ( ; i<20*2 ; i+=2)
            RAWSCREEN[ (SCREENHEIGHT-1)*SCREENWIDTH + i] = 0x0;
        
        } */

        if (true)
        {

        i = TICK.GetTime();
        tics = i - lasttic;
        lasttic = i;
        if (tics<1) 
            frames++;
        else
        {
        //frames*=35;
        for (i=0 ; i<frames*2 ; i+=2)
            RAWSCREEN[ (height-1)*width + i] = (short) 0xffff;
        for ( ; i<20*2 ; i+=2)
            RAWSCREEN[ (height-1)*width + i] = 0x0;
        frames=0;
        }
        }

        this.update(null);
        //this.getInputContext().selectInputMethod(java.util.Locale.US);
        
    }
}

public static final class TrueColor extends AWTDoom<int[]>{
    
    /**
     * 
     */
    private static final long serialVersionUID = 1L;

    public TrueColor(DoomMain<?, int[]> DM, DoomVideoRenderer<?,int[]> V) {
        super(DM, V);
    }

    @Override
    public void ReadScreen(int[] scr) {
        System.arraycopy(this.RAWSCREEN, 0, scr, 0, RAWSCREEN.length);
        }
    
    @Override
    public void FinishUpdate() {
        int     tics;
        int     i;
        
        // draws little dots on the bottom of the screen
        /*if (true)
        {

        i = I.GetTime();
        tics = i - lasttic;
        lasttic = i;
        if (tics > 20) tics = 20;
        if (tics < 1) tics = 1;

        for (i=0 ; i<tics*2 ; i+=2)
            RAWSCREEN[ (SCREENHEIGHT-1)*SCREENWIDTH + i] = (byte) 0xff;
        for ( ; i<20*2 ; i+=2)
            RAWSCREEN[ (SCREENHEIGHT-1)*SCREENWIDTH + i] = 0x0;
        
        } */

        if (true)
        {

        i = TICK.GetTime();
        tics = i - lasttic;
        lasttic = i;
        if (tics<1) 
            frames++;
        else
        {
        //frames*=35;
        for (i=0 ; i<frames*2 ; i+=2)
            RAWSCREEN[ (height-1)*width + i] = (short) 0xffff;
        for ( ; i<20*2 ; i+=2)
            RAWSCREEN[ (height-1)*width + i] = 0x0;
        frames=0;
        }
        }

        this.update(null);
        //this.getInputContext().selectInputMethod(java.util.Locale.US);
        
    }
}

}

//$Log: AWTDoom.java,v $
//Revision 1.16  2012/11/06 16:04:34  velktron
//Spiffy new fullscreen switching system.
//
//Revision 1.15  2012/09/24 17:16:23  velktron
//Massive merge between HiColor and HEAD. There's no difference from now on, and development continues on HEAD.
//
//Revision 1.14.2.5  2012/09/24 16:58:06  velktron
//TrueColor, Generics.
//
//Revision 1.14.2.4  2012/09/20 14:06:58  velktron
//Generic AWTDoom
//
//Revision 1.14.2.3  2012/09/17 15:57:07  velktron
//Moved common code to DoomFrame
//
//Revision 1.14.2.2  2011/11/18 21:38:30  velktron
//Uses 16-bit stuff.
//
//Revision 1.14.2.1  2011/11/14 00:27:11  velktron
//A barely functional HiColor branch. Most stuff broken. DO NOT USE
//
//Revision 1.14  2011/11/01 19:03:10  velktron
//Using screen number constants
//
//Revision 1.13  2011/10/23 18:11:07  velktron
//Split functionality into DoomFrame, gerenic compliance.
//
//Revision 1.12  2011/10/11 13:24:51  velktron
//Major overhaul to work with new renderer interface. Now only available windowing system.
//
//Revision 1.11  2011/08/01 00:59:57  velktron
//Shut up debug messages.
//
//Revision 1.10  2011/07/15 13:57:54  velktron
//Implement VI.ReadScreen as a future good practice.
//
//Revision 1.9  2011/06/23 15:42:38  velktron
//Added modular palette rotation to handle sub-14 cases.
//
//Revision 1.8  2011/06/14 09:54:20  velktron
//Separated palette generation/fixed OldAWTDoom
//
//Revision 1.7  2011/06/08 17:24:59  velktron
//Added support for gamma changes.
//
//Revision 1.6  2011/06/02 14:54:18  velktron
//Old AWTEvents deprecated. MochaEvents now default.
//
//Revision 1.3  2011/06/01 17:42:49  velktron
//Removed stupid nagging.
//
//Revision 1.2  2011/06/01 17:17:24  velktron
//New event system.
//
//Revision 1.1  2011/06/01 17:04:23  velktron
//New event system.
//
//Revision 1.4  2011/05/30 02:25:50  velktron
//Centering and offsetting on expose, proper exiting.
//
//Revision 1.3  2011/05/29 22:15:32  velktron
//Introduced IRandom interface.
//
//Revision 1.2  2011/05/29 20:58:58  velktron
//Added better mouse grabbing method, more reliable, more cross-OS.
//
//Revision 1.1  2011/05/27 13:26:56  velktron
//A slightly better, though not perfect, way to handle input, partially based on_D_'s work.
//
package awt;

import static g.Keys.*;
import i.DoomEventInterface;

import java.awt.AWTEvent;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Event;
import java.awt.KeyEventDispatcher;
import java.awt.KeyboardFocusManager;
import java.awt.MouseInfo;
import java.awt.Point;
import java.awt.Robot;
import java.awt.Toolkit;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.LinkedList;

import doom.DoomMain;
import doom.event_t;
import doom.evtype_t;

/** A very basic event handling class, directly based on linuxdoom's.
 *  Sadly, it doesn't work so well cross-platform. Use MochaEvents instead.
 * 
 * @author velktron
 *
 */

@Deprecated
public class AWTEvents implements WindowListener,KeyEventDispatcher,KeyListener,MouseListener,MouseMotionListener,DoomEventInterface {

    // modifications of eventQueue must be thread safe!
    static LinkedList<AWTEvent> eventQueue = new LinkedList<AWTEvent>();

    //// STATUS STUFF ///////////
    public final DoomMain DM;
    public final Component canvas;
    Robot robby;
    Cursor hidden;
    Cursor normal;

    //////// CURRENT MOVEMENT AND INPUT STATUS //////////////

    protected static final int POINTER_WARP_COUNTDOWN = 1;
    protected int lastmousex;
    protected int lastmousey;
    protected Point lastmouse;
    protected int mousedx;
    protected int mousedy;
    protected boolean mousemoved;
    protected boolean ignorebutton;
    protected boolean grabMouse=true;
    protected int doPointerWarp=POINTER_WARP_COUNTDOWN;


    public AWTEvents(DoomMain DM, Component canvas) {
        this.DM = DM;
        this.canvas = canvas;

        // AWT: create cursors.
        this.normal=canvas.getCursor();
        this.hidden=this.createInvisibleCursor();

        // Create AWT Robot for forcing mouse
        try {
            robby=new Robot();
        } catch (Exception e){
            System.out.println("AWT Robot could not be created, mouse input focus will be loose!");
        }
        
    }

    //////////// LISTENERS //////////

    @Override
    public boolean dispatchKeyEvent(KeyEvent e) {

        return false;
    }

    public void keyPressed(KeyEvent e) {
        if (e.getKeyCode()<=KeyEvent.VK_F12) {  
            addEvent(e);            
        }

        e.consume();
    }

    public void keyReleased(KeyEvent e) {

        //if ((e.getModifiersEx() & UNACCEPTABLE_MODIFIERS) ==0) {
        if (e.getKeyCode()<=KeyEvent.VK_F12) {
            addEvent(e);
        }
        e.consume();
    }

    public void keyTyped(KeyEvent e) {
        if (e.getKeyCode()<=KeyEvent.VK_F12) {
            addEvent(e);
        }

        e.consume();        
    }

    ///////////////////// MOUSE EVENTS   ///////////////////////

    @Override
    public void mouseClicked(MouseEvent mouseevent) {
        addEvent(mouseevent);

    }

    @Override
    public void mouseEntered(MouseEvent mouseevent) {
        addEvent(mouseevent);

    }

    @Override
    public void mouseExited(MouseEvent mouseevent) {
        addEvent(mouseevent);

    }

    @Override
    public void mousePressed(MouseEvent mouseevent) {
        addEvent(mouseevent);

    }

    @Override
    public void mouseReleased(MouseEvent mouseevent) {
        addEvent(mouseevent);

    }

    @Override
    public void mouseDragged(MouseEvent mouseevent) {
        addEvent(mouseevent);

    }

    @Override
    public void mouseMoved(MouseEvent mouseevent) {
        addEvent(mouseevent);

    }


    ///////////////////// QUEUE HANDLING ///////////////////////

    static void addEvent(AWTEvent ev) {
        synchronized (eventQueue) {
            eventQueue.addLast(ev);
        }
    }

    public static AWTEvent nextEvent() {
        AWTEvent ev;
        synchronized (eventQueue) {
            ev = (!eventQueue.isEmpty())?(AWTEvent)eventQueue.removeFirst():null;
        }
        return ev;
    }

    // This event here is used as a static scratch copy. When sending out
    // messages, its contents are to be actually copied (struct-like).
    // This avoids the continuous object creation/destruction overhead,
    // And it also allows creating "sticky" status.

    final event_t event=new event_t();
    // Special FORCED and PAINFUL key and mouse cancel event.
    final event_t cancelkey=new event_t(evtype_t.ev_clear,0xFF,0,0);
    final event_t cancelmouse=new event_t(evtype_t.ev_mouse,0,0,0);
    int prevmousebuttons;
    
    @Override
    public void GetEvent() {
        AWTEvent X_event;
        MouseEvent MEV;
        Point tmp;

        // put event-grabbing stuff in here
        if (eventQueue.isEmpty()) return;   
        X_event=nextEvent();
        //System.out.println("Event type:"+X_event.getID());

        // Keyboard events get priority vs mouse events.
        // In the case of combined input, however, we need
        if (!ignorebutton){
            switch (X_event.getID())
            {
            case Event.KEY_PRESS: {

                event.type=evtype_t.ev_keydown;
                event.data1=xlatekey((KeyEvent)X_event);
                DM.PostEvent(event);
                if (prevmousebuttons!=0){
                    
                // Allow combined mouse/keyboard events.
                event.data1=prevmousebuttons;
                event.type=evtype_t.ev_mouse;
                DM.PostEvent(event);
                }
                //System.err.println("k");
                break;
            }
            case Event.KEY_RELEASE:
                event.type=evtype_t.ev_keyup;
                event.data1=xlatekey((KeyEvent)X_event);
                DM.PostEvent(event);
                
                if (prevmousebuttons!=0){
                    
                    // Allow combined mouse/keyboard events.
                    event.data1=prevmousebuttons;
                    event.type=evtype_t.ev_mouse;
                    DM.PostEvent(event);
                    }
                //System.err.println( "ku");
                break;
            }
        }

        // Mouse events are also handled, but with secondary priority.
        switch (X_event.getID()){
        // ButtonPress
        case Event.MOUSE_DOWN:
            MEV=(MouseEvent)X_event;
            event.type=evtype_t.ev_mouse;
            event.data1 = prevmousebuttons=
                (MEV.getButton() == MouseEvent.BUTTON1 ? 1: 0) |
                (MEV.getButton() == MouseEvent.BUTTON2 ? 2: 0)|
                (MEV.getButton() == MouseEvent.BUTTON3 ? 4: 0);
            event.data2 = event.data3 = 0;

            DM.PostEvent(event);
            //System.err.println( "b");
            break;

            // ButtonRelease
            // This must send out an amended event.

        case Event.MOUSE_UP:
            MEV=(MouseEvent)X_event;
            event.type = evtype_t.ev_mouse;
            event.data1 =prevmousebuttons^= 
                (MEV.getButton() == MouseEvent.BUTTON1 ? 1: 0) |
                (MEV.getButton() == MouseEvent.BUTTON2 ? 2: 0)|
                (MEV.getButton() == MouseEvent.BUTTON3 ? 4: 0);
            // A PURE mouse up event has no movement.
            event.data2 = event.data3 = 0;
            DM.PostEvent(event);
            //System.err.println("bu");
            break;
            // MotionNotify:
        case Event.WINDOW_MOVED:
        	// Moving the window does change the absolute reference point, while events only
        	// give a RELATIVE one.
        	offset.x=(int) (canvas.getLocationOnScreen().x);
        	offset.y=(int) (canvas.getLocationOnScreen().y);
        	//System.out.printf("Center MOVED to %d %d\n", center.x, center.y);
        case Event.MOUSE_MOVE:
            MEV=(MouseEvent)X_event;
            tmp=MEV.getPoint();
            //this.AddPoint(tmp,center);
            event.type = evtype_t.ev_mouse;
            this.mousedx=tmp.x-this.lastmousex;
            this.mousedy=this.lastmousey-tmp.y;
            this.lastmousex=tmp.x;
            this.lastmousey=tmp.y;                 
            MEV=(MouseEvent)X_event;
            // A pure move has no buttons.
            event.data1=prevmousebuttons=0;
            event.data2 = (mousedx) << 3;
            event.data3 = (mousedy) << 3;

           // System.out.printf("Mouse MOVED to %d %d\n", lastmousex, lastmousey);
            //System.out.println("Mouse moved without buttons: "+event.data1);
            if ((event.data2 | event.data3)!=0)
            {

                DM.PostEvent(event);
                //System.err.println( "m");
                mousemoved = true;
            } else
            {
                mousemoved = false;
            }
            break;


        case Event.MOUSE_DRAG:
            MEV=(MouseEvent)X_event;
            tmp=MEV.getPoint();
            //this.AddPoint(tmp,center);
            this.mousedx=tmp.x-this.lastmousex;
            this.mousedy=this.lastmousey-tmp.y;
            this.lastmousex=tmp.x;
            this.lastmousey=tmp.y;
           // System.out.printf("Mouse MOVED to %d %d\n", lastmousex, lastmousey);

            event.type = evtype_t.ev_mouse;
            // Get buttons, as usual.
            // Well, NOT as usual: in case of a drag, the usual 
            // mousebutton flags don't work. A "drag" means that at
            // least the 1st mouse button is held down, while the other
            // two might or might not be so.
            event.data1 = prevmousebuttons;
                //(MEV.getModifiers() == MouseEvent.BUTTON1_DOWN_MASK ? 1: 0)|
                //(MEV.getModifiers() == MouseEvent.BUTTON2_DOWN_MASK ? 2: 0)|
                //(MEV.getModifiers() == MouseEvent.BUTTON3_DOWN_MASK ? 4: 0);
            event.data2 = (mousedx) << 3;
            event.data3 = (mousedy) << 3;

            //System.out.printf("Mouse DRAGGED to %d %d\n", mousedx, mousedy);
            //System.out.println("Mouse moved with buttons pressed: "+event.data1);
            if ((event.data2 | event.data3)!=0)
            {
                DM.PostEvent(event);
                //System.err.println( "m");
                mousemoved = false;
            } else
            {
                mousemoved = true;
            }
            break;
        case Event.MOUSE_ENTER:
            //System.err.println("ACCEPTING keyboard input");
            canvas.requestFocus();
            canvas.setCursor(hidden);
        	offset.x=(int) (canvas.getLocationOnScreen().x);
        	offset.y=(int) (canvas.getLocationOnScreen().y);
        	System.out.printf("Offset MOVED to %d %d\n", offset.x, offset.y);
            this.grabMouse();
            ignorebutton=false;
            break;
        case Event.MOUSE_EXIT:
            // Forcibly clear events                 

            DM.PostEvent(cancelmouse);
            DM.PostEvent(cancelkey);
            reposition();
        	System.out.printf("FORCED and PAINFUL event clearing!\n");
            canvas.setCursor(normal);             
            ignorebutton=true;
            break;
        case Event.WINDOW_EXPOSE:
        	offset.x=(int) (canvas.getLocationOnScreen().x);
        	offset.y=(int) (canvas.getLocationOnScreen().y);
        	System.out.printf("Center MOVED to %d %d\n", offset.x, offset.y);
            // No real single equivalent for "ConfigureNotify"

        case Event.WINDOW_DESTROY:
            break;
        default:
            // NOT NEEDED in AWT if (doShm && X_event.type == X_shmeventtype) shmFinished = true;
            break;

        }

    }

	/** Update relative position offset, and force mouse there.
	 * 
	 */
	void reposition() {
		lastmousex=canvas.getWidth()/2;
		lastmousey=canvas.getHeight()/2;
		offset.x=(int) (canvas.getLocationOnScreen().x);
		offset.y=(int) (canvas.getLocationOnScreen().y);
	}
    
    Point offset=new Point();

    /** Returns true if flags are included in arg.
     * Synonymous with (flags & arg)!=0
     * 
     * @param flags
     * @param arg
     * @return
     */
    public static final boolean flags(int flags, int arg){
        return ((flags & arg)!=0);
    }


    /////////////////////    WINDOW STUFF //////////////////////


    @Override
    public void windowActivated(WindowEvent windowevent) {
        System.out.println("Window activated");
        canvas.getInputContext().selectInputMethod(java.util.Locale.US);

    }

    @Override
    public void windowClosed(WindowEvent windowevent) {
        // TODO Auto-generated method stub

    }

    @Override
    public void windowClosing(WindowEvent windowevent) {
        // TODO Auto-generated method stub

    }

    @Override
    public void windowDeactivated(WindowEvent windowevent) {
        // Clear the queue if focus is lost.
        System.out.println("Eventqueue flushed!");
        eventQueue.clear();

    }

    @Override
    public void windowDeiconified(WindowEvent windowevent) {
        canvas.getInputContext().selectInputMethod(java.util.Locale.US);

    }

    @Override
    public void windowIconified(WindowEvent windowevent) {
        // TODO Auto-generated method stub

    }

    @Override
    public void windowOpened(WindowEvent windowevent) {
        canvas.getInputContext().selectInputMethod(java.util.Locale.US);

    }

    /**
     * NASTY hack to hide the cursor.
     * 
     * Create a 'hidden' cursor by using a transparent image
     * ...return the invisible cursor
     */

    protected Cursor createInvisibleCursor()

    {
        Dimension bestCursorDim = Toolkit.getDefaultToolkit().getBestCursorSize(2, 2);
        BufferedImage transparentImage = new BufferedImage(bestCursorDim.width, bestCursorDim.height, BufferedImage.TYPE_INT_ARGB);
        Cursor hiddenCursor = Toolkit.getDefaultToolkit( ).createCustomCursor(transparentImage, new Point(1, 1),    "HiddenCursor");
        return hiddenCursor;
    }

    public void grabMouse() {
        // Warp the pointer back to the middle of the window
        //  or it will wander off - that is, the game will
        //  loose input focus within X11.
        if (grabMouse)
        {
            if (doPointerWarp--<=0)
            {
                // Don't warp back if we deliberately alt-tabbed away.
                Point p=canvas.getMousePosition();
                if (p!=null){
                	
                	
                   robby.mouseMove(	offset.x+canvas.getWidth()/2,
                		   offset.y+canvas.getHeight()/2);
                    lastmousex=/*center.x+*/canvas.getWidth()/2;
                    lastmousey=/*center.y+*/canvas.getHeight()/2;
                    //System.out.printf("Mouse FORCED back to %d %d\n", lastmousex, lastmousey);
                }
                doPointerWarp = POINTER_WARP_COUNTDOWN;
            } 

        }

        mousemoved = false;

    }

    /** FIXME: input must be made scancode dependent rather than VK_Dependent,
     *  else a lot of things just don't work. 
     * 
     * @param e
     * @return
     */

    public int xlatekey(KeyEvent e)
    {

        int rc;

        switch(rc =     e.getKeyCode())

        //Event.XKeycodeToKeysym(X_display, X_event.xkey.keycode, 0))

        {
        case KeyEvent.VK_LEFT:    rc = KEY_LEFTARROW; break;
        case KeyEvent.VK_RIGHT:   rc = KEY_RIGHTARROW;    break;
        case KeyEvent.VK_DOWN:    rc = KEY_DOWNARROW; break;
        case KeyEvent.VK_UP:  rc = KEY_UPARROW;   break;
        case KeyEvent.VK_ESCAPE:  rc = KEY_ESCAPE;    break;
        case KeyEvent.VK_ENTER:   rc = KEY_ENTER;     break;
        case KeyEvent.VK_CONTROL: rc= KEY_CTRL; break;
        case KeyEvent.VK_ALT: rc=KEY_ALT; break;
        case KeyEvent.VK_SHIFT: rc=KEY_SHIFT; break;
        // Added handling of pgup/pgdown/home etc.
        case KeyEvent.VK_PAGE_DOWN: rc= KEY_PGDN;	break;
        case KeyEvent.VK_PAGE_UP: rc= KEY_PGUP;	break;
        case KeyEvent.VK_HOME: rc= KEY_HOME;	break;
        case KeyEvent.VK_F1:  rc = KEY_F1;        break;
        case KeyEvent.VK_F2:  rc = KEY_F2;        break;
        case KeyEvent.VK_F3:  rc = KEY_F3;        break;
        case KeyEvent.VK_F4:  rc = KEY_F4;        break;
        case KeyEvent.VK_F5:  rc = KEY_F5;        break;
        case KeyEvent.VK_F6:  rc = KEY_F6;        break;
        case KeyEvent.VK_F7:  rc = KEY_F7;        break;
        case KeyEvent.VK_F8:  rc = KEY_F8;        break;
        case KeyEvent.VK_F9:  rc = KEY_F9;        break;
        case KeyEvent.VK_F10: rc = KEY_F10;       break;
        case KeyEvent.VK_F11: rc = KEY_F11;       break;
        case KeyEvent.VK_F12: rc = KEY_F12;       break;
        case KeyEvent.VK_BACK_SPACE:
        case KeyEvent.VK_DELETE:  rc = KEY_BACKSPACE; break;
        case KeyEvent.VK_PAUSE:   rc = KEY_PAUSE;     break;
        case KeyEvent.KEY_RELEASED:
        case KeyEvent.VK_TAB: rc = KEY_TAB;       break;

        case KeyEvent.KEY_PRESSED:
            switch(e.getKeyCode()){

            case KeyEvent.VK_PLUS:
            case KeyEvent.VK_EQUALS: 
                rc = KEY_EQUALS;    
                break;

            case (13):
            case KeyEvent.VK_SUBTRACT: 
            case KeyEvent.VK_MINUS:   
                rc = KEY_MINUS;     
                break;

            case KeyEvent.VK_SHIFT:
                rc = KEY_SHIFT;
                break;

            case KeyEvent.VK_CONTROL:
                rc = KEY_CTRL;
                break;                           

            case KeyEvent.VK_ALT:
                rc = KEY_ALT;
                break;

            case KeyEvent.VK_SPACE:
                rc = ' ';
                break;

            }

        default:

            /*if (rc >= KeyEvent.VK_SPACE && rc <= KeyEvent.VK_DEAD_TILDE)
            {
            rc = (int) (rc - KeyEvent.FOCUS_EVENT_MASK + ' ');
            break;
            } */
            if (rc >= KeyEvent.VK_A && rc <= KeyEvent.VK_Z){
                rc = rc-KeyEvent.VK_A +'a';
                break;
            }
            // Unknown. Probably fucking up with the keyboard locale. Switch to be sure.
            // Sorry for this horrible hack, but Java can't read locale-free keycodes -_-
            // this.getInputContext().selectInputMethod(java.util.Locale.US);
            //if (rc>KEY_F12) rc=KEY_RSHIFT;
            break;
        }

        //System.out.println("Typed "+e.getKeyCode()+" char "+e.getKeyChar()+" mapped to "+Integer.toHexString(rc));

        // Sanitize. Nothing beyond F12 must pass through, else you will
        // get the "all cheats" bug.
        return rc;//Math.min(rc,KEY_F12);

    }

    protected void AddPoint(Point A, Point B){
    	A.x+=B.x;
    	A.y+=B.y;
    }
    
}
package awt;

import java.awt.DisplayMode;
import java.awt.GraphicsDevice;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class DisplayModePicker {
    
    protected GraphicsDevice device;
    protected DisplayMode default_mode;
    
    public DisplayModePicker(GraphicsDevice device){
        this.device=device;
        this.default_mode=device.getDisplayMode();
    }
    
    public DisplayMode pickClosest(int width, int height){
        
        DisplayMode[] modes=device.getDisplayModes();
        
        List<DisplayMode> picks=new ArrayList<DisplayMode>();
        
        WidthComparator wc=new WidthComparator();
        HeightComparator hc=new HeightComparator();
        
        // Filter out those with too small dimensions.
        
        for (DisplayMode dm:modes){
            if (dm.getWidth()>=width && dm.getHeight()>=height)
                picks.add(dm);
        }
        
       if (picks.size()>0) {        
        Collections.sort(picks, wc);
        Collections.sort(picks, hc);
       }
        
       // First one is the minimum that satisfies the desired criteria.
        return picks.get(0);
        
    }
    
    /** Return offsets to center rasters too oddly shaped to fit entirely into 
     *  a standard display mode (unfortunately, this means most stuff > 640 x 400),
     *  with doom's standard 8:5 ratio.
     * 
     * @param width
     * @param height
     * @param dm
     * @return array, x-offset and y-offset.
     */
    
public int[] getCentering(int width, int height,DisplayMode dm){
        
        int xy[]=new int[2];
        
        xy[0]=(dm.getWidth()-width)/2;
        xy[1]=(dm.getHeight()-height)/2;
    

        return xy;
        
    }
    
    class WidthComparator implements Comparator<DisplayMode>{

        @Override
        public int compare(DisplayMode arg0, DisplayMode arg1) {
            if (arg0.getWidth()> arg1.getWidth()) return 1;
            if (arg0.getWidth()< arg1.getWidth()) return -1;
            return 0;
        }
        
    }
    
    class HeightComparator implements Comparator<DisplayMode>{

        @Override
        public int compare(DisplayMode arg0, DisplayMode arg1) {
            if (arg0.getHeight()> arg1.getHeight()) return 1;
            if (arg0.getHeight()< arg1.getHeight()) return -1;
            return 0;
        }
        
    }

}

package awt;

import java.awt.Canvas;
import java.awt.Color;
import java.awt.Graphics;

public class DoomCanvas extends Canvas {
    /**
     * 
     */
    private static final long serialVersionUID = 1L;

    public void paint(Graphics g){
        
        g.setColor(new Color(.3f, .4f, .5f, .6f));
        g.fillRect(0, 0, this.getWidth(), this.getHeight());
    }
}

package awt;

import i.InputListener;

import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.InputEvent;

import javax.swing.JPanel;

import v.DoomVideoRenderer;
import doom.DoomMain;
import doom.event_t;

/** A simple Doom display & keyboard driver for AWT.
 *  Uses a Canvas for painting and implements some
 *  of the IVideo methods.
 * 
 *  Uses 8-bit buffered images and switchable IndexColorModels.
 *  
 *  It's really basic, but allows testing and user interaction.
 *  Heavily based on the original LinuxDoom X11 implementation, and
 *  is similar in goals: just a functional, reference implementation
 *  to build upon whatever fancy extensions you like.
 *  
 *  The only "hitch" is that this implementation expects to be 
 *  initialized upon a BufferedRenderer with multiple images per 
 *  screen buffer in order to perform the palette switching trick.
 *  
 *  The images themselves don't have to be "BufferedImage",
 *  and in theory it could be possible to use e.g. MemoryImageSource
 *  for the same purpose . Oh well.
 *    
 *  
 * 
 * @author Velktron
 *
 */
public abstract class SwingDoom<V> extends DoomFrame<V> {


    private static final long serialVersionUID = 3118508722502652276L;

		JPanel drawhere;

		/** Gimme some raw palette RGB data.
		 *  I will do the rest
		 *  
		 *  (hint: read this from the PLAYPAL
		 *   lump in the IWAD!!!).
		 * 
		 */
     
        @SuppressWarnings("unchecked")
		public SwingDoom(DoomMain<?,V> DM, DoomVideoRenderer<?,V> V) {
        	super(DM, V);
        	drawhere=new JPanel();
        	}
        
        Point center;
        Rectangle rect;

        
        public String processEvents(){
            StringBuffer tmp=new StringBuffer();
            event_t event;
            while ( (event=InputListener.nextEvent()) != null ) {
                tmp.append(event.type.ordinal()+"\n");
            }
            return tmp.toString();
        }
        
    	public void SetGamma(int level){
    		if (D) System.err.println("Setting gamma "+level);
    		V.setUsegamma(level);
    		screen=V.getCurrentScreen(); // Refresh screen after change.
    		RAWSCREEN=V.getScreen(DoomVideoRenderer.SCREEN_FG);
    	}
        
        // This stuff should NOT get through in keyboard events.
        protected final int UNACCEPTABLE_MODIFIERS=(int) (InputEvent.ALT_GRAPH_DOWN_MASK+
        										 InputEvent.META_DOWN_MASK+
        										 InputEvent.META_MASK+
        										 InputEvent.WINDOW_EVENT_MASK+
        										 InputEvent.WINDOW_FOCUS_EVENT_MASK);

    public static final class HiColor extends SwingDoom<short[]>{
        
        /**
         * 
         */
        private static final long serialVersionUID = 1L;

        public HiColor(DoomMain<?, short[]> DM, DoomVideoRenderer<?,short[]> V) {
            super(DM, V);
        }

        @Override
        public void ReadScreen(short[] scr) {
            System.arraycopy(this.RAWSCREEN, 0, scr, 0, RAWSCREEN.length);
            }
        
        @Override
        public void FinishUpdate() {
            int     tics;
            int     i;
            
            // draws little dots on the bottom of the screen
            /*if (true)
            {

            i = I.GetTime();
            tics = i - lasttic;
            lasttic = i;
            if (tics > 20) tics = 20;
            if (tics < 1) tics = 1;

            for (i=0 ; i<tics*2 ; i+=2)
                RAWSCREEN[ (SCREENHEIGHT-1)*SCREENWIDTH + i] = (byte) 0xff;
            for ( ; i<20*2 ; i+=2)
                RAWSCREEN[ (SCREENHEIGHT-1)*SCREENWIDTH + i] = 0x0;
            
            } */

            if (true)
            {

            i = TICK.GetTime();
            tics = i - lasttic;
            lasttic = i;
            if (tics<1) 
                frames++;
            else
            {
            //frames*=35;
            for (i=0 ; i<frames*2 ; i+=2)
                RAWSCREEN[ (height-1)*width + i] = (short) 0xffff;
            for ( ; i<20*2 ; i+=2)
                RAWSCREEN[ (height-1)*width + i] = 0x0;
            frames=0;
            }
            }

            this.update(null);
            //this.getInputContext().selectInputMethod(java.util.Locale.US);
            
        }
    }
    
    public static final class Indexed extends SwingDoom<byte[]>{
        
        /**
         * 
         */
        private static final long serialVersionUID = 1L;

        public Indexed(DoomMain<?,byte[]> DM, DoomVideoRenderer<?,byte[]> V) {
            super(DM, V);
        }

        @Override
        public void ReadScreen(byte[] scr) {
            System.arraycopy(this.RAWSCREEN, 0, scr, 0, RAWSCREEN.length);
            }
        
        @Override
        public void FinishUpdate() {
            int     tics;
            int     i;
            
            // draws little dots on the bottom of the screen
            /*if (true)
            {

            i = I.GetTime();
            tics = i - lasttic;
            lasttic = i;
            if (tics > 20) tics = 20;
            if (tics < 1) tics = 1;

            for (i=0 ; i<tics*2 ; i+=2)
                RAWSCREEN[ (SCREENHEIGHT-1)*SCREENWIDTH + i] = (byte) 0xff;
            for ( ; i<20*2 ; i+=2)
                RAWSCREEN[ (SCREENHEIGHT-1)*SCREENWIDTH + i] = 0x0;
            
            } */

            if (true)
            {

            i = TICK.GetTime();
            tics = i - lasttic;
            lasttic = i;
            if (tics<1) 
                frames++;
            else
            {
            //frames*=35;
            for (i=0 ; i<frames*2 ; i+=2)
                RAWSCREEN[ (height-1)*width + i] = (short) 0xffff;
            for ( ; i<20*2 ; i+=2)
                RAWSCREEN[ (height-1)*width + i] = 0x0;
            frames=0;
            }
            }

            this.update(null);
            //this.getInputContext().selectInputMethod(java.util.Locale.US);
            
        }
    }

    public static final class TrueColor extends SwingDoom<int[]>{
        
        /**
         * 
         */
        private static final long serialVersionUID = 1L;

        public TrueColor(DoomMain<?, int[]> DM, DoomVideoRenderer<?,int[]> V) {
            super(DM, V);
        }

        @Override
        public void ReadScreen(int[] scr) {
            System.arraycopy(this.RAWSCREEN, 0, scr, 0, RAWSCREEN.length);
            }
        
        @Override
        public void FinishUpdate() {
            int     tics;
            int     i;
            
            // draws little dots on the bottom of the screen
            /*if (true)
            {

            i = I.GetTime();
            tics = i - lasttic;
            lasttic = i;
            if (tics > 20) tics = 20;
            if (tics < 1) tics = 1;

            for (i=0 ; i<tics*2 ; i+=2)
                RAWSCREEN[ (SCREENHEIGHT-1)*SCREENWIDTH + i] = (byte) 0xff;
            for ( ; i<20*2 ; i+=2)
                RAWSCREEN[ (SCREENHEIGHT-1)*SCREENWIDTH + i] = 0x0;
            
            } */

            if (true)
            {

            i = TICK.GetTime();
            tics = i - lasttic;
            lasttic = i;
            if (tics<1) 
                frames++;
            else
            {
            //frames*=35;
            for (i=0 ; i<frames*2 ; i+=2)
                RAWSCREEN[ (height-1)*width + i] = (short) 0xffff;
            for ( ; i<20*2 ; i+=2)
                RAWSCREEN[ (height-1)*width + i] = 0x0;
            frames=0;
            }
            }

            this.update(null);
            //this.getInputContext().selectInputMethod(java.util.Locale.US);
            
        }
    }
}
package awt;

import static g.Keys.*;
import i.DoomEventInterface;

import java.awt.Component;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.KeyEventDispatcher;
import java.awt.Point;
import java.awt.Robot;
import java.awt.Toolkit;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.WindowEvent;
import java.awt.event.WindowFocusListener;
import java.awt.event.WindowListener;
import java.awt.image.BufferedImage;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map;

import doom.DoomMain;
import doom.event_t;
import doom.evtype_t;

/** An alternate events class, more suited for handling the complex situations
 *  that might arise during daily use, window moving, etc.
 *  Use instead of the old AWTEvents, which is present but deprecated.
 * 
 * @author vekltron
 *
 */

public class MochaEvents implements WindowListener,ComponentListener,KeyEventDispatcher,KeyListener,MouseListener,MouseMotionListener,WindowFocusListener,DoomEventInterface {

    // modifications of eventQueue must be thread safe!
    private LinkedList<MochaDoomInputEvent> eventQueue = new LinkedList<MochaDoomInputEvent>();

    private HashMap<Integer, Boolean> lockingKeyStates = new HashMap<Integer, Boolean>();
    
    private static final boolean D=false;

    //// STATUS STUFF ///////////
    public final DoomMain DM;
    public final Component canvas;
    Robot robby;
    Cursor hidden;
    Cursor normal;

    //////// CURRENT MOVEMENT AND INPUT STATUS //////////////

    protected static final int POINTER_WARP_COUNTDOWN = 1;
    protected int lastmousex;
    protected int lastmousey;
    protected Point lastmouse;
    protected int mousedx;
    protected int mousedy;
    protected boolean mousemoved;
    protected boolean ignorebutton;
    protected boolean grabMouse=true;
    protected int doPointerWarp=POINTER_WARP_COUNTDOWN;


    public MochaEvents(DoomMain DM, Component canvas) {
        this.DM = DM;
        this.canvas = canvas;

        // AWT: create cursors.
        this.normal=canvas.getCursor();
        this.hidden=this.createInvisibleCursor();

        // Create AWT Robot for forcing mouse
        try {
            robby=new Robot();
        } catch (Exception e){
           System.err.println("AWT Robot could not be created, mouse input focus will be loose!");
        }

        lockingKeyStates.put(KeyEvent.VK_CAPS_LOCK, null);
        lockingKeyStates.put(KeyEvent.VK_NUM_LOCK, null);
        lockingKeyStates.put(KeyEvent.VK_SCROLL_LOCK, null);
    }

    
    ///////////////////// QUEUE HANDLING ///////////////////////

    public synchronized void addEvent(MochaDoomInputEvent ev) {
        synchronized (eventQueue) {
            eventQueue.addLast(ev);
        }
    }

    public MochaDoomInputEvent nextEvent() {
    	MochaDoomInputEvent ev;
        synchronized (eventQueue) {
            ev = (!eventQueue.isEmpty())?(MochaDoomInputEvent)eventQueue.removeFirst():null;
        }
        return ev;
    }

    // This event here is used as a static scratch copy. When sending out
    // messages, its contents are to be actually copied (struct-like).
    // This avoids the continuous object creation/destruction overhead,
    // And it also allows creating "sticky" status.

    final event_t event=new event_t();
    // Special FORCED and PAINFUL key and mouse cancel event.
    final event_t cancelkey=new event_t(evtype_t.ev_clear,0xFF,0,0);
    final event_t cancelmouse=new event_t(evtype_t.ev_mouse,0,0,0);
    int prevmousebuttons;
    // Nasty hack for CAPS LOCK. Apparently, there's no RELIABLE way
    // to get the caps lock state programmatically, so we have to make 
    // do with simply toggling
    boolean capstoggle=false;
    
    
    @Override
    public void GetEvent() {
        MochaDoomInputEvent X_event;
        MouseEvent MEV;
        Point tmp;
        // Unlike most keys, caps lock etc. can be polled, so no need to worry
        // about them getting stuck.  So they are re-polled after all other
        // key states have beeen cleared.
      /*  if (DM.shouldPollLockingKeys()) {
            for (Map.Entry<Integer, Boolean> e: lockingKeyStates.entrySet()) {
                e.setValue(null);
            }
            updateLockingKeys();
        } */
        // put event-grabbing stuff in here
        if (eventQueue.isEmpty()) return;   
        X_event=nextEvent();
        //System.out.println("Event type:"+X_event.getID());

        // Keyboard events get priority vs mouse events.
        // In the case of combined input, however, we need
        if (!ignorebutton){
            switch (X_event.type)
            {
            case MochaDoomInputEvent.KeyPress: {
                event.type=evtype_t.ev_keydown;
                event.data1=xlatekey((KeyEvent)X_event.ev, -1);
                
                // Toggle, but don't it go through.
                if (event.data1==KEY_CAPSLOCK)
                	 capstoggle=true;
                
                if (event.data1!=KEY_CAPSLOCK)
                	DM.PostEvent(event);
                
                if (prevmousebuttons!=0){
                    
                // Allow combined mouse/keyboard events.
                event.data1=prevmousebuttons;
                event.type=evtype_t.ev_mouse;
                DM.PostEvent(event);
                }
                //System.err.println("k");
                break;
            }
            
            case MochaDoomInputEvent.KeyRelease:
                event.type=evtype_t.ev_keyup;
                event.data1=xlatekey((KeyEvent)X_event.ev, -1);

                if ((event.data1!=KEY_CAPSLOCK) ||
                	((event.data1==KEY_CAPSLOCK)&&capstoggle)) {
                	DM.PostEvent(event);
                }
                
                capstoggle=false;
                
                if (prevmousebuttons!=0){
                    
                    // Allow combined mouse/keyboard events.
                    event.data1=prevmousebuttons;
                    event.type=evtype_t.ev_mouse;
                    DM.PostEvent(event);
                    }
                //System.err.println( "ku");
                break;
              /* UNUSED, see caps lock problems
            case MochaDoomInputEvent.LockOn: {
                event.type=evtype_t.ev_keydown;
                event.data1=xlatekey(null, X_event.value);
                DM.PostEvent(event);
                if (prevmousebuttons!=0){
                        
                    // Allow combined mouse/keyboard events.
                    event.data1=prevmousebuttons;
                    event.type=evtype_t.ev_mouse;
                    DM.PostEvent(event);
                }
                //System.err.println("l1");
                break;
            }

            case MochaDoomInputEvent.LockOff: {
                event.type=evtype_t.ev_keyup;
                event.data1=xlatekey(null, X_event.value);
                DM.PostEvent(event);
                
                if (prevmousebuttons!=0){
                    
                    // Allow combined mouse/keyboard events.
                    event.data1=prevmousebuttons;
                    event.type=evtype_t.ev_mouse;
                    DM.PostEvent(event);
                    }
                //System.err.println( "l0");
                break;
            }
            */
            
            case MochaDoomInputEvent.KeyType:
                event.type=evtype_t.ev_keyup;
                event.data1=xlatekey((KeyEvent)X_event.ev, -1);
                DM.PostEvent(event);
                
                if (prevmousebuttons!=0){
                    
                    // Allow combined mouse/keyboard events.
                    event.data1=prevmousebuttons;
                    event.type=evtype_t.ev_mouse;
                    DM.PostEvent(event);
                    }
                //System.err.println( "ku");
                break;
            }

        }

        // Ignore ALL mouse events if we are moving the window.
        // Mouse events are also handled, but with secondary priority.
        switch (X_event.type){
        // ButtonPress
        case MochaDoomInputEvent.ButtonPress:
            MEV=(MouseEvent)X_event.ev;
            event.type=evtype_t.ev_mouse;
            event.data1 = prevmousebuttons=
                (MEV.getButton() == MouseEvent.BUTTON1 ? 1: 0) |
                (MEV.getButton() == MouseEvent.BUTTON2 ? 2: 0)|
                (MEV.getButton() == MouseEvent.BUTTON3 ? 4: 0);
            event.data2 = event.data3 = 0;

            DM.PostEvent(event);
            //System.err.println( "b");
            break;

            // ButtonRelease
            // This must send out an amended event.

        case MochaDoomInputEvent.ButtonRelease:
            MEV=(MouseEvent)X_event.ev;
            event.type = evtype_t.ev_mouse;
            event.data1 =prevmousebuttons^= 
                (MEV.getButton() == MouseEvent.BUTTON1 ? 1: 0) |
                (MEV.getButton() == MouseEvent.BUTTON2 ? 2: 0)|
                (MEV.getButton() == MouseEvent.BUTTON3 ? 4: 0);
            // A PURE mouse up event has no movement.
            event.data2 = event.data3 = 0;
            DM.PostEvent(event);
            //System.err.println("bu");
            break;
        case MochaDoomInputEvent.MotionNotify:
            MEV=(MouseEvent)X_event.ev;
            tmp=MEV.getPoint();
            //this.AddPoint(tmp,center);
            event.type = evtype_t.ev_mouse;
            this.mousedx=(tmp.x-win_w2);
            this.mousedy=(win_h2-tmp.y);
             
            // A pure move has no buttons.
            event.data1=prevmousebuttons=0;
            event.data2 = (mousedx) << 2;
            event.data3 = (mousedy) << 2;

           // System.out.printf("Mouse MOVED to %d %d\n", lastmousex, lastmousey);
            //System.out.println("Mouse moved without buttons: "+event.data1);
            if ((event.data2 | event.data3)!=0)
            {

                DM.PostEvent(event);
                //System.err.println( "m");
                mousemoved = true;
            } else
            {
                mousemoved = false;
            }
            break;

        case MochaDoomInputEvent.DragNotify:
            MEV=(MouseEvent)X_event.ev;
            tmp=MEV.getPoint();
            this.mousedx=(tmp.x-win_w2);
            this.mousedy=(win_h2-tmp.y);
            event.type = evtype_t.ev_mouse;
            
            // A drag means no change in button state.
            event.data1 = prevmousebuttons;
            event.data2 = (mousedx) << 2;
            event.data3 = (mousedy) << 2;

            if ((event.data2 | event.data3)!=0)
            {
                DM.PostEvent(event);
                //System.err.println( "m");
                mousemoved = true;
            } else
            {
                mousemoved = false;
            }
            break;

        }
        
        // Now for window events. This includes the mouse breaking the border.
        
        switch (X_event.type){
        case MochaDoomInputEvent.FocusLost:
        case MochaDoomInputEvent.MouseExited:
            // Forcibly clear events                 
            DM.PostEvent(cancelmouse);
            DM.PostEvent(cancelkey);
            canvas.setCursor(normal);             
            ignorebutton=true;
            break;
            
        case MochaDoomInputEvent.WindowMoving:
        	// Don't try to reposition immediately during a move
        	// event, wait for a mouse click.
        	we_are_moving=true;
        	ignorebutton=true;
            // Forcibly clear events                 
            DM.PostEvent(cancelmouse);
            DM.PostEvent(cancelkey);
        	move++;
        	break;
        case MochaDoomInputEvent.MouseEntered:
        case MochaDoomInputEvent.FocusGained:
        	we_are_moving=false;
        	//reposition();
        case MochaDoomInputEvent.ConfigureNotify:
        case MochaDoomInputEvent.CreateNotify:
            // All events that have to do with the window being changed,
        	// moved etc. should go here. The most often result
        	// in focus being lost and position being changed, so we
        	// need to take charge.
            DM.justfocused=true;
            canvas.requestFocus();
            reposition();            			
        	ignorebutton=false;
			break;
        default:
            // NOT NEEDED in AWT if (doShm && X_event.type == X_shmeventtype) shmFinished = true;
            break;

        }
        
        
        // If the mouse moved, don't wait until it managed to get out of the 
        // window to bring it back.
		if (!we_are_moving && (mousedx != 0 || mousedy != 0)) {
			// move the mouse to the window center again
			robby.mouseMove(offset.x + win_w2, offset.y + win_h2);
			
		}
		
		mousedx=mousedy=0; // don't spaz.

    }
    
    private int  win_w2, win_h2;
    private int move=0;

	/** Update relative position offset, and force mouse there.
	 * 
	 */
	void reposition() {
		offset.x=(int) (canvas.getLocationOnScreen().x);
    	offset.y=(int) (canvas.getLocationOnScreen().y);
    	// Shamelessly ripped from Jake 2. Maybe it works better?
		Component c = this.canvas;
		//offset.x = 0;
		//offset.y = 0;
		win_w2 = c.getWidth() / 2;
		win_h2 = c.getHeight() / 2;
		
		robby.mouseMove(offset.x + win_w2, offset.y + win_h2);

        
        canvas.getInputContext().selectInputMethod(java.util.Locale.US);
        canvas.setCursor(hidden);
		if (D) System.err.printf("Jake 2 method: offset MOVED to %d %d\n", offset.x, offset.y);
		
	}
    
    Point offset=new Point();

    /** Returns true if flags are included in arg.
     * Synonymous with (flags & arg)!=0
     * 
     * @param flags
     * @param arg
     * @return
     */
    public static final boolean flags(int flags, int arg){
        return ((flags & arg)!=0);
    }


    /////////////////////    WINDOW STUFF //////////////////////


    @Override
    public void windowActivated(WindowEvent windowevent) {
    	if (D) System.err.println("Window activated");
    	eventQueue.add(new MochaDoomInputEvent(MochaDoomInputEvent.ConfigureNotify, null));
    	}

    @Override
    public void windowClosed(WindowEvent windowevent) {
        // TODO Auto-generated method stub

    	}

    @Override
    public void windowClosing(WindowEvent windowevent) {
        // TODO Auto-generated method stub

    }

    @Override
    public void windowDeactivated(WindowEvent windowevent) {
        // Clear the queue if focus is lost.
        eventQueue.clear();

    }

    @Override
    public void windowDeiconified(WindowEvent windowevent) {
    	eventQueue.add(new MochaDoomInputEvent(MochaDoomInputEvent.ConfigureNotify, null));

    }

    @Override
    public void windowIconified(WindowEvent windowevent) {
        // TODO Auto-generated method stub
    	eventQueue.clear();

    }

    @Override
    public void windowOpened(WindowEvent windowevent) {
    	eventQueue.add(new MochaDoomInputEvent(MochaDoomInputEvent.CreateNotify, null));
    }

    /**
     * NASTY hack to hide the cursor.
     * 
     * Create a 'hidden' cursor by using a transparent image
     * ...return the invisible cursor
     */

    protected Cursor createInvisibleCursor()

    {
        Dimension bestCursorDim = Toolkit.getDefaultToolkit().getBestCursorSize(2, 2);
        BufferedImage transparentImage = new BufferedImage(bestCursorDim.width, bestCursorDim.height, BufferedImage.TYPE_INT_ARGB);
        Cursor hiddenCursor = Toolkit.getDefaultToolkit( ).createCustomCursor(transparentImage, new Point(1, 1),    "HiddenCursor");
        return hiddenCursor;
    }
   
    /** FIXME: input must be made scancode dependent rather than VK_Dependent,
     *  else a lot of things just don't work. 
     * 
     * @param e
     * @param rc 
     * @return
     */

    public int xlatekey(KeyEvent e, int rc)
    {

        if (e != null) rc = e.getKeyCode();
        switch(rc)

        //Event.XKeycodeToKeysym(X_display, X_event.xkey.keycode, 0))

        {
        case KeyEvent.VK_LEFT:    rc = KEY_LEFTARROW; break;
        case KeyEvent.VK_RIGHT:   rc = KEY_RIGHTARROW;    break;
        case KeyEvent.VK_DOWN:    rc = KEY_DOWNARROW; break;
        case KeyEvent.VK_UP:  rc = KEY_UPARROW;   break;
        case KeyEvent.VK_ESCAPE:  rc = KEY_ESCAPE;    break;
        case KeyEvent.VK_ENTER:   rc = KEY_ENTER;     break;
        case KeyEvent.VK_CONTROL: rc= KEY_CTRL; break;
        case KeyEvent.VK_ALT: rc=KEY_ALT; break;
        case KeyEvent.VK_SHIFT: rc=KEY_SHIFT; break;
        // Added handling of pgup/pgdown/home etc.
        case KeyEvent.VK_PAGE_DOWN: rc= KEY_PGDN;	break;
        case KeyEvent.VK_PAGE_UP: rc= KEY_PGUP;	break;
        case KeyEvent.VK_HOME: rc= KEY_HOME;	break;
        case KeyEvent.VK_END: rc= KEY_END;	break;
        case KeyEvent.VK_F1:  rc = KEY_F1;        break;
        case KeyEvent.VK_F2:  rc = KEY_F2;        break;
        case KeyEvent.VK_F3:  rc = KEY_F3;        break;
        case KeyEvent.VK_F4:  rc = KEY_F4;        break;
        case KeyEvent.VK_F5:  rc = KEY_F5;        break;
        case KeyEvent.VK_F6:  rc = KEY_F6;        break;
        case KeyEvent.VK_F7:  rc = KEY_F7;        break;
        case KeyEvent.VK_F8:  rc = KEY_F8;        break;
        case KeyEvent.VK_F9:  rc = KEY_F9;        break;
        case KeyEvent.VK_F10: rc = KEY_F10;       break;
        case KeyEvent.VK_F11: rc = KEY_F11;       break;
        case KeyEvent.VK_F12: rc = KEY_F12;       break;

        case KeyEvent.VK_BACK_SPACE:
        case KeyEvent.VK_DELETE:  rc = KEY_BACKSPACE; break;

        case KeyEvent.VK_PAUSE:   rc = KEY_PAUSE;     break;

        case KeyEvent.VK_TAB: rc = KEY_TAB;       break;
        case KeyEvent.VK_CAPS_LOCK: rc = KEY_CAPSLOCK; break;
        case KeyEvent.VK_NUM_LOCK: rc = KEY_NUMLOCK; break;
        case KeyEvent.VK_SCROLL_LOCK: rc = KEY_SCROLLLOCK; break;
        /*
        case KeyEvent.KEY_RELEASED:
        case KeyEvent.KEY_PRESSED:
            switch(e.getKeyCode()){

            case KeyEvent.VK_PLUS:
            case KeyEvent.VK_EQUALS: 
                rc = KEY_EQUALS;    
                break;

            case (13):
            case KeyEvent.VK_SUBTRACT: 
            case KeyEvent.VK_MINUS:   
                rc = KEY_MINUS;     
                break;

            case KeyEvent.VK_SHIFT:
                rc = KEY_RSHIFT;
                break;

            case KeyEvent.VK_CONTROL:
                rc = KEY_RCTRL;
                break;                           

            case KeyEvent.VK_ALT:
                rc = KEY_RALT;
                break;

            case KeyEvent.VK_SPACE:
                rc = ' ';
                break;

            }
            */

        default:

            /*if (rc >= KeyEvent.VK_SPACE && rc <= KeyEvent.VK_DEAD_TILDE)
            {
            rc = (int) (rc - KeyEvent.FOCUS_EVENT_MASK + ' ');
            break;
            } */
            if (rc >= KeyEvent.VK_A && rc <= KeyEvent.VK_Z){
                rc = rc-KeyEvent.VK_A +'a';
                break;
            }
            break;
        }

        //System.out.println("Typed "+e.getKeyCode()+" char "+e.getKeyChar()+" mapped to "+Integer.toHexString(rc));
        return rc;//Math.min(rc,KEY_F12);

    }

    protected void AddPoint(Point A, Point B){
    	A.x+=B.x;
    	A.y+=B.y;
    }

    ////////////LISTENERS //////////

    
    ///////////////////////// KEYBOARD EVENTS ///////////////////////////////////


    // UNUSED used commented out because it doesn't appear to work
    // I think that there is no bulletproof method to poll the state of
    // the CAPS LOCK key, and forcing a call to getLockingKeyState()
    // results in an UnsupportedOperationException
    // For now, the best course of action seems to be intercepting toggling only,
    // rather than state. It also looks like a compicated thing to
    // run at every key update.
    
    private void updateLockingKeys() {
        Toolkit toolkit = canvas.getToolkit();
        for (Iterator<Map.Entry<Integer, Boolean>> it =
                 lockingKeyStates.entrySet().iterator();
             it.hasNext();
             ) {
            Map.Entry<Integer, Boolean> entry = it.next(); 
            Integer keyCode = entry.getKey();
            Boolean oldState = entry.getValue();
            try {
            	if (D) System.err.println("Trying");
                boolean newState = toolkit.getLockingKeyState(keyCode);
                if (! Boolean.valueOf(newState).equals(oldState)) {
                	if (D) System.out.println("New event");
                    int eventType =
                        newState ? MochaDoomInputEvent.LockOn
                                 : MochaDoomInputEvent.LockOff;
                    addEvent(new MochaDoomInputEvent(eventType,keyCode));
                    entry.setValue(newState);
                }
            } catch (UnsupportedOperationException ex) {
                // Key not present
                it.remove();
            }
        }
    }

    public void keyPressed(KeyEvent e) {
     // //  updateLockingKeys();
       // if (! lockingKeyStates.containsKey(e.getKeyCode())) {
            if (e.getKeyCode()<=KeyEvent.VK_F12) {  
                addEvent(new MochaDoomInputEvent(MochaDoomInputEvent.KeyPress,e));            
            }
    
            e.consume();
       // }
    }

    public void keyReleased(KeyEvent e) {
       // updateLockingKeys();
       // if (! lockingKeyStates.containsKey(e.getKeyCode())) {
    
            //if ((e.getModifiersEx() & UNACCEPTABLE_MODIFIERS) ==0) {
            if (e.getKeyCode()<=KeyEvent.VK_F12) {
            	addEvent(new MochaDoomInputEvent(MochaDoomInputEvent.KeyRelease,e));
            }
            e.consume();
       // }
    }

    public void keyTyped(KeyEvent e) {
    	//updateLockingKeys();
    	//if (! lockingKeyStates.containsKey(e.getKeyCode())) {
        if (e.getKeyCode()<=KeyEvent.VK_F12) {
        	addEvent(new MochaDoomInputEvent(MochaDoomInputEvent.KeyType,e));
       }

        e.consume();
    	//}
    }

    //////////////////////////// MOUSE EVENTS   ////////////////////////////

    @Override
    public void mouseClicked(MouseEvent mouseevent) {
    	// Marks the end of a move. A press + release during a move will
    	// trigger a "click" event, which is handled specially.
    	if (we_are_moving) {
    		we_are_moving=false;
    		reposition();
    		ignorebutton=false;
    	}
    
    	}

    @Override
    public void mouseEntered(MouseEvent mouseevent) {
    	//System.out.println("Mouse entered");
    	addEvent(new MochaDoomInputEvent(MochaDoomInputEvent.MouseEntered,null));
    	}

    @Override
    public void mouseExited(MouseEvent mouseevent) {
    	//System.out.println("Mouse exited");
    	addEvent(new MochaDoomInputEvent(MochaDoomInputEvent.MouseExited,null));
    	}

    @Override
    public void mousePressed(MouseEvent mouseevent) {
    	if (!we_are_moving) // Don't let presses go through when moving.
    	addEvent(new MochaDoomInputEvent(MochaDoomInputEvent.ButtonPress,mouseevent));
    	}

    @Override
    public void mouseReleased(MouseEvent mouseevent) {
    	if (!we_are_moving) // Don't let presses go through when moving.
    	addEvent(new MochaDoomInputEvent(MochaDoomInputEvent.ButtonRelease,mouseevent));
    	}

    @Override
    public void mouseDragged(MouseEvent mouseevent) {
    	addEvent(new MochaDoomInputEvent(MochaDoomInputEvent.DragNotify,mouseevent));
    	}

    @Override
    public void mouseMoved(MouseEvent mouseevent) {
    	addEvent(new MochaDoomInputEvent(MochaDoomInputEvent.MotionNotify,mouseevent));
    	}

    //////////////// COMPONENT EVENTS //////////////////////////
    
    boolean we_are_moving=false;
        
	@Override
	public void componentHidden(ComponentEvent e) {
		// Do what, here? Pausing would be a good idea.
		
	}

	@Override
	public void componentMoved(ComponentEvent e) {
		eventQueue.add(new MochaDoomInputEvent(MochaDoomInputEvent.WindowMoving, null));
	}

	@Override
	public void componentResized(ComponentEvent e) {
		eventQueue.add(new MochaDoomInputEvent(MochaDoomInputEvent.ConfigureNotify, null));
		
	}

	@Override
	public void componentShown(ComponentEvent e) {
		eventQueue.add(new MochaDoomInputEvent(MochaDoomInputEvent.CreateNotify, null));
	}

	@Override
	public void windowGainedFocus(WindowEvent arg0) {
		eventQueue.add(new MochaDoomInputEvent(MochaDoomInputEvent.FocusGained, null));
	}


	@Override
	public void windowLostFocus(WindowEvent arg0) {
		eventQueue.add(new MochaDoomInputEvent(MochaDoomInputEvent.FocusLost, null));
	}


	public  boolean hasMoreEvents() {
		return !this.eventQueue.isEmpty();
	}


	@Override
	public boolean dispatchKeyEvent(KeyEvent e) {
		//if (e.getKeyCode() == KeyEvent.VK_TAB)
		//	eventQueue.add(new MochaDoomInputEvent(MochaDoomInputEvent.KeyRelease, e));
		return false;
	}
    
}
package awt;

import java.awt.Canvas;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.DisplayMode;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Image;
import java.awt.KeyEventDispatcher;
import java.awt.KeyboardFocusManager;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.util.StringTokenizer;

import javax.swing.JFrame;

import i.DoomVideoInterface;
import i.IDoomSystem;
import i.InputListener;
import i.Strings;
import timing.ITicker;
import v.DoomVideoRenderer;
import doom.DoomMain;
import doom.ICommandLineManager;
import doom.event_t;

/** Common code for Doom's video frames */

public abstract class DoomFrame<V> extends JFrame implements DoomVideoInterface<V> {

    protected V RAWSCREEN;
    // Normally only used in fullscreen mode
    
    /** This might differ from the raster's width & height attribute for number of reasons */
    protected Dimension size;
    protected DisplayMode oldDisplayMode;
    protected DisplayMode currentDisplayMode;
    protected GraphicsDevice device;
    protected int X_OFF;
    protected int Y_OFF;    
    
    public DoomFrame(DoomMain<?,V> DM,DoomVideoRenderer<?,V> V) {
        GraphicsEnvironment env = GraphicsEnvironment.
        getLocalGraphicsEnvironment();
        
        GraphicsDevice[] devices = env.getScreenDevices();
        
        // Get device 0, because we're lazy.
        if (devices!=null)
            if (devices.length>0)
                device=devices[0];
        
    	this.DM=DM;
    	this.CM=DM.CM;
    	this.TICK=DM.TICK;
    	this.I=DM.I;
    	this.V= V;
    	
        this.width=V.getWidth();
        this.height=V.getHeight();

    	
    	// Set those here. If fullscreen isn't used, then they won't change.
    	// They are necessary for normal initialization, though.
    	setDefaultDimension(width,height);

	}
    
    /** Default window size and center spot. These might change 
     * upon entering full screen, so don't consider them absolute. 
     * Due to letterboxing and screen doubling, stretching etc. 
     * they might be different that the screen buffer (typically,
     * larger).
     * 
     * @param width
     * @param height
     */
    
    private void setDefaultDimension(int width, int height){
        this.size=new Dimension(width*multiply,height*multiply);
        this.center=new Point (X_OFF+size.width/2, Y_OFF+size.height/2);
    }
    
	/**
	 * 
	 */
	private static final long serialVersionUID = -4130528877723831825L;

	

	protected static final boolean D=false;
	
	// Must be aware of "Doom" so we can pass it event messages inside a crude queue.
	public DoomMain<?,V> DM;            // Must be aware of general status.
	public ICommandLineManager CM; // Must be aware of command line interface.
	protected IDoomSystem I;         // Must be aware of some other shit like event handler
	protected DoomVideoRenderer<?,V> V;    // Must have a video renderer....
	protected ITicker TICK;          // Must be aware of the ticker/
	protected MochaEvents eventhandler; // Separate event handler a la _D_.
	                               // However I won't make it fully "eternity like" yet
	                               // also because it works quite flakey on Linux.
	

  	protected Robot robby;
	protected Canvas drawhere;
	protected Canvas gelatine;
	/** This is the actual screen */
    protected Image screen;
    protected int palette=0;
    //InputListener in;
    protected Graphics2D g2d;
    protected Graphics2D gel2d;
    
    protected Point center;
        
    /** Dimensions of the screen buffers. The display however, might differ due 
     * to e.g. letterboxing */
    protected int width,height;
    protected int multiply=1;
    
    // This stuff should NOT get through in keyboard events.
    protected final int UNACCEPTABLE_MODIFIERS=(int) (InputEvent.ALT_GRAPH_DOWN_MASK+
    										 InputEvent.META_DOWN_MASK+
    										 InputEvent.META_MASK+
    										 InputEvent.WINDOW_EVENT_MASK+
    										 InputEvent.WINDOW_FOCUS_EVENT_MASK);
    
    public String processEvents(){
        StringBuffer tmp=new StringBuffer();
        event_t event;
        while ( (event=InputListener.nextEvent()) != null ) {
            tmp.append(event.type.ordinal()+"\n");
        }
        return tmp.toString();
    }
    

    /**
     * I_SetPalette
     * 
     * Any bit-depth specific palette manipulation is performed by 
     * the VideoRenderer. It can range from simple (paintjob) to
     * complex (multiple BufferedImages with locked data bits...ugh!
     * 
     *@param palette index (normally between 0-14).
     */
	
	@Override
	public void SetPalette (int palette)
	{
		V.setPalette(palette);
		this.screen=V.getCurrentScreen();      
	}
	
	
	/** Call this before attempting to draw anything.
	 * This will create the window, the canvas and everything.
	 * Unlike a simple JFrame, this is not automatic because of the order
	 * Doom does things.
	 * 
	 */
	
	@Override
	public void InitGraphics()
	{

	  String		displayname;
	  String		d;
	  int			n;
	  int			pnum;
	  int			x=0;
	  int			y=0;
	  
	  // warning: char format, different type arg
	  int		xsign=' ';
	  int		ysign=' ';
	  
	  boolean			oktodraw;
	  long	attribmask;

	  // Try setting the locale the US, otherwise there will be problems
	  // with non-US keyboards.
	  if (this.getInputContext()==null || !this.getInputContext().selectInputMethod(java.util.Locale.US)){
		  System.err.println("Could not set the input context to US! Keyboard input will be glitchy!");
	  } else {
		  System.err.println("Input context successfully set to US.");
	  }

	  // check for command-line display name
	  if ( (pnum=CM.CheckParm("-disp"))!=0 ) // suggest parentheses around assignment
		displayname = CM.getArgv(pnum+1);
	  else
		displayname = null;

	  // check for command-line geometry
	  if ( (pnum=CM.CheckParm("-geom"))!=0 ) // suggest parentheses around assignment
	  {
		  try{
		  String eval=CM.getArgv(pnum+1).trim();
		// warning: char format, different type arg 3,5
		//n = sscanf(myargv[pnum+1], "%c%d%c%d", &xsign, &x, &ysign, &y);
		// OK, so we have to read a string that may contain
		// ' '/'+'/'-' and a number. Twice.
		StringTokenizer tk=new StringTokenizer(eval,"-+ ");
		// Signs. Consider positive.
		xsign=1;ysign=1;
		for (int i=0;i<eval.length();i++){
			if (eval.charAt(i)=='-'){
				// First '-' on trimmed string: negagive
				if (i==0)
					xsign=-1;
				else 
					ysign=-1;
				}
			}
		
		//this should parse two numbers.
		if (tk.countTokens()==2){
			x=xsign*Integer.parseInt(tk.nextToken());
			y=ysign*Integer.parseInt(tk.nextToken());
		}
		  

		  } catch (NumberFormatException e){
		    I.Error("bad -geom parameter");
		  }
	  }

	  // open the display
	  // AWT: create the canvas.
	  try{
      //drawhere=new Canvas();
      // MAES: this method works even on "stubborn" Linux distros that 
      // fuck up the window size.
	  setCanvasSize(size);
      
      this.eventhandler=new MochaEvents(DM,drawhere);
      
      // AWT: Add listeners to CANVAS element.
      // Maybe it should go to the gelatine component?
      drawhere.addKeyListener(eventhandler);
      drawhere.addMouseListener(eventhandler);
      drawhere.addMouseMotionListener(eventhandler);
      addComponentListener(eventhandler);
      addWindowFocusListener(eventhandler);
      addWindowListener(eventhandler);
      
      if (DM.VM.getSetting("fullscreen").getBoolean())
          switchToFullScreen();
      
      
	  } catch (Exception e){
		  I.Error("Error creating AWTDoom frame. Exiting. Reason: %s",e.getMessage());
	  }
	 
	  // AWT: tab is a special case :-/
	  // We need to "peg" it to the JFrame, rather than the canvas,
	  // and the handler itself cannot auto-assign it.
	  
      final Component me=drawhere;
	  
      KeyboardFocusManager.
      getCurrentKeyboardFocusManager().
      addKeyEventDispatcher(new KeyEventDispatcher() {
    	  
    	  boolean press=false;
          public boolean dispatchKeyEvent(KeyEvent e) {    
            if (e.getKeyCode() == KeyEvent.VK_TAB) {
            	// MAES: simulating a key type.
            	if (press)
                eventhandler.keyPressed(
                		new KeyEvent(me, e.getID(), System.nanoTime(),0 , KeyEvent.VK_TAB, KeyEvent.CHAR_UNDEFINED));
            	else
                    eventhandler.keyReleased(
                    		new KeyEvent(me, e.getID(), System.nanoTime(),0 , KeyEvent.VK_TAB, KeyEvent.CHAR_UNDEFINED));
            	press=!press;
            }  
            return false;
          }
      });
	  
	  this.add(drawhere);
	 // this.add(gelatine);
	  this.getContentPane().setPreferredSize(drawhere.getPreferredSize());
	  
	  // JFrame's size is auto-set here.
	  this.pack();
	  this.setVisible(true);
      this.setResizable(false);
	  this.setTitle(Strings.MOCHA_DOOM_TITLE);
	  this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
	  // Gently tell the eventhandler to wake up and set itself.	  
	  this.requestFocus();
	  this.eventhandler.addEvent(MochaDoomInputEvent.GET_YOUR_ASS_OFF);
	  SetGamma(0); 
	  
	}
	
    private void setCanvasSize(Dimension size) {
        
        drawhere.setPreferredSize(size);
        drawhere.setBounds(0, 0, drawhere.getWidth()-1,drawhere.getHeight()-1);
        drawhere.setBackground(Color.black);

        gelatine.setPreferredSize(size);
        gelatine.setBounds(0, 0, drawhere.getWidth()-1,drawhere.getHeight()-1);
        gelatine.setBackground(Color.black);
        
    }


    /** FULLSCREEN SWITCH CODE
     * TODO: it's not enough to do this without also switching the screen's resolution.
     * Unfortunately, Java only has a handful of options which depend on the OS, driver,
     * display, JVM etc. and it's not possible to switch to arbitrary resolutions. 
     * 
     * Therefore, a "best fit" strategy with centering is used.
     * 
     */
	
	private void switchToFullScreen() {
	    boolean isFullScreen = device.isFullScreenSupported();
	      setUndecorated(isFullScreen);
	      setResizable(!isFullScreen);
	      
	      // In case we need to revert.
	      oldDisplayMode 
	      = device.getDisplayMode();
	      
	      DisplayModePicker dmp=new DisplayModePicker(device);
	      
	      // TODO: what if bit depths are too small?
	      DisplayMode dm=dmp.pickClosest(width,height);
	      
	      int[] xy=dmp.getCentering(width,height, dm);
	      this.X_OFF=xy[0];
	      this.Y_OFF=xy[1];
	      
	      device.getDisplayModes(); 
	      
	      if (isFullScreen) {
	          // Full-screen mode
	          device.setFullScreenWindow(this);
	          if (device.isDisplayChangeSupported())
	              device.setDisplayMode(dm);
	          validate();
	          
	          Dimension newsize=new Dimension(dm.getWidth(),dm.getHeight());
	          this.setDefaultDimension(dm.getWidth(),dm.getHeight());
	          setCanvasSize(newsize);
	          
	      } else {
	          // Windowed mode
	          pack();
	          setVisible(true);
	      }
        
    }


    @Override
	public void StartFrame() {
		// Dummy. Nothing special to do...yet.
		
	}

	@Override
	public void StartTic() {

		  if (!this.isActive()) return;

		//  System.out.println("Getting events...");
		  while (eventhandler.hasMoreEvents())
			eventhandler.GetEvent();
		      
		    //eventhandler.grabMouse();

		
	}

	protected int lasttic;
	protected int frames;

    @Override
    public void UpdateNoBlit() {
        // Quite pointless, no?
        
    }

    // Convenience, for testers.
	public void GetEvent() {
		this.eventhandler.GetEvent();		
	}

	@Override
	public void ShutdownGraphics() {
		this.dispose();
		((JFrame)this).dispose();
		
	}

	   
    public void SetGamma(int level){
        if (D) System.err.println("Setting gamma "+level);
        V.setUsegamma(level);
        screen=V.getCurrentScreen(); // Refresh screen after change.
        RAWSCREEN=V.getScreen(DoomVideoRenderer.SCREEN_FG);
    }
    
    
    /** Modified update method: no context needs to passed.
     *  Will render only internal screens. Common between AWT 
     *  and Swing
     * 
     */
    public void paint(Graphics g) {
       // Techdemo v1.3: Mac OSX fix, compatible with Windows and Linux.
       // Should probably run just once. Overhead is minimal
       // compared to actually DRAWING the stuff.
       if (g2d==null) g2d = (Graphics2D)drawhere.getGraphics();
       //if (gel2d==null) gel2d= (Graphics2D)gelatine.getGraphics();
       V.update();
       //voli.getGraphics().drawImage(bi,0,0,null);
       g2d.drawImage(screen,X_OFF,Y_OFF,this);
       //gel2d.setColor(new Color(.3f, .4f, .5f, .1f));
       //gel2d.fillRect(0, 0, this.getWidth(), this.getHeight());
       
    }

}

package awt;

import java.awt.AWTEvent;

/** The older system was just a miserable pile of fuck.
 *  This system clearly codifies everything we need to be aware
 *  of, namely keypresses, mousebuttonpresses, window state
 *  changes, etc.
 *  
 *  Based on Jake2's JakeInputEvent.
 *   
 * @author velktron
 *
 */

public class MochaDoomInputEvent {
		static final int KeyPress = 0;
		static final int KeyRelease = 1;
		static final int KeyType = 2;
		static final int MotionNotify = 3;
		static final int DragNotify = 4;
		static final int ButtonPress = 5;
		static final int ButtonRelease = 6;
		static final int CreateNotify = 7;
		static final int ConfigureNotify = 8;
		static final int WheelMoved = 9;
		static final int MouseExited =10;
		static final int MouseEntered=11;
		static final int WindowMoving=12;
		static final int FocusGained=13;
		static final int FocusLost=14;
		static final int LockOn=15;
		static final int LockOff=16;
		static final int KEY_MASK=0X100; // Extract info from lower bits for this
		
		int type;
		int value;
		AWTEvent ev;
		
		MochaDoomInputEvent(int type, AWTEvent ev) {
			this.type = type;
			this.ev = ev;
		}
		
		MochaDoomInputEvent(int type, int value) {
			this.type=type;
			this.value=value;
			}

		/** Just a friendly way to remind the child component to
		 *  position and initialize itself correctly for the first
		 *  use.
		 */
		
		static MochaDoomInputEvent GET_YOUR_ASS_OFF = new
		MochaDoomInputEvent(ConfigureNotify, null);
	}

package hu;

// Emacs style mode select -*- C++ -*-
// -----------------------------------------------------------------------------
//
// $Id: HU.java,v 1.32 2012/09/24 17:16:23 velktron Exp $
//
// Copyright (C) 1993-1996 by id Software, Inc.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// DESCRIPTION: Heads-up displays
//
// -----------------------------------------------------------------------------

import static data.Defines.*;
import static g.Keys.*;

import defines.*;
import static data.Limits.*;
import static doom.englsh.*;
import i.DoomStatusAware;
import utils.C2JUtils;
import v.DoomVideoRenderer;
import v.IVideoScale;
import v.IVideoScaleAware;
import m.IDoomMenu;
import m.Menu;
import rr.RendererState;
import rr.patch_t;
import s.IDoomSound;
import w.IWadLoader;
import data.sounds.sfxenum_t;
import doom.DoomMain;
import doom.DoomStatus;
import doom.event_t;
import doom.evtype_t;
import doom.player_t;


public class HU implements DoomStatusAware, IVideoScaleAware, IHeadsUp{
    public final static String rcsid =
        "$Id: HU.java,v 1.32 2012/09/24 17:16:23 velktron Exp $";

    // MAES: Status and wad data.
    IWadLoader W;

    DoomMain<?,?> DM;

    IDoomMenu M;

    RendererState<?,?> R;
    
    DoomVideoRenderer<?,?> V;

    IDoomSound S;
    //
    // Locally used constants, shortcuts.
    // MAES: Some depend on STATE, so moved into constructor.

    String HU_TITLE, HU_TITLE2, HU_TITLEP, HU_TITLET;

    protected final static int HU_TITLEHEIGHT = 1;

    protected final static int HU_TITLEX = 0;

    protected int HU_TITLEY;// = (167 - Swap.SHORT(hu_font[0].height));

    protected final static char HU_INPUTTOGGLE = 't';

    protected final static int HU_INPUTX = HU_MSGX;

    protected int HU_INPUTY;// = (HU_MSGY +

    // HU_MSGHEIGHT*(Swap.SHORT(hu_font[0].height) +1));

    protected final static int HU_INPUTWIDTH = 64;

    protected final static int HU_INPUTHEIGHT = 1;

    public String[] chat_macros =
        { HUSTR_CHATMACRO0, HUSTR_CHATMACRO1, HUSTR_CHATMACRO2,
                HUSTR_CHATMACRO3, HUSTR_CHATMACRO4, HUSTR_CHATMACRO5,
                HUSTR_CHATMACRO6, HUSTR_CHATMACRO7, HUSTR_CHATMACRO8,
                HUSTR_CHATMACRO9 };
    
    @Override
    public void setChatMacro(int i, String s){
        this.chat_macros[i]=s;
    }

    /** Needs to be seen by DoomGame */
    public final static String[] player_names =
        { HUSTR_PLRGREEN, HUSTR_PLRINDIGO, HUSTR_PLRBROWN, HUSTR_PLRRED };

    char chat_char; // remove later.

    player_t plr;

    // MAES: a whole lot of "static" stuff which really would be HU instance
    // status.
    patch_t[] hu_font = new patch_t[HU_FONTSIZE];

    char[] chat_dest = new char[MAXPLAYERS];

    // MAES: these used to be defined in hu_lib. We're going 100% OO here...

    hu_itext_t[] w_inputbuffer;

    hu_textline_t w_title ;

    hu_itext_t w_chat;

    boolean[] always_off = { false };

    // Needs to be referenced by one of the widgets.
    public boolean[] chat_on = new boolean[1];

    // MAES: Ugly hack which allows it to be passed as reference. Sieg heil!
    boolean[] message_on = new boolean[]{true};

    boolean message_dontfuckwithme;

    boolean message_nottobefuckedwith;

    hu_stext_t w_message;

    int message_counter;

    // This is actually an "extern" pointing inside m_menu (Menu.java). So we
    // need to share Menu context.
    // int showMessages;
    // MAES: I think this is supposed to be visible by the various hu_ crap...
   // boolean automapactive;

    boolean headsupactive = false;

    //
    // Builtin map names.
    // The actual names can be found in DStrings.h.
    //

    protected String[] mapnames = // DOOM shareware/registered/retail (Ultimate)
        // names.
        {

        HUSTR_E1M1, HUSTR_E1M2, HUSTR_E1M3, HUSTR_E1M4, HUSTR_E1M5, HUSTR_E1M6,
                HUSTR_E1M7, HUSTR_E1M8, HUSTR_E1M9,

                HUSTR_E2M1, HUSTR_E2M2, HUSTR_E2M3, HUSTR_E2M4, HUSTR_E2M5,
                HUSTR_E2M6, HUSTR_E2M7, HUSTR_E2M8, HUSTR_E2M9,

                HUSTR_E3M1, HUSTR_E3M2, HUSTR_E3M3, HUSTR_E3M4, HUSTR_E3M5,
                HUSTR_E3M6, HUSTR_E3M7, HUSTR_E3M8, HUSTR_E3M9,

                HUSTR_E4M1, HUSTR_E4M2, HUSTR_E4M3, HUSTR_E4M4, HUSTR_E4M5,
                HUSTR_E4M6, HUSTR_E4M7, HUSTR_E4M8, HUSTR_E4M9,

                "NEWLEVEL", "NEWLEVEL", "NEWLEVEL", "NEWLEVEL", "NEWLEVEL",
                "NEWLEVEL", "NEWLEVEL", "NEWLEVEL", "NEWLEVEL" };

    protected String[] mapnames2 = // DOOM 2 map names.
        { HUSTR_1, HUSTR_2, HUSTR_3, HUSTR_4, HUSTR_5, HUSTR_6, HUSTR_7,
                HUSTR_8, HUSTR_9, HUSTR_10, HUSTR_11,

                HUSTR_12, HUSTR_13, HUSTR_14, HUSTR_15, HUSTR_16, HUSTR_17,
                HUSTR_18, HUSTR_19, HUSTR_20,

                HUSTR_21, HUSTR_22, HUSTR_23, HUSTR_24, HUSTR_25, HUSTR_26,
                HUSTR_27, HUSTR_28, HUSTR_29, HUSTR_30, HUSTR_31, HUSTR_32,HUSTR_33 };

    protected String[] mapnamesp = // Plutonia WAD map names.
        { PHUSTR_1, PHUSTR_2, PHUSTR_3, PHUSTR_4, PHUSTR_5, PHUSTR_6, PHUSTR_7,
                PHUSTR_8, PHUSTR_9, PHUSTR_10, PHUSTR_11,

                PHUSTR_12, PHUSTR_13, PHUSTR_14, PHUSTR_15, PHUSTR_16,
                PHUSTR_17, PHUSTR_18, PHUSTR_19, PHUSTR_20,

                PHUSTR_21, PHUSTR_22, PHUSTR_23, PHUSTR_24, PHUSTR_25,
                PHUSTR_26, PHUSTR_27, PHUSTR_28, PHUSTR_29, PHUSTR_30,
                PHUSTR_31, PHUSTR_32 };

    protected String[] mapnamest = // TNT WAD map names.
        { THUSTR_1, THUSTR_2, THUSTR_3, THUSTR_4, THUSTR_5, THUSTR_6, THUSTR_7,
                THUSTR_8, THUSTR_9, THUSTR_10, THUSTR_11,

                THUSTR_12, THUSTR_13, THUSTR_14, THUSTR_15, THUSTR_16,
                THUSTR_17, THUSTR_18, THUSTR_19, THUSTR_20,

                THUSTR_21, THUSTR_22, THUSTR_23, THUSTR_24, THUSTR_25,
                THUSTR_26, THUSTR_27, THUSTR_28, THUSTR_29, THUSTR_30,
                THUSTR_31, THUSTR_32 };

    char[] shiftxform;

    public static final char[] french_shiftxform =
        {
                0,
                1,
                2,
                3,
                4,
                5,
                6,
                7,
                8,
                9,
                10,
                11,
                12,
                13,
                14,
                15,
                16,
                17,
                18,
                19,
                20,
                21,
                22,
                23,
                24,
                25,
                26,
                27,
                28,
                29,
                30,
                31,
                ' ',
                '!',
                '"',
                '#',
                '$',
                '%',
                '&',
                '"', // shift-'
                '(',
                ')',
                '*',
                '+',
                '?', // shift-,
                '_', // shift--
                '>', // shift-.
                '?', // shift-/
                '0', // shift-0
                '1', // shift-1
                '2', // shift-2
                '3', // shift-3
                '4', // shift-4
                '5', // shift-5
                '6', // shift-6
                '7', // shift-7
                '8', // shift-8
                '9', // shift-9
                '/',
                '.', // shift-;
                '<',
                '+', // shift-=
                '>', '?', '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I',
                'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U',
                'V', 'W',
                'X',
                'Y',
                'Z',
                '[', // shift-[
                '!', // shift-backslash - OH MY GOD DOES WATCOM SUCK
                ']', // shift-]
                '"',
                '_',
                '\'', // shift-`
                'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',
                'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
                'Y', 'Z', '{', '|', '}', '~', 127

        };

    public static final char[] english_shiftxform =
        {
                0,
                1,
                2,
                3,
                4,
                5,
                6,
                7,
                8,
                9,
                10,
                11,
                12,
                13,
                14,
                15,
                16,
                17,
                18,
                19,
                20,
                21,
                22,
                23,
                24,
                25,
                26,
                27,
                28,
                29,
                30,
                31,
                ' ',
                '!',
                '"',
                '#',
                '$',
                '%',
                '&',
                '"', // shift-'
                '(',
                ')',
                '*',
                '+',
                '<', // shift-,
                '_', // shift--
                '>', // shift-.
                '?', // shift-/
                ')', // shift-0
                '!', // shift-1
                '@', // shift-2
                '#', // shift-3
                '$', // shift-4
                '%', // shift-5
                '^', // shift-6
                '&', // shift-7
                '*', // shift-8
                '(', // shift-9
                ':',
                ':', // shift-;
                '<',
                '+', // shift-=
                '>', '?', '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I',
                'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U',
                'V', 'W',
                'X',
                'Y',
                'Z',
                '[', // shift-[
                '!', // shift-backslash - OH MY GOD DOES WATCOM SUCK
                ']', // shift-]
                '"',
                '_',
                '\'', // shift-`
                'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',
                'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
                'Y', 'Z', '{', '|', '}', '~', 127 };

    // Maes: char?
    char[] frenchKeyMap =
        { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
                20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, ' ', '!', '"',
                '#', '$', '%', '&', '%', '(', ')', '*', '+', ';', '-', ':',
                '!', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':',
                'M', '<', '=', '>', '?', '@', 'Q', 'B', 'C', 'D', 'E', 'F',
                'G', 'H', 'I', 'J', 'K', 'L', ',', 'N', 'O', 'P', 'A', 'R',
                'S', 'T', 'U', 'V', 'Z', 'X', 'Y', 'W', '^', '\\', '$', '^',
                '_', '@', 'Q', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
                'K', 'L', ',', 'N', 'O', 'P', 'A', 'R', 'S', 'T', 'U', 'V',
                'Z', 'X', 'Y', 'W', '^', '\\', '$', '^', 127 };

    protected final char ForeignTranslation(char ch) {
        return ch < 128 ? frenchKeyMap[ch] : ch;
    }

    public HU(DoomStatus DM) {
    	this.updateStatus(DM);
    	
    	this.w_message=new hu_stext_t();

    	this.w_inputbuffer=new hu_itext_t[MAXPLAYERS];
    	for (int i=0;i<MAXPLAYERS;i++){
    		this.w_inputbuffer[i]=new hu_itext_t();
    	}
    	this.w_title=new hu_textline_t();
    	this.w_chat=new hu_itext_t();
    }

    
    /** Used only for testing */
    public HU() {

    }

    /**
     * Loads a bunch of STCFNx fonts from WAD, and sets some of the remaining
     * constants.
     * 
     * @throws Exception
     */

    @Override
    public void Init()
             {
        String xxx = new String("STCFN%03d");
        int i;
        int j;
        String buffer;

        if (DM.language == Language_t.french)
            shiftxform = french_shiftxform;
        else
            shiftxform = english_shiftxform;

        // load the heads-up font
        j = HU_FONTSTART;

        // So it basically loads a bunch of patch_t's from memory.
        C2JUtils.initArrayOfObjects(hu_font, patch_t.class);

        for (i = 0; i < HU_FONTSIZE; i++) {
            buffer = String.format(xxx,j++);
            // hu_font[i] = ((patch_t[]) wd.CacheLumpName(buffer, PU_STATIC);
            hu_font[i] = (W.CachePatchName(buffer, PU_STATIC));
        }

        // MAES: Doom's SC had a really fucked up endianness change for height.
        // I don't really see the point in that, as in the WAD patches appear
        // to be all Little Endian... mystery :-S
        // HU_TITLEY = (167 - Swap.SHORT(hu_font[0].height));
        HU_TITLEY = (167 - hu_font[0].height);
        HU_INPUTY = (HU_MSGY + HU_MSGHEIGHT * hu_font[0].height + 1);

    }
    
    @Override
    public void Stop() {
        headsupactive = false;
    }

    @Override
    public void Start() {

        int i;
        String s;
        
        // MAES: fugly hax. These were compile-time inlines,
        // so they can either work as functions, or be set whenever the HU is started
        // (typically once per level). They need to be aware of game progress,
        // and episode numbers <1 will cause it to bomb.
        // MAES: hack to handle Betray in XBLA 31/5/2011
        if ((DM.gamemap>32) && (DM.getGameMode()==GameMode_t.pack_xbla)){
        this.HU_TITLE = mapnames[(DM.gameepisode - 1) * 9 + DM.gamemap - 2];


        this.HU_TITLE2 = mapnames2[DM.gamemap - 1];
        this.HU_TITLEP = mapnamesp[DM.gamemap - 2]; // fixed from HU_TITLEPw
        this.HU_TITLET = mapnamest[DM.gamemap - 2];
        } else {
            this.HU_TITLE = mapnames[(DM.gameepisode - 1) * 9 + DM.gamemap - 1];
            this.HU_TITLE2 = mapnames2[DM.gamemap - 1];
            this.HU_TITLEP = mapnamesp[DM.gamemap - 1]; // fixed from HU_TITLEP
            this.HU_TITLET = mapnamest[DM.gamemap - 1];
        }

        if (headsupactive)
            this.Stop();

        plr = DM.players[DM.consoleplayer];
        message_on[0] = false;
        message_dontfuckwithme = false;
        message_nottobefuckedwith = false;
        chat_on[0] = false;

        // create the message widget
        this.w_message.initSText(HU_MSGX, HU_MSGY, HU_MSGHEIGHT, hu_font,
            HU_FONTSTART, this.message_on);

        // create the map title widget
        this.w_title.initTextLine(HU_TITLEX, HU_TITLEY, hu_font, HU_FONTSTART);

        switch (DM.getGameMode()) {
        case shareware:
        case registered:
        case retail:
            s = HU_TITLE;
            break;

       case pack_plut: s = HU_TITLEP; 
           break; 
       case pack_tnt: s = HU_TITLET;
       break;
       
        case commercial:
        default:
            s = HU_TITLE2;
            break;
        }

        // MAES: oh great, more pointer-char magic... oh no you don't, you ugly
        // cow horse and reindeer lover.

        // while (*s) this.w_title.addCharToTextLine(*(s++));
        int ptr=0;
        while(ptr<s.length()){
        this.w_title.addCharToTextLine(s.charAt(ptr++));
        }
        // create the chat widget
        this.w_chat.initIText(HU_INPUTX, HU_INPUTY, hu_font, HU_FONTSTART,
            chat_on);

        // create the inputbuffer widgets
        for (i = 0; i < MAXPLAYERS; i++) {
            w_inputbuffer[i] = new hu_itext_t();
            w_inputbuffer[i].initIText(0, 0, null, 0, always_off);
        }
        headsupactive = true;

    }

    @Override
    public void Drawer() {
        this.w_message.drawSText();
        this.w_chat.drawIText();
        if (DM.automapactive)
            this.w_title.drawTextLine(false);
    }

    @Override
    public void Erase() {
        this.w_message.eraseSText();
        this.w_chat.eraseIText();
        this.w_title.eraseTextLine();
    }

    @Override
    public void Ticker() {

        int i;
        boolean rc;
        char c;

        // tick down message counter if message is up
        if ((message_counter != 0) && !((--message_counter) != 0)) {
            message_on[0] = false;
            message_nottobefuckedwith = false;
        }

        if (M.getShowMessages() || message_dontfuckwithme) {

            // display message if necessary
            if (((plr.message != null) && !message_nottobefuckedwith)
                    || ((plr.message != null) && message_dontfuckwithme)) {
                this.w_message.addMessageToSText(null, plr.message);
                plr.message = null;
                message_on[0] = true;
                message_counter = HU_MSGTIMEOUT;
                message_nottobefuckedwith = message_dontfuckwithme;
                message_dontfuckwithme = false;
            }

        } // else message_on = false;

        // check for incoming chat characters
        if (DM.netgame) {
            for (i = 0; i < MAXPLAYERS; i++) {
                if (!DM.playeringame[i])
                    continue;
                if ((i != DM.consoleplayer)
                        && ((c = DM.players[i].cmd.chatchar) != 0)) {
                    if (c <= HU_BROADCAST)
                        chat_dest[i] = c;
                    else {
                        if (c >= 'a' && c <= 'z')
                            c = (char) shiftxform[c];
                        rc = w_inputbuffer[i].keyInIText(c);
                        if (rc && c == KEY_ENTER) {
                            if ((w_inputbuffer[i].l.len != 0)
                                    && (chat_dest[i] == DM.consoleplayer + 1)
                                    || (chat_dest[i] == HU_BROADCAST)) {
                                w_message.addMessageToSText(player_names[i]
                                        , w_inputbuffer[i].l.text.toString());

                                message_nottobefuckedwith = true;
                                message_on[0] = true;
                                message_counter = HU_MSGTIMEOUT;
                                if (DM.isCommercial())
                                    S.StartSound(null, sfxenum_t.sfx_radio);
                                    
                                else
                                    S.StartSound(null, sfxenum_t.sfx_tink);
                                    
                            }
                            w_inputbuffer[i].resetIText();
                        }
                    }
                    DM.players[i].cmd.chatchar = 0;
                }
            }
        }

    }

    protected final int QUEUESIZE = 128;

    protected char[] chatchars = new char[QUEUESIZE];

    protected int head = 0;

    protected int tail = 0;

    protected void queueChatChar(char c) {
        if (((head + 1) & (QUEUESIZE - 1)) == tail) {
            plr.message = HUSTR_MSGU;
        } else {
            chatchars[head] = c;
            head = (head + 1) & (QUEUESIZE - 1);
        }
    }

    @Override
    public char dequeueChatChar() {
        char c;

        if (head != tail) {
            c = chatchars[tail];
            tail = (tail + 1) & (QUEUESIZE - 1);
        } else {
            c = 0;
        }

        return c;
    }

    // MAES: These were "static" inside HU_Responder, since they were meant to
    // represent state.
    protected StringBuilder lastmessage = new StringBuilder(HU_MAXLINELENGTH + 1);

    // protected char[] lastmessage=new char[HU_MAXLINELENGTH+1];
    protected boolean shiftdown = false;

    protected boolean altdown = false;

    protected char[] destination_keys =
        { HUSTR_KEYGREEN, HUSTR_KEYINDIGO, HUSTR_KEYBROWN, HUSTR_KEYRED };

    protected int num_nobrainers = 0;

    @Override
    public boolean Responder(event_t ev) {

    	//System.out.println("Player "+DM.players[0].mo.x);
        char[] macromessage;
        boolean eatkey = false;

        char c;
        int i;
        int numplayers;

        numplayers = 0;
        // MAES: Adding BOOLEANS to ints, are we ?!
        for (i = 0; i < MAXPLAYERS; i++) {
            numplayers += (DM.playeringame[i]) ? 1 : 0;
        }

        if (ev.data1 == KEY_SHIFT) {
            shiftdown = (ev.type == evtype_t.ev_keydown);
            return false;
        } else if (ev.data1 == KEY_ALT || ev.data1 == KEY_ALT) {
            altdown = (ev.type == evtype_t.ev_keydown);
            return false;
        }

        if (ev.type != evtype_t.ev_keydown)
            return false;

        if (!chat_on[0]) {
            if (ev.data1 == HU_MSGREFRESH) {
                message_on[0] = true;
                message_counter = HU_MSGTIMEOUT;
                eatkey = true;
            } else if (DM.netgame && ev.data1 == HU_INPUTTOGGLE) {
                eatkey = chat_on[0] = true;
                w_chat.resetIText();
                this.queueChatChar(HU_BROADCAST);
            } else if (DM.netgame && numplayers > 2) {
                for (i = 0; i < MAXPLAYERS; i++) {
                    if (ev.data1 == destination_keys[i]) {
                        if (DM.playeringame[i] && i != DM.consoleplayer) {
                            eatkey = chat_on[0] = true;
                            w_chat.resetIText();
                            this.queueChatChar((char) (i + 1));
                            break;
                        } else if (i == DM.consoleplayer) {
                            num_nobrainers++;
                            if (num_nobrainers < 3)
                                plr.message = HUSTR_TALKTOSELF1;
                            else if (num_nobrainers < 6)
                                plr.message = HUSTR_TALKTOSELF2;
                            else if (num_nobrainers < 9)
                                plr.message = HUSTR_TALKTOSELF3;
                            else if (num_nobrainers < 32)
                                plr.message = HUSTR_TALKTOSELF4;
                            else
                                plr.message = HUSTR_TALKTOSELF5;
                        }
                    }
                }
            }
        } else {
            c = (char) ev.data1;
            // send a macro
            if (altdown) {
                c = (char) (c - '0');
                if (c > 9)
                    return false;
                // fprintf(stderr, "got here\n");
                macromessage = chat_macros[c].toCharArray();

                // kill last message with a '\n'
                this.queueChatChar(KEY_ENTER); // DEBUG!!!

                // send the macro message
                int index = 0;
                while (macromessage[index] != 0) {
                    this.queueChatChar(macromessage[index]);
                }
                this.queueChatChar(KEY_ENTER);

                // leave chat mode and notify that it was sent
                chat_on[0] = false;
                lastmessage.setLength(0);
                lastmessage.append(chat_macros[c]);
                plr.message = lastmessage.toString();
                eatkey = true;
            } else {
                if (DM.language == Language_t.french)
                    c = ForeignTranslation(c);
                if (shiftdown || (c >= 'a' && c <= 'z'))
                    c = shiftxform[c];
                eatkey = w_chat.keyInIText(c);
                if (eatkey) {
                    // static unsigned char buf[20]; // DEBUG
                    this.queueChatChar(c);

                    // sprintf(buf, "KEY: %d => %d", ev->data1, c);
                    // plr->message = buf;
                }
                if (c == KEY_ENTER) {
                    chat_on[0] = false;
                    if ((w_chat.l.len != 0)) {
                        lastmessage.setLength(0);
                        lastmessage.append( w_chat.l.text);
                        plr.message = new String(lastmessage);
                    }
                } else if (c == KEY_ESCAPE)
                    chat_on[0] = false;
            }
        }

        return eatkey;

    }

    // ///////////////////////////////// STRUCTS
    // ///////////////////////////////////

    /**
     *  Input Text Line widget
     *  (child of Text Line widget)
     */
    
    class hu_itext_t {


        hu_textline_t l; // text line to input on

        // left margin past which I am not to delete characters
        int lm;

        // pointer to boolean stating whether to update window
        boolean[] on;

        boolean laston; // last value of *->on;

        public hu_itext_t(){
        	
        }
        
        public void initIText(int x, int y, patch_t[] font, int startchar,
                boolean[] on) {
            this.lm = 0; // default left margin is start of text
            this.on = on;
            this.laston = true;
            l = new hu_textline_t(x, y, font, startchar);
        }

        // The following deletion routines adhere to the left margin restriction
        public void delCharFromIText() {
            if (this.l.len != this.lm)
                this.l.delCharFromTextLine();
        }

        public void eraseLineFromIText() {
            while (this.lm != this.l.len)
                l.delCharFromTextLine();
        }

        // Resets left margin as well
        public void resetIText() {
            this.lm = 0;
            this.l.clearTextLine();
        }

        public void addPrefixToIText(char[] str) {
            int ptr = 0;
            while (str[ptr] > 0) {
                l.addCharToTextLine(str[ptr++]);
                this.lm = this.l.len;
            }
        }

        // Maes: String overload
        public void addPrefixToIText(String str) {
            int ptr = 0;
            while (str.charAt(ptr) > 0) {
                l.addCharToTextLine(str.charAt(ptr++));
                this.lm = this.l.len;
            }
        }

        // wrapper function for handling general keyed input.
        // returns true if it ate the key
        public boolean keyInIText(char ch) {

            if (ch >= ' ' && ch <= '_')
                this.l.addCharToTextLine((char) ch);
            else if (ch == KEY_BACKSPACE)
                this.delCharFromIText();
            else if (ch != KEY_ENTER)
                return false; // did not eat key

            return true; // ate the key

        }

        public void drawIText() {

            if (!this.on[0])
                return;
            this.l.drawTextLine(true); // draw the line w/ cursor

        }

        void eraseIText() {
            if (this.laston && !this.on[0])
                this.l.needsupdate = 4;
            this.l.eraseTextLine();
            this.laston = this.on[0];
        }

    }

    /** Scrolling Text window widget
     *  (child of Text Line widget)
     */  

    class hu_stext_t {

        hu_textline_t[] lines = new hu_textline_t[HU_MAXLINES]; // text lines to draw

        int height; // height in lines

        int currline; // current line number

        // pointer to boolean stating whether to update window
        boolean[] on;

        boolean laston; // last value of *->on.

        public hu_stext_t(){
        	
        }
        
        public hu_stext_t(int x, int y, int h, patch_t[] font, int startchar,
                boolean[] on) {
            this.initSText(x, y, h, font, startchar, on);
        }

        public void initSText(int x, int y, int h, patch_t[] font,
                int startchar, boolean[] on) {

        	for (int i=0;i<HU_MAXLINES;i++){
        		this.lines[i]=new hu_textline_t();
        	}
            this.height = h;
            this.on = on;
            this.laston = true;
            this.currline = 0;
            for (int i = 0; i < h; i++)
                this.lines[i].initTextLine(x, y - i
                        * (font[0].height + 1), font, startchar);

        }

        public void addLineToSText() {

            // add a clear line
            if (++this.currline == this.height)
                this.currline = 0;
            this.lines[this.currline].clearTextLine();

            // everything needs updating
            for (int i = 0; i < this.height; i++)
                this.lines[i].needsupdate = 4;

        }

        public void addMessageToSText(char[] prefix, char[] msg) {
            this.addLineToSText();
            int ptr = 0;
            if ((prefix != null) && (prefix.length > 0)) {
                
                while ((ptr < prefix.length) && (prefix[ptr] > 0))
                    this.lines[this.currline].addCharToTextLine(prefix[ptr++]);
                }
            
                ptr = 0;
                while ((ptr < msg.length) && (msg[ptr] > 0))
                    this.lines[this.currline].addCharToTextLine(msg[ptr++]);
            }

        public void addMessageToSText(String prefix, String msg) {
            this.addLineToSText();
            if ((prefix != null) && (prefix.length() > 0)) {
                for (int i = 0; i < prefix.length(); i++)
                    this.lines[this.currline].addCharToTextLine(prefix.charAt(i));
            }
                for (int i = 0; i < msg.length(); i++)
                    this.lines[this.currline].addCharToTextLine(msg.charAt(i));
        }

        public void drawSText() {
            int i, idx;
            hu_textline_t l;

            if (!this.on[0])
                return; // if not on, don't draw

            
            
            // draw everything
            for (i = 0; i < this.height; i++) {                
                idx = this.currline - i;
                if (idx < 0)
                    idx += this.height; // handle queue of lines

                l = this.lines[idx];

                // need a decision made here on whether to skip the draw
                l.drawTextLine(false); // no cursor, please
            }

        }

        public void eraseSText() {
            for (int i = 0; i < this.height; i++) {
                if (laston && !on[0])
                    lines[i].needsupdate = 4;
                this.lines[i].eraseTextLine();
            }
            laston = on[0];

        }

        /**
         * MAES: this was the only variable in HUlib.c, and only instances of
         * hu_textline_t ever use it. For this reason, it makes sense to have it
         * common (?) between all instances of hu_textline_t and set it
         * somewhere else. Of course, if could be made an instance method or a
         * HUlib object could be defined.
         */
        protected boolean automapactive; // in AM_map.c

        public boolean isAutomapactive() {
            return automapactive;
        }

        public void setAutomapactive(boolean automapactive) {
            this.automapactive = automapactive;
        }

        /**
         * Same here.
         */

        // TODO: boolean : whether the screen is always erased
        protected boolean noterased; // =viewwindowx;

        public boolean isNoterased() {
            return noterased;
        }

        public void setNoterased(boolean noterased) {
            this.noterased = noterased;
        }
        
        StringBuilder sb=new StringBuilder();
        
        public String toString(){
            sb.setLength(0);
            sb.append(this.lines[0].text);
            sb.append(this.lines[1].text);
            sb.append(this.lines[2].text);
            sb.append(this.lines[3].text);
            return sb.toString();
        }

    }

    // Text Line widget
    // (parent of Scrolling Text and Input Text widgets)

    class hu_textline_t {

        // left-justified position of scrolling text window
        int x;

        int y;

        // MAES: was **
        patch_t[] f; // font

        int sc; // start character

        char[] text = new char[HU_MAXLINELENGTH+1]; // line of text

        int len; // current line length

        // whether this line needs to be udpated
        int needsupdate;

        public hu_textline_t(){
        	
        }
        
        public void clearTextLine() {
            this.len = 0;
            C2JUtils.memset(this.text, (char)0,this.text.length);
            // It's actually used as a status, go figure.
            this.needsupdate = 1;
        }

        // Maes: this could as well be the contructor

        public void initTextLine(int x, int y, patch_t[] f, int sc) {
            this.x = x;
            this.y = y;
            this.f = f;
            this.sc = sc;
            this.clearTextLine();
        }

        public hu_textline_t(int x, int y, patch_t[] f, int sc) {
            this.x = x;
            this.y = y;
            this.f = f;
            this.sc = sc;
            this.clearTextLine();
        }

        public boolean addCharToTextLine(char ch) {

            if (len == HU_MAXLINELENGTH)
                return false;
            else {
                this.text[len++]=ch;
                this.text[len]=(char)0;
                // this.l[this.len] = 0;
                // MAES: for some reason this is set as "4", so this is a status
                // rather than a boolean.
                this.needsupdate = 4;
                return true;
            }

        }

        /**
         * MAES: This is much better than cluttering up the syntax everytime a
         * STRING must be added.
         * 
         * @param s
         * @return
         */
/*
        public boolean addStringToTextLine(String s) {
            int index = 0;
            if (this.len == HU_MAXLINELENGTH)
                return false;
            else
                while ((index<s.length())&&(this.len < HU_MAXLINELENGTH)) {

                    this.l[len]append(s.charAt(index++));
                    this.len++;
                }
            this.l.append((char) 0);// final padding.

            // MAES: for some reason this is set as "4", so this is a
            // status rather than a boolean.

            this.needsupdate = 4;
            return true;
        } */

        boolean delCharFromTextLine() {

            if (this.len == 0)
                return false;
            else {
                this.text[--len]= (char)0;
                this.needsupdate = 4;
                return true;
            }

        }

        void drawTextLine(boolean drawcursor) {

            int i;
            int w;
            int x;
            char c;

            // draw the new stuff
            x = this.x;
            for (i = 0; i < this.len; i++) {
                c = Character.toUpperCase(text[i]);
                if (c != ' ' && c >= this.sc && c <= '_') {
                    // MAES: fixed a FUCKING STUPID bug caused by SWAP.SHORT
                    w = this.f[c - this.sc].width;
                    if (x + w > SCREENWIDTH)
                        break;
                    
                    V.DrawScaledPatch(x, y, FG,vs, f[c - sc]);
                    x += w;
                } else {
                    // Leave a space
                    x += 4;
                    if (x >= SCREENWIDTH)
                        break;
                }
            }

            // draw the cursor if requested
            if (drawcursor
                    && x + this.f['_' - this.sc].width <= SCREENWIDTH) {
                V.DrawScaledPatch(x, this.y, FG,vs, this.f['_' - this.sc]);
            }
        }

        // MAES: was "static" in C within HUlib. Which may mean it's instance
        // specific or global-ish. Or both.
        protected boolean lastautomapactive = true;

        // sorta called by HU_Erase and just better darn get things straight
        public void eraseTextLine() {
            int lh;

            // Only erases when NOT in automap and the screen is reduced,
            // and the text must either need updating or refreshing
            // (because of a recent change back from the automap)

            if (!DM.automapactive && (R.view.windowx != 0)
                    && (this.needsupdate > 0)) {
                lh = this.f[0].height + 1;

                for (int y = this.y, yoffset = y * SCREENWIDTH; y < this.y + lh; y++, yoffset +=
                    SCREENWIDTH) {
                    // Stuff is probably in am_map??
                    if (y < R.view.windowy
                            || y >= R.view.windowy + R.view.height)
                        R.VideoErase(yoffset, SCREENWIDTH); // erase entire
                    // line
                    else {
                        R.VideoErase(yoffset, R.view.windowx); // erase left
                        // border
                        R.VideoErase(yoffset + R.view.windowx + R.view.width,
                            R.view.windowx);
                        // erase right border
                    }
                }
            }

            lastautomapactive = DM.automapactive;
            if (this.needsupdate != 0)
                this.needsupdate--;

        }

    }

    @Override
    public patch_t[] getHUFonts() {        
        return this.hu_font;
    }

	@Override
	public void updateStatus(DoomStatus<?,?> DM) {
        this.DM = DM.DM;
        this.W = DM.W;
        this.R = (RendererState<?, ?>) DM.R;
        this.V=DM.V;
        this.S=DM.S;
        this.M=(Menu) DM.M;
		
	}
	
////////////////////////////VIDEO SCALE STUFF ////////////////////////////////

	protected int SCREENWIDTH;
	protected int SCREENHEIGHT;
	protected int SAFE_SCALE;
	protected IVideoScale vs;


	@Override
	public void setVideoScale(IVideoScale vs) {
	    this.vs=vs;
	}

	@Override
	public void initScaling() {
	    this.SCREENHEIGHT=vs.getScreenHeight();
	    this.SCREENWIDTH=vs.getScreenWidth();
	    this.SAFE_SCALE=vs.getSafeScaling();
	}
	
}

//$Log: HU.java,v $
//Revision 1.32  2012/09/24 17:16:23  velktron
//Massive merge between HiColor and HEAD. There's no difference from now on, and development continues on HEAD.
//
//Revision 1.31.2.2  2012/09/24 16:57:43  velktron
//Addressed generics warnings.
//
//Revision 1.31.2.1  2012/09/19 17:43:06  velktron
//Aware of new ViewVars structure.
//
//Revision 1.31  2011/11/01 22:17:46  velktron
//Cleaned up a bit, implements IHeadsUp
//
//Revision 1.30  2011/10/23 18:11:58  velktron
//Generic compliance for DoomVideoInterface
//
//Revision 1.29  2011/10/07 16:05:22  velktron
//Now using g.Keys for key input stuff.
//
//Revision 1.28  2011/05/31 23:46:18  velktron
//Fixed scaling.
//
//Revision 1.27  2011/05/31 21:42:30  velktron
//Handling for map33
//
//Revision 1.26  2011/05/24 17:45:08  velktron
//IHeadsUp interface, setChatMacro method.
//
//Revision 1.25  2011/05/23 16:56:44  velktron
//Migrated to VideoScaleInfo.
//
//Revision 1.24  2011/05/21 14:42:32  velktron
//Adapted to use new gamemode system.
//
//Revision 1.23  2011/05/20 18:27:12  velktron
//DoomMenu -> IDoomMenu
//
//Revision 1.22  2011/05/20 18:24:19  velktron
//FINALLY fixed a stupid bug that broke HU messages.
//
//Revision 1.21  2011/05/18 16:52:40  velktron
//Changed to DoomStatus
package hu;

import rr.patch_t;
import doom.event_t;

public interface IHeadsUp {

	void Ticker();

	void Erase();

	void Drawer();

	boolean Responder(event_t ev);

	patch_t[] getHUFonts();

	char dequeueChatChar();

	void Init();

	void setChatMacro(int i, String s);

	void Start();

	void Stop();

}

package w;

public class JadDecompress {

    public final static int WINDOW_SIZE = 4096;

    public final static int LOOKAHEAD_SIZE = 16;

    public final static int LENSHIFT = 4; /* this must be log2(LOOKAHEAD_SIZE) */

    public static void decode(byte[] input, byte[] output) {
        /*
         * #ifdef JAGUAR decomp_input = input; decomp_output = output;
         * gpufinished = zero; gpucodestart = (int)&decomp_start; while
         * (!I_RefreshCompleted () ) ; #else
         */
        int getidbyte = 0;
        int len;
        int pos;
        int i;
        int source_ptr, input_ptr = 0, output_ptr = 0;
        int idbyte = 0;

        while (true) {

            /* get a new idbyte if necessary */
            if (getidbyte == 0) idbyte = 0xFF & input[input_ptr++];
            getidbyte = (getidbyte + 1) & 7;

            if ((idbyte & 1) != 0) {
                /* decompress */
                pos = (0xFF & input[input_ptr++]) << LENSHIFT;
                pos = pos | ((0xFF & input[input_ptr]) >> LENSHIFT);
                source_ptr = output_ptr - pos - 1;

                len = ((0xFF & input[input_ptr++]) & 0xf) + 1;

                if (len == 1)
                    break;
                for (i = 0; i < len; i++)
                    output[output_ptr++] = output[source_ptr++];
            } else {
                output[output_ptr++] = input[input_ptr++];
            }

            idbyte = idbyte >> 1;

        }

        System.out.printf("Expanded %d to %d\n", input_ptr, output_ptr);
    }

}

package w;

/** killough 4/17/98: namespace tags, to prevent conflicts between resources */

public enum li_namespace {
      ns_global,
      ns_sprites,
      ns_flats,
      ns_colormaps,
      ns_prboom,
      ns_demos,
      ns_hires //e6y
    } // haleyjd 05/21/02: renamed from "namespace"


package w;

import java.io.IOException;
import java.nio.ByteBuffer;

/** A container allowing for caching of arrays of CacheableDoomObjects 
 *  
 *  It's a massive improvement over the older system, allowing for proper 
 *  caching and auto-unpacking of arrays of CacheableDoomObjects and much 
 *  cleaner code throughout.
 *  
 *  The container itself is a CacheableDoomObject....can you feel the
 *  abuse? ;-)
 * 
 */

public class CacheableDoomObjectContainer<T extends CacheableDoomObject> implements CacheableDoomObject {
	
	private T[] stuff;
	
	public CacheableDoomObjectContainer(T[] stuff){
		this.stuff=stuff;
	}
	
	public T[] getStuff(){
		return stuff;
	}
	
	@Override
	public void unpack(ByteBuffer buf) throws IOException {
		for (int i = 0; i < stuff.length; i++) {
				stuff[i].unpack(buf);
			}
		}
	
	/** Statically usable method
	 * 
	 * @param buf
	 * @param stuff
	 * @throws IOException
	 */
	
	public static void unpack(ByteBuffer buf, CacheableDoomObject[] stuff) throws IOException{
	       for (int i = 0; i < stuff.length; i++) {
               stuff[i].unpack(buf);
           }
	}

}

package w;

import java.io.InputStream;
import java.util.zip.ZipEntry;

// CPhipps - changed wad init
// We _must_ have the wadfiles[] the same as those actually loaded, so there 
// is no point having these separate entities. This belongs here.

public class wadfile_info_t {
      public String name; // Also used as a resource identifier, so save with full path and all.
      public ZipEntry entry; // Secondary resource identifier e.g. files inside zip archives.
      public int type; // as per InputStreamSugar
      public wad_source_t src;
      public InputStream handle;
      public boolean cached; // Whether we use local caching e.g. for URL or zips
      public long maxsize=-1; // Update when known for sure. Will speed up seeking.
    }

package w;

import java.io.IOException;
import java.nio.ByteBuffer;

public interface IPackableDoomObject {
    public void pack(ByteBuffer buf) throws IOException ;
}

package w;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

import utils.C2JUtils;

/**
 * As we know, Java can be a bit awkward when handling streams e.g. you can't
 * really skip at will without doing some nasty crud. This class helps doing
 * such crud. E.g. if we are dealing with a stream that has an underlying file,
 * we can try and skip directly by using the file channel, otherwise we can try
 * (eww) closing the stream, reopening it (ASSUMING WE KNOW THE SOURCE'S URI AND
 * TYPE), and then skipping.
 * 
 * @author Maes
 */

public class InputStreamSugar {

    public static final int UNKNOWN_TYPE = 0x0;

    public static final int FILE = 0x1; // Local file. Easiest case

    public static final int NETWORK_FILE = 0x2;

    public static final int ZIP_FILE = 0x4; // Zipped file
    
    public static final int BAD_URI = -1; // Bad or unparseable 

    /**
     * Creates an inputstream from a local file, network resource, or zipped
     * file (also over a network). If an entry name is specifid AND the type is
     * specified to be zip, then a zipentry with that name will be sought.
     * 
     * @param resource
     * @param contained
     * @param type
     * @return
     */

    public static final InputStream createInputStreamFromURI(String resource,
            ZipEntry entry, int type) {

        InputStream is = null;
        URL u;

        // No entry specified or no zip type, try everything BUT zip.
        if (entry == null || !C2JUtils.flags(type,ZIP_FILE)) {
            is = getDirectInputStream(resource);
        } else {
            // Entry specified AND type specified to be zip
            // We might want to open even a zip file without looking 
            // for any particular entry.
            if (entry != null && C2JUtils.flags(type,ZIP_FILE)) {
                
                ZipInputStream zis;
                // Try it as a NET zip file
                try {
                    u = new URL(resource);
                    zis = new ZipInputStream(u.openStream());
                } catch (Exception e) {
                    // Local zip file?
                    try {
                        // Open resource as local file-backed zip input stream, 
                        // and search proper entry.
                        zis= new ZipInputStream(new FileInputStream(resource));
                        }
                    catch (Exception e1) {
                        // Well, it's not that either.
                        // At this point we almost ran out of options
                        // Try a local file and that's it.
                        is = getDirectInputStream(resource);
                        return is;
                    }
                }
                
                // All OK?
                is=getZipEntryStream(zis, entry.getName());
                if (is!=null) return is; 
                }
            }

        // At this point, you'll either get a stream or jack.

        return getDirectInputStream(resource);
    }

    /** Match zip entries in a ZipInputStream based only on their name. 
     * Luckily (?) ZipEntries do not keep references to their originating
     * streams, so opening/closing ZipInputStreams all the time won't result
     * in a garbage hell...I hope.
     * 
     * @param zis
     * @param entryname
     * @return
     */
    
    private static InputStream getZipEntryStream(ZipInputStream zis,String entryname) {
        
        ZipEntry ze = null;
        try {
            while ((ze = zis.getNextEntry()) != null) {
                // Directories cannot be opened
                if (ze.isDirectory())
                    continue;

                if (ze.getName().equals(entryname)) {
                    return zis;
                }
            }
        } catch (IOException e) {
            // Get jack
            return null;
        }
    
        // Get jack
        return null;
    }
    
    private final static InputStream getDirectInputStream(String resource) {
        InputStream is = null;
        URL u;

        try { // Is it a net resource?
            u = new URL(resource);
            is = u.openStream();
        } catch (Exception e) {
            // OK, not a valid URL or no network. We don't care.
            // Try opening as a local file.
            try {
                is = new FileInputStream(resource);
            } catch (FileNotFoundException e1) {
                // Well, it's not that either.
                // At this point we really ran out of options
                // and you'll get null
            }
        }

        return is;
    }

    /**
     * Attempt to do the Holy Grail of Java Streams, aka seek to a particular
     * position. With some types of stream, this is possible if you poke deep
     * enough. With others, it's not, and you can only close & reopen them
     * (provided you know how to do that) and then skip to a particular position
     * 
     * @param is
     * @param pos
     *        The desired position
     * @param URI
     *        Information which can help reopen a stream, e.g. a filename, URL,
     *        or zip file.
     * @peram entry If we must look into a zipfile entry
     * @return the skipped stream. Might be a totally different object.
     * @throws IOException
     */

    public static final InputStream streamSeek(InputStream is, long pos,
            long size,String URI, ZipEntry entry, int type)
            throws IOException {
        if (is == null)
            return is;

        // If we know our actual position in the stream, we can aid seeking
        // forward

        /*
         * Too buggy :-/ pity if (knownpos>=0 && knownpos<=pos){ if
         * (pos==knownpos) return is; try{ final long mustskip=pos-knownpos;
         * long skipped=0; while (skipped<mustskip){
         * skipped+=is.skip(mustskip-skipped);
         * System.out.printf("Must skip %d skipped %d\n",mustskip,skipped); }
         * return is; } catch (Exception e){ // We couldn't skip cleanly.
         * Swallow up and try normally. System.err.println("Couldn't skip"); } }
         */

        // This is a more reliable method, although it's less than impressive in
        // results.
        if (size > 0) {
            try {
                long available = is.available();
                long guesspos = size - available;
                // The stream is at a position before or equal to
                // our desired one. We can attempt skipping forward.
                if (guesspos > 0 && guesspos <= pos) {
                    long skipped=0;
                    long mustskip=pos-guesspos;
                    // Repeat skipping until proper amount reached 
                    while (skipped<mustskip)
                        skipped+=is.skip(mustskip-skipped);
                    return is;
                }
            } catch (Exception e) {
                // We couldn't skip cleanly. Swallow up and try normally.
            }
        }
        
        

        // Cast succeeded
        if (is instanceof FileInputStream) {
            try {
                ((FileInputStream) is).getChannel().position(pos);
                return is;
            } catch (IOException e) {
                // Ouch. Do a dumb close & reopening.
                is.close();
                is = createInputStreamFromURI(URI, null, 1);
                is.skip(pos);
                return is;
            }
        }

        // Cast succeeded
        if (is instanceof ZipInputStream) {
            // ZipInputStreams are VERY dumb. so...
                is.close();
                is = createInputStreamFromURI(URI,entry,type);
                is.skip(pos);
                return is;

        }

        try { // Is it a net resource? We have to reopen it :-/
              // long a=System.nanoTime();
            URL u = new URL(URI);
            InputStream nis = u.openStream();
            nis.skip(pos);
            is.close();
            // long b=System.nanoTime();
            // System.out.printf("Network stream seeked WITH closing %d\n",(b-a)/1000);
            return nis;
        } catch (Exception e) {

        }

        // TODO: zip handling?

        return is;
    }

    public static List<ZipEntry> getAllEntries(ZipInputStream zis)
            throws IOException {
        ArrayList<ZipEntry> zes = new ArrayList<ZipEntry>();

        ZipEntry z;

        while ((z = zis.getNextEntry()) != null) {
            zes.add(z);
        }

        return zes;
    }

    /** Attempts to return a stream size estimate. Only guaranteed to work 100% 
     * for streams representing local files, and zips (if you have the entry).
     * 
     * @param is
     * @param z
     * @return
     */
    
    public static long getSizeEstimate(InputStream is, ZipEntry z) {
        if (is instanceof FileInputStream) {
            try {
                return ((FileInputStream) is).getChannel().size();
            } catch (IOException e) {

            }
        }

        if (is instanceof FileInputStream) {
            if (z != null)
                return z.getSize();
        }

        // Last ditch
        try {
            return is.available();
        } catch (IOException e) {
            try {
                return is.available();
            } catch (IOException e1) {
                return -1;
            }
        }
    }

}
package w;

public enum animenum_t {

    ANIM_ALWAYS,
    ANIM_RANDOM,
    ANIM_LEVEL

}

package w;

import java.io.IOException;
import rr.patch_t;

public interface IWadLoader {

	/**
	 * W_Reload Flushes any of the reloadable lumps in memory and reloads the
	 * directory.
	 * 
	 * @throws Exception
	 */
	public abstract void Reload() throws Exception;

	/**
	 * W_InitMultipleFiles
	 *
	 * Pass a null terminated list of files to use (actually a String[] array in Java).
	 * 
	 * All files are optional, but at least one file must be found. 
	 * 
	 * Files with a .wad extension are idlink files with multiple lumps.
	 * 
	 * Other files are single lumps with the base filename for the lump name.
	 * 
	 * Lump names can appear multiple times.
	 * The name searcher looks backwards, so a later file does override all earlier ones.
	 * 
	 * @param filenames
	 * @param coalesce Choose whether to coalesce or not sprites, flats etc.
	 * 
	 */

	public abstract void InitMultipleFiles(String[] filenames, boolean coalesce) throws Exception;

	/**
	 * W_InitFile
	 * 
	 * Just initialize from a single file.
	 * 
	 * @param filename 
	 * @param coalesce Choose whether to coalesce or not sprites, flats etc.
	 * 
	 */
	public abstract void InitFile(String filename, boolean coalesce) throws Exception;

	/**
	 * W_NumLumps
	 * 
	 * Returns the total number of lumps loaded in this Wad manager. Awesome. 
	 * 
	 */
	public abstract int NumLumps();

	/**
	 * Returns actual lumpinfo_t object for a given name. Useful if you want to
	 * access something on a file, I guess?
	 * 
	 * @param name
	 * @return
	 */

	public abstract lumpinfo_t GetLumpinfoForName(String name);

	/**
	 * W_GetNumForName
	 * Calls W_CheckNumForName, but bombs out if not found.
	 */

	public abstract int GetNumForName(String name);

	/**
	 *          
	 * @param lumpnum
	 * @return
	 */
	public abstract String GetNameForNum(int lumpnum);

	//
	// W_LumpLength
	// Returns the buffer size needed to load the given lump.
	//
	public abstract int LumpLength(int lump);


    /**
     * W_CacheLumpNum Modified to read a lump as a specific type of
     * CacheableDoomObject. If the class is not identified or is null, then a
     * generic DoomBuffer object is left in the lump cache and returned.
     * @param <T>
     */
    public abstract <T> T CacheLumpNum(int lump, int tag,
            Class<T> what);

	// MAES 24/8/2011: superseded by auto-allocating version with proper 
	// container-based caching.

	@Deprecated
	public abstract void CacheLumpNumIntoArray(int lump, int tag,
			Object[] array, Class what) throws IOException;

	/**
	 * Return a cached lump based on its name, as raw bytes, no matter what.
	 * It's rare, but has its uses.
	 * 
	 * @param name
	 * @param tag
	 * @param what
	 * @return
	 */

	public abstract byte[] CacheLumpNameAsRawBytes(String name, int tag);

	/**
	 * Return a cached lump based on its num, as raw bytes, no matter what.
	 * It's rare, but has its uses.
	 * 
	 * @param name
	 * @param tag
	 * @param what
	 * @return
	 */

	public abstract byte[] CacheLumpNumAsRawBytes(int num, int tag);

	/** Get a DoomBuffer of the specified lump name
	 * 
	 * @param name
	 * @param tag
	 * @return
	 */
	
	public abstract DoomBuffer CacheLumpName(String name, int tag);

    /** Get a DoomBuffer of the specified lump num
     * 
     * @param lump
     * @return
     */

	public abstract DoomBuffer CacheLumpNumAsDoomBuffer(int lump);

	/**
	 * Specific method for loading cached patches by name, since it's by FAR the
	 * most common operation.
	 * 
	 * @param name
	 * @return
	 */

	public abstract patch_t CachePatchName(String name);

	/**
	 * Specific method for loading cached patches, since it's by FAR the most
	 * common operation.
	 * 
	 * @param name
	 * @param tag
	 * @return
	 */

	public abstract patch_t CachePatchName(String name, int tag);

	/**
	 * Specific method for loading cached patches by number.
	 * 
	 * @param num
	 * @return
	 */

	public abstract patch_t CachePatchNum(int num);

	public abstract <T extends CacheableDoomObject> T CacheLumpName(String name, int tag, Class<T> what);

	/** A lump with size 0 is a marker. This means that it
	 *  can/must be skipped, and if we want actual data we must
	 *  read the next one. 
	 * 
	 * @param lump
	 * @return
	 */
	public abstract boolean isLumpMarker(int lump);

	public abstract String GetNameForLump(int lump);

	public abstract int CheckNumForName(String name/* , int namespace */);
	
	/** Return ALL possible results for a given name, in order to resolve name clashes without
	 *  using namespaces
	 *  
	 * @param name
	 * @return
	 */
	
	public abstract int[] CheckNumsForName(String name);

	public abstract lumpinfo_t GetLumpInfo(int i);

	/** A way to cleanly close open file handles still pointed at by lumps.
	 *  Is also called upon finalize */
	public void CloseAllHandles();

	/** Null the disk lump associated with a particular object,
	 *  if any. This will NOT induce a garbage collection, unless
	 *  you also null any references you have to that object.
	 *  	  
	 * @param lump
	 */
	
	void UnlockLumpNum(int lump);

	void UnlockLumpNum(CacheableDoomObject lump);
	
	public <T extends CacheableDoomObject> T[] CacheLumpNumIntoArray(int lump, int num,
			Class<T> what);
	
	/** Verify whether a certain lump number is valid and has
	 *  the expected name.
	 *  
	 * @param lump
	 * @param lumpname
	 * @return
	 */

	boolean verifyLumpName(int lump, String lumpname);
	
	/** The index of a known loaded wadfile
	 * 
	 * @param wad1
	 * @return
	 */

    public abstract int GetWadfileIndex(wadfile_info_t wad1);

    /** The number of loaded wadfile
     * 
     * @return
     */
    public abstract int GetNumWadfiles();

    /** Force a lump (in memory) to be equal to a dictated content. Useful
     *  for when you are e.g. repairing palette lumps or doing other sanity
     *  checks.
     * 
     * @param lump
     * @param obj
     */
	void InjectLumpNum(int lump, CacheableDoomObject obj);

	/** Read a lump into a bunch of bytes straight. No caching, no frills.
	 * 
	 * @param lump
	 * @return
	 */
    byte[] ReadLump(int lump);

    /** Use your own buffer, of proper size of course.
     * 
     * @param lump
     * @param buf
     */
    void ReadLump(int lump, byte[] buf);
    
    /** Use your own buffer, of proper size AND offset.
     * 
     * @param lump
     * @param buf
     */

    void ReadLump(int lump, byte[] buf, int offset);
	
}
package w;

import java.io.DataInputStream;
import java.io.IOException;

/** This is for objects that can be read from disk, but cannot
 *  self-determine their own length for some reason.
 * 
 * @author Maes
 *
 */

public interface AidedReadableDoomObject {
    
    public void read(DataInputStream f, int len) throws IOException ;
}

package w;

import java.io.InputStream;

/*
typedef struct
{
  // WARNING: order of some fields important (see info.c).

  char  name[9];
  int   size;

    // killough 4/17/98: namespace tags, to prevent conflicts between resources
  enum {
    ns_global=0,
    ns_sprites,
    ns_flats,
    ns_colormaps,
    ns_prboom,
    ns_demos,
    ns_hires //e6y
  } li_namespace; // haleyjd 05/21/02: renamed from "namespace"

  wadfile_info_t *wadfile;
  int position;
  wad_source_t source;
  int flags; //e6y
} lumpinfo_t; */

public class lumpinfo_t implements Cloneable{
        public String    name;
        public InputStream     handle;
        public long     position;
        public long     size;
        // A 32-bit hash which should be enough for searching through hashtables.
        public int hash;
        // A 64-bit hash that just maps an 8-char string to a long num, good for hashing
        // or for direct comparisons.
        //public long stringhash;
        // Intepreting the first 32 bits of their name as an int. Used in initsprites.
        public int intname; 
        // public int next;
        //public int index;
        
        // For BOOM compatibility
        public li_namespace namespace;
        public wadfile_info_t wadfile;
        
        public int hashCode(){
            return hash;
        }
        
        public String toString(){
            return (name +" "+ Integer.toHexString(hash));
        }
        
        public lumpinfo_t clone(){
        	lumpinfo_t tmp=new lumpinfo_t();
        	tmp.name=name; // Well... a reference will do.
        	tmp.handle=handle;
        	tmp.position=position;
        	tmp.size=size;
        	tmp.hash=hash;
        	tmp.intname=intname;
        	tmp.namespace=namespace;
        	tmp.wadfile=wadfile;
        	
			return tmp;
        	
        }
        
    }

package w;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;

public class wadheader_t implements IReadableDoomObject, IWritableDoomObject {
    public String type;
    public int numentries;
    public int tablepos;
    
    public boolean big_endian=false;
    
    public void read(DataInputStream f) throws IOException{

        type=DoomIO.readNullTerminatedString(f,4);
        
        if (!big_endian){
        numentries=(int) DoomIO.readUnsignedLEInt(f);
        tablepos=(int) DoomIO.readUnsignedLEInt(f);

        } else {
            numentries=f.readInt();
            tablepos=f.readInt();
        }
        
    }

    public static int sizeof(){
        return 16;
    }

    @Override
    public void write(DataOutputStream dos)
            throws IOException {
        DoomIO.writeString(dos, type, 4);
        
        if (!big_endian){
            DoomIO.writeLEInt(dos, (int) numentries);
            DoomIO.writeLEInt(dos, (int) tablepos);
        } else {
                dos.writeInt((int) numentries);
                dos.writeInt((int) tablepos);
        }
        
        
    }

}

package w;

import java.io.DataOutputStream;
import java.io.IOException;

public interface IWritableDoomObject {
    
    public void write(DataOutputStream dos) throws IOException ;
}

package w;

// CPhipps - defined enum in wider scope
// Ty 08/29/98 - add source field to identify where this lump came from

public enum wad_source_t {
   // CPhipps - define elements in order of 'how new/unusual'
   source_iwad,    // iwad file load 
   source_pre,       // predefined lump
   source_auto_load, // lump auto-loaded by config file
   source_pwad,      // pwad file load
   source_lmp,       // lmp file load
   source_net        // CPhipps
   //e6y
 //  ,source_deh_auto_load
   ,source_deh
   ,source_err

 }

package w;

import java.io.DataOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/** A very simple WAD building utility class, just enough to lay some organized lumps on disk. Not particularly optimized 
 * 
 *  It works simply by adding IWritableDoomObjects, IPackableDoomObjects and raw byte[] data to a list. Then, when the time 
 *  to write everything to disk comes, a header, index table etc. are created as well.
 * 
 * */

public class WadBuilder {
	
	protected File file;
	protected wadheader_t header;
	protected List<IWritableDoomObject> lumps;
	protected DataOutputStream dos;
	protected FileOutputStream fos;
	protected int indexpos,totalsize;
	
	public WadBuilder(String file,String type) {
		this.header=new wadheader_t();
		this.lumps=new ArrayList<IWritableDoomObject>();
		this.header.type=type;
		this.file=new File(file);
		this.indexpos=0;
	}
	
	public void start() throws IOException {
		if (file.exists()) file.delete();
		fos=new FileOutputStream(file,false);
		dos=new DataOutputStream(fos);		
		// This header is only temporary, until we know how many lumps and data we've actually written.
		header.write(dos);
	}

	/** Add a zero-sized marker
	 * 
	 * @param lumpname
	 * @throws IOException
	 */
	public void add(String lumpname) throws IOException{		
		filelump_t lump=new filelump_t(lumpname,0,dos.size());
		lumps.add(lump);
		}
	
	/** Add and write immediately a raw byte[] bunch of data.
	 * 
	 * @param lumpname
	 * @param data
	 * @throws IOException
	 */
	
	public void add(String lumpname, byte[] data) throws IOException{		
		filelump_t lump=new filelump_t(lumpname,data.length,dos.size());
		lumps.add(lump);
		dos.write(data);
		}
	
	/** Add and write immediately an object implementing the IWritableDoomObject interface.
	 * 
	 * @param lumpname
	 * @param stuff
	 * @throws IOException
	 */
	public void add(String lumpname, IWritableDoomObject stuff) throws IOException{		
		
		long pos=dos.size(); // Save current stream position
		stuff.write(dos);
		
		// The new lump can be created ONLY after the write
		filelump_t lump=new filelump_t(lumpname,dos.size()-pos,pos);
		lumps.add(lump);
		
		}
	
	/** Add and write immediately an array of objects implementing the IWritableDoomObject interface.
	 *  Objects will be written contiguously.
	 * 
	 * @param lumpname
	 * @param stuff
	 * @throws IOException
	 */
	
	public void add(String lumpname, IWritableDoomObject stuff[]) throws IOException{		
		
		long pos=dos.size(); // Save current stream position
		
		DoomIO.writeListOfObjects(dos, stuff,stuff.length);
		
		// The new lump can be created ONLY after the write
		filelump_t lump=new filelump_t(lumpname,dos.size()-pos,pos);
		lumps.add(lump);		
		}
	
	/** When called, the WAD index will be written to the file, the header will be
	 *  updated for the total number of entries and index position, and the file closed.
	 * 
	 * @throws IOException
	 */
	
	public void close() throws IOException{
		
		this.indexpos=dos.size();
		// Time to write the index
		DoomIO.writeListOfObjects(dos, this.lumps, this.lumps.size());
		this.totalsize=dos.size();
		
		// Amend header
		this.header.numentries=lumps.size();
		this.header.tablepos=this.indexpos;
		
		// Write amended header
		fos.getChannel().position(0);
		header.write(dos);
		
		// Close streams
		dos.close();
		fos.close();
		}
	
	/** Directly copy a bunch of lumps from an IWadLoader object to this WAD file.
	 * 
	 * @param W
	 * @param lumplist
	 * @throws IOException
	 */
	
    public void add(IWadLoader W,String[] lumplist) throws IOException{
    	byte[] data;
    	
    	for (String lumpname: lumplist){
    		data=W.CacheLumpNameAsRawBytes(lumpname, 0);
    		this.add(lumpname,data);
    	}
    }
	
}

package w;

public class name8 {
    private byte[]  s;
    static byte[] ss=new byte[9];
    public int[]    x;
    public long hash;
    
    public name8(String name){
        s=new byte[9];
        x=new int[2];
        // in case the name was a full 8 chars
        this.s[8] = 0;
        
        byte[] tmp=name.getBytes();
        System.arraycopy(tmp, 0, this.s, 0, Math.min(8,tmp.length));
        this.x[0]=byteArrayToInt(s,0);
        this.x[1]=byteArrayToInt(s,4);
        this.hash=byteArrayToLong(s,0);
    }
    
    /** Returns a 64-bit number that maps directly to the ASCII
     *  8-bit representation of a fixed-length 8 char string.
     *  It's for all effects and purposes a unique 64-bit hash, and can be used to
     *  speed up comparisons.
     *  
     * @param name
     * @return
     */
    
    public static long getLongHash(String name){
        // in case the name was a full 8 chars
        for (int i=0;i<ss.length;i++){
            ss[i]=0;
        }
        
        byte[] tmp=name.getBytes();
        // We must effectively limit hashes to 31 bits to be able to use them.
        System.arraycopy(tmp, 0, ss, 0, Math.min(8,tmp.length));
        return byteArrayToLong(ss,0);
    }
    
    public static int getIntName(String name){
        // in case the name was a full 8 chars
        for (int i=0;i<ss.length;i++){
            ss[i]=0;
        }
        
        byte[] tmp=name.getBytes();
        System.arraycopy(tmp, 0, ss, 0, Math.min(4,tmp.length));
        return byteArrayToInt(ss,0);
    }
    
    public static int byteArrayToInt(byte[] src, int ofs){
        return (src[ofs]<<24)|(src[ofs+1]<<16)|(src[ofs+2]<<8)|src[ofs+3];
    }
    
    public static long byteArrayToLong(byte[] src, int ofs){
        return (((long)byteArrayToInt(src, 0)<<32)|byteArrayToInt(src, 4));
    }
    
    
    /** Probably has horrible performance...
     * 
     * @param src
     * @param ofs
     * @return
     */
    
    public static int stringToInt(String src, int ofs){
         byte[]  s=new byte[9];
         for (int i=0;i<src.length();i++){
             s[i]=(byte) src.charAt(i);
         }
        
            return (s[ofs]<<24)|(s[ofs+1]<<16)|(s[ofs+2]<<8)|s[ofs+3];
    }
        
    }

package w;

public interface IReadWriteDoomObject extends IReadableDoomObject, IWritableDoomObject{

}

package w;

import java.io.IOException;
import java.nio.ByteBuffer;

/** All objects that can be deserialized from raw byte buffers such as those
 *  read from WAD lumps should implement this method, so that the WadLoader 
 *  can cache them, and recursive calls to  sub-objects can be made.
 *  
 *  E.g. an object of type A consists of a header and a list of objects of type B.
 *  Calling A.unpack(buf) will cause A to unmarshal its own header, set the list of 
 *  B objects, and then call B.unpack() for each of them, by passing the same buffer
 *  along.
 *  
 *  This system works cleanly, and allows to simulate Doom's "cached memory" while
 *  returning proper objects of the correct type and keeping close to Java's
 *  "correct" way of doing things. 
 *   *  
 *  For example, if a patch_t is read from disk, the WadLoader uses its unpack() method
 *  to read it from a lump read from disk, and creates a new patch_t object, which is placed
 *  in the lump cache (which holds CacheableDoomObject, incidentally). The next time this
 *  same patch_t is requested, the reference to the already cached patch_t will be returned,
 *  if it hasn't been forcedly flushed from the cache. Voila', lump caching!
 *  
 *  The principle can be applied to ARRAYS of similar objects too: using the same buffer,
 *  iterative serial unpacking is possible, while still mantaining a "cached" reference
 *  to their array (TODO: actually, this needs to be implemented more efficiently. Look in
 *  WadLoader) 
 *  
 *  The opposite would be a "PackableDoomObject", aka objects that can pack themselves into
 *  a byte buffer for transmission purposes, although Doom doesn't really need to write as
 *  much as it needs reading.
 *  
 *  For the purpose of saving/loading games, which need to read/write to variable disk 
 *  structures ALL the time, use the ReadableDoomObject/WritableDoomObject interfaces.
 *  Their difference is that they are highly mutable and supposed to be read from files 
 *  or input/output streams, and that a continuous reference to them as deserialized 
 *  objects (e.g. in the caching mechanism) is not needed.
 *    
 * 
 * @author Velktron
 *
 */

public interface CacheableDoomObject {
    
    public void unpack(ByteBuffer buf) throws IOException ;
}

package w;

public enum statenum_t {

        NoState(-1),
        StatCount(0),
        ShowNextLoc(1);
        
        private int value;
        
        private statenum_t(int val){
            this.value=val;
        }

        public int getValue() {
            return value;
        }
        
    
}

package w;

/*
Copyright (C) 1997-2001 Id Software, Inc.

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  

See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

*/

//Created on 24.07.2004 by RST.

//$Id: DoomIO.java,v 1.4 2016/06/06 13:45:44 velktron Exp $

import java.io.*;
import java.nio.ByteOrder;
import java.nio.charset.Charset;
import java.util.List;

import m.Swap;

/**
* An extension of RandomAccessFile, which handles readString/WriteString specially 
* and offers several Doom related (and cross-OS) helper functions for reading/writing
* arrays of multiple objects or fixed-length strings from/to disk.
* 
* TO DEVELOPERS: this is the preferrered method of I/O for anything implemented.
* In addition, Doomfiles can be passed to objects implementing the IReadableDoomObject 
* and IWritableDoomObject interfaces, which will "autoread" or "autowrite" themselves
* to the implied stream.
* 
* TODO: in the light of greater future portabililty and compatibility in certain
* environments, PERHAPS this should have been implemented using Streams. Perhaps
* it's possible to change the underlying implementation without (?) changing too
* much of the exposed interface, but it's not a priority for me right now. 
* 
*/
public class DoomIO  {

	private DoomIO(){
		
	}

   /** Writes a Vector to a RandomAccessFile. */
   public static void writeVector(DataOutputStream dos,float v[]) throws IOException {
       for (int n = 0; n < 3; n++)
           dos.writeFloat(v[n]);
   }

   /** Writes a Vector to a RandomAccessFile. */
   public static float[] readVector(DataInputStream dis) throws IOException {
       float res[] = { 0, 0, 0 };
       for (int n = 0; n < 3; n++)
           res[n] = dis.readFloat();

       return res;
   }

   /** Reads a length specified string from a file. */
   public static final String readString(DataInputStream dis) throws IOException {
       int len = dis.readInt();

       if (len == -1)
           return null;

       if (len == 0)
           return "";

       byte bb[] = new byte[len];

       dis.read(bb, 0, len);

       return new String(bb, 0, len,Charset.forName("ISO-8859-1"));
   }

/** MAES: Reads a specified number of bytes from a file into a new String.
 *  With many lengths being implicit, we need to actually take the loader by the hand.
 *  
 * @param len
 * @return
 * @throws IOException
 */
   
   public final static String readString(DataInputStream dis,int len) throws IOException {

       if (len == -1)
           return null;

       if (len == 0)
           return "";

       byte bb[] = new byte[len];

       dis.read(bb, 0, len);

       return new String(bb, 0, len);
   }
   
   public static String readString(InputStream f,int len) throws IOException {

       if (len == -1)
           return null;

       if (len == 0)
           return "";

       byte bb[] = new byte[len];

       f.read(bb, 0, len);

       return new String(bb, 0, len,Charset.forName("ISO-8859-1"));
   }
   
   /** MAES: Reads a specified number of bytes from a file into a new, NULL TERMINATED String.
    *  With many lengths being implicit, we need to actually take the loader by the hand.
    *  
    * @param len
    * @return
    * @throws IOException
    */
   
      public static final String readNullTerminatedString(InputStream dis,int len) throws IOException {

          if (len == -1)
              return null;

          if (len == 0)
              return "";

          byte bb[] = new byte[len];
          int terminator=len;

          dis.read(bb, 0, len);
          
          for (int i=0;i<bb.length;i++){
              if (bb[i]==0) {
                  terminator=i;
                  break; // stop on first null
              }
              
          }
          
          // This is the One True Encoding for Doom.
          return new String(bb, 0, terminator,Charset.forName("ISO-8859-1"));
      }
   
   /** MAES: Reads multiple strings with a specified number of bytes from a file.
    * If the array is not large enough, only partial reads will occur.
    *  
    * @param len
    * @return
    * @throws IOException
    */
      
      public static final String[] readMultipleFixedLengthStrings(DataInputStream dis,String[] dest, int num, int len) throws IOException {

    	  // Some sanity checks...
          if (num<=0 || len < 0)
              return null;

          if (len == 0) {
        	  for (int i=0;i<dest.length;i++){
        		  dest[i]=new String("");
        	  }
        	  return dest;
          }        	  
          
          for (int i=0;i<num;i++){
        	  dest[i]=readString(dis,len);
          }
          return dest;
      }

   
   /** Writes a length specified string (Pascal style) to a file. 
    * 
    * */
   public static void writeString(DataOutputStream dos,String s) {
       try {
       if (s == null) {
           dos.writeInt(-1);
           return;
       }

       dos.writeInt(s.length());
       if (s.length() != 0)
           dos.writeBytes(s);
       } catch (Exception e){
           System.err.println("writeString "+s+" to DoomFile failed!");
       }
   }

   /** Writes a String with a specified len to a file.
    *  This is useful for fixed-size String fields in 
    *  files. Any leftover space will be filled with 0x00s. 
    * 
    * @param s
    * @param len
    * @throws IOException
    */
    
   public static void writeString(DataOutputStream dos,String s,int len) throws IOException {

       if (s==null) return;
       
       if (s.length() != 0){
           byte[] dest=s.getBytes("ISO-8859-1");
           dos.write(dest,0,Math.min(len,dest.length));
           // Fill in with 0s if something's left.
           if (dest.length<len){
               for (int i=0;i<len-dest.length;i++){
                   dos.write((byte)0x00);
               }
           }
       }
   }

   public static void readObjectArray(DataInputStream dis,IReadableDoomObject[] s,int len) throws IOException {

       if ((s==null)||(len==0)) return;
       
       for (int i=0;i<Math.min(len,s.length);i++){           
           s[i].read(dis);
       }
   }

   public static void readObjectArrayWithReflection(DataInputStream dis,IReadableDoomObject[] s,int len) throws Exception {

       if (len==0) return;
       Class<?> c=s.getClass().getComponentType();
       
       for (int i=0;i<Math.min(len,s.length);i++){
           if (s[i]==null) s[i]=(IReadableDoomObject) c.newInstance();
           s[i].read(dis);
       }
   }
   
   public static void readObjectArray(DataInputStream dis,IReadableDoomObject[] s,int len, Class<?> c) throws Exception {

       if ((s==null)||(len==0)) return;
       
       for (int i=0;i<Math.min(len,s.length);i++){
           if (s[i]==null) {
               s[i]=(IReadableDoomObject) c.newInstance();
           }
           s[i].read(dis);
       }
   }
   
   public static final void readIntArray(DataInputStream dis,int[] s,int len, ByteOrder bo) throws IOException {

       if ((s==null)||(len==0)) return;
       
       for (int i=0;i<Math.min(len,s.length);i++){           
           s[i]=dis.readInt();
           if (bo==ByteOrder.LITTLE_ENDIAN){
               s[i]=Swap.LONG(s[i]);
           }
       }
   }
   
   public static final void readShortArray(DataInputStream dis,short[] s,int len, ByteOrder bo) throws IOException {

       if ((s==null)||(len==0)) return;
       
       for (int i=0;i<Math.min(len,s.length);i++){           
           s[i]=dis.readShort();
           if (bo==ByteOrder.LITTLE_ENDIAN){
               s[i]=Swap.SHORT(s[i]);
           }
       }
   }
   
   public static final void readIntArray(DataInputStream dis,int[] s,ByteOrder bo) throws IOException {
       readIntArray(dis,s,s.length,bo);
   }
   
   public static final void readShortArray(DataInputStream dis,short[] s,ByteOrder bo) throws IOException {
       readShortArray(dis,s,s.length,bo);
   }
   
   public static void readBooleanArray(DataInputStream dis,boolean[] s,int len) throws IOException {

       if ((s==null)||(len==0)) return;
       
       for (int i=0;i<Math.min(len,s.length);i++){
           s[i]=dis.readBoolean();
           }
   }
   
   
   
   /** Reads an array of "int booleans" into an array or
    * proper booleans. 4 bytes per boolean are used!
    * 
    * @param s
    * @param len
    * @throws IOException
    */
   
   public final static void readBooleanIntArray(DataInputStream dis,boolean[] s,int len) throws IOException {

       if ((s==null)||(len==0)) return;
       
       for (int i=0;i<Math.min(len,s.length);i++){
           s[i]=readIntBoolean(dis);
           }
   }
   
   public static final void readBooleanIntArray(DataInputStream dis,boolean[] s) throws IOException {
       readBooleanIntArray(dis,s,s.length);
   }
   
   public static final void writeBoolean(DataOutputStream dos,boolean[] s,int len) throws IOException {

       if ((s==null)||(len==0)) return;
       
       for (int i=0;i<Math.min(len,s.length);i++){
           dos.writeBoolean(s[i]);
           }
   }
   
   public static final void writeObjectArray(DataOutputStream dos,IWritableDoomObject[] s,int len) throws IOException {

       if ((s==null)||(len==0)) return;
       
       for (int i=0;i<Math.min(len,s.length);i++){           
           s[i].write(dos);
       }
   }
   
   public static final void writeListOfObjects(DataOutputStream dos,List<IWritableDoomObject> s,int len) throws IOException {

       if ((s==null)||(len<=0)) return;
       
       for (int i=0;i<Math.min(len,s.size());i++){           
           s.get(i).write(dos);
       }
   }
   
   public static final void writeListOfObjects(DataOutputStream dos,IWritableDoomObject s[],int len) throws IOException {

       if ((s==null)||(len<=0)) return;
       
       for (int i=0;i<Math.min(len,s.length);i++){           
           s[i].write(dos);
       }
   }
   
   public final static void readBooleanArray(DataInputStream dis,boolean[] s) throws IOException {
       readBooleanArray(dis,s,s.length);
       }
   
   public final static void readIntBooleanArray(DataInputStream dis,boolean[] s) throws IOException {
       readBooleanIntArray(dis,s,s.length);
       }
  
   public static final void writeCharArray(DataOutputStream dos,char[] charr,int len) throws IOException {

       if ((charr==null)||(len==0)) return;
       
       for (int i=0;i<Math.min(len,charr.length);i++){           
           dos.writeChar(charr[i]);
       }
   }
   
   /** Will read an array of proper Unicode chars.
    * 
    * @param charr
    * @param len
    * @throws IOException
    */
   
   public static final void readCharArray(DataInputStream dis,char[] charr,int len) throws IOException {

       if ((charr==null)||(len==0)) return;
       
       for (int i=0;i<Math.min(len,charr.length);i++){           
           charr[i]=dis.readChar();
       }
   }
   
   /** Will read a bunch of non-unicode chars into a char array.
    *  Useful when dealing with legacy text files.
    * 
    * @param charr
    * @param len
    * @throws IOException
    */
   
   public static final void readNonUnicodeCharArray(DataInputStream dis,char[] charr,int len) throws IOException {

       if ((charr==null)||(len==0)) return;
       
       for (int i=0;i<Math.min(len,charr.length);i++){           
           charr[i]=(char) dis.readUnsignedByte();
       }
   }
   
   /** Writes an item reference. 
   public void writeItem(gitem_t item) throws IOException {
       if (item == null)
           writeInt(-1);
       else
           writeInt(item.index);
   }
*/
   /** Reads the item index and returns the game item. 
   public gitem_t readItem() throws IOException {
       int ndx = readInt();
       if (ndx == -1)
           return null;
       else
           return GameItemList.itemlist[ndx];
   }
 * @throws IOException 
*/
   
   public static final long readUnsignedLEInt(DataInputStream dis) throws IOException{
       int tmp=dis.readInt();
       return 0xFFFFFFFFL&Swap.LONG(tmp);
   }
   
   public static final int readLEInt(DataInputStream dis) throws IOException{
       int tmp=dis.readInt();
       return Swap.LONG(tmp);
   }
   
   public static final int readLEInt(InputStream dis) throws IOException{	   
       int tmp=new DataInputStream(dis).readInt();
       return Swap.LONG(tmp);
   }
   
   public static final void writeLEInt(DataOutputStream dos,int value) throws IOException{       
	   dos.writeInt(Swap.LONG(value));
   }
   
// 2-byte number
   public static int SHORT_little_endian_TO_big_endian(int i)
   {
       return ((i>>8)&0xff)+((i << 8)&0xff00);
   }

   // 4-byte number
   public static int INT_little_endian_TO_big_endian(int i)
   {
       return((i&0xff)<<24)+((i&0xff00)<<8)+((i&0xff0000)>>8)+((i>>24)&0xff);
   }

public static final short readLEShort(DataInputStream dis) throws IOException {
    short tmp=dis.readShort();
    return Swap.SHORT(tmp);
}

/** Reads a "big boolean" using 4 bytes.
 * 
 * @return
 * @throws IOException
 */
public static final boolean readIntBoolean(DataInputStream dis) throws IOException {
    return (dis.readInt()!=0);

}

   
}

package w;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;

public class wadinfo_t implements IReadableDoomObject,IWritableDoomObject{
        // Should be "IWAD" or "PWAD".
        String       identification;      
        long         numlumps;
        long         infotableofs;
             
        /** Reads the wadinfo_t from the file.*/
        public void read(DataInputStream f) throws IOException {
            identification = DoomIO.readString(f,4);
            numlumps=DoomIO.readUnsignedLEInt(f);
            infotableofs=DoomIO.readUnsignedLEInt(f);
        }

		@Override
		public void write(DataOutputStream dos) throws IOException {
			DoomIO.writeString(dos, identification, 4);
			DoomIO.writeLEInt(dos, (int)numlumps);
			DoomIO.writeLEInt(dos, (int)infotableofs);
		}
        
    }
package w;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;

/** filelumps are on-disk structures. lumpinfos are almost the same, but are memory only.
 * 
 * @author Maes
 *
 */

public class filelump_t  implements IReadableDoomObject, IWritableDoomObject {
    public long         filepos;
    public long         size; // Is INT 32-bit in file!
    public String        name; // Whatever appears inside the wadfile
    public String actualname; // Sanitized name, e.g. after compression markers
        
    public boolean big_endian=false; // E.g. Jaguar
    public boolean compressed=false; // Compressed lump
    
    public filelump_t(String name, long size, long filepos) {
    	this.filepos=filepos;
    	this.size=size;
    	this.name=name;
		
	}

	public filelump_t() {
	}

	public void read(DataInputStream f) throws IOException{
        // MAES: Byte Buffers actually make it convenient changing byte order on-the-fly.
        // But RandomAccessFiles (and inputsteams) don't :-S

        if (!big_endian){
        filepos=DoomIO.readUnsignedLEInt(f);
        size=DoomIO.readUnsignedLEInt(f);

        } else {
            filepos=f.readInt();
            size=f.readInt();

        }
        
        // Names used in the reading subsystem should be upper case,
        // but check for compressed status first
        name=DoomIO.readNullTerminatedString(f,8);
        
       
        char[] stuff= name.toCharArray();
        
        // It's a compressed lump
        if (stuff[0] > 0x7F) {
            this.compressed=true;
            stuff[0]&=0x7F; 
        }
        
        actualname=new String(stuff).toUpperCase();
        
        
    }

    public static int sizeof(){
        return (4+4+8);
    }

    @Override
    public void write(DataOutputStream dos)
            throws IOException {
        if (!big_endian){
            DoomIO.writeLEInt(dos, (int) filepos);
            DoomIO.writeLEInt(dos, (int) size);
        } else {
                dos.writeInt((int) filepos);
                dos.writeInt((int) size);
        }
        DoomIO.writeString(dos, name, 8);
        
    }
    
}
// Emacs style mode select -*- C++ -*-
// -----------------------------------------------------------------------------
//
// $Id: WadLoader.java,v 1.66 2016/07/04 13:50:50 velktron Exp $
//
// Copyright (C) 1993-1996 by id Software, Inc.
//
//This program is free software; you can redistribute it and/or
//modify it under the terms of the GNU General Public License
//as published by the Free Software Foundation; either version 2
//of the License, or (at your option) any later version.
//
//This program is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//GNU General Public License for more details.
//
//
//
// DESCRIPTION:
// Handles WAD file header, directory, lump I/O.
//
// -----------------------------------------------------------------------------

package w;

import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

import rr.patch_t;
import utils.C2JUtils;
import static data.Defines.*;
import i.*;

public class WadLoader implements IWadLoader {
	
	//// FIELDS
	
	/** DoomSystem object */
	protected IDoomSystem I;

	/** Location of each lump on disk. */
	public lumpinfo_t[] lumpinfo;

	public int numlumps;

	/**
	 * MAES: probably array of byte[]??? void** lumpcache;
	 * 
	 * Actually, loaded objects will be deserialized here as the general type
	 * "CacheableDoomObject" (in the worst case they will be byte[] or
	 * ByteBuffer).
	 * 
	 * Not to brag, but this system is FAR superior to the inline unmarshaling
	 * used in other projects ;-)
	 */

	private CacheableDoomObject[] lumpcache;

	private boolean[] preloaded;

	/** Added for Boom compliance */
	private List<wadfile_info_t> wadfiles;
	
	/**
	 * #define strcmpi strcasecmp MAES: this is just capitalization. However we
	 * can't manipulate String object in Java directly like this, so this must
	 * be a return type.
	 * 
	 * TODO: maybe move this in utils?
	 */

	public String strupr(String s) {
		return s.toUpperCase();
	}

	/* ditto */
	public void strupr(char[] s) {
		for (int i = 0; i < s.length; i++) {
			s[i] = Character.toUpperCase(s[i]);
		}
	}


	int reloadlump;
	
	/** Offset inside an input stream to allow us to read lumps as "wad archives" or "WADs withing WADs". The catch is that
	 * each such lump will have to be handled with its own reader.
	 * 
	 */
	int wadarchive_offset;
	
	/** Standard constructor
	 * 
	 * @param I
	 */
	
	   public WadLoader(IDoomSystem I) {
	        this();
	        this.I = I;
	    }

	   
	   /** A default constructor with a built-in dummy system */
	   
	    public WadLoader() {
	        lumpinfo = new lumpinfo_t[0];
	        zone= new Hashtable<CacheableDoomObject, Integer>();
	        wadfiles=new ArrayList<wadfile_info_t>();
	        this.I=new DummySystem();
	    }
	

	    /** A simplified constructor for opening a single local file without coalescing
	     * 
	     * @param wadfile
	     */
		   
	    public WadLoader(String wadfile) {
	    	this();
	    	try {
				this.InitFile(wadfile,false);
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

	    }
	    
	
	// MAES: was char*
	String reloadname;
    /**
     * W_AddFile:
     * 
     * This is where lumps are actually read + loaded from a file.
     * 
     * All files are optional, but at least one file must be
     * found (PWAD, if all required lumps are present).
     * Files with a .wad extension are wadlink files
     * with multiple lumps.
     * Other files are single lumps with the base filename
     * for the lump name.
     * 
     * If filename starts with a tilde, the file is handled
     * specially to allow map reloads.
     * But: the reload feature is a fragile hack...
     * 
     * @param uri
     * @param entry
     * @throws type
     */

	private void AddFile(String uri,ZipEntry entry,int type) throws Exception {
		wadinfo_t header = new wadinfo_t();
		int lump_p; // MAES: was lumpinfo_t* , but we can use it as an array
		// pointer.
		InputStream handle,storehandle;
		long length;
		int startlump;
		
		filelump_t[] fileinfo = new filelump_t[1]; // MAES: was *
		filelump_t singleinfo = new filelump_t();

		// handle reload indicator.
		if (uri.charAt(0) == '~') {
		    uri = uri.substring(1);
			reloadname = uri;
			reloadlump = numlumps;
		}

        // open the resource and add to directory
		// It can be any streamed type handled by the "sugar" utilities.
		
		try {
			handle = InputStreamSugar.createInputStreamFromURI(uri,entry,type);
		} catch (Exception e) {
			I.Error(" couldn't open resource %s \n", uri);
			return;
		}
		
        // Create and set wadfile info
        wadfile_info_t wadinfo=new wadfile_info_t();
        wadinfo.handle= handle;
        wadinfo.name=uri;
        wadinfo.entry=entry;
        wadinfo.type=type;
        
		// System.out.println(" adding " + filename + "\n");

		// We start at the number of lumps. This allows appending stuff.
		startlump = this.numlumps;
		
		String checkname=(wadinfo.entry!=null?wadinfo.entry.getName():uri);
		// If not "WAD" then we check for single lumps.
		if (!C2JUtils.checkForExtension(checkname,"wad")) {
		    
		    fileinfo[0] = singleinfo;
			singleinfo.filepos = 0;
			singleinfo.size = InputStreamSugar.getSizeEstimate(handle,wadinfo.entry);
			
			// Single lumps. Only use 8 characters			
			singleinfo.actualname=singleinfo.name = C2JUtils.removeExtension(uri).toUpperCase();
			
			// MAES: check out certain known types of extension
			if (C2JUtils.checkForExtension(uri,"lmp"))			
			    wadinfo.src=wad_source_t.source_lmp;
			else
            if (C2JUtils.checkForExtension(uri,"deh"))         
                wadinfo.src=wad_source_t.source_deh;
            else        
            if (C2JUtils.checkForExtension(uri,null))         
                    wadinfo.src=wad_source_t.source_deh;
                
			numlumps++;			
			
		} else {
			// MAES: 14/06/10 this is historical, for this is the first time I
			// implement reading something from RAF into Doom's structs. 
		    // Kudos to the JAKE2 team who solved  this problem before me.
		    // MAES: 25/10/11: In retrospect, this solution, while functional, was
		    // inelegant and limited.
		    
		    DataInputStream dis=new DataInputStream(handle);
		    
		    // Read header in one go. Usually doesn't cause trouble?
			header.read(dis);			
			
			if (header.identification.compareTo("IWAD") != 0) {
				// Homebrew levels?
				if (header.identification.compareTo("PWAD") != 0) {
					I.Error("Wad file %s doesn't have IWAD or PWAD id\n",checkname);
				} else wadinfo.src=wad_source_t.source_pwad;

				// modifiedgame = true;
			} else wadinfo.src=wad_source_t.source_iwad;

			length = header.numlumps;
			// Init everything:
			fileinfo = C2JUtils.createArrayOfObjects(filelump_t.class,(int)length);
			
			dis.close();
			
			handle=InputStreamSugar.streamSeek(handle,header.infotableofs,wadinfo.maxsize,uri,entry,type);
			
			// FIX: sometimes reading from zip files doesn't work well, so we pre-cache the TOC
			byte[] TOC=new byte[(int) (length*filelump_t.sizeof())];
			
			int read=0;
			while (read<TOC.length){ 
			 // Make sure we have all of the TOC, sometimes ZipInputStream "misses" bytes.
			 // when wrapped.
			    read+=handle.read(TOC,read,TOC.length-read);
			    }
			
			ByteArrayInputStream bais=new ByteArrayInputStream(TOC);
			
			// MAES: we can't read raw structs here, and even less BLOCKS of
			// structs.

			dis=new DataInputStream(bais);
			DoomIO.readObjectArray(dis,fileinfo, (int) length);

			numlumps += header.numlumps;
			wadinfo.maxsize=estimateWadSize(header,lumpinfo);
			
		    } // end loading wad
		
		    //  At this point, a WADFILE or LUMPFILE been successfully loaded, 
		    // and so is added to the list
		    this.wadfiles.add(wadinfo);
		    
			// Fill in lumpinfo
			// MAES: this was a realloc(lumpinfo, numlumps*sizeof(lumpinfo_t)),
			// so we have to increase size and copy over. Maybe this should be
			// an ArrayList?

			int oldsize = lumpinfo.length;
			lumpinfo_t[] newlumpinfo = new lumpinfo_t[numlumps];

			try {
				C2JUtils.initArrayOfObjects(newlumpinfo, lumpinfo_t.class);
				System.arraycopy(lumpinfo, 0, newlumpinfo, 0, oldsize);
			} catch (Exception e) {
				// if (!lumpinfo)
				I.Error("Couldn't realloc lumpinfo");
			}

			// Bye bye, old lumpinfo!
			lumpinfo = newlumpinfo;

			// MAES: lump_p was an alias for lumpinfo[startlump]. I know it's a
			// bit crude as an approximation but heh...

			lump_p = startlump;

			// MAES: if reloadname is null, handle is stored...else an invalid
			// handle?
			storehandle = (reloadname != null) ? null : handle;

			// This iterates through single files.
			int fileinfo_p = 0;

			
			for (int i = startlump; i < numlumps; i++, lump_p++, fileinfo_p++) {
				lumpinfo[lump_p].handle = storehandle;
				lumpinfo[lump_p].position = fileinfo[fileinfo_p].filepos;
				lumpinfo[lump_p].size = fileinfo[fileinfo_p].size;
				// Make all lump names uppercase. Searches should also be uppercase only.
				lumpinfo[lump_p].name = fileinfo[fileinfo_p].name.toUpperCase();
				lumpinfo[lump_p].hash =lumpinfo[lump_p].name.hashCode();
				// lumpinfo[lump_p].stringhash = name8.getLongHash(strupr(lumpinfo[lump_p].name));
				// LumpNameHash(lumpinfo[lump_p].name);
				lumpinfo[lump_p].intname = name8.getIntName(strupr(lumpinfo[lump_p].name));
				//System.out.println(lumpinfo[lump_p]);
				lumpinfo[lump_p].wadfile=wadinfo; // MAES: Add Boom provenience info
			}
			
			
			
			if (reloadname != null)
				handle.close();
	}

	/** Try to guess a realistic wad size limit based only on the number of lumps and their
	 *  STATED contents, in case it's not possible to get an accurate stream size otherwise.
	 *  Of course, they may be way off with deliberately malformed files etc.
	 *  
	 * @param header
	 * @param lumpinfo2
	 * @return
	 */
	
	private long estimateWadSize(wadinfo_t header, lumpinfo_t[] lumpinfo) {
	    
	    long maxsize=header.infotableofs+header.numlumps*16;
	    
	    for (int i=0;i<lumpinfo.length;i++){
	        if ((lumpinfo[i].position+lumpinfo[i].size) >maxsize){
	            maxsize=lumpinfo[i].position+lumpinfo[i].size;
	        }
	    }
	    
        return maxsize;
    }

    /* (non-Javadoc)
	 * @see w.IWadLoader#Reload()
	 */
	@SuppressWarnings("null")
	public void Reload() throws Exception {
		wadinfo_t header = new wadinfo_t();
		int lumpcount;
		int lump_p; // Maes: same as in W_WADload
		int i;
		DataInputStream handle = null;
		int length;
		filelump_t[] fileinfo;

		if (reloadname == null)
			return;

		try {
			handle = new DataInputStream(new BufferedInputStream(new FileInputStream(reloadname)));
		} catch (Exception e) {
			I.Error("W_Reload: couldn't open %s", reloadname);
		}

		header.read(handle);
		// Actual number of lumps in file...
		lumpcount = (int) header.numlumps;
		header.infotableofs = header.infotableofs;
		length = lumpcount;
		fileinfo = new filelump_t[length];
		
		handle.reset();
		handle.skip(header.infotableofs);

		// MAES: we can't read raw structs here, and even less BLOCKS of
		// structs.

		DoomIO.readObjectArrayWithReflection(handle,fileinfo, (int) length);

		/*
		 * for (int j=0;j<length;j++){ fileinfo[j].load (handle); }
		 */

		// numlumps += header.numlumps;
		// read (handle, fileinfo, length);

		// Fill in lumpinfo
		lump_p = reloadlump;
		int fileinfo_p = 0;
		for (i = reloadlump; i < reloadlump + lumpcount; i++, lump_p++, fileinfo_p++) {
			if (lumpcache[i] != null) {
				// That's like "freeing" it, right?
				lumpcache[i] = null;
				preloaded[i] = false;
			}

			lumpinfo[lump_p].position = fileinfo[fileinfo_p].filepos;
			lumpinfo[lump_p].size = fileinfo[fileinfo_p].size;
		}

	}

	/* (non-Javadoc)
	 * @see w.IWadLoader#InitMultipleFiles(java.lang.String[])
	 */
	
	public void InitMultipleFiles(String[] filenames,boolean coalesce) throws Exception {
		int size;

		// open all the files, load headers, and count lumps
		numlumps = 0;

		// will be realloced as lumps are added
		lumpinfo = new lumpinfo_t[0];

		for (String s : filenames) {
			if (s != null){
				if (C2JUtils.testReadAccess(s))
				{
				    // Resource is readable, guess type.
				    int type=C2JUtils.guessResourceType(s);
				    if (C2JUtils.flags(type,InputStreamSugar.ZIP_FILE)){
				        addZipFile(s, type);
				    } else {
				    	this.AddFile(s,null, type);			        
				    }
				    
				    System.out.printf("\tadded %s (zipped: %s network: %s)\n",s,
				        C2JUtils.flags(type, InputStreamSugar.ZIP_FILE),
				        C2JUtils.flags(type, InputStreamSugar.NETWORK_FILE));
				    
				}
				else
					System.err.printf("Couldn't open resource %s\n",s);
			}
		}

		if (numlumps == 0)
			I.Error("W_InitFiles: no files found");

		if (coalesce) {
			CoalesceMarkedResource("S_START", "S_END", li_namespace.ns_sprites);
			CoalesceMarkedResource("F_START", "F_END", li_namespace.ns_flats);
			// 	CoalesceMarkedResource("P_START", "P_END", li_namespace.ns_flats);
		}
		
		// set up caching
		size = numlumps;
		lumpcache = new CacheableDoomObject[size];
		preloaded = new boolean[size];

		if (lumpcache == null)
			I.Error("Couldn't allocate lumpcache");

		this.InitLumpHash();
	}

    /**
     * @param s
     * @param type
     * @throws IOException
     * @throws Exception
     */
    protected void addZipFile(String s, int type)
            throws IOException, Exception {
        // Get entries				        
        BufferedInputStream is=new BufferedInputStream(
            InputStreamSugar.createInputStreamFromURI(s, null, type)
            );
        ZipInputStream zip=new ZipInputStream(is);
        List<ZipEntry> zes=InputStreamSugar.getAllEntries(zip);
        zip.close();
        for (ZipEntry zz:zes){
            // The name of a zip file will be used as an identifier
            if (!zz.isDirectory())
            this.AddFile(s,zz, type);
        }
    }

	public void InitFile(String filename,boolean coalesce) throws Exception {
		String[] names = new String[1];

		names[0] = filename;
		// names[1] = null;
		InitMultipleFiles(names,coalesce);
	}

	/* (non-Javadoc)
	 * @see w.IWadLoader#NumLumps()
	 */
	public final int NumLumps() {
		return numlumps;
	}

	/**
	 * W_CheckNumForName2 Returns -1 if name not found.
	 * 
	 * A slightly better implementation, uses string hashes
	 * as direct comparators (though 64-bit long descriptors
	 * could be used). It's faster than the old method, but
	 * still short from the Hashtable's performance by 
	 * an order of magnitude. 
	 * 
     * @param name
     * @return
	 *
	 * UNUSED

	    public int CheckNumForName2(String name) {

		// scan backwards so patch lump files take precedence
		int lump_p = numlumps;

		// make the name into two integers for easy compares
		// case insensitive

		long hash = name8.getLongHash(name);
		// System.out.print("Looking for "+name + " with hash "
		// +Long.toHexString(hash));
		while (lump_p-- != 0)
			if (lumpinfo[lump_p].stringhash == hash) {
				// System.out.print(" found "+lumpinfo[lump_p]+"\n" );
				return lump_p;
			}

		// TFB. Not found.
		return -1;
	} */

	/**
	 * Old, shitty method for CheckNumForName. It's an overly literal
	 * translation of how the C original worked, which was none too good 
	 * even without the overhead of converting a string to
	 * its integer representation. It's so bad, that it's two orders
	 * of magnitude slower than a Hashtable implemetation, and one from
	 * a direct hash/longname comparison with linear search.
	 * 
	 * @param name
	 * @return
	 *

	public int CheckNumForName3(String name) {

		int v1;
		int v2;
		// lumpinfo_t lump_p;

		int lump_p;
		// make the name into two integers for easy compares
		// case insensitive
		name8 union = new name8(strupr(name));

		v1 = union.x[0];
		v2 = union.x[1];

		// scan backwards so patch lump files take precedence
		lump_p = numlumps;

		while (lump_p-- != 0) {
			int a = name8.stringToInt(lumpinfo[lump_p].name, 0);
			int b = name8.stringToInt(lumpinfo[lump_p].name, 4);
			if ((a == v1) && (b == v2)) {
				return lump_p;
			}
		}

		// TFB. Not found.
		return -1;
	} */

	/* (non-Javadoc)
	 * @see w.IWadLoader#GetLumpinfoForName(java.lang.String)
	 */

	public lumpinfo_t GetLumpinfoForName(String name) {

		int v1;
		int v2;
		// lumpinfo_t lump_p;

		int lump_p;
		// make the name into two integers for easy compares
		// case insensitive
		name8 union = new name8(strupr(name));

		v1 = union.x[0];
		v2 = union.x[1];

		// scan backwards so patch lump files take precedence
		lump_p = numlumps;

		while (lump_p-- != 0) {
			int a = name8.stringToInt(lumpinfo[lump_p].name, 0);
			int b = name8.stringToInt(lumpinfo[lump_p].name, 4);
			if ((a == v1) && (b == v2)) {
				return lumpinfo[lump_p];
			}
		}

		// TFB. Not found.
		return null;
	}

	/* (non-Javadoc)
	 * @see w.IWadLoader#GetNumForName(java.lang.String)
	 */
	
	public int GetNumForName(String name) {
		int i;

		i = CheckNumForName(name.toUpperCase());

		if (i == -1) {
			Exception e = new Exception();
			e.printStackTrace();
			System.err.println("Error:" + name + "not found");
			System.err.println("Hash:"
					+ Long.toHexString(name8.getLongHash(name)));
			I.Error("W_GetNumForName: %s not found!", name);
		}

		return i;
	}

	/* (non-Javadoc)
	 * @see w.IWadLoader#GetNameForNum(int)
	 */
    public String GetNameForNum(int lumpnum) {
        if (lumpnum>=0 && lumpnum<this.numlumps){
            return this.lumpinfo[lumpnum].name;
        }
        return null;
    }
	
	//
	// W_LumpLength
	// Returns the buffer size needed to load the given lump.
	//
	/* (non-Javadoc)
	 * @see w.IWadLoader#LumpLength(int)
	 */
	public int LumpLength(int lump) {
		if (lump >= numlumps)
			I.Error("W_LumpLength: %i >= numlumps", lump);

		return (int) lumpinfo[lump].size;
	}

	@Override
	public final byte[] ReadLump(int lump){
	    lumpinfo_t l=lumpinfo[lump];
	    byte[] buf=new byte[(int) l.size];
	    ReadLump(lump, buf,0);
	    return buf;
	    
	}
	
	@Override
	public final void ReadLump(int lump, byte[] buf) {
	    ReadLump(lump, buf, 0);
	}
	
    /**
     * W_ReadLump Loads the lump into the given buffer, which must be >=
     * W_LumpLength(). SKIPS CACHING
     * 
     * @throws IOException
     */

	@Override
	public final void ReadLump(int lump, byte[] buf, int offset) {
		int c=0;
		lumpinfo_t l;
		InputStream handle = null;

		if (lump >= this.numlumps) {
			I.Error("W_ReadLump: %i >= numlumps", lump);
			return;
		}

		l = lumpinfo[lump];

		if (l.handle == null) {
			// reloadable file, so use open / read / close
			try {
			    // FIXME: reloadable files can only be that. Files.
				handle = InputStreamSugar.createInputStreamFromURI(this.reloadname,null,0);
			} catch (Exception e) {
				e.printStackTrace();
				I.Error("W_ReadLump: couldn't open %s", reloadname);
			}
		} else
			handle = l.handle;

		try {

			handle=InputStreamSugar.streamSeek(handle,l.position,
		    l.wadfile.maxsize,l.wadfile.name,l.wadfile.entry,l.wadfile.type);
		    
			// read buffered. Unfortunately that interferes badly with 
			// guesstimating the actual stream position.
			BufferedInputStream bis=new BufferedInputStream(handle,8192);
			
			while (c<l.size)
			    c+= bis.read(buf,offset+c, (int) (l.size-c));
			
			// Well, that's a no-brainer.
			//l.wadfile.knownpos=l.position+c;
				
			if (c < l.size)
				System.err.printf("W_ReadLump: only read %d of %d on lump %d %d\n", c, l.size,
						lump,l.position);

			if (l.handle == null)
				handle.close();
			else
			    l.handle=handle;
	
			I.BeginRead ();
			
			return;
			
			// ??? I_EndRead ();
		} catch (Exception e) {
			e.printStackTrace();
			I.Error("W_ReadLump: could not read lump " + lump);
			e.printStackTrace();
			return;
		}

	}


	/** The most basic of the Wadloader functions. Will attempt to read a lump
	 *  off disk, based on the specific class type (it will call the unpack()
	 *  method). If not possible to call the unpack method, it will leave a 
	 *  DoomBuffer object in its place, with the raw byte contents. It's
	 *   
	 * 
	 */
	
	@SuppressWarnings("unchecked")
    public <T> T CacheLumpNum(int lump, int tag, Class<T> what) {
		
		if (lump >= numlumps) {
			I.Error("W_CacheLumpNum: %i >= numlumps", lump);
		}

		// Nothing cached here...
		// SPECIAL case : if no class is specified (null), the lump is re-read anyway
		// and you get a raw doombuffer. Plus, it won't be cached.
		
		if ((lumpcache[lump] == null)||(what==null)) {

			// read the lump in

			// System.out.println("cache miss on lump "+lump);
			// Fake Zone system: mark this particular lump with the tag specified
			// ptr = Z_Malloc (W_LumpLength (lump), tag, &lumpcache[lump]);
			// Read as a byte buffer anyway.
			ByteBuffer thebuffer = ByteBuffer.wrap(ReadLump(lump));

			// Class type specified

			if (what != null) {
				try {
					// Can it be uncached? If so, deserialize it.

					if (implementsInterface(what, w.CacheableDoomObject.class)) {
						// MAES: this should be done whenever single lumps
						// are read. DO NOT DELEGATE TO THE READ OBJECTS THEMSELVES.
						// In case of sequential reads of similar objects, use 
						// CacheLumpNumIntoArray instead.
						thebuffer.rewind();
						lumpcache[lump] = (CacheableDoomObject) what.newInstance();
						((CacheableDoomObject) lumpcache[lump]).unpack((ByteBuffer) thebuffer);
						
						// Track it for freeing
						Track(lumpcache[lump],lump);
						
						if (what == patch_t.class) {
							((patch_t) lumpcache[lump]).name = this.lumpinfo[lump].name;
						}
					} else {
						// replace lump with parsed object.
						lumpcache[lump] = (CacheableDoomObject) thebuffer;
						
						// Track it for freeing
						Track((CacheableDoomObject)thebuffer,lump);
					}
				} catch (Exception e) {
					System.err.println("Could not auto-instantiate lump "
							+ lump + " of class " + what);
					e.printStackTrace();
				}

			} else {
				// Class not specified? Then gimme a containing DoomBuffer!
				DoomBuffer db = new DoomBuffer(thebuffer);				
				lumpcache[lump] = db;
			}
		} else {
			// System.out.println("cache hit on lump " + lump);
			// Z.ChangeTag (lumpcache[lump],tag);
		}
		
		return (T) lumpcache[lump];
	}

	/** A very useful method when you need to load a lump which can consist
	 *  of an arbitrary number of smaller fixed-size objects (assuming that you
	 *  know their number/size and the size of the lump). Practically used 
	 *  by the level loader, to handle loading of sectors, segs, things, etc.
	 *  since their size/lump/number relationship is well-defined.
	 *  
	 *  It possible to do this in other ways, but it's extremely convenient this way.
	 *  
	 *  MAES 24/8/2011: This method is deprecated, Use the much more convenient
	 *  and slipstreamed generic version, which also handles caching of arrays
	 *  and auto-allocation.
	 *  
	 *  @param lump The lump number to load.
	 *  @param tag  Caching tag
	 *  @param array The array with objects to load. Its size implies how many to read.
	 *  @return
	 */
	
	@Deprecated
	public void CacheLumpNumIntoArray(int lump, int tag, Object[] array,
			Class what) throws IOException {

		if (lump >= numlumps) {
			I.Error("W_CacheLumpNum: %i >= numlumps", lump);
		}

		// Nothing cached here...
		if ((lumpcache[lump] == null)) {

			// read the lump in

			//System.out.println("cache miss on lump " + lump);
			// Read as a byte buffer anyway.
			ByteBuffer thebuffer = ByteBuffer.wrap(ReadLump(lump));
			// Store the buffer anyway (as a DoomBuffer)
			lumpcache[lump] = new DoomBuffer(thebuffer);
			
			// Track it (as ONE lump)
			Track(lumpcache[lump],lump);


		} else {
			//System.out.println("cache hit on lump " + lump);
			// Z.ChangeTag (lumpcache[lump],tag);
		}

		// Class type specified. If the previously cached stuff is a
		// "DoomBuffer" we can go on.

		if ((what != null) && (lumpcache[lump].getClass() == DoomBuffer.class)) {
			try {
				// Can it be uncached? If so, deserialize it. FOR EVERY OBJECT.
				ByteBuffer b = ((DoomBuffer) (lumpcache[lump])).getBuffer();
				b.rewind();

				for (int i = 0; i < array.length; i++) {
					if (implementsInterface(what, w.CacheableDoomObject.class)) {
						((CacheableDoomObject) array[i]).unpack(b);
					}
				}
				// lumpcache[lump]=array;
			} catch (Exception e) {
				System.err.println("Could not auto-unpack lump " + lump
						+ " into an array of objects of class " + what);
				e.printStackTrace();
			}

		}
		
		

		return;
	}

	/** A very useful method when you need to load a lump which can consist
	 *  of an arbitrary number of smaller fixed-size objects (assuming that you
	 *  know their number/size and the size of the lump). Practically used 
	 *  by the level loader, to handle loading of sectors, segs, things, etc.
	 *  since their size/lump/number relationship is well-defined.
	 *  
	 *  It possible to do this in other (more verbose) ways, but it's 
	 *  extremely convenient this way, as a lot of common and repetitive code
	 *  is only written once, and generically, here. Trumps the older
	 *  method in v 1.43 of WadLoader, which is deprecated.
	 *  
	 *  @param lump The lump number to load.
	 *  @param num number of objects to read	 *  
	 *  @return a properly sized array of the correct type.
	 */
	
	public <T extends CacheableDoomObject> T[] CacheLumpNumIntoArray(int lump, int num,
			Class<T> what){

		if (lump >= numlumps) {
			I.Error("CacheLumpNumIntoArray: %i >= numlumps", lump);
		}

		if (!implementsInterface(what, CacheableDoomObject.class)){
			I.Error("CacheLumpNumIntoArray: %s does not implement CacheableDoomObject", what.getName());
		}
	
		// Nothing cached here...
		if ((lumpcache[lump] == null)&&(what!=null)) {
			//System.out.println("cache miss on lump " + lump);
			// Read as a byte buffer anyway.
		    ByteBuffer thebuffer = ByteBuffer.wrap(ReadLump(lump));

			T[] stuff=(T[]) C2JUtils.createArrayOfObjects(what, num);
			
			// Store the buffer anyway (as a CacheableDoomObjectContainer)
			lumpcache[lump] = new CacheableDoomObjectContainer<T>(stuff);
			
			// Auto-unpack it, if possible.

			try {
				thebuffer.rewind();
				lumpcache[lump].unpack(thebuffer);
				} catch (Exception e) {
					System.err.println("Could not auto-unpack lump " + lump
							+ " into an array of objects of class " + what);
					e.printStackTrace();
				}
			
			// Track it (as ONE lump)
			Track(lumpcache[lump],lump);


		} else {
			//System.out.println("cache hit on lump " + lump);
			// Z.ChangeTag (lumpcache[lump],tag);
		}

		if (lumpcache[lump]==null) return null;
		
		return (T[]) ((CacheableDoomObjectContainer<T>)lumpcache[lump]).getStuff();
	}
	
	public CacheableDoomObject CacheLumpNum(int lump)
	{
	  return lumpcache[lump];
	}
	
	
	/** Tells us if a class implements a certain interface.
	 *  If you know of a better way, be my guest.
	 * 
	 * @param what
	 * @param which
	 * @return
	 */
	
	protected boolean implementsInterface(Class<?> what, Class<?> which) {
		Class<?>[] shit = what.getInterfaces();
		for (int i = 0; i < shit.length; i++) {
			if (shit[i].equals(which))
				return true;
		}

		return false;
	}

	/* (non-Javadoc)
	 * @see w.IWadLoader#CacheLumpNameAsRawBytes(java.lang.String, int)
	 */

	public byte[] CacheLumpNameAsRawBytes(String name, int tag) {
		return ((DoomBuffer) this.CacheLumpNum(this.GetNumForName(name), tag,
				null)).getBuffer().array();
	}
	
	 /* (non-Javadoc)
	 * @see w.IWadLoader#CacheLumpNumAsRawBytes(int, int)
	 */

    public byte[] CacheLumpNumAsRawBytes(int num, int tag) {
        return ((DoomBuffer) this.CacheLumpNum(num, tag,
                null)).getBuffer().array();
    	}
	

	/* (non-Javadoc)
	 * @see w.IWadLoader#CacheLumpName(java.lang.String, int)
	 */

	public DoomBuffer CacheLumpName(String name, int tag) {
		return (DoomBuffer) this.CacheLumpNum(this.GetNumForName(name), tag,
				DoomBuffer.class);

	}
	
	   public DoomBuffer CacheLumpNumAsDoomBuffer(int lump) {
	        return (DoomBuffer) this.CacheLumpNum(lump, 0,
	                DoomBuffer.class);
	    }
	

	/* (non-Javadoc)
	 * @see w.IWadLoader#CachePatchName(java.lang.String)
	 */

	public patch_t CachePatchName(String name) {
		return (patch_t) this.CacheLumpNum(this.GetNumForName(name), PU_CACHE,
				patch_t.class);

	}

	/* (non-Javadoc)
	 * @see w.IWadLoader#CachePatchName(java.lang.String, int)
	 */

	public patch_t CachePatchName(String name, int tag) {
		return (patch_t) this.CacheLumpNum(this.GetNumForName(name), tag,
				patch_t.class);
	}

	/* (non-Javadoc)
	 * @see w.IWadLoader#CachePatchNum(int, int)
	 */

	public patch_t CachePatchNum(int num) {
		return (patch_t) this.CacheLumpNum(num, PU_CACHE, patch_t.class);
	}

	/* (non-Javadoc)
	 * @see w.IWadLoader#CacheLumpName(java.lang.String, int, java.lang.Class)
	 */
	public <T extends CacheableDoomObject> T CacheLumpName(String name, int tag, Class<T> what) {
		return this.CacheLumpNum(this.GetNumForName(name.toUpperCase()), tag,
				what);
	}

	//
	// W_Profile
	//
	/* USELESS
	 char[][] info = new char[2500][10];

	int profilecount;

	void Profile() throws IOException {
		int i;
		// memblock_t block = null;
		Object ptr;
		char ch;
		FileWriter f;
		int j;
		String name;

		for (i = 0; i < numlumps; i++) {
			ptr = lumpcache[i];
			if ((ptr == null)) {
				ch = ' ';
				continue;
			} else {
				// block = (memblock_t *) ( (byte *)ptr - sizeof(memblock_t));
				if (block.tag < PU_PURGELEVEL)
					ch = 'S';
				else
					ch = 'P';
			}
			info[i][profilecount] = ch;
		}
		profilecount++;

		f = new FileWriter(new File("waddump.txt"));
		// name[8] = 0;

		for (i = 0; i < numlumps; i++) {
			name = lumpinfo[i].name;

			f.write(name);

			for (j = 0; j < profilecount; j++)
				f.write("    " + info[i][j]);

			f.write("\n");
		}
		f.close();
	} */
	
	/* (non-Javadoc)
	 * @see w.IWadLoader#isLumpMarker(int)
	 */
	public boolean isLumpMarker(int lump){
	    return (lumpinfo[lump].size==0);
	}
	
	   /* (non-Javadoc)
	 * @see w.IWadLoader#GetNameForLump(int)
	 */
	public String GetNameForLump(int lump){
	        return lumpinfo[lump].name;
	    }

	// /////////////////// HASHTABLE SYSTEM ///////////////////

	//
	// killough 1/31/98: Initialize lump hash table
	//

	/**
	 * Maes 12/12/2010: Some credit must go to Killough for first
	 * Introducing the hashtable system into Boom. On early releases I had
	 * copied his implementation, but it proved troublesome later on and slower
	 * than just using the language's built-in hash table. Lesson learned, kids:
	 * don't reinvent the wheel.
	 * 
	 * TO get an idea of how superior using a hashtable is, on 1000000 random
	 * lump searches the original takes 48 seconds, searching for precomputed
	 * hashes takes 2.84, and using a Hashtable takes 0.2 sec.
	 * 
	 * And the best part is that Java provides a perfectly reasonable implementation.
	 * 
	 */

	Hashtable<String, Integer> doomhash;

	protected void InitLumpHash() {

		doomhash = new Hashtable<String, Integer>(numlumps);

		//for (int i = 0; i < numlumps; i++)
		//	lumpinfo[i].index = -1; // mark slots empty

		// Insert nodes to the beginning of each chain, in first-to-last
		// lump order, so that the last lump of a given name appears first
		// in any chain, observing pwad ordering rules. killough

		for (int i = 0; i < numlumps; i++) { // hash function:
			doomhash.put(lumpinfo[i].name.toUpperCase(), new Integer(i));
		    }
	}

	/* (non-Javadoc)
	 * @see w.IWadLoader#CheckNumForName(java.lang.String)
	 */
	public int CheckNumForName(String name/* , int namespace */)

	{
		Integer r = doomhash.get(name);
		// System.out.print("Found "+r);

		if (r != null)
			return r.intValue();

		// System.out.print(" found "+lumpinfo[i]+"\n" );
		return -1;
	}

	   /* (non-Javadoc)
     * @see w.IWadLoader#CheckNumForName(java.lang.String)
     */
    public int[] CheckNumsForName(String name)

    {
        
        list.clear();
        
        // Dumb search, no chained hashtables I'm afraid :-/
        // Move backwards, so list is compiled with more recent ones first.
        for (int i=numlumps-1;i>=0;i--){
            if (name.compareToIgnoreCase(lumpinfo[i].name)==0) list.add(i);
        }
        
        final int num=list.size();
        int[] result=new int[num];
        for (int i=0;i<num;i++){
            result[i]=list.get(i);
        }
    
        // Might be empty/null, so check that out.
        return result;
    }
    
    private final ArrayList<Integer> list=new ArrayList<Integer>();
	
	@Override
	public lumpinfo_t GetLumpInfo(int i) {
		return this.lumpinfo[i];
	}
	
	@Override
	public void CloseAllHandles(){
		ArrayList<InputStream> d=new ArrayList<InputStream>();
		
		for (int i=0;i<this.lumpinfo.length;i++){
			if (!d.contains(lumpinfo[i].handle)) d.add(lumpinfo[i].handle);
		}
		
		int count=0;
		
		for (InputStream e:d){
			try {
				e.close();
				//System.err.printf("%s file handle closed",e.toString());
				count++;
			} catch (IOException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}
		}
		
		//System.err.printf("%d file handles closed",count);
				
	}
	
	@Override
	public void finalize(){
		CloseAllHandles();
	}

	public static final int ns_global=0;
	public static final int ns_flats=1;
	public static final int ns_sprites=2;
	
	/** 
	 * Based on Boom's W_CoalesceMarkedResource
	 * Sort of mashes similar namespaces together so that they form
	 * a continuous space (single start and end, e.g. so that multiple
	 * S_START and S_END as well as special DEUTEX lumps mash together
	 * under a common S_START/S_END boundary). Also also sort of performs
	 * a "bubbling down" of marked lumps at the end of the namespace.
	 * 
	 * It's convenient for sprites, but can be replaced by alternatives
	 * for flats.
	 * 
	 * killough 4/17/98: add namespace tags
	 *   
	 * @param start_marker
	 * @param end_marker
	 * @param namespace
	 * @return
	 */
	public int CoalesceMarkedResource(String start_marker,
	                                     String end_marker, li_namespace namespace)
	{
	  int result = 0;
	  lumpinfo_t[] marked = new lumpinfo_t[numlumps];
	 // C2JUtils.initArrayOfObjects(marked, lumpinfo_t.class);
	  int num_marked = 0, num_unmarked = 0;
	  boolean is_marked = false, mark_end = false;
	  lumpinfo_t lump;

	  // Scan for specified start mark
	  for (int i=0;i<numlumps;i++){
		  lump=lumpinfo[i];
	    if (IsMarker(start_marker,lump.name)) // start marker found
	      { // If this is the first start marker, add start marker to marked lumps
//	    	System.err.printf("%s identified as starter mark for %s index %d\n",lump.name,
//	    			start_marker,i);
	        if (num_marked==0)
	          {
	        	marked[num_marked]=new lumpinfo_t();
	            marked[num_marked].name=new String(start_marker);
	            marked[num_marked].size = 0;  // killough 3/20/98: force size to be 0
	            marked[num_marked].namespace =li_namespace.ns_global;        // killough 4/17/98
	            marked[num_marked].handle=lump.handle;
	            // No real use for this yet
	            marked[num_marked].wadfile = lump.wadfile;
	            num_marked = 1;
		    	//System.err.printf("%s identified as FIRST starter mark for %s index %d\n",lump.name,
		    	//		start_marker,i);
	          }
	        is_marked = true;                            // start marking lumps
	      }
	    else
	      if (IsMarker(end_marker, lump.name))       // end marker found
	        {
		    //	System.err.printf("%s identified as end mark for %s index %d\n",lump.name,
		    //			end_marker,i);
	          mark_end = true;                           // add end marker below
	          is_marked = false;                          // stop marking lumps
	        }
	      else
	        if (is_marked || lump.namespace == namespace)
	          {
	            // if we are marking lumps,
	            // move lump to marked list
	            // sf: check for namespace already set

	            // sf 26/10/99:
	            // ignore sprite lumps smaller than 8 bytes (the smallest possible)
	            // in size -- this was used by some dmadds wads
	            // as an 'empty' graphics resource
	            if(namespace != li_namespace.ns_sprites || lump.size > 8)
	            {
	              marked[num_marked] = lump.clone();
	             // System.err.printf("Marked %s as %d for %s\n",lump.name,num_marked,namespace);
	              marked[num_marked++].namespace = namespace;  // killough 4/17/98
	              result++;
	            }
	          }
	        else
	          lumpinfo[num_unmarked++] = lump.clone();       // else move down THIS list
	  }
	    
	  // Append marked list to end of unmarked list
	  System.arraycopy(marked, 0, lumpinfo, num_unmarked, num_marked);

	  numlumps = num_unmarked + num_marked;           // new total number of lumps

	  if (mark_end)                                   // add end marker
	    {
	      lumpinfo[numlumps].size = 0;  // killough 3/20/98: force size to be 0
	      //lumpinfo[numlumps].wadfile = NULL;
	      lumpinfo[numlumps].namespace = li_namespace.ns_global;   // killough 4/17/98
	      lumpinfo[numlumps++].name=end_marker;
	    }

	  return result;
	}
	  
	public final static boolean IsMarker(String marker, String name)
	{
		// Safeguard against nameless marker lumps e.g. in Galaxia.wad
		if (name==null || name.length()==0) return false;
	  boolean result= name.equalsIgnoreCase(marker) ||
	    // doubled first character test for single-character prefixes only
	    // FF_* is valid alias for F_*, but HI_* should not allow HHI_*
	    (marker.charAt(1) == '_' && name.charAt(0) == marker.charAt(0) && 
	    		name.substring(1).equalsIgnoreCase(marker));

	  return result;
	}

	@Override
	public void UnlockLumpNum(int lump) {
		lumpcache[lump]=null;
	}

	@Override
	public void InjectLumpNum(int lump, CacheableDoomObject obj){
		lumpcache[lump]=obj;
	}
	
	//// Merged remnants from LumpZone here.

	Hashtable<CacheableDoomObject, Integer> zone;

	/** Add a lump to the tracking */

	public void Track(CacheableDoomObject lump, int index){
		zone.put(lump, index);
	}

	@Override
	public void UnlockLumpNum(CacheableDoomObject lump){
		// Remove it from the reference
		Integer lumpno=zone.remove(lump);
		

		// Force nulling. This should trigger garbage collection,
		// and reclaim some memory, provided you also nulled any other 
		// reference to a certain lump. Therefore, make sure you null 
		// stuff right after calling this method, if you want to make sure 
		// that they won't be referenced anywhere else.
		
		if (lumpno!=null) {
			lumpcache[lumpno]=null;
			//System.out.printf("Lump %d %d freed\n",lump.hashCode(),lumpno);
		}
	}

    @Override
    public boolean verifyLumpName(int lump, String lumpname) {
        
        // Lump number invalid
        if (lump<0 || lump>numlumps-1) return false;
        
        String name=GetNameForLump(lump);
        
        // Expected lump name not found
        if (name==null || lumpname.compareToIgnoreCase(name)!=0) return false;        
        
        // Everything should be OK now...
        return true;
    }

    @Override
    public int GetWadfileIndex(wadfile_info_t wad1) {        
        return wadfiles.indexOf(wad1);
    }

    @Override
    public int GetNumWadfiles() {
        return wadfiles.size();
    }

	
}

//$Log: WadLoader.java,v $
//Revision 1.66  2016/07/04 13:50:50  velktron
//GPL license header
//
//Revision 1.65  2016/06/06 13:45:44  velktron
//New features to support basic WAD building/writing.
//
//Revision 1.64  2014/03/28 00:55:32  velktron
//Cleaner, generic-based design to minimize warnings and suppressions. <T extends CacheableDoomObject> used whenever possible.
//
//Revision 1.63  2013/06/03 10:36:33  velktron
//Jaguar handling (actualname)
//
//Revision 1.62.2.1  2013/01/09 14:24:12  velktron
//Uses the rest of the crap
//
//Revision 1.62  2012/11/08 17:16:12  velktron
//Made GetLumpForNum generic.
//
//Revision 1.61  2012/09/25 16:33:36  velktron
//Dummy Doomsystem for easy testing.
//
//Revision 1.60  2012/09/24 17:16:22  velktron
//Massive merge between HiColor and HEAD. There's no difference from now on, and development continues on HEAD.
//
//Revision 1.57.2.5  2012/09/19 21:46:46  velktron
//Simpler call for getPATCH
//
//Revision 1.57.2.4  2012/09/04 15:08:34  velktron
//New GetNumsForName function.
//
//Revision 1.57.2.3  2012/06/14 22:38:20  velktron
//Uses new disk flasher.
//
//Revision 1.57.2.2  2011/12/08 00:40:40  velktron
//Fix for Galaxia.wad nameless lumps.
//
//Revision 1.57.2.1  2011/12/05 12:05:13  velktron
//Fixed a vexing bug with ZIP file header & TOC reading.
//
//Revision 1.57  2011/11/09 19:07:40  velktron
//Adapted to handling ZIP files
//
//Revision 1.56  2011/11/03 21:14:30  velktron
//Added -FINALLY!- resource access testing before adding them -_-
//
//Revision 1.55  2011/11/01 22:09:11  velktron
//Some more progress on URI handling. Not essential/not breaking.
//
//Revision 1.54  2011/10/25 19:45:51  velktron
//More efficient use of bis ;-)
//
//Revision 1.53  2011/10/25 19:42:48  velktron
//Added advanced streaming input support and more convenient byte[] ReadLump methods.
//
//Revision 1.52  2011/10/24 02:07:08  velktron
//DoomFile model abandoned. Now streams are used whenever possible, with possible future expandability to use e.g. URL streams or other types of resources other than RandomAccessFiles.
//
//Revision 1.51  2011/10/23 22:50:42  velktron
//Added InjectLumpNum function to force generated contents.
//
//Revision 1.50  2011/10/23 18:16:31  velktron
//Cleanup, moved logs to the end of the file
//
//Revision 1.49  2011/10/19 12:34:29  velktron
//Using extractFileBase in C2JUtils now, got rid of filelength
//
//Revision 1.48  2011/09/29 15:18:31  velktron
//Resource coalescing correctly handles wadfiles
//
//Revision 1.47  2011/09/27 15:57:09  velktron
//Full wadinfo (lump "ownership") system in place, borrowed from prBoom+ with a twist ;-)
//
//Revision 1.46  2011/09/16 11:17:22  velktron
//Added verifyLumpName function
//
//Revision 1.45  2011/09/02 16:29:59  velktron
//Minor interface change
//
//Revision 1.44  2011/08/24 14:55:42  velktron
//Deprecated old CacheLumpNumIntoArray method, much cleaner system introduced.
//
//Revision 1.43  2011/08/23 16:10:20  velktron
//Got rid of Z remnants, commenter out Profile (useless as it is)
//
//Revision 1.42  2011/08/23 16:08:43  velktron
//Integrated Zone functionality in WadLoader, Boom-like UnlockLump. Makes things MUCH easier.
//
//Revision 1.41  2011/08/02 13:49:56  velktron
//Fixed missing handle on generated lumpinfo_t
//
//Revision 1.40  2011/08/01 22:09:14  velktron
//Flats coalescing.
//
//Revision 1.39  2011/08/01 21:42:56  velktron
//Added BOOM CoaleseResources function.
//
//Revision 1.38  2011/07/13 16:34:18  velktron
//Started adding some BOOM wad handling stuff. Still WIP though.
//
//Revision 1.37  2011/07/05 13:26:30  velktron
//Added handle closing functionality.
//
//Revision 1.36  2011/06/12 21:52:11  velktron
//Made CheckNumForName uppercase-proof, at last.
//
//Revision 1.35  2011/06/03 16:35:27  velktron
//Default fakezone
//
//Revision 1.34  2011/06/02 14:23:20  velktron
//Added ability to "peg" an IZone manager.
//
//Revision 1.33  2011/05/23 17:00:39  velktron
//Got rid of verbosity
//
//Revision 1.32  2011/05/22 21:08:28  velktron
//Added better filename handling.
//
//Revision 1.31  2011/05/18 16:58:11  velktron
//Changed to DoomStatus
//
//Revision 1.30  2011/05/13 11:20:07  velktron
//Why the hell did this not implement IReadableDoomObject?
//
//Revision 1.29  2011/05/13 11:17:48  velktron
//Changed default read buffer behavior. Now it's ALWAYS reset when reading from disk, and not up to the CacheableDoomObject. This does not affect bulk/stream reads.
//
//Revision 1.28  2011/05/10 10:39:18  velktron
//Semi-playable Techdemo v1.3 milestone
//
//Revision 1.27  2011/01/26 00:04:45  velktron
//DEUTEX flat support, Unrolled drawspan precision fix.
//
//Revision 1.26  2011/01/10 16:40:54  velktron
//Some v1.3 commits: OSX fix, limit-removing flat management (to fix),
//
//Revision 1.25  2010/12/22 01:23:15  velktron
//Definitively fixed plain DrawColumn.
//Fixed PATCH/TEXTURE and filelump/wadloader capitalization.
//Brought back some testers.
//
//Revision 1.24  2010/12/14 17:55:59  velktron
//Fixed weapon bobbing, added translucent column drawing, separated rendering commons.
//
//Revision 1.23  2010/12/13 16:03:20  velktron
//More fixes  in the wad loading code
//
//Revision 1.22  2010/12/12 21:27:17  velktron
//Fixed hashtable bug. Now using Java's one, faster AND easier to follow.
//
//Revision 1.21  2010/10/08 16:55:50  velktron
//Duh
//
//Revision 1.20  2010/09/27 02:27:29  velktron
//BEASTLY update
//
//Revision 1.19  2010/09/24 17:58:39  velktron
//Menus and HU  functional -mostly.
//
//Revision 1.18  2010/09/23 20:36:45  velktron
//*** empty log message ***
//
//Revision 1.17  2010/09/23 15:11:57  velktron
//A bit closer...
//
//Revision 1.16  2010/09/22 16:40:02  velktron
//MASSIVE changes in the status passing model.
//DoomMain and DoomGame unified.
//Doomstat merged into DoomMain (now status and game functions are one).
//
//Most of DoomMain implemented. Possible to attempt a "classic type" start but will stop when reading sprites.
//
//Revision 1.15  2010/09/13 15:39:17  velktron
//Moving towards an unified gameplay approach...
//
//Revision 1.14  2010/09/09 01:13:19  velktron
//MUCH better rendering and testers.
//
//Revision 1.13  2010/09/07 16:23:00  velktron
//*** empty log message ***
//
//Revision 1.12  2010/09/03 15:30:34  velktron
//More work on unified renderer
//
//Revision 1.11  2010/09/02 15:56:54  velktron
//Bulk of unified renderer copyediting done.
//
//Some changes like e.g. global separate limits class and instance methods for seg_t and node_t introduced.
//
//Revision 1.10  2010/08/30 15:53:19  velktron
//Screen wipes work...Finale coded but untested.
//GRID.WAD included for testing.
//
//Revision 1.9  2010/08/23 14:36:08  velktron
//Menu mostly working, implemented Killough's fast hash-based GetNumForName, although it can probably be finetuned even more.
//
//Revision 1.8  2010/08/13 14:06:36  velktron
//Endlevel screen fully functional!
//
//Revision 1.7  2010/08/11 16:31:34  velktron
//Map loading works! Check out LevelLoaderTester for more.
//
//Revision 1.6  2010/08/10 16:41:57  velktron
//Threw some work into map loading.
//
//Revision 1.5  2010/07/22 15:37:53  velktron
//MAJOR changes in Menu system.
//
//Revision 1.4  2010/07/15 14:01:49  velktron
//Added reflector Method stuff for function pointers.
//
//Revision 1.3  2010/07/06 15:20:23  velktron
//Several changes in the WAD loading routine. Now lumps are directly unpacked as "CacheableDoomObjects" and only defaulting will result in "raw" DoomBuffer reads.
//
//Makes caching more effective.
//
//Revision 1.2 2010/06/30 11:44:40 velktron
//Added a tester for patches (one of the most loosely-coupled structs in Doom!)
//and fixed some minor stuff all around.
//
//Revision 1.1 2010/06/30 08:58:50 velktron
//Let's see if this stuff will finally commit....
//
//
//Most stuff is still being worked on. For a good place to start and get an
//idea of what is being done, I suggest checking out the "testers" package.
//
//Revision 1.1 2010/06/29 11:07:34 velktron
//Release often, release early they say...
//
//Commiting ALL stuff done so far. A lot of stuff is still broken/incomplete,
//and there's still mixed C code in there. I suggest you load everything up in
//Eclpise and see what gives from there.
//
//A good place to start is the testers/ directory, where you can get an idea of
//how a few of the implemented stuff works.
package w;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.CharBuffer;
import java.nio.DoubleBuffer;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.nio.LongBuffer;
import java.nio.ShortBuffer;

/** Very similar to the concept of ReadableDoomObjects
 *  but made to work with byte buffers instead. 
 *  
 *  This is normally NOT used to pass data around: I am 
 *  using it as a workaround to store raw byte buffers
 *  into a "CacheableDoomObject" array, as Java
 *  doesn't seem to like storing both ByteBuffers and
 *  CacheableDoomObjects in the same array. WTF...
 * 
 * @author admin
 *
 */

public class DoomBuffer implements CacheableDoomObject  {

    public DoomBuffer(){

    }
    
    public DoomBuffer(ByteBuffer b){
        this.buffer=b;
    }

    private ByteBuffer buffer;

    public static void readObjectArray(ByteBuffer buf,CacheableDoomObject[] s,int len) throws IOException {

        if ((s==null)||(len==0)) return;
        
        for (int i=0;i<Math.min(len,s.length);i++){    
            s[i].unpack(buf);
        }
    }
    
    public static void readIntArray(ByteBuffer buf,int[] s,int len) throws IOException {

        if ((s==null)||(len==0)) return;
        
        for (int i=0;i<Math.min(len,s.length);i++){           
            s[i]=buf.getInt();
        }
    }
    
    public static void putIntArray(ByteBuffer buf,int[] s,int len,ByteOrder bo) throws IOException {
        buf.order(bo);
        
        if ((s==null)||(len==0)) return;
        
        for (int i=0;i<Math.min(len,s.length);i++){           
            buf.putInt(s[i]);
        }
    }
    
    public static void putBooleanIntArray(ByteBuffer buf,boolean[] s,int len,ByteOrder bo) throws IOException {
        buf.order(bo);
        
        if ((s==null)||(len==0)) return;
        
        for (int i=0;i<Math.min(len,s.length);i++){           
            buf.putInt(s[i]?1:0);
        }
    }
    
    public static void putBooleanInt(ByteBuffer buf,boolean s,ByteOrder bo) throws IOException {
        buf.order(bo);        
            buf.putInt(s?1:0);
    }
    
    public static void readCharArray(ByteBuffer buf,char[] s,int len) throws IOException {

        if ((s==null)||(len==0)) return;
        
        for (int i=0;i<Math.min(len,s.length);i++){           
            s[i]=buf.getChar();
        }
    }
    
    public static void readShortArray(ByteBuffer buf,short[] s,int len) throws IOException {

        if ((s==null)||(len==0)) return;
        
        for (int i=0;i<Math.min(len,s.length);i++){           
            s[i]=buf.getShort();
        }
    }

    public void readShortArray(short[] s,int len) throws IOException {

        if ((s==null)||(len==0)) return;
        
        for (int i=0;i<Math.min(len,s.length);i++){           
            s[i]=this.buffer.getShort();
            
        }
    }
    
    public void readCharArray(char[] s,int len) throws IOException {

        if ((s==null)||(len==0)) return;
        
        for (int i=0;i<Math.min(len,s.length);i++){           
            s[i]=this.buffer.getChar();
            
        }
    }
    
    public void readCharArray(int[] s,int len) throws IOException {

        if ((s==null)||(len==0)) return;
        
        for (int i=0;i<Math.min(len,s.length);i++){           
            s[i]=this.buffer.getChar();
            
        }
    }
    
    
    /** Reads a length specified string from a buffer. */
    public static String readString(ByteBuffer buf) throws IOException {
        int len = buf.getInt();

        if (len == -1)
            return null;

        if (len == 0)
            return "";

        byte bb[] = new byte[len];

        buf.get(bb, 0, len);

        return new String(bb, 0, len);
    }

 /** MAES: Reads a specified number of bytes from a buffer into a new String.
  *  With many lengths being implicit, we need to actually take the loader by the hand.
  * 
  * @param buf
  * @param len
  * @return
  * @throws IOException
  */
    
    public static String getString(ByteBuffer buf, int len) throws IOException {

        if (len == -1)
            return null;

        if (len == 0)
            return "";

        byte bb[] = new byte[len];

        buf.get(bb, 0, len);

        return new String(bb, 0, len);
    }
    
    /** MAES: Reads a maximum specified number of bytes from a buffer into a new String,
     * considering the bytes as representing a null-terminated, C-style string.
     * 
     * @param buf
     * @param len
     * @return
     * @throws IOException
     */
       
       public static String getNullTerminatedString(ByteBuffer buf, int len) throws IOException {

           if (len == -1)
               return null;

           if (len == 0)
               return "";

           byte bb[] = new byte[len];
           
           buf.get(bb, 0, len);
           // Detect null-termination.
           for (int i=0;i<len;i++){
               if (bb[i]==0x00){
                   len=i;
                   break;
               }
           }
           
           return new String(bb, 0, len);
       }
    
       /** MAES: WRITES a maximum specified number of characters from a String to a buffer, using C-style
        * null Termination.
        * 
        * @param buf
        * @param str
        * @param len
        * @return 
        * @throws IOException
        */
          
        public static int putNullTerminatedString(ByteBuffer buf, String str, int len) throws IOException {

              if (str==null || len<=0) return 0;
              
              if (str.length() != 0){
                  byte[] str8=str.getBytes("ISO-8859-1");
                  buf.put(str8,0,Math.min(len,str.length()));
                  // Fill in with 0s if something's left.
                  if (str8.length<len){
                      for (int i=0;i<len-str8.length;i++){
                          buf.put((byte)0x00);
                      }
                  }
                  return str8.length;
              }
              
              return 0;
          }
       
       
    /** MAES: Reads a specified number of bytes from a buffer into a new String.
     *  With many lengths being implicit, we need to actually take the loader by the hand.
     * 
     * @param buf
     * @param len
     * @return
     * @throws IOException
     */
       
       public static char[] getCharSeq(ByteBuffer buf, int len) throws IOException {
           return (getString(buf,len)).toCharArray();
       }

    

    @Override
    public void unpack(ByteBuffer buf)
            throws IOException {
        this.buffer=buf;
        
    }

    public ByteBuffer getBuffer() {
        return buffer;
    }
    
    public void setOrder(ByteOrder bo){
        this.buffer.order(bo);
    }

    public void rewind() {
        this.buffer.rewind();
    }

    public static int getBEInt(byte b3,byte b2, byte b1,byte b0) {        
        return (b3<<24|b2<<16|b1<<8|b0);
    }
    
    public static int getBEInt(byte[] buf,int offset) {        
        return (buf[offset]<<24|buf[offset+1]<<16|buf[offset+2]<<8|buf[offset+3]);
    }
    
    public static int getBEInt(byte[] buf) {        
        return (buf[0]<<24|buf[1]<<16|buf[2]<<8|buf[3]);
    }
    
    public static int getLEInt(byte b0,byte b1, byte b2,byte b3) {        
        return (b3<<24|b2<<16|b1<<8|b0);
    }
    
    public static int getLEInt(byte[] buf) {        
        return (buf[3]<<24|buf[2]<<16|buf[1]<<24|buf[0]);
    }
    
    public static short getBEShort(byte[] buf) {        
        return (short) (buf[0]<<8|buf[1]);
    }
    
    public static short getLEShort(byte[] buf) {        
        return (short) (buf[0]<<8|buf[1]);
    }
    
}

package w;

import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;

/** This is an interface implemented by objects that must be read form disk.
 *  Every object is supposed to do its own umarshalling. This way,
 *  structured and hierchical reads are possible. Another superior innovation
 *  of Mocha Doom ;-) Err....ok :-p
 *  
 * @author Velktron
 *
 */

public interface IReadableDoomObject {
    
    public void read(DataInputStream f) throws IOException ;
}

package g;

import java.lang.reflect.Array;

import p.intercept_t;
import rr.line_t;
import data.mapthing_t;
import doom.DoomStatus;

/* Emacs style mode select   -*- C++ -*-
 *-----------------------------------------------------------------------------
 *
 *
 *  PrBoom: a Doom port merged with LxDoom and LSDLDoom
 *  based on BOOM, a modified and improved DOOM engine
 *  Copyright (C) 1999 by
 *  id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman
 *  Copyright (C) 1999-2000 by
 *  Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze
 *  Copyright 2005, 2006 by
 *  Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko
 *
 *  This program is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU General Public License
 *  as published by the Free Software Foundation; either version 2
 *  of the License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 *  02111-1307, USA.
 *
 * DESCRIPTION:
 *
 * Overflow emulation code, totally ripped off prBoom+! Sieg Heil!
 *
 *---------------------------------------------------------------------
 */

public class Overflow {

    String[] overflow_cfgname =
    {
      "overrun_spechit_emulate",
      "overrun_reject_emulate",
      "overrun_intercept_emulate",
      "overrun_playeringame_emulate",
      "overrun_donut_emulate",
      "overrun_missedbackside_emulate"
    };
    
    class overrun_param_t{
      boolean warn;
      boolean emulate;
      boolean tmp_emulate;
      int promted;
      int shit_happens;
    }
    
    class intercepts_overrun_t
    {
        int len;
        long addr;
        boolean int16_array;
    } 


    public static final int OVERFLOW_SPECHIT=0;
    public static final int OVERFLOW_REJECT=1;
    public static final int OVERFLOW_INTERCEPT=2;
    public static final int OVERFLOW_PLYERINGAME=3;
    public static final int OVERFLOW_DONUT=4;
    public static final int OVERFLOW_MISSEDBACKSIDE=5;
    public static final int OVERFLOW_MAX=6;
    
    public static final int MAXINTERCEPTS_ORIGINAL=128;

    
    
    public static final boolean EMULATE(int overflow) {
        return (overflows[overflow].emulate || overflows[overflow].tmp_emulate);
    }
    
    public static final boolean PROCESS(int overflow) {
        return (overflows[overflow].warn || EMULATE(overflow));
    }

    static overrun_param_t[] overflows=new overrun_param_t[OVERFLOW_MAX];
    
    public static intercepts_overrun_t[] intercepts_overrun;

/*
    static void ShowOverflowWarning(int overflow, int fatal, String ... params)
    {
      overflows[overflow].shit_happens = true;

      if (overflows[overflow].warn && !overflows[overflow].promted)
      {
        va_list argptr;
        char buffer[1024];

        static const char *name[OVERFLOW_MAX] = {
          "SPECHIT", "REJECT", "INTERCEPT", "PLYERINGAME", "DONUT", "MISSEDBACKSIDE"};

        static const char str1[] =
          "Too big or not supported %s overflow has been detected. "
          "Desync or crash can occur soon "
          "or during playback with the vanilla engine in case you're recording demo.%s%s";
        
        static const char str2[] = 
          "%s overflow has been detected.%s%s";

        static const char str3[] = 
          "%s overflow has been detected. "
          "The option responsible for emulation of this overflow is switched off "
          "hence desync or crash can occur soon "
          "or during playback with the vanilla engine in case you're recording demo.%s%s";

        overflows[overflow].promted = true;

        sprintf(buffer,
          (fatal ? str1 : (EMULATE(overflow) ? str2 : str3)), 
          name[overflow],
          "\nYou can change PrBoom behaviour for this overflow through in-game menu.",
          params);
        
        va_start(argptr, params);
        I_vWarning(buffer, argptr);
        va_end(argptr);
      }
    } */

    // e6y
    //
    // Intercepts Overrun emulation
    // See more information on:
    // doomworld.com/vb/doom-speed-demos/35214-spechits-reject-and-intercepts-overflow-lists
    //
    // Thanks to Simon Howard (fraggle) for refactor the intercepts
    // overrun code so that it should work properly on big endian machines
    // as well as little endian machines.

    // Overwrite a specific memory location with a value.
    
    /*
    static void InterceptsMemoryOverrun(int location, int value)
    {
      int i, offset;
      int index;
      long addr;

      i = 0;
      offset = 0;

      // Search down the array until we find the right entry

      while (intercepts_overrun[i].len != 0)
      {
        if (offset + intercepts_overrun[i].len > location)
        {
          addr = intercepts_overrun[i].addr;

          // Write the value to the memory location.
          // 16-bit and 32-bit values are written differently.

          if (addr != NULL)
          {
            if (intercepts_overrun[i].int16_array)
            {
              index = (location - offset) / 2;
              ((short *) addr)[index] = value & 0xffff;
              ((short *) addr)[index + 1] = (value >> 16) & 0xffff;
            }
            else
            {
              index = (location - offset) / 4;
              ((int *) addr)[index] = value;
            }
          }

          break;
        }

        offset += intercepts_overrun[i].len;
        ++i;
      }
    }
*/

    /*
    void InterceptsOverrun(int num_intercepts, intercept_t intercept)
    {
      if (num_intercepts > MAXINTERCEPTS_ORIGINAL && demo_compatibility && PROCESS(OVERFLOW_INTERCEPT))
      {
        ShowOverflowWarning(OVERFLOW_INTERCEPT, false, "");

        if (EMULATE(OVERFLOW_INTERCEPT))
        {
          int location = (num_intercepts - MAXINTERCEPTS_ORIGINAL - 1) * 12;

          // Overwrite memory that is overwritten in Vanilla Doom, using
          // the values from the intercept structure.
          //
          // Note: the .d.{thing,line} member should really have its
          // address translated into the correct address value for 
          // Vanilla Doom.

          InterceptsMemoryOverrun(location, intercept.frac);
          InterceptsMemoryOverrun(location + 4, intercept.isaline);
          InterceptsMemoryOverrun(location + 8, (int) intercept.d.thing);
        }
      }
    }
*/
    // e6y
    // playeringame overrun emulation
    // it detects and emulates overflows on vex6d.wad\bug_wald(toke).lmp, etc.
    // http://www.doom2.net/doom2/research/runningbody.zip

    static boolean PlayeringameOverrun(final mapthing_t mthing, DoomStatus DS)
    {
      if (mthing.type == 0 && PROCESS(OVERFLOW_PLYERINGAME))
      {
        ShowOverflowWarning(OVERFLOW_PLYERINGAME, DS.players[4].didsecret, "");

        if (EMULATE(OVERFLOW_PLYERINGAME))
        {
          return true;
        }
      }
      return false;
    }

 //
 // spechit overrun emulation
 //

 // Spechit overrun magic value.
 public static final int DEFAULT_SPECHIT_MAGIC =0x01C09C98;

 class spechit_overrun_param_t
 {
   line_t line;

   line_t[] spechit;
   int numspechit;

   int[] tmbbox;
   int[] tmfloorz;
   int[] tmceilingz;

   boolean crushchange;
   boolean nofit;
 }

    long spechit_baseaddr = 0;

    // e6y
    // Code to emulate the behavior of Vanilla Doom when encountering an overrun
    // of the spechit array.
    // No more desyncs on compet-n\hr.wad\hr18*.lmp, all strain.wad\map07 demos etc.
    // http://www.doomworld.com/vb/showthread.php?s=&threadid=35214
    // See more information on:
    // doomworld.com/vb/doom-speed-demos/35214-spechits-reject-and-intercepts-overflow-lists
    
    /*
    static void SpechitOverrun(spechit_overrun_param_t params)
    {
      int numspechit = params.numspechit;

      if (demo_compatibility && numspechit > 8)
      {
        line_t[] spechit = params.spechit;

        ShowOverflowWarning(OVERFLOW_SPECHIT,
          numspechit > 
            (compatibility_level == dosdoom_compatibility || 
            compatibility_level == tasdoom_compatibility ? 10 : 14), 
          "\n\nThe list of LineID leading to overrun:\n%d, %d, %d, %d, %d, %d, %d, %d, %d.",
          spechit[0].iLineID, spechit[1].iLineID, spechit[2].iLineID,
          spechit[3].iLineID, spechit[4].iLineID, spechit[5].iLineID,
          spechit[6].iLineID, spechit[7].iLineID, spechit[8].iLineID);

        if (EMULATE(OVERFLOW_SPECHIT))
        {
          unsigned int addr;

          if (spechit_baseaddr == 0)
          {
            int p;

            // This is the first time we have had an overrun.  Work out
            // what base address we are going to use.
            // Allow a spechit value to be specified on the command line.

            //
            // Use the specified magic value when emulating spechit overruns.
            //

            p = M_CheckParm("-spechit");
            
            if (p > 0)
            {
              //baseaddr = atoi(myargv[p+1]);
              M_StrToInt(myargv[p+1], (long*)&spechit_baseaddr);
            }
            else
            {
              spechit_baseaddr = DEFAULT_SPECHIT_MAGIC;
            }
          }

          // Calculate address used in doom2.exe

          addr = spechit_baseaddr + (params.line - lines) * 0x3E;

          if (compatibility_level == dosdoom_compatibility || compatibility_level == tasdoom_compatibility)
          {
            // There are no more desyncs in the following dosdoom demos: 
            // flsofdth.wad\fod3uv.lmp - http://www.doomworld.com/sda/flsofdth.htm
            // hr.wad\hf181430.lmp - http://www.doomworld.com/tas/hf181430.zip
            // hr.wad\hr181329.lmp - http://www.doomworld.com/tas/hr181329.zip
            // icarus.wad\ic09uv.lmp - http://competn.doom2.net/pub/sda/i-o/icuvlmps.zip

            switch(numspechit)
            {
            case 9: 
              *(params.tmfloorz) = addr;
              break;
            case 10:
              *(params.tmceilingz) = addr;
              break;
              
            default:
              fprintf(stderr, "SpechitOverrun: Warning: unable to emulate"
                              "an overrun where numspechit=%i\n",
                               numspechit);
              break;
            }
          }
          else
          {
            switch(numspechit)
            {
            case 9: 
            case 10:
            case 11:
            case 12:
              params.tmbbox[numspechit-9] = addr;
              break;
            case 13:
              *(params.nofit) = addr;
              break;
            case 14:
              *(params.crushchange) = addr;
              break;

            default:
              lprintf(LO_ERROR, "SpechitOverrun: Warning: unable to emulate"
                                " an overrun where numspechit=%i\n",
                                numspechit);
              break;
            }
          }
        }
      }
    }
    */

    //
    // reject overrun emulation
    //

    // padding the reject table if it is too short
    // totallines must be the number returned by P_GroupLines()
    // an underflow will be padded with zeroes, or a doom.exe z_zone header
    // 
    // e6y
    // reject overrun emulation code
    // It's emulated successfully if the size of overflow no more than 16 bytes.
    // No more desync on teeth-32.wad\teeth-32.lmp.
    // http://www.doomworld.com/vb/showthread.php?s=&threadid=35214

    public static byte[] RejectOverrun(int rejectlump, final byte[] rejectmatrix, int totallines, int numsectors)
    {
      int required;
      byte []newreject;
      byte pad;
      int length=rejectmatrix.length;

      required = (numsectors * numsectors + 7) / 8;

      if (length < required)
      {
        // allocate a new block and copy the reject table into it; zero the rest
        // PU_LEVEL => will be freed on level exit
        newreject = new byte[required];
        System.arraycopy(rejectmatrix, 0, newreject, 0, length);

        // e6y
        // PrBoom 2.2.5 and 2.2.6 padded a short REJECT with 0xff
        // This command line switch is needed for all potential demos 
        // recorded with these versions of PrBoom on maps with too short REJECT
        // I don't think there are any demos that will need it but yes that seems sensible
        // pad = prboom_comp[PC_REJECT_PAD_WITH_FF].state ? 0xff : 0;
        // MAES: obviously we don't need that.
        pad=0;

        Array.setByte(newreject,length,pad);

        // unlock the original lump, it is no longer needed
        //W_UnlockLumpNum(rejectlump);
        rejectlump = -1;

        if (demo_compatibility && PROCESS(OVERFLOW_REJECT))
        {
          ShowOverflowWarning(OVERFLOW_REJECT, (required - length > 16) || (length%4 != 0), "");

          if (EMULATE(OVERFLOW_REJECT))
          {
            // merged in RejectOverrunAddInt(), and the 4 calls to it, here
            unsigned int rejectpad[4] = {
              0,        // size, will be filled in using totallines
              0,        // part of the header of a doom.exe z_zone block
              50,       // DOOM_CONST_PU_LEVEL
              0x1d4a11  // DOOM_CONST_ZONEID
            };
            unsigned int i, pad = 0, *src = rejectpad;
            byte *dest = newreject + length;

            rejectpad[0] = ((totallines*4+3)&~3)+24; // doom.exe zone header size

            // copy at most 16 bytes from rejectpad
            // emulating a 32-bit, little-endian architecture (can't memmove)
            for (i = 0; i < (unsigned int)(required - length) && i < 16; i++) { // 16 hard-coded
              if (!(i&3)) // get the next 4 bytes to copy when i=0,4,8,12
                pad = *src++;
              *dest++ = pad & 0xff; // store lowest-significant byte
              pad >>= 8; // rotate the next byte down
            }
          }
        }

        lprintf(LO_WARN, "P_LoadReject: REJECT too short (%u<%u) - padded\n", length, required);
      }
    }

    //
    // Read Access Violation emulation.
    //

    // C:\>debug
    // -d 0:0
    //
    // DOS 6.22:
    // 0000:0000  (57 92 19 00) F4 06 70 00-(16 00)
    // DOS 7.1:
    // 0000:0000  (9E 0F C9 00) 65 04 70 00-(16 00)
    // Win98:
    // 0000:0000  (9E 0F C9 00) 65 04 70 00-(16 00)
    // DOSBox under XP:
    // 0000:0000  (00 00 00 F1) ?? ?? ?? 00-(07 00)

    #define DOS_MEM_DUMP_SIZE 10

    unsigned char mem_dump_dos622[DOS_MEM_DUMP_SIZE] = {
      0x57, 0x92, 0x19, 0x00, 0xF4, 0x06, 0x70, 0x00, 0x16, 0x00};
    unsigned char mem_dump_win98[DOS_MEM_DUMP_SIZE] = {
      0x9E, 0x0F, 0xC9, 0x00, 0x65, 0x04, 0x70, 0x00, 0x16, 0x00};
    unsigned char mem_dump_dosbox[DOS_MEM_DUMP_SIZE] = {
      0x00, 0x00, 0x00, 0xF1, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00};

    unsigned char *dos_mem_dump = mem_dump_dos622;

    static int GetMemoryValue(unsigned int offset, void *value, int size)
    {
      static int firsttime = true;

      if (firsttime)
      {
        int p, i, val;

        firsttime = false;
        i = 0;

        if ((p = M_CheckParm("-setmem")) && (p < myargc-1))
        {
          if (!strcasecmp(myargv[p + 1], "dos622"))
            dos_mem_dump = mem_dump_dos622;
          if (!strcasecmp(myargv[p + 1], "dos71"))
            dos_mem_dump = mem_dump_win98;
          else if (!strcasecmp(myargv[p + 1], "dosbox"))
            dos_mem_dump = mem_dump_dosbox;
          else
          {
            while (++p != myargc && *myargv[p] != '-' && i < DOS_MEM_DUMP_SIZE)
            {
              M_StrToInt(myargv[p], &val);
              dos_mem_dump[i++] = (unsigned char)val;
            }
          }
        }
      }

      if (value)
      {
        switch (size)
        {
        case 1:
          *((unsigned char*)value) = *((unsigned char*)(&dos_mem_dump[offset]));
          return true;
        case 2:
          *((unsigned short*)value) = *((unsigned short*)(&dos_mem_dump[offset]));
          return true;
        case 4:
          *((unsigned int*)value) = *((unsigned int*)(&dos_mem_dump[offset]));
          return true;
        }
      }

      return false;
    }

    //
    // donut overrun emulation (linedef action #9)
    //

    #define DONUT_FLOORPIC_DEFAULT 0x16
    int DonutOverrun(fixed_t *pfloorheight, short *pfloorpic)
    {
      if (demo_compatibility && PROCESS(OVERFLOW_DONUT))
      {
        ShowOverflowWarning(OVERFLOW_DONUT, 0, "");

        if (EMULATE(OVERFLOW_DONUT))
        {
          if (pfloorheight && pfloorpic)
          {
            GetMemoryValue(0, pfloorheight, 4);
            GetMemoryValue(8, pfloorpic, 2);
            
            // bounds-check floorpic
            if ((*pfloorpic) <= 0 || (*pfloorpic) >= numflats)
            {
              *pfloorpic = MIN(numflats - 1, DONUT_FLOORPIC_DEFAULT);
            }

            return true;
          }
        }
      }

      return false;
    }

    //
    // MissedBackSideOverrun
    //
    int MissedBackSideOverrun(sector_t *sector, seg_t *seg)
    {
      if (demo_compatibility && PROCESS(OVERFLOW_MISSEDBACKSIDE))
      {
        if (seg)
        {
          ShowOverflowWarning(OVERFLOW_MISSEDBACKSIDE, 0,
            "\n\nLinedef %d has two-sided flag set, but no second sidedef",
            seg.linedef.iLineID);
        }
        else
        {
          ShowOverflowWarning(OVERFLOW_MISSEDBACKSIDE, 0, "");
        }

        if (EMULATE(OVERFLOW_MISSEDBACKSIDE))
        {
          if (sector)
          {
            GetMemoryValue(0, &sector.floorheight, 4);
            GetMemoryValue(4, &sector.ceilingheight, 4);
            
            return true;
          }
        }
      }

      return false;
    }

}


package g;

import defines.*;
import doom.event_t;
import doom.gameaction_t;

// Emacs style mode select   -*- C++ -*- 
//-----------------------------------------------------------------------------
//
// $Id: DoomGameInterface.java,v 1.4 2010/12/20 17:15:08 velktron Exp $
//
// Copyright (C) 1993-1996 by id Software, Inc.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// DESCRIPTION:
//   Duh.
// 
//-----------------------------------------------------------------------------

public interface DoomGameInterface {


//
// GAME
//
public void DeathMatchSpawnPlayer (int playernum);

public void InitNew (skill_t skill, int episode, int map);

/** Can be called by the startup code or M_Responder.
    A normal game starts at map 1,
    but a warp test can start elsewhere */
public void DeferedInitNew (skill_t skill, int episode, int map);

public void DeferedPlayDemo (String demo);

/** Can be called by the startup code or M_Responder,
  calls P_SetupLevel or W_EnterWorld. */
public void LoadGame (String name);

public void DoLoadGame ();

/** Called by M_Responder. */
public void SaveGame (int slot, String description);

/** Only called by startup code. */
public void RecordDemo (String name);

public void BeginRecording ();

public void PlayDemo (String name);
public void TimeDemo (String name);
public boolean CheckDemoStatus ();

public void ExitLevel ();
public void SecretExitLevel() ;

public void WorldDone() ;

public void Ticker() ;
public boolean Responder (event_t	ev);

public void ScreenShot() ;

public gameaction_t getGameAction();

public void setGameAction(gameaction_t ga);

public boolean getPaused();

public void setPaused(boolean on);
    
}

package g;

import static data.Defines.VERSION;
import static data.Limits.*;


import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;

import defines.*;
import doom.DoomStatus;
import utils.C2JUtils;
import w.CacheableDoomObject;
import w.DoomBuffer;
import w.DoomIO;
import w.IReadableDoomObject;
import w.IWritableDoomObject;


/** represents the header of Doom savegame, so that basic info can be checked quickly.
 * 
 *  To load the whole game and check if there are final mistakes, you must go through it all.
 *  Savegames need to be aware of ALL status and context, so maybe they should be inner classes?
 *  
 */


public class DoomSaveGame
        implements CacheableDoomObject, IReadableDoomObject, IWritableDoomObject{
    
    public DoomSaveGame(){
        playeringame=new boolean[MAXPLAYERS];
    }
    
    public String name; // max size SAVEGAMENAME
    public String vcheck;
    // These are for DS
    public int gameskill;
    public int gameepisode;
    public int gamemap;
    public boolean[] playeringame;
    /** what bullshit, stored as 24-bit integer?! */
    public int leveltime;
    // These help checking shit.
    public boolean wrongversion;
    public boolean properend;
   

    @Override
    public void unpack(ByteBuffer buf)
            throws IOException {
        name=DoomBuffer.getNullTerminatedString(buf, SAVESTRINGSIZE);
        vcheck=DoomBuffer.getNullTerminatedString(buf, VERSIONSIZE);
        String vcheckb= ("version "+VERSION);
        // no more unpacking, and report it.
        if (wrongversion = !(vcheckb.equalsIgnoreCase(vcheck))) return;
        gameskill = buf.get(); 
        gameepisode = buf.get();
        gamemap = buf.get();
        
        for (int i=0 ; i<MAXPLAYERS ; i++) 
        playeringame[i] = buf.get()!=0; 

        // load a base level (this doesn't advance the pointer?) 
        //G_InitNew (gameskill, gameepisode, gamemap); 
     
        // get the times 
        int a = C2JUtils.toUnsignedByte(buf.get()); 
        int b = C2JUtils.toUnsignedByte(buf.get());
        int c =  C2JUtils.toUnsignedByte(buf.get());
        // Quite anomalous, leveltime is stored as a BIG ENDIAN, 24-bit unsigned integer :-S
        leveltime = (a<<16) + (b<<8) + c; 

        // Mark this position...
        buf.mark();
        buf.position(buf.limit()-1);
        if (buf.get() != 0x1d) properend=false; else
            properend=true;
        buf.reset();
            
        // We've loaded whatever consistutes "header" info, the rest must be unpacked by proper
        // methods in the game engine itself.
    }
    
   
    @Override
    public void write(DataOutputStream f)
            throws IOException {
        DoomIO.writeString(f,name,SAVESTRINGSIZE);
        DoomIO.writeString(f,vcheck,VERSIONSIZE);
        f.writeByte(gameskill); 
        f.writeByte(gameepisode);
        f.writeByte(gamemap);
        for (int i=0 ; i<MAXPLAYERS ; i++) 
        f.writeBoolean(playeringame[i]); 

        // load a base level (this doesn't advance the pointer?) 
        //G_InitNew (gameskill, gameepisode, gamemap); 
     
        // get the times 
        byte a = (byte) (0x0000FF&(leveltime>>>16)); 
        byte b = (byte) (0x00FF&(leveltime>>>8));
        byte c =  (byte) (0x00FF&(leveltime));
        // Quite anomalous, leveltime is stored as a BIG ENDIAN, 24-bit unsigned integer :-S
        f.writeByte(a);
        f.writeByte(b);
        f.writeByte(c);
        
        // TODO: after this point, we should probably save some packed buffers representing raw state...
        // needs further study.

        // The end.
        f.writeByte(0x1d);
            
    } 
    
    @Override
    public void read(DataInputStream f)
            throws IOException {
        name=DoomIO.readNullTerminatedString(f,SAVESTRINGSIZE);
        vcheck=DoomIO.readNullTerminatedString(f,VERSIONSIZE);
        String vcheckb= ("version "+VERSION);
        // no more unpacking, and report it.
        if (wrongversion = !(vcheckb.equalsIgnoreCase(vcheck))) return;
        gameskill = f.readByte(); 
        gameepisode = f.readByte();
        gamemap = f.readByte();
        playeringame=new boolean[MAXPLAYERS];
        for (int i=0 ; i<MAXPLAYERS ; i++) 
        playeringame[i] = f.readBoolean(); 

        // load a base level (this doesn't advance the pointer?) 
        //G_InitNew (gameskill, gameepisode, gamemap); 
     
        // get the times 
        int a = f.readUnsignedByte(); 
        int b = f.readUnsignedByte();
        int c =  f.readUnsignedByte();
        // Quite anomalous, leveltime is stored as a BIG ENDIAN, 24-bit unsigned integer :-S
        leveltime = (a<<16) + (b<<8) + c; 

        // Mark this position...
        //long mark=f.getFilePointer();
        //f.seek(f.length()-1);
        //if (f.readByte() != 0x1d) properend=false; else
        //    properend=true;
        //f.seek(mark);
            
        long available=f.available();
        f.skip(available-1);
        if (f.readByte() != 0x1d) properend=false; else
            properend=true;
        
        // We've loaded whatever consistutes "header" info, the rest must be unpacked by proper
        // methods in the game engine itself.
        
    } 

     public void toStat(DoomStatus DS){
         System.arraycopy(this.playeringame, 0, DS.playeringame, 0, this.playeringame.length);
         DS.gameskill=skill_t.values()[this.gameskill];
         DS.gameepisode=this.gameepisode;
         DS.gamemap=this.gamemap;
         DS.leveltime=this.leveltime;        
         
     }
     
     public void fromStat(DoomStatus DS){
         System.arraycopy(DS.playeringame, 0, this.playeringame, 0, DS.playeringame.length);
         this.gameskill=DS.gameskill.ordinal();
         this.gameepisode=DS.gameepisode;
         this.gamemap=DS.gamemap;
         this.leveltime=DS.leveltime;        
         
     }

}

package g;

//
//DOOM keyboard definition.
//This is the stuff configured by Setup.Exe.
//Most key data are simple ascii (uppercased).
//
//NOTE: the codes used here are arbitrary, non-system specificm 
//however they must match what's used in the default.cfg files
//and throughout the code. Once the system input barrier has been
//crossed, Doom only understands these codes. It's the job of
//the system-specific interface to produce the correct mappings.


public class Keys {
	public static final char KEY_NULL        = 0;       // null key, triggers nothing

	public static final char KEY_ESCAPE      = 27;
	public static final char KEY_SPACE       = 32;

	public static final char KEY_NUMLOCK     = (0x80+69);
	public static final char KEY_SCROLLLOCK  = (0x80+70);

	public static final char KEY_MINUS       = 45;
	public static final char KEY_EQUALS      = 61;
	public static final char KEY_BACKSPACE   = 8;
	public static final char KEY_TAB         = 9;
	public static final char KEY_ENTER       = 13;

	//
	//  scancodes 71-83 (non-extended)
	//
	public static final char KEY_KEYPAD7     = (0x80+71);
	public static final char KEY_KEYPAD8     = (0x80+72);
	public static final char KEY_KEYPAD9     = (0x80+73);
	public static final char KEY_MINUSPAD    = (0x80+74);
	public static final char KEY_KEYPAD4     = (0x80+75);
	public static final char KEY_KEYPAD5     = (0x80+76);
	public static final char KEY_KEYPAD6     = (0x80+77);
	public static final char KEY_PLUSPAD     = (0x80+78);
	public static final char KEY_KEYPAD1     = (0x80+79);
	public static final char KEY_KEYPAD2     = (0x80+80);
	public static final char KEY_KEYPAD3     = (0x80+81);
	public static final char KEY_KEYPAD0     = (0x80+82);
	public static final char KEY_KPADDEL     = (0x80+83);

	//  windows95 keys...

	public static final char KEY_LEFTWIN     = (0x80+91);
	public static final char KEY_RIGHTWIN    = (0x80+92);
	public static final char KEY_MENU        = (0x80+93);

	//
	//  scancodes 71-83 EXTENDED are remapped
	//  to these by the keyboard handler (just add 30)
	//
	public static final char KEY_KPADSLASH   = (0x80+100);      //extended scancode 53 '/' remapped

	public static final char KEY_HOME        = (0x80+101);
	public static final char KEY_UPARROW     = (0x80+102);
	public static final char KEY_PGUP        = (0x80+103);
	public static final char KEY_LEFTARROW   = (0x80+105);
	public static final char KEY_RIGHTARROW  = (0x80+107);
	public static final char KEY_END         = (0x80+109);
	public static final char KEY_DOWNARROW   = (0x80+110);
	public static final char KEY_PGDN        = (0x80+111);
	public static final char KEY_INS         = (0x80+112);
	public static final char KEY_DEL         = (0x80+113);


	public static final char KEY_F1          = (0x80+0x3b);
	public static final char KEY_F2          = (0x80+0x3c);
	public static final char KEY_F3          = (0x80+0x3d);
	public static final char KEY_F4          = (0x80+0x3e);
	public static final char KEY_F5          = (0x80+0x3f);
	public static final char KEY_F6          = (0x80+0x40);
	public static final char KEY_F7          = (0x80+0x41);
	public static final char KEY_F8          = (0x80+0x42);
	public static final char KEY_F9          = (0x80+0x43);
	public static final char KEY_F10         = (0x80+0x44);
	public static final char KEY_F11         = (0x80+0x57);
	public static final char KEY_F12         = (0x80+0x58);

	public static final char KEY_PAUSE       = 255;

	// these ones must be non-extended scancodes (rctrl,rshift,lalt)
	public static final char KEY_SHIFT       = (0x80+54);
	public static final char KEY_CTRL        = (0x80+29);
	public static final char KEY_ALT         = (0x80+56);

	public static final char KEY_CAPSLOCK    = (0x80+58);
	public static final char KEY_CONSOLE     = (int)'`';

//	public static final char KEY_OPENBRACKETS
//	public static final char KEY_CLOSEBRACKETS

}
package testers;

import m.VarsManager;

public class TestSettingsManager {

    public static void main(String argv[]){
    
        VarsManager SM=new VarsManager();
        SM.LoadDefaults("default.cfg");
        
        System.out.println(SM.getSetting("use_mouse").getBoolean());
        
        SM.putSetting("jump_height", "56", false);
        
        System.out.println(SM.getSetting("jump_height").getInteger());
        
        System.out.println(SM.getSetting("use_mouse").getBoolean());
        
        SM.putSetting("crap_setting", "false", true);
        
        System.out.println(SM.getSetting("crap_setting").getInteger());
        System.out.println(SM.getSetting("key_left").getChar());
        
        SM.SaveDefaults("default.cfg");

        
    }
}

package testers;

import java.io.File;
import java.io.FileOutputStream;

import s.DMXSound;
import s.DSP;
import w.WadLoader;

public class LinearSoundInterpolation {
    
    public static void main(String[] argv) throws Exception{

        
        WadLoader W=new WadLoader();
        W.InitMultipleFiles(new String[]{"c:\\iwads\\doom1.wad"});
        
        int sfxlump = W.GetNumForName("dspistol");
        
        DMXSound dmx= W.CacheLumpNum(sfxlump, 0, DMXSound.class);
        
        File out=new File("original.raw");
        FileOutputStream fos=new FileOutputStream(out);
        fos.write(dmx.data);
        fos.close();
        
        fos=new FileOutputStream("linear.raw");
        
        // KRUDE
        if (dmx.speed==11025){
             //Plain linear interpolation.
            dmx.data=DSP.crudeResample(dmx.data,2);
            //DSP.filter(dmx.data,SAMPLERATE, SAMPLERATE/4);
            dmx.datasize=dmx.data.length;            
        }
        
        fos.write(dmx.data);
        fos.close();
        
            }
    
}

package testers;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import w.*;

/** DUMPS a WAD's LUMPS to disk. Any questions?! 
 * 
 */

public class WadDumper {

    public static void main(String[] argv) {
        try {
    IWadLoader W=new WadLoader();
    W.InitMultipleFiles(argv,false);
    BufferedOutputStream bos;
    
    int numlumps=W.NumLumps();
    
    for (int i=0;i<numlumps;i++){
        byte[] crap=W.ReadLump(i);
        String name=W.GetNameForLump(i);
        File f=new File(String.format("%s.lmp",name));
        bos=new BufferedOutputStream(new FileOutputStream(f));
        bos.write(crap);
        bos.close();        
        }

    
        } catch (Exception e){
            e.printStackTrace();
        }
    }
    
}

package testers;

import pooling.AudioChunkPool;
import s.AudioChunk;

public class TestAudioChunkPooling {
    public static final int TESTS=10000;
    
    public static void main(String[] argv){
    
    AudioChunk[] chunks=new AudioChunk[TESTS];
    AudioChunkPool chunkpool=new AudioChunkPool();
    
    long a=System.nanoTime();
    
    for (int i=0;i<TESTS;i++){
        chunks[i]=new AudioChunk();
    }

    for (int i=0;i<TESTS;i++){
        chunks[i]=new AudioChunk();
    }
    
    for (int i=0;i<TESTS;i++){
        chunks[i]=new AudioChunk();
    }
    
    for (int i=0;i<TESTS;i++){
        chunks[i]=new AudioChunk();
    }
    long b=System.nanoTime();
    
    System.out.println("Time: "+(float)(b-a)/1000000000f);
    
    a=System.nanoTime();
    
    for (int i=0;i<TESTS;i++){
        chunks[i]=chunkpool.checkOut();
    }

    for (int i=0;i<TESTS;i++){
        chunkpool.checkIn(chunks[i]);
        chunks[i]=chunkpool.checkOut();
    }
    
    for (int i=0;i<TESTS;i++){
        chunkpool.checkIn(chunks[i]);
        chunks[i]=chunkpool.checkOut();
    }
    
    for (int i=0;i<TESTS;i++){
        chunkpool.checkIn(chunks[i]);
        chunks[i]=chunkpool.checkOut();
    }
    b=System.nanoTime();
    
    System.out.println("Time: "+(float)(b-a)/1000000000f);
    
    
    }
    

}

package testers;

import java.nio.ByteBuffer;

import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.SourceDataLine;

import s.SpeakerSound;

import w.WadLoader;

import defines.GameMission_t;
import doom.DoomStatus;

public class TestSpeakerSound {
    
    
    public static void main(String[] argv) throws Exception {
    WadLoader W=new WadLoader();
    W.InitMultipleFiles(new String[] {"C:\\iwads\\doom1.wad"});

    SpeakerSound sp=(SpeakerSound) W.CacheLumpName("DPSAWUP", 0, SpeakerSound.class);
    
    byte[] stuff=sp.toRawSample();
    
    AudioFormat format = new AudioFormat(11025,8,1,false,true);
    
    DataLine.Info info = new DataLine.Info(SourceDataLine.class, format);
    
    SourceDataLine line=null;

    if (AudioSystem.isLineSupported(info))
        try {
            line=  (SourceDataLine) AudioSystem.getSourceDataLine(format);
            line.open(format);
        } catch (Exception e){
            e.printStackTrace();
            System.err.print( "Could not play signed 16 data\n");
        }

        if (line!=null) System.err.print(" configured audio device\n" );
        line.start();
        line.write(stuff, 0,stuff.length);
        line.drain();
    
    Thread.sleep(1000);
    }
}

package testers;

import static data.Defines.PU_STATIC;
import hu.HU;

import java.nio.ByteBuffer;

import m.FixedFloat;

import rr.patch_t;
import rr.vertex_t;

import data.mapvertex_t;
import defines.*;
import doom.DoomContext;
import doom.DoomStatus;

import utils.C2JUtils;
import w.*;

/** This is a very simple tester for the WadLoader and HU modules.
 *  We use the same exact methods used in the C source code, only
 *  with a more OO approach.
 * 
 * 
 */

public class PatchLoaderTester {

    public static void main(String[] argv) {
        try {
    WadLoader W=new WadLoader();
    W.InitMultipleFiles(new String[] {"doom1.wad"});
    
    patch_t wall=W.CachePatchName("BAL1d0");
    System.out.println(wall.leftoffset);
    
        } catch (Exception e){
            e.printStackTrace();
        }
    }
    
}

package testers;

import static data.Defines.PU_STATIC;
import hu.HU;

import java.nio.ByteBuffer;

import m.FixedFloat;

import rr.vertex_t;

import data.mapvertex_t;
import defines.*;
import doom.DoomContext;
import doom.DoomStatus;

import utils.C2JUtils;
import w.*;

/** This is a very simple tester for the WadLoader and HU modules.
 *  We use the same exact methods used in the C source code, only
 *  with a more OO approach.
 * 
 * 
 */

public class WadCoalescerTester {

    public static void main(String[] argv) {
        try {
    WadLoader W=new WadLoader();
    W.InitMultipleFiles(new String[] {"doom1.wad"/*,"sprite2.wad"*/});
    //W.AddFile("bitter.wad");

    //W.CoalesceMarkedResource("F_START", "F_END",li_namespace.ns_flats);
    //W.CoalesceMarkedResource("S_START", "S_END",li_namespace.ns_sprites);
    
    
    System.out.println("Total lumps read: "+W.numlumps);
    
    for (int i=0;i<W.numlumps;i++){
    	System.out.println(W.lumpinfo[i].name+" "+i);
    }
    
        } catch (Exception e){
            e.printStackTrace();
        }
    }
    
}

package testers;

import m.DoomRandom;
import m.IRandom;
import s.ClassicDoomSoundDriver;
import s.SpeakerDoomSoundDriver;
import s.SuperDoomSoundDriver;
import data.sounds.sfxenum_t;
import doom.DoomStatus;
import w.WadLoader;

public class TestSuperSound {
	public static void main(String[] argv) throws Exception{

	DoomStatus DS=new DoomStatus();
	
	
	WadLoader W=new WadLoader();
	IRandom RND=new DoomRandom();
	
	DS.W=W;
	DS.RND=RND;
	W.InitMultipleFiles(new String[]{"doom1.wad"});
	
	SuperDoomSoundDriver sound=new SuperDoomSoundDriver(DS,4);
	
	sound.InitSound();
	sound.SetChannels(3);
	
	Thread.sleep(1000);
	//sound.StartSound(1, 127, 127, 127, 0);
	for (int i=0;i<1000;i++){
	    
	    Thread.sleep(1000/35);

	    if (i%10==0) sound.StartSound(sfxenum_t.sfx_plpain.ordinal(), 127, 127, 127, 0);
	    //if (i%50==0) sound.StartSound(sfxenum_t.sfx_barexp.ordinal(), 127, 0, 127, 0);
	    //if (i%35==0) sound.StartSound(sfxenum_t.sfx_plpain.ordinal(), 127, 255, 127, 0);
	    //if (i%71==0) sound.StartSound(sfxenum_t.sfx_oof.ordinal(), 127, 192, 127, 0);
	    sound.UpdateSound();
	    
	    sound.SubmitSound();
	    
	       DS.gametic++;
	}
	sound.ShutdownSound();
	}
	
}

package testers;

import static data.Defines.KEY_F1;
import static data.Defines.PU_STATIC;
import static data.Limits.MAXEVENTS;

import hu.HU;
import i.DoomSystem;
import i.IDoomSystem;
import i.InputListener;

import java.awt.image.BufferedImage;
import java.awt.image.IndexColorModel;

import javax.swing.JFrame;

import awt.OldAWTDoom;

import rr.patch_t;
import s.DummySoundDriver;

import m.IDoomMenu;
import m.Menu;
import m.DoomRandom;
import utils.C2JUtils;
import v.BufferedRenderer;
import v.SimpleRenderer;
import w.DoomBuffer;
import w.WadLoader;
import data.Defines;
import data.Defines.GameMission_t;
import data.Defines.GameMode_t;
import data.Defines.Language_t;
import data.Defines.gamestate_t;
import doom.DoomContext;
import doom.DoomMain;
import doom.DoomStatus;
import doom.event_t;
import doom.player_t;
import doom.ticcmd_t;
import doom.wbstartstruct_t;

/** This is a very simple tester for Menu module  */

public class AWTMenuTester {
    public static final int WIDTH=320;
    public static final int HEIGHT=200;
    
    public static void main(String[] argv) {
        try {
        	
    // Create a Wad file loader.
    WadLoader W=new WadLoader();
    W.InitMultipleFiles(new String[] {"doom1.wad"});
    
    System.out.println("Total lumps read: "+W.numlumps);
    patch_t help1=W.CachePatchName("TITLEPIC", PU_STATIC);

    // Read the paletter.
    DoomBuffer palette = W.CacheLumpName("PLAYPAL", PU_STATIC);
    // Create a video renderer
    BufferedRenderer V=new BufferedRenderer(WIDTH,HEIGHT);
    V.Init();
    byte[] pal=palette.getBuffer().array();
    

    
    IDoomSystem I=new DoomSystem();
    DoomMain DM=new DoomMain();
    // Create the frame.
    OldAWTDoom frame = new OldAWTDoom(DM,V,pal);
    DM.I=I;
    DM.VI=frame;
    DM.S=new DummySoundDriver();
    frame.DM=DM;
    DM.W=W;
    DM.V=V;
    DM.DM=DM;
    HU HU=new HU(DM);
    DM.language=Language_t.english;
    HU.Init();
    DM.HU=HU;
    DM.gameepisode=1;
    DM.gamemap=1;
    DM.gamemission=GameMission_t.doom;
    DM.gamemode=GameMode_t.shareware;
    DM.wminfo=new wbstartstruct_t();
    // Simulate being in the mid of a level.
    DM.usergame=true;
    DM.gamestate=gamestate_t.GS_LEVEL;
    C2JUtils.initArrayOfObjects(DM.players,player_t.class);
    
    DM.RND=new DoomRandom();
    DM.players[0].cmd=new ticcmd_t();
    DM.players[0].itemcount=1337;
    DM.players[0].killcount=1337;
    DM.players[0].secretcount=1337;
    
    DM.wminfo.plyr[0].in=true;
    DM.wminfo.plyr[0].sitems=1337;
    DM.wminfo.plyr[0].skills=1337;
    DM.wminfo.plyr[0].stime=28595;
    DM.wminfo.plyr[0].ssecret=1337;
    DM.playeringame[0]=true;
    DM.wminfo.last=6;
    DM.wminfo.epsd=0;
    DM.wminfo.maxitems=100;
    DM.wminfo.maxkills=100;
    DM.wminfo.maxsecret=100;
    DM.wminfo.partime=28595;
   
    IDoomMenu M=DM.M=new Menu(DM);

    M.Init();
    frame.InitGraphics();
    long a=System.nanoTime();
    DM.R.menuactive=true;        
        for (int i=0;i<100000;i++){
            int ba=DM.I.GetTime();
            while (ba-DM.I.GetTime()==0){
                //Don't do that! frame.setVisible(true);
                Thread.sleep(1);               
            }
           frame.GetEvent();
           
           for ( ; DM.eventtail != DM.eventhead ; DM.eventtail = (++DM.eventtail)&(MAXEVENTS-1) )
           {
       	event_t ev = DM.events[DM.eventtail];

            //System.out.println(ev);
            if (ev!=null)
            ((Menu)M).Responder(ev);
           }
            
       
         
        V.DrawPatch(0,0,0,help1);
        M.Ticker();
        M.Drawer();
        DM.gametic++;
        frame.FinishUpdate();
        if (i%100==0){
        	   long b=System.nanoTime();
        	    
        	    System.out.println(i +" frames in " +((b-a)/1e09) +" = "+i/((b-a)/1e09) + " fps");
        }
        System.out.print(frame.processEvents());
        }
            } catch (Exception e){
                e.printStackTrace();
            }
    
    }
    
}

package testers;

import static data.Defines.*;
import static data.Limits.*;
import static m.fixed_t.FRACBITS;

import i.InputListener;

import java.awt.Frame;
import java.awt.GraphicsDevice;
import java.awt.image.BufferedImage;
import java.awt.image.IndexColorModel;

import javax.swing.JFrame;

import m.DoomRandom;
import p.LevelLoader;
import p.mobj_t;
import st.StatusBar;
import utils.C2JUtils;
import v.BufferedRenderer;
import w.DoomBuffer;
import w.WadLoader;
import automap.DoomAutoMap;
import automap.Map;
import data.Defines;
import data.Defines.GameMission_t;
import data.Defines.GameMode_t;
import data.Defines.skill_t;
import doom.DoomContext;
import doom.DoomStatus;
import doom.event_t;
import doom.player_t;
import doom.ticcmd_t;
import doom.wbstartstruct_t;
import doom.weapontype_t;

/** This is a very simple tester for the Automap. Combined with status bar + Level loader. */

public class AutoMapTester3 {
    
    public static final int WIDTH=320;

    public static void main(String[] argv) {
        try {
    WadLoader W=new WadLoader();
    W.InitMultipleFiles(new String[] {"doom1.wad"});
    //W.AddFile("bitter.wad");
    System.out.println("Total lumps read: "+W.numlumps);

    
    DoomBuffer palette = W.CacheLumpName("PLAYPAL", PU_STATIC);
    byte[] pal=palette.getBuffer().array();
    BufferedRenderer V=new BufferedRenderer(WIDTH,200);
    V.Init();
    BufferedImage bi=new BufferedImage(V.getWidth(),V.getHeight(),BufferedImage.TYPE_INT_RGB);
    V.setPalette(pal);
    V.mapInternalRasterToBufferedImage(bi);
    
    DoomStatus ds = new DoomStatus();
    ds.gameepisode=1;
    ds.gamemap=1;
    ds.gamemission=GameMission_t.doom;
    ds.gamemode=GameMode_t.shareware;
    ds.wminfo=new wbstartstruct_t();
    C2JUtils.initArrayOfObjects(ds.players,player_t.class);
    Defines.SCREENWIDTH=WIDTH;
    Defines.SCREENHEIGHT=200;
    
    DoomContext DC=new DoomContext();
    DC.DS=ds;
    DC.W=W;
    DC.V=V;
    DC.RND=new DoomRandom();
    ds.players[0].cmd=new ticcmd_t();
    ds.players[0].itemcount=1337;
    ds.players[0].killcount=1337;
    ds.players[0].secretcount=1337;
    ds.players[0].weaponowned[0]=true;
    ds.players[0].weaponowned[1]=true;
    ds.players[0].weaponowned[2]=true;
    ds.players[0].weaponowned[3]=true;
    ds.players[0].readyweapon=weapontype_t.wp_pistol;
    ds.players[0].health[0]=100;
    ds.players[0].armorpoints[0]=100;
    ds.players[0].ammo[0]=400;
    ds.players[0].maxammo[0]=400;
    ds.players[0].ammo[1]=100;
    ds.players[0].maxammo[1]=100;
    ds.players[0].ammo[2]=100;
    ds.players[0].maxammo[2]=100;
    ds.players[0].ammo[3]=600;
    ds.players[0].maxammo[3]=600;

    
    ds.players[0].cards[0]=true;
    ds.players[0].cards[2]=true;
    ds.players[0].cards[4]=true;
    ds.players[0].mo=new mobj_t();
    ds.players[0].mo.x=1056<<FRACBITS;
    ds.players[0].mo.y=-3616<<FRACBITS;
    
    ds.players[0].powers[pw_allmap]=100;
    ds.deathmatch=false;
    ds.statusbaractive=true;
    
    ds.wminfo.plyr[0].in=true;
    ds.wminfo.plyr[0].sitems=1337;
    ds.wminfo.plyr[0].skills=1337;
    ds.wminfo.plyr[0].stime=28595;
    ds.wminfo.plyr[0].ssecret=1337;
    ds.playeringame[0]=true;
    ds.wminfo.last=6;
    ds.wminfo.epsd=0;
    ds.wminfo.maxitems=100;
    ds.wminfo.maxkills=100;
    ds.wminfo.maxsecret=100;
    ds.wminfo.partime=28595;

    StatusBar ST=new StatusBar(DC);
    ST.Start();
    LevelLoader PL=new LevelLoader(DC);
    PL.SetupLevel(1, 1, 0, skill_t.sk_hard);
    DC.LL=PL;
    DC.ST=ST;
    DoomAutoMap AM=new Map(DC);
    AM.Start();
    
    ST.Responder(new event_t('i'));
    ST.Responder(new event_t('d'));
    ST.Responder(new event_t('d'));
    ST.Responder(new event_t('t'));
    
    AM.Responder(new event_t(Map.AM_FOLLOWKEY));
    AM.Responder(new event_t(Map.AM_ZOOMOUTKEY));
    AM.Responder(new event_t(Map.AM_GRIDKEY));
    
    CrappyDisplay frame = new CrappyDisplay(bi);
    frame.setTitle("MochaDoom");
    InputListener in = new InputListener();
    frame.addComponentListener(in);
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.pack();
    frame.setLocationRelativeTo(null);
    //frame.setUndecorated(true);
    frame.setVisible(true);
    frame.setBounds(frame.getX(), frame.getY(), WIDTH, 240);
    
    
    long a=System.nanoTime();
    int TICKS=10000;
    for (int i=0;i<TICKS;i++){
    if ((i%20)<10)
        AM.Responder(new event_t(Map.AM_ZOOMINKEY));
       else
       AM.Responder(new event_t(Map.AM_ZOOMOUTKEY)); 

    if ((i%25)<12) {
        AM.Responder(new event_t(Map.AM_PANUPKEY));
        AM.Responder(new event_t(Map.AM_PANRIGHTKEY));
    } 
       else {
       AM.Responder(new event_t(Map.AM_PANDOWNKEY));
       AM.Responder(new event_t(Map.AM_PANLEFTKEY));
       }

    
    AM.Ticker();
    AM.Drawer();
    ST.Ticker();
    ST.Drawer(false,true);
    V.changePalette((i/(2000/14))%14);
    V.remap(0);
    frame.update(null);
    /*File outputFile =
        new File(
            "tic"+i+".png");
    ImageIO.write(bi, "PNG", outputFile); */
//    V.takeScreenShot(0, "tic"+i,icm);    
    //AM.Responder(new event_t(Map.AM_PANLEFTKEY));

    }
    
    long b=System.nanoTime();
    
    System.out.println(TICKS +" tics in " +((b-a)/1e09) +" = "+TICKS/((b-a)/1e09) + " fps");
    /*
    V.takeScreenShot(0, "tic1",icm);
    for (int i=20;i<150;i++){
        EL.Ticker();
        EL.Drawer();
        if (i==100){
            ds.players[0].cmd.buttons=1; // simulate attack
            ds.players[0].attackdown=false; // simulate attack
        }
        
        if (i==120){
            ds.players[0].cmd.buttons=1; // simulate attack
            ds.players[0].attackdown=false; // simulate attack
        }
        V.takeScreenShot(0,( "tic"+i),icm);
        } */
       
        } catch (Exception e){
            e.printStackTrace();
        }
    }
    
    public static void initFullScreen(GraphicsDevice gd, Frame gf) {
        // initialize the main app frame
        gf = new Frame("Game Frame");
        gf.setUndecorated(true);
        // disable repaint mechanism
        gf.setIgnoreRepaint(true);
        // the next call shows the window
        gd.setFullScreenWindow(gf);
       }
        
    
}

package testers;

import static data.Defines.KEY_F1;
import static data.Defines.PU_STATIC;

import java.awt.image.BufferedImage;
import java.awt.image.IndexColorModel;

import javax.swing.JFrame;

import m.IDoomMenu;
import m.Menu;
import m.DoomRandom;
import utils.C2JUtils;
import v.BufferedRenderer;
import w.DoomBuffer;
import w.WadLoader;
import data.Defines;
import data.Defines.GameMission_t;
import data.Defines.GameMode_t;
import doom.DoomContext;
import doom.DoomStatus;
import doom.event_t;
import doom.player_t;
import doom.ticcmd_t;
import doom.wbstartstruct_t;

/** This is a very simple tester for the End Level screen drawer.
 * 
 * 
 */

public class MenuVideoTester {

    public static final int WIDTH=320;
    public static void main(String[] argv) {
        try {
    WadLoader W=new WadLoader();
    W.InitMultipleFiles(new String[] {"doom1.wad"});
    //W.AddFile("bitter.wad");
    System.out.println("Total lumps read: "+W.numlumps);
    DoomBuffer palette = W.CacheLumpName("PLAYPAL", PU_STATIC);
    byte[] pal=palette.getBuffer().array();

    IndexColorModel icm=new IndexColorModel(8, 256,pal, 0, false);
    Defines.SCREENWIDTH=WIDTH;
    Defines.SCREENHEIGHT=200;
    BufferedRenderer V=new BufferedRenderer(WIDTH,200,icm);
    V.Init();
    
    IndexColorModel[] icms=new IndexColorModel[pal.length/768];
    BufferedImage[] pals=new BufferedImage[icms.length];
    
    for (int i=0;i<icms.length;i++){
        icms[i]=new IndexColorModel(8, 256,pal, i*768, false);
            pals[i]=new BufferedImage(icms[i],V.screenbuffer[0].getRaster(), false, null);
           }
    
    DoomStatus ds = new DoomStatus();
    ds.gameepisode=1;
    ds.gamemap=1;
    ds.gamemission=GameMission_t.doom;
    ds.gamemode=GameMode_t.shareware;
    ds.wminfo=new wbstartstruct_t();
    C2JUtils.initArrayOfObjects(ds.players,player_t.class);

    DoomContext DC=new DoomContext();
    DC.DS=ds;
    DC.W=W;
    DC.V=V;
    DC.RND=new DoomRandom();
    ds.players[0].cmd=new ticcmd_t();
    ds.players[0].itemcount=1337;
    ds.players[0].killcount=1337;
    ds.players[0].secretcount=1337;
    
    ds.wminfo.plyr[0].in=true;
    ds.wminfo.plyr[0].sitems=1337;
    ds.wminfo.plyr[0].skills=1337;
    ds.wminfo.plyr[0].stime=28595;
    ds.wminfo.plyr[0].ssecret=1337;
    ds.playeringame[0]=true;
    ds.wminfo.last=6;
    ds.wminfo.epsd=0;
    ds.wminfo.maxitems=100;
    ds.wminfo.maxkills=100;
    ds.wminfo.maxsecret=100;
    ds.wminfo.partime=28595;
    JFrame frame = new JFrame("MochaDoom");
    CrappyDisplay shit = new CrappyDisplay(pals);
    frame.add(shit);
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.pack();
    frame.setLocationRelativeTo(null);
    //frame.setUndecorated(true);
    frame.setVisible(true);


    frame.setBounds(frame.getX(), frame.getY(), WIDTH, 240);
    IDoomMenu M=new Menu(DC);
    M.Init();
   
    
    for (int i=0;i<20;i++){
        M.Ticker();
        M.Drawer();
        }
   
    V.takeScreenShot(0, "menutic19",icm);
    ds.menuactive=true;        
        for (int i=20;i<150;i++){
            M.Ticker();
            M.Drawer();

            if (i==40){
                M.Responder(new event_t(Defines.KEY_DOWNARROW));
            }

            if (i==60){
                M.Responder(new event_t(Defines.KEY_DOWNARROW));
            }
            
            if (i==80){
                M.Responder(new event_t(Defines.KEY_ESCAPE));
            }
            
            if (i==100){
                M.Responder(new event_t(KEY_F1));
            }
            
            if (i==120){
                M.Responder(new event_t(Defines.KEY_ESCAPE));
            }
            V.takeScreenShot(0,( "menutic"+i),icm);
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }
    
}

package testers;

import rr.patch_t;

import w.*;

/** A tester for loading patches off cached memory and directly from disk,
 *  proving how direct WAD access is possible, too.
 *  Will need to implement reading column pixel data and dumping it into a file somewhat...
 * 
 */

public class DiskPatchReader {

    public static void main(String[] argv) throws Exception {
    WadLoader W=new WadLoader();
    W.InitMultipleFiles(new String[] {"C:\\iwads\\doom1.wad"});
    System.out.println("Total lumps read: "+W.numlumps);
   
    System.out.println("Num for WALL00_1: "+W.GetNumForName("WALL00_1"));
    // We prepare a ByteBuffer to receive a "SECTORS" object. Deserializing it is
    // another matter.
    //ByteBuffer bb=W.CacheLumpName("WALL00_1", 0);
    //

    patch_t wall= W.CachePatchName("WALL00_1");
    lumpinfo_t lump= W.GetLumpinfoForName("WALL00_1");
    System.out.println(lump.name);
    System.out.println(lump.position);
    System.out.println(lump.size);
    
    // Now open Doom1.wad standalone...
    //DoomFile f=new DoomFile("doom1.wad","r");
    //patch_t wall1=new patch_t();
    //f.seek(lump.position);
    //wall1.read(f);
    //System.out.println(wall1.height);
    
    System.out.println("Num for HELP1: "+W.GetNumForName("HELP1"));
    //bb=W.CacheLumpName("HELP1", 0);
    patch_t stbar= W.CachePatchName("HELP1");
    System.out.println(stbar.height);
    System.out.println(stbar.width);
    stbar=(patch_t)W.CacheLumpName("HELP1", 0,stbar.getClass());
        }
    
}

package testers;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketTimeoutException;
import java.util.LinkedList;

import n.BasicNetworkInterface;
import doom.DoomContext;
import doom.DoomMain;
import doom.doomdata_t;
import doom.ticcmd_t;

/** Tester for Net included from DW's work
 * 
 * $Log
 *
 */

public class NetTester {
	
	static DoomMain D1, D2;
	static Object synchronizer = new Object();
	
	public static class NetInterfaceTester extends BasicNetworkInterface {
		DoomContext DC;
		
		public NetInterfaceTester(DoomContext DC) {
			super(DC);
			// TODO Auto-generated constructor stub
		}

	  private doomdata_t  sendData = new doomdata_t();
	  private doomdata_t  recvData = new doomdata_t();
		
		@Override  
		public void sendSocketPacket(DatagramSocket ds, DatagramPacket dp) throws IOException {
			DoomMain receiver;
			
			if (DM.consoleplayer == 0) {
				receiver = D2;
			}
			else {
				receiver = D1;
			}
			
			NetInterfaceTester N = (NetInterfaceTester)receiver.DNI;
			N.addReceivedPacket(dp);
			
			sendData.unpack(dp.getData());
			//System.out.println("Player "+DM.consoleplayer+" SENT: "+dataToStr(sendData));
		}
		
		/*class Elem<E> {
			E e;
			E next = null;
			public Elem(E e) {
				this.e = e;
			}
		}
		
		Elem<DatagramPacket> head = new Elem<DatagramPacket>(null);*/
		
		//ArrayList<DatagramPacket> al = new ArrayList<DatagramPacket>();
		LinkedList<DatagramPacket> al = new LinkedList<DatagramPacket>();
		
		public void addReceivedPacket(DatagramPacket dp) {
			synchronized(synchronizer) {
				al.addLast(dp);
			}
		}
		
		@Override  
		public void socketGetPacket(DatagramSocket ds, DatagramPacket dp) throws IOException {
			synchronized(synchronizer) {
				if (al.size() < 1)
					throw new SocketTimeoutException();
				DatagramPacket pop = al.removeFirst();
				dp.setData(pop.getData());
				dp.setLength(pop.getLength());
				dp.setSocketAddress(pop.getSocketAddress());
				
				recvData.unpack(dp.getData());
			}
			
			//System.out.println("Player "+DM.consoleplayer+" RECV: "+dataToStr(recvData));
		}
		
		public String dataToStr(doomdata_t dt) {
			StringBuffer sb = new StringBuffer();
			sb.append("STIC: "+dt.starttic+" NTIC: "+dt.numtics+" CMDS:\r\n");
			for (int i = 0; i < dt.numtics; i++) {
				ticcmd_t tc = dt.cmds[i];
				sb.append("    FMOVE: "+tc.forwardmove+" CONS: "+tc.consistancy+"\r\n");
			}
			return sb.toString();
				
		}
		
	}

	
	public static void main(String[] args) {
		D1=new DoomMain();
	    D1.Init();
	    D1.DNI = new NetInterfaceTester(D1);
	    D1.myargv = new String[] {"", "-net", "1", "localhost"};
	    D1.myargc = D1.myargv.length; // Bump argcount +1 to maintain CheckParm behavior

		D2=new DoomMain();
	    D2.Init();
	    D2.DNI = new NetInterfaceTester(D2);
	    D2.myargv = new String[] {"", "-net", "2", "localhost"};
	    D2.myargc = D2.myargv.length; // Bump argcount +1 to maintain CheckParm behavior

	    new Thread() {
	    	public void run() {
	    	    D1.Start (); 		
	    	}
	    }.start();
	    
	    try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	    
	    new Thread() {
	    	public void run() {
	    	    D2.Start (); 		
	    	}
	    }.start();

	}

}

package testers;

import m.fixed_t;

public class Mul
        implements Operation {

    @Override
    public void invoke(fixed_t a, fixed_t b) {
        a.FixedMul(b);

    }

}

package testers;

import w.*;

/** This is a very simple tester for the End Level screen drawer.
 * 
 * 
 */

public class LumpGetterTester {

    public static void main(String[] argv) {
        try {
    WadLoader W=new WadLoader();
    W.InitMultipleFiles(new String[] {"C:\\iwads\\doom1.wad"});
    //W.AddFile("bitter.wad");
    System.out.println("Total lumps read: "+W.numlumps);
    int random;
    int TESTS=10000;
    int[] tests=new int[TESTS];
    int hash;
    
    long a=System.nanoTime();
    for (int i=0;i<TESTS;i++){
        random =(int) (Math.random()*W.numlumps);
        String what=W.lumpinfo[random].name;
        hash=W.lumpinfo[random].hash;
        tests[i] = W.CheckNumForName3(what);
        if (!W.lumpinfo[tests[i]].name.equalsIgnoreCase(what)) System.err.println("Mismatch");
    }
    
    long b=System.nanoTime();
    
    System.out.println(b-a);
           
        } catch (Exception e){
            e.printStackTrace();
        }
    }
    
}

package testers;

import static data.Defines.*;
import static data.Limits.*;
import static m.fixed_t.FRACBITS;

import i.InputListener;

import java.awt.Frame;
import java.awt.GraphicsDevice;
import java.awt.image.BufferedImage;
import java.awt.image.IndexColorModel;

import javax.swing.JFrame;

import m.IDoomMenu;
import m.Menu;
import m.DoomRandom;
import p.Actions;
import p.LevelLoader;
import p.mobj_t;
import st.StatusBar;
import utils.C2JUtils;
import v.BufferedRenderer;
import w.DoomBuffer;
import w.WadLoader;
import automap.DoomAutoMap;
import automap.Map;
import data.Defines;
import data.Defines.GameMission_t;
import data.Defines.GameMode_t;
import data.Defines.skill_t;
import doom.DoomContext;
import doom.DoomMain;
import doom.DoomStatus;
import doom.event_t;
import doom.player_t;
import doom.ticcmd_t;
import doom.wbstartstruct_t;
import doom.weapontype_t;

/** This is a very simple tester for the Automap. Combined with status bar + Level loader. */

public class AutoMapTester2 {
    
    public static final int WIDTH=320;

    public static void main(String[] argv) {
        try {
    WadLoader W=new WadLoader();
    W.InitMultipleFiles(new String[] {"doom1.wad"});
    //W.AddFile("bitter.wad");
    System.out.println("Total lumps read: "+W.numlumps);

    
    DoomBuffer palette = W.CacheLumpName("PLAYPAL", PU_STATIC);
    byte[] pal=palette.getBuffer().array();

    IndexColorModel icm=new IndexColorModel(8, 256,pal, 0, false);
    Defines.SCREENWIDTH=WIDTH;
    Defines.SCREENHEIGHT=200;
    BufferedRenderer V=new BufferedRenderer(WIDTH,200,icm);
    V.Init();

    IndexColorModel[] icms=new IndexColorModel[palette.getBuffer().limit()/768];
    BufferedImage[] pals=new BufferedImage[icms.length];


    for (int i=0;i<icms.length;i++){
     icms[i]=new IndexColorModel(8, 256,pal, i*768, false);
    }/*
         pals[i]=new BufferedImage(icms[i],V.screenbuffer[0].getRaster(), false, null);
        }*/
    
    pals=V.getBufferedScreens(0, icms);
    
    //=V.getBufferedScreens(0,icm);>= numlumps
    
    DoomMain ds = new DoomMain();
    ds.gameepisode=1;
    ds.gamemap=1;
    ds.gamemission=GameMission_t.doom;
    ds.gamemode=GameMode_t.shareware;
    ds.wminfo=new wbstartstruct_t();
    C2JUtils.initArrayOfObjects(ds.players,player_t.class);

    
    ds.DM=ds;
    ds.W=W;
    ds.V=V;
    ds.RND=new DoomRandom();
    ds.players[0].cmd=new ticcmd_t();
    ds.players[0].itemcount=1337;
    ds.players[0].killcount=1337;
    ds.players[0].secretcount=1337;
    ds.players[0].weaponowned[0]=true;
    ds.players[0].weaponowned[1]=true;
    ds.players[0].weaponowned[2]=true;
    ds.players[0].weaponowned[3]=true;
    ds.players[0].readyweapon=weapontype_t.wp_pistol;
    ds.players[0].health[0]=100;
    ds.players[0].armorpoints[0]=100;
    ds.players[0].ammo[0]=400;
    ds.players[0].maxammo[0]=400;
    ds.players[0].ammo[1]=100;
    ds.players[0].maxammo[1]=100;
    ds.players[0].ammo[2]=100;
    ds.players[0].maxammo[2]=100;
    ds.players[0].ammo[3]=600;
    ds.players[0].maxammo[3]=600;

    
    ds.players[0].cards[0]=true;
    ds.players[0].cards[2]=true;
    ds.players[0].cards[4]=true;
    ds.players[0].mo=new mobj_t();
    ds.players[0].mo.x=1056<<FRACBITS;
    ds.players[0].mo.y=-3616<<FRACBITS;
    
    ds.players[0].powers[pw_allmap]=100;
    ds.deathmatch=false;
    ds.statusbaractive=true;
    
    ds.wminfo.plyr[0].in=true;
    ds.wminfo.plyr[0].sitems=1337;
    ds.wminfo.plyr[0].skills=1337;
    ds.wminfo.plyr[0].stime=28595;
    ds.wminfo.plyr[0].ssecret=1337;
    ds.playeringame[0]=true;
    ds.wminfo.last=6;
    ds.wminfo.epsd=0;
    ds.wminfo.maxitems=100;
    ds.wminfo.maxkills=100;
    ds.wminfo.maxsecret=100;
    ds.wminfo.partime=28595;

    StatusBar ST=new StatusBar(ds);
    ds.ST=ST;
    ST.Start();
    LevelLoader LL=new LevelLoader(ds);
    LL.SetupLevel(1, 1, 0, skill_t.sk_hard);
    ds.LL=LL;
    ds.ST=ST;
    DoomAutoMap AM=new Map(ds);
    ds.AM=AM;
    AM.Start();
    Actions P=new Actions(ds);
    ds.P=P;
    
    ST.Responder(new event_t('i'));
    ST.Responder(new event_t('d'));
    ST.Responder(new event_t('d'));
    ST.Responder(new event_t('t'));
    
    AM.Responder(new event_t(Map.AM_FOLLOWKEY));
    AM.Responder(new event_t(Map.AM_ZOOMOUTKEY));
    AM.Responder(new event_t(Map.AM_GRIDKEY));
    //BufferedImage bi=((BufferedRenderer)V).screenbuffer[0];
    //BufferedImage bi2=((BufferedRenderer)V).cloneScreen(0, icm2);

    CrappyDisplay frame = new CrappyDisplay(pals);
    frame.setTitle("MochaDoom");
    

    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.pack();
    frame.setLocationRelativeTo(null);
    //frame.setUndecorated(true);
    frame.setVisible(true);
    frame.setBounds(frame.getX(), frame.getY(), WIDTH, 240);

    
    
    long a=System.nanoTime();
    int TICKS=10000;
    for (int i=0;i<TICKS;i++){
    if ((i%20)<10)
        AM.Responder(new event_t(Map.AM_ZOOMINKEY));
       else
       AM.Responder(new event_t(Map.AM_ZOOMOUTKEY)); 

    if ((i%25)<12) {
        AM.Responder(new event_t(Map.AM_PANUPKEY));
        AM.Responder(new event_t(Map.AM_PANRIGHTKEY));
    } 
       else {
       AM.Responder(new event_t(Map.AM_PANDOWNKEY));
       AM.Responder(new event_t(Map.AM_PANLEFTKEY));
       }

    
    AM.Ticker();
    AM.Drawer();
    ST.Ticker();
    ST.Drawer(false,true);
    frame.setPalette((i/(10000/14))%14);
    frame.processEvents();
    frame.update();
    //frame.update();
    //frame.update(shit.getGraphics());
 
    }
    
    long b=System.nanoTime();
    
    System.out.println(TICKS +" tics in " +((b-a)/1e09) +" = "+TICKS/((b-a)/1e09) + " fps");
    
       
        } catch (Exception e){
            e.printStackTrace();
        }
    }
    
}

package testers;

/** Tester for bots...heh goodnight.
 * 
 */

import static p.MapUtils.InterceptVector;

import java.awt.Point;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketTimeoutException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedList;

import n.BasicNetworkInterface;
import p.divline_t;
import p.intercept_t;
import p.mobj_t;
import rr.line_t;
import rr.sector_t;
import awt.OldAWTDoom;
import b.BotGame;
import b.Reachable;
import b.SearchNode_t;
import b.bot_t;
import b.BotGame.ObjKind;
import b.BotGame.ReachableGroup;
import b.BotGame.SeenNode;
import b.BotPaths.BotPath;
import b.SearchNode_t.BlockingSector;
import defines.card_t;
import doom.DoomContext;
import doom.DoomMain;
import doom.doomdata_t;
import doom.player_t;
import doom.think_t;
import doom.thinker_t;
import doom.ticcmd_t;

public class BotTester {
	static DoomMain D4, D2, D1;
	static Object synchronizer = new Object();
	
	public static class NetInterfaceTester extends BasicNetworkInterface {
		DoomContext DC;
		
		public NetInterfaceTester(DoomContext DC) {
			super(DC);
			// TODO Auto-generated constructor stub
		}

	  private doomdata_t  sendData = new doomdata_t();
	  private doomdata_t  recvData = new doomdata_t();
		
		@Override  
		public void sendSocketPacket(DatagramSocket ds, DatagramPacket dp) throws IOException {
			DoomMain receiver;
			
			if (DM.consoleplayer == 0) {
				receiver = D2;
			}
			else {
				receiver = D4;
			}
			
			NetInterfaceTester N = (NetInterfaceTester)receiver.DNI;
			N.addReceivedPacket(dp);
			
			sendData.unpack(dp.getData());
			//System.out.println("Player "+DM.consoleplayer+" SENT: "+dataToStr(sendData));
		}
		
		/*class Elem<E> {
			E e;
			E next = null;
			public Elem(E e) {
				this.e = e;
			}
		}
		
		Elem<DatagramPacket> head = new Elem<DatagramPacket>(null);*/
		
		//ArrayList<DatagramPacket> al = new ArrayList<DatagramPacket>();
		LinkedList<DatagramPacket> al = new LinkedList<DatagramPacket>();
		
		public void addReceivedPacket(DatagramPacket dp) {
			synchronized(synchronizer) {
				al.addLast(dp);
			}
		}
		
		@Override  
		public void socketGetPacket(DatagramSocket ds, DatagramPacket dp) throws IOException {
			synchronized(synchronizer) {
				if (al.size() < 1)
					throw new SocketTimeoutException();
				DatagramPacket pop = al.removeFirst();
				dp.setData(pop.getData());
				dp.setLength(pop.getLength());
				dp.setSocketAddress(pop.getSocketAddress());
				
				recvData.unpack(dp.getData());
			}
			
			//System.out.println("Player "+DM.consoleplayer+" RECV: "+dataToStr(recvData));
		}
		
		public String dataToStr(doomdata_t dt) {
			StringBuffer sb = new StringBuffer();
			sb.append("STIC: "+dt.starttic+" NTIC: "+dt.numtics+" CMDS:\r\n");
			for (int i = 0; i < dt.numtics; i++) {
				ticcmd_t tc = dt.cmds[i];
				sb.append("    FMOVE: "+tc.forwardmove+" CONS: "+tc.consistancy+"\r\n");
			}
			return sb.toString();
				
		}
		
	}

	public static class DoomMainTester extends DoomMain {
		public player_t playerBot;
		
		public DoomMainTester(String[] args) {
		    this.Init();
		    this.VI = new OldAWTDoom(this,null,null);
		    this.DNI = new NetInterfaceTester(this);
		    this.myargv = args;
		    this.myargc = this.myargv.length; // Bump argcount +1 to maintain CheckParm behavior
		    this.Start();
		    this.bgame.BN.B_InitNodes();
		    playerBot = this.players[0];
		    playerBot.bot = new bot_t(this.bgame, playerBot);
		    playerBot.bot.walkReachable = new Reachable.WalkReachable(bgame, playerBot);
		}
		
		@Override
		public void DoomLoop() {
			System.out.println("Overriding DoomLoop");
		}
	}
	
	public static void testE1L2() {
		DoomMainTester DM=new DoomMainTester(new String[] {"", "-warp", "12"});
		
		line_t unlockLine = findLine(DM, new int[] {489, -1});
		sector_t sec = DM.bgame.BSL.sectorsTaggedBy(unlockLine).get(0);
		sec.ceilingheight = 20709376;

		
		// this this was a bug where a 2nd round bridge was chosen instead of a 1st round in FindReachableNodes
		// So B_Findpath could not find a path
		mobj_t mo = findThing(DM, -2097152, -56623104);
		DM.playerBot.mo.x = 100962571;
		DM.playerBot.mo.y = -19975541;
		
		DM.playerBot.cards[card_t.it_redcard.ordinal()] = true;
		        
		DM.bgame.FindReachableNodes(DM.playerBot);

		SearchNode_t srcNode = DM.bgame.botNodeArray[135][27];
		SearchNode_t destNode = DM.bgame.botNodeArray[58][44];
		ReachableGroup contained = (DM.playerBot.bot.botPaths.nodesContained(srcNode, destNode)); 

		BotPath path = DM.playerBot.bot.botPaths.B_FindPath(srcNode, destNode, DM.playerBot.bot.walkReachable, DM.playerBot.bot.walkReachable, new HashSet<BlockingSector>(), contained);
		assrt(contained != null && path != null);
		///////////////////
		
		
		
		
		
		
		
		
		/*SearchNode_t srcNode = DM.bgame.botNodeArray[135][27];
		SearchNode_t destNode = DM.bgame.botNodeArray[67][19];
		ReachableGroup contained = (DM.playerBot.bot.botPaths.nodesContained(srcNode, destNode)); */


		SeenNode SN = DM.bgame.new SeenNode(DM.playerBot, mo);
		SN.setKind(ObjKind.item);
		SN.findPath();
		
		DM.playerBot.bot.botPaths.B_FindPath(srcNode, destNode, DM.playerBot.bot.walkReachable, DM.playerBot.bot.walkReachable, new HashSet<BlockingSector>(), contained);
		
		ArrayList<SearchNode_t> srcNodes = DM.bgame.BN.findNodesFromPoint(DM.playerBot, new Point(DM.playerBot.mo.x, DM.playerBot.mo.y), DM.playerBot.bot.walkReachable /*finalReach*/);
		//if (srcNodes.size() == 0) // bot is really stuck!
		//	return null;

		ArrayList<SearchNode_t> destNodes = SN.getNodes();
		//if (destNodes.size() == 0) 
		//	return null;
		
		/*boolean contained = false;
		for (SearchNode_t destNode: destNodes) {
			
			//if (reachableNodes.contains(destNode)) {
				for (SearchNode_t srcNode: srcNodes) {
					if (DM.playerBot.bot.botPaths.nodesContained(srcNode, destNode)) {
						contained = true;
					}
				}
		}*/
		
		assrt((SN.path!=null) == (contained!=null) );
		System.out.println("test");

		
	}
	
	public static void testDoomI() {
		testE1L2();
	}
	
	public static void main(String[] args) {
		testDoomI();
		
		// tested with Doom II maps
		D1=new DoomMainTester(new String[] {"", "-warp", "1"});
		D2=new DoomMainTester(new String[] {"", "-warp", "2"});
		D4=new DoomMainTester(new String[] {"", "-warp", "4"});

	    int[] sh = BotGame.shiftDxDy(new Point(25,25), new Point(50,50), 5);
	    assrt(sh[0] == (int)(5/Math.sqrt(2)));
	    assrt(sh[1] == (int)(-5/Math.sqrt(2)));
	    
	    
	    

	    
	    
//	    
//	    //bot should not be able to traverse the two poles at startup of level 02
//	    Point inPoint = new Point(79352584, 120685114);
//	    Point outPoint = new Point(88774769, 121236663);
//	    D2.players[0].mo.x = inPoint.x;
//	    D2.players[0].mo.y = inPoint.y;
//	    LinkedList_t<SearchNode_t> path = new LinkedList_t<SearchNode_t>();
//	    
//		Reachable.WalkReachable wr2 = new Reachable.WalkReachable(D2.bgame, D2.players[0]);
//	    assrt(!D2.bgame.BN.B_FindPath(D2.players[0], outPoint, path, wr2, wr2));
//
//	    // make sure we cant make through the pole with this diagonal way
//	    SearchNode_t node1 = D2.bgame.botNodeArray[35][53];
//	    SearchNode_t node2 = D2.bgame.botNodeArray[36][54];
//	    assrt(!D2.bgame.BN.FindPathReachable(D2.players[0], node1, node2, true));
//	    assrt(!D2.bgame.BN.FindPathReachable(D2.players[0], D2.bgame.BN.nodeToPoint(node1), D2.bgame.BN.nodeToPoint(node2), true));
//	    
//	    // test if the first switch is reachable
//	    line_t switch1 = findLine(D2, new int[] {188, -1}); // special: 102 v1: (80740352, 77594624)
//	    SeenNode snSw1 = D2.bgame.new SeenNode(D2.players[0], switch1);
//	    snSw1.reachable = new Reachable.LineReachable(D2.bgame, D2.players[0], switch1);
//	    assrt(!snSw1.isReachable());
//	    inPoint = new Point(79181929, 81924198);
//	    D2.players[0].mo.x = inPoint.x;
//	    D2.players[0].mo.y = inPoint.y;
//	    assrt(snSw1.isReachable());
//	    
//
//	    
//	    inPoint = new Point(-48443906, 31553733);
//	    line_t switchL4 = findLine(D4, new int[] {317, -1}); // special: 102
//	    SeenNode snSW = D4.bgame.new SeenNode(D4.players[0], switchL4);
//	    snSW.reachable = new Reachable.LineReachable(D4.bgame, D4.players[0], switchL4);
//	    D4.players[0].mo.x = inPoint.x;
//	    D4.players[0].mo.y = inPoint.y;
//	    
//	    assrt(snSW.getNode() != null);
//	    
//	    assrt(snSW.isReachable());
//
//	    
//	    line_t floorL4 = findLine(D4, new int[] {733, -1}); // special: 18
//	    SeenNode snFloor = D4.bgame.new SeenNode(D4.players[0], floorL4);
//	    inPoint = new Point(-111892518, 71522245);
//	    D4.players[0].mo.x = inPoint.x;
//	    D4.players[0].mo.y = inPoint.y;
//	    snFloor.reachable = new Reachable.LineReachable(D4.bgame, D4.players[0], floorL4);
//	    assrt(snFloor.isReachable());
//	    assrt(snFloor.findPath());
//
//	    // in front of teleport
//	    inPoint = new Point(-93134091, 52253824);
//	    D4.players[0].mo.x = inPoint.x;
//	    D4.players[0].mo.y = inPoint.y;
//	    assrt(snFloor.findPath());
//
//	    
//	    line_t exitL4 = findLine(D4, new int[] {757, -1}); // special: 11
//	    SeenNode snExit = D4.bgame.new SeenNode(D4.players[0], exitL4);
//	    inPoint = new Point(-127891116, 86164298);
//	    D4.players[0].mo.x = inPoint.x;
//	    D4.players[0].mo.y = inPoint.y;
//	    snExit.reachable = new Reachable.LineReachable(D4.bgame, D4.players[0], exitL4);
//	    assrt(snExit.isReachable() /*D4.bgame.BN.seenSwitchReachable(D4.players[0], inPoint, exitL4)*/);
//	    assrt(snExit.findPath());
//	    //D4.bgame.BN.B_FindPath(D4.players[0], destPoint, path, walkReach, finalReach)
//
//	    //seems not to work if on the other side of the yellow door (because of the Demon?)
//	    /*inPoint = new Point(-120594720, 85885648);
//	    D4.players[0].mo.x = inPoint.x;
//	    D4.players[0].mo.y = inPoint.y;
//	    assrt(snExit.findPath());*/
//	    
//	    
//		DoomMain D12=new DoomMainTester(new String[] {"", "-warp", "12"});
//		inPoint = new Point(89169710, 117939298);
//		outPoint = new Point(87979765, 108184173);
//		Reachable.WalkReachable wr = new Reachable.WalkReachable(D12.bgame, D12.players[0]);
//		assrt(wr.isReachable(inPoint, outPoint));
//		line_t lineD12 = findLine(D12, new int[] {1053, 1054});
//		lineD12.frontsector.floorheight = 300000;
//		assrt(wr.isReachable(inPoint, outPoint));   // should still be considered as reachable because it is an elevator...
//	    
//	    
//		D2.P.PathTraverse(69206016, 125829120, 67108864, 125829120, 0, null); // seems OK
//		D2.P.PathTraverse(69206015, 125829119, 67108864, 125829120, 0, null); // seems to bug
//		D2.P.PathTraverse(69206017, 125829119, 67108864, 125829120, 0, null); // seems to bug
//
//		// a rare case when changeX and changeY are both true
//		D1.P.PathTraverse(132120575, -56623105, 132120576, -54525952, 0, null); // seems OK
//
//		//D2: Bug with Pathtraverse: (69206015,125829119) to (67108864,125829120)
//		//D2: Bug with Pathtraverse: (69206017,125829119) to (67108864,125829120)
//		//D2: Seems OK: (69206016,125829120) to (67108864,125829120)
//		
//		//D1: Bug with Pathtraverse: (132120575,-56623105) to (132120576,-54525952)
//	    
//	    traverseTester(D4);
//	    
//	    /*SearchNode_t srcNode = D4.bgame.BN.B_GetNodeAt(-29897546, 44775535,null);
//	    SearchNode_t destNode = D4.bgame.BN.B_GetNodeAt(-27247827, 42609240,null);
//	    LinkedList<intercept_t> q = D4.bgame.QueuePathTraverse(nodeToPoint(D4, srcNode), nodeToPoint(D4, destNode));
//	    assrt(!D4.bgame.BN.FindPathReachable(D4.players[0], srcNode, destNode, true));
//
//	    srcNode.x = 48;
//	    srcNode.y = 13;
//	    destNode.x = 47;
//	    destNode.y = 12;
//	    assrt(!D4.bgame.BN.FindPathReachable(D4.players[0], srcNode, destNode, true));
//
//	    D4.players[0].mo.x = -28202630;
//	    D4.players[0].mo.y = 48802275;
//	    
//	    path = new LinkedList_t<SearchNode_t>();
//	    assrt(!D4.bgame.BN.B_FindPath(D4.players[0], new Point(-28778704, 34686148), path, D4.bgame.BN.walkReachable, D4.bgame.BN.walkReachable)); //should not have a path from "lava" to "step too high"
//	    
//	    for (SearchNode_t sn: path) {
//	    	System.out.println(D4.bgame.BN.nodeInfo(sn));
//	    }*/
//	    
//	    /*new Thread() {
//	    	public void run() {
//	    	    D1.Start (); 		
//	    	}
//	    }.start();
//
//	    try {
//			Thread.sleep(1000);
//		} catch (InterruptedException e) {
//			// TODO Auto-generated catch block
//			e.printStackTrace();
//		}
//	    
//		//D1.bgame.Got_AddBot(0,0);
//
//		/*D2=new DoomMain();
//	    D2.Init();
//	    D2.DNI = new NetInterfaceTester(D2);
//	    D2.myargv = new String[] {"", "-net", "2", "localhost"};
//	    D2.myargc = D2.myargv.length; // Bump argcount +1 to maintain CheckParm behavior
//
//	    new Thread() {
//	    	public void run() {
//	    	    D1.Start (); 		
//	    	}
//	    }.start();
//	    
//	    try {
//			Thread.sleep(1000);
//		} catch (InterruptedException e) {
//			// TODO Auto-generated catch block
//			e.printStackTrace();
//		}
//	    
//	    new Thread() {
//	    	public void run() {
//	    	    D2.Start (); 		
//	    	}
//	    }.start();*/
//	    

	}
	
	public static Point nodeToPoint(DoomMain DM, SearchNode_t node) {
		return new Point(DM.bgame.BN.posX2x(node.x), DM.bgame.BN.posY2y(node.y));
	}
	public static sector_t findSector(DoomMain DM, int id/*int special, int floorheight*/) {
		for (sector_t sec: DM.LL.sectors) {
			if (/*sec.special == special && sec.floorheight == floorheight*/sec.id == id) {
				return sec;
			}
		}
		return null;
	}
	
	public static line_t findLine(DoomMain DM, int[] sidenum /*int special, int floorheight*/) {
		for (line_t line: DM.LL.lines) {
			if (line.sidenum.length == 2 && sidenum[0]==line.sidenum[0] && sidenum[1]==line.sidenum[1]) {
				return line;
			}
		}
		return null;
	}
	
	public static mobj_t findThing(DoomMain DM, int x, int y) {
		thinker_t currentthinker = DM.P.thinkercap.next;
		while (currentthinker != DM.P.thinkercap) // search through the list
													// of all thinkers
		{
			if (currentthinker.function/* .acp1 */== think_t./* (actionf_p1) */P_MobjThinker) {
				mobj_t mo = (mobj_t) currentthinker;
				if (mo.x == x && mo.y == y)
					return mo;
			}
			currentthinker = currentthinker.next;
		}
		return null;
	}

	
	public static  void traverseTester(DoomMain DM) {
		sector_t secLava = findSector(DM, 24);//findSector(DM, 16, -1572864);
		line_t lineLava = findLine(DM, new int[] {111,112});//findSector(DM, 16, -1572864);

		Point p1 = new Point((lineLava.v1x + lineLava.v2x)/2, lineLava.v1y + 10); // in lava sector
		Point p2 = new Point((lineLava.v1x + lineLava.v2x)/2, lineLava.v1y - 10); // on step
		Point p3 = new Point((lineLava.v1x + lineLava.v2x)/2, lineLava.v1y);
		
	    D4.bgame.pointOnLine(p3, lineLava);
	    
	    divline_t trac=new divline_t();
	    trac.x = p1.x;
	    trac.y = p1.y;
	    trac.dx = p2.x-p1.x;
	    trac.dy = p2.y-p1.y;

	    divline_t trac2=new divline_t();
	    trac2.x = p2.x;
	    trac2.y = p2.y;
	    trac2.dx = p1.x-p2.x;
	    trac2.dy = p1.y-p2.y;

	    divline_t dl1 = new divline_t();
	    dl1.MakeDivline(lineLava);
	    
	    InterceptVector(trac, /*new divline_t(lineLava)*/dl1);
	    InterceptVector(trac2, /*new divline_t(lineLava)*/dl1);

	    LinkedList<intercept_t> q = D4.bgame.QueuePathTraverse(p1, p2);
	    assrt(q.size()==1 && q.get(0).line == lineLava);
	    LinkedList<intercept_t> q2 = D4.bgame.CorrectedQueuePathTraverse(p1, p2);
	    assrt(q2.size()==1 && q2.get(0).line == lineLava);

	    LinkedList<intercept_t> q3 = D4.bgame.QueuePathTraverse(p1, p3);
	    assrt(q3.size()==1 && q3.get(0).line == lineLava); // on one side we will see the line
	    LinkedList<intercept_t> q4 = D4.bgame.CorrectedQueuePathTraverse(p1, p3);
	    assrt(q4.size()==1 && q4.get(0).line == lineLava);

	    LinkedList<intercept_t> q5 = D4.bgame.QueuePathTraverse(p3, p1); 
	    assrt(q5.size()==1 && q5.get(0).line == lineLava); 
	    LinkedList<intercept_t> q6 = D4.bgame.CorrectedQueuePathTraverse(p3, p1);
	    assrt(q6.size()==0); // we want to ignore a line that srcPoint is on

	    LinkedList<intercept_t> q7 = D4.bgame.QueuePathTraverse(p2, p1);
	    assrt(q7.size()==1 && q7.get(0).line == lineLava);
	    LinkedList<intercept_t> q8 = D4.bgame.CorrectedQueuePathTraverse(p2, p1);
	    assrt(q8.size()==1 && q8.get(0).line == lineLava);

	    LinkedList<intercept_t> q9 = D4.bgame.QueuePathTraverse(p3, p2);
	    assrt(q9.size()==0 /*&& q9.get(0).line == lineLava*/); //on the other side we don't see the line
	    LinkedList<intercept_t> q10 = D4.bgame.CorrectedQueuePathTraverse(p3, p2);
	    assrt(q10.size()==0);

	    LinkedList<intercept_t> q11 = D4.bgame.QueuePathTraverse(p2, p3);
	    assrt(q11.size()==0 /*&& q11.get(0).line == lineLava*/); //on the other side we don't see the line
	    LinkedList<intercept_t> q12 = D4.bgame.CorrectedQueuePathTraverse(p2, p3);
	    assrt(q12.size()==1 && q12.get(0).line == lineLava); // we want to include the line destPoint is on

	    
	    LinkedList<BlockingSector> bs1 = D4.bgame.TraversedSecLines(p1, p2);
	    assrt(bs1.size() == 1);
	    LinkedList<BlockingSector> bs2 = D4.bgame.TraversedSecLines(p2, p1);
	    assrt(bs2.size() == 1);
	    assrt(bs1.get(0).srcSect == bs2.get(0).destSect);
	    assrt(bs2.get(0).srcSect == bs1.get(0).destSect); // bs1 should be bs2 reversed
	    
	    LinkedList<BlockingSector> bs3 = D4.bgame.TraversedSecLines(p1, p3);
	    assrt(bs3.size() == 1);
	    LinkedList<BlockingSector> bs4 = D4.bgame.TraversedSecLines(p3, p1);
	    assrt(bs4.size() == 0); // because we ignore the lines we start exactly on
	    assrt(bs1.get(0).srcSect == bs3.get(0).srcSect);
	    assrt(bs1.get(0).destSect == bs3.get(0).destSect); //bs3 should be the same as bs1

	}
	static void assrt(boolean b) {
		if (!b) {
			System.out.println("Assert failed");
		}
	}

}

package testers;

import static data.Defines.PU_STATIC;

import i.IDoomSystem;

import java.awt.image.BufferedImage;
import java.awt.image.IndexColorModel;

import awt.OldAWTDoom;

import m.DoomRandom;
import s.IDoomSound;
import s.DummySoundDriver;
import utils.C2JUtils;
import v.BufferedRenderer;
import w.DoomBuffer;
import w.WadLoader;
import data.Defines;
import defines.*;
import doom.DoomMain;
import doom.player_t;
import doom.ticcmd_t;
import doom.wbstartstruct_t;
import f.EndLevel;

/** This is a very simple tester for the End Level screen drawer.
 *  MAES: this is mostly historical. Too many changes have occured
 *  and it's no longer easy to operate stand-alone.
 *  
 *  Edit: well, maybe it is...
 * 
 */

public class EndLevelTester {

    public static void main(String[] argv) {
        try {
    WadLoader W=new WadLoader();
    W.InitMultipleFiles(new String[] {"doom1.wad"});
    //W.AddFile("bitter.wad");
    System.out.println("Total lumps read: "+W.numlumps);
    DoomBuffer palette = W.CacheLumpName("PLAYPAL", PU_STATIC);
    byte[] pal=palette.getBuffer().array();

    IndexColorModel icm=new IndexColorModel(8, 256,pal, 0, false);
    BufferedRenderer V=new BufferedRenderer(Defines.SCREENWIDTH,Defines.SCREENHEIGHT,icm);
    V.Init();
    
    IndexColorModel[] icms=new IndexColorModel[pal.length/768];
    BufferedImage[] pals=new BufferedImage[icms.length];
    
    for (int i=0;i<icms.length;i++){
        icms[i]=new IndexColorModel(8, 256,pal, i*768, false);
            pals[i]=new BufferedImage(icms[i],V.screenbuffer[0].getRaster(), false, null);
           }
    
    DoomMain DC=new DoomMain();
    DC.DM=DC;
    IDoomSystem I=new i.DoomSystem();
    IDoomSound S=new DummySoundDriver();
    
    DC.S=S;
    DC.I=I;
    DC.gameepisode=1;
    DC.gamemap=1;
    DC.gamemission=GameMission_t.doom;
    DC.gamemode=GameMode_t.shareware;
    DC.wminfo=new wbstartstruct_t();
    C2JUtils.initArrayOfObjects(DC.players,player_t.class);

    
    DC.DM=DC;
    DC.W=W;
    DC.V=V;
    DC.RND=new DoomRandom();
    DC.players[0].cmd=new ticcmd_t();
    DC.players[0].itemcount=1337;
    DC.players[0].killcount=1337;
    DC.players[0].secretcount=1337;
    
    DC.wminfo.plyr[0].in=true;
    DC.wminfo.plyr[0].sitems=1337;
    DC.wminfo.plyr[0].skills=1337;
    DC.wminfo.plyr[0].stime=28595;
    DC.wminfo.plyr[0].ssecret=1337;
    DC.playeringame[0]=true;
    DC.wminfo.last=6;
    DC.wminfo.epsd=0;
    DC.wminfo.maxitems=100;
    DC.wminfo.maxkills=100;
    DC.wminfo.maxsecret=100;
    DC.wminfo.partime=28595;
    //JFrame frame = new JFrame("MochaDoom");
    OldAWTDoom shit = new OldAWTDoom(DC, V, pal);
    shit.InitGraphics();
    
    //frame.setBounds(frame.getX(), frame.getY(), WIDTH, 240);
    EndLevel EL=new EndLevel(DC);
    

   // EL.Start(wbstartstruct);
    int a,b;
    a=I.GetTime();
    b=a;
    for (int i=0;i<2000;i++){
    	
    EL.Ticker();
    EL.Drawer();
    shit.update(shit.getGraphics());
    if (i==100){
            DC.players[0].cmd.buttons=1; // simulate attack
            DC.players[0].attackdown=false; // simulate attack
        }
        
        if (i==120){
            DC.players[0].cmd.buttons=1; // simulate attack
            DC.players[0].attackdown=false; // simulate attack
        }
   // Do we still have time>
        
        while((b-a)==0) {
        	b=I.GetTime();
        	}
       a=b;
    }
        }catch (Exception e){
            e.printStackTrace();
        }
    }
    
}

package testers;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.ByteBuffer;

import p.LevelLoader;

import rr.SimpleTextureManager;
import rr.TextureManager;
import s.DummySoundDriver;
import s.IDoomSound;
import savegame.VanillaDSG;
import savegame.VanillaDSGHeader;

import m.MenuMisc;
import defines.GameMission_t;
import defines.GameMode_t;
import defines.skill_t;
import demo.VanillaDoomDemo;
import doom.DoomMain;
import doom.DoomStatus;
import w.*;

public class SaveGameHeaderTester {

    public static void main(String[] argv) throws Exception {

    ByteBuffer buf=MenuMisc.ReadFile("doomsav0.dsg");
    DoomFile f=new DoomFile("doomsav0.dsg","r");
    DoomStatus DS=new DoomStatus();
    
    VanillaDSGHeader vdsg=new VanillaDSGHeader();
    try {
        vdsg.read(f);
    
    
    f.seek(0);
    
    WadLoader W=new WadLoader();
    W.InitMultipleFiles(new String[] {"C:\\DOOMS\\doom1.wad"});
    //W.AddFile("bitter.wad");
    System.out.println("Total lumps read: "+W.numlumps);
    System.out.println("NUm for E1M1: "+W.GetNumForName("E1M1"));
    DS.gameepisode=1;
    DS.gamemap=1;
    DS.gamemission=GameMission_t.doom;
    DS.setGameMode(GameMode_t.shareware);
    IDoomSound S=new DummySoundDriver();            
    DS.S=S;
    DS.W=W;
    LevelLoader LL=new LevelLoader(DS);
    DS.LL=LL;
    TextureManager TM=new SimpleTextureManager(DS);
    DS.TM=TM;
    LL.updateStatus(DS);
    TM.InitFlats();
    TM.InitTextures();
    
    //HU hu=new HU(DS);
    //hu.Init();
    
    LL.SetupLevel(1, 1, 0, skill_t.sk_hard);

    
    VanillaDSG DSG=new VanillaDSG();
    DSG.updateStatus(DS);    
    
    DS.playeringame[0]=true;
    DS.players[0].updateStatus(DS);
    DSG.doLoad(f);
    
    
    //    W.AddFile("bitter.wad");

    System.out.println(vdsg);
    
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    }
    
}

package testers;

import java.awt.image.BufferedImage;
import java.awt.image.DataBufferByte;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

import javax.imageio.ImageIO;

public class PaletteShower15 {

    public static final int BLOCKSIZE=16;
    public static final int SHIFT=3;
    
    public static void main(String[] argv) {
        try {
    BufferedInputStream bis;
    bis=new BufferedInputStream(new FileInputStream(argv[0]));
    
    int size=bis.available();
    
    int blocks=size/512;
    
    
    byte[] palbuf=new byte[512];
    ByteBuffer bb= ByteBuffer.wrap(palbuf);
    bb.order(ByteOrder.LITTLE_ENDIAN);
    bb.mark();
    for (int i=0;i<blocks;i++){
        bis.read(palbuf);
        
        BufferedImage bim=new BufferedImage(16*BLOCKSIZE,16*BLOCKSIZE,BufferedImage.TYPE_INT_ARGB);
        
            for (int j=0;j<256;j++){
                short shcol=bb.getShort();
                int r=(0x7C00&shcol)>>10;
                int g=(0x3E0&shcol)>>5;
                int b=(0x01F&shcol);
                System.out.printf("%x %d %d %d\n",shcol,r,g,b);
                int colo=  0xFF000000|(r<<(16+SHIFT))|
                            (g<<(8+SHIFT))|
                            (b<<SHIFT);
            
            for (int x=0;x<BLOCKSIZE;x++){
                for (int y=0;y<BLOCKSIZE;y++){
                    bim.setRGB(BLOCKSIZE*(j%16)+x, BLOCKSIZE*(j/16)+y, colo);
                }
            }
            
        }
        
        FileOutputStream f=new FileOutputStream(String.format("%s-%d.PNG",argv[0],i));
        
        ImageIO.write(bim,"PNG",f);
        bb.reset();
        }
    
        bis.close();
    
        } catch (Exception e){
            e.printStackTrace();
        }
    }
    
}

package testers;

import p.LevelLoader;
import rr.UnifiedRenderer;
import utils.C2JUtils;
import w.WadLoader;
import data.Defines.GameMission_t;
import data.Defines.GameMode_t;
import data.Defines.skill_t;
import doom.DoomContext;
import doom.DoomStatus;
import doom.player_t;
import doom.wbstartstruct_t;

/** This is a very simple tester for the WadLoader and HU modules.
 *  We use the same exact methods used in the C source code, only
 *  with a more OO approach.
 * 
 * 
 */

public class RenderDataLoader {

    public static void main(String[] argv) {
        try {
    WadLoader W=new WadLoader();
    W.InitMultipleFiles(new String[] {"doom1.wad"});
    System.out.println("Total lumps read: "+W.numlumps);

    DoomStatus ds = new DoomStatus();
    ds.gameepisode=1;
    ds.gamemap=1;
    ds.gamemission=GameMission_t.doom;
    ds.gamemode=GameMode_t.shareware;
    ds.wminfo=new wbstartstruct_t();
    C2JUtils.initArrayOfObjects(ds.players,player_t.class);
    DoomContext DC=new DoomContext();
    DC.DS=ds;
    DC.W=W;
    
    LevelLoader LL=new LevelLoader(DC);
    DC.LL=LL;
    LL.SetupLevel(1, 1, 0, skill_t.sk_hard);
    
    
    
    UnifiedRenderer RD=new UnifiedRenderer(DC);
    RD.InitData();
    RD.RenderPlayerView(ds.players[0]);
    
        } catch (Exception e){
            e.printStackTrace();
        }
    }
    
}

package testers;

import m.FixedFloat;
import m.fixed_t;
import static m.fixed_t.*;

class FPTest{         
public static final int PRECISION=16;

    public static void main(String argv[])
{
        
    byte aa=(byte) 129;
        
    fixed_t a=new fixed_t(0x8FFF0000);
    fixed_t b=new fixed_t(0xFFFFFFFF);
    
   // a=F2F(32393.244141f);
   // b=F2F(2.5f);
    
    /*System.out.println(Integer.toHexString(a.val));
    System.out.println(Integer.toHexString(b.val));
    System.out.println(FixedFloat.toFloat(a.val));
    System.out.println(FixedFloat.toFloat(b.val));

    System.out.println(FixedFloat.toDouble(a.val));
    System.out.println(FixedFloat.toDouble(b.val));*/

    int c=FixedFloat.toFixed(2.512344f);
    System.out.println(Integer.toHexString(c));
    System.out.println(FixedFloat.toFloat(c));
    System.out.println(Integer.toBinaryString(Float.floatToRawIntBits(FixedFloat.toFloat(c))));

    int d=FixedFloat.toFixed(32768.0125d);
    int e=FixedFloat.toFixed(Double.NEGATIVE_INFINITY);
    int f=FixedFloat.toFixed(-2.5123d);
    System.out.println(FixedFloat.toFloat(d));
    System.out.println(FixedFloat.toFloat(e));
    System.out.println(FixedFloat.toFloat(f));
    
    System.out.println(FixedFloat.toDouble(d));
    System.out.println(FixedFloat.toDouble(e));
    System.out.println(FixedFloat.toDouble(f));
    
    int g=FixedFloat.toFixed(10.0);
    int h=FixedFloat.toFixed(3.0);
    System.out.println(FixedFloat.toFloat(FixedDiv(g,h)));
    
    //System.out.println(Integer.toBinaryString(Float.floatToRawIntBits(FixedFloat.toDouble(c))));

    /*
    fixed_t.FixedMul(a,b,b);    
    System.out.println(Integer.toHexString(b.get()));    

    a=F2F(2.5f);
    b=F2F(2.5f);
    
    
   // a.set(a.val+b.val-(fixed_t.FixedMul(F2F(1.5f),a)).val);
    System.out.println(Integer.toHexString(a.get()));  
    
    a=F2F(10000.0f);
    b=F2F(0.5657f);
    
    System.out.println(Integer.toHexString(a.val));
    System.out.println(Integer.toHexString(b.val));
    
    a=fixed_t.FixedDiv(a,b);
    System.out.println(Integer.toHexString(a.val));
*/
}

public static fixed_t F2F(float f){
    fixed_t tmp;
    int ing;
    float frac;


    ing=(int)Math.floor(f);
    //System.out.println("Int: "+(int)(f));
    //System.out.println("Hex: "+Integer.toHexString(ing));

    //System.out.println("Frac: "+(f-Math.floor(f)));
    //System.out.println("Frac hex: "+Integer.toHexString(FixedDecimal(f)));

    tmp= new fixed_t((ing<<16)|FixedDecimal(f));
    //System.out.println(Integer.toHexString(tmp.val));    
    return tmp;

}

public static char FixedDecimal(float f){
         char fixint_value=0;
         float decimal_part= (float) (f-Math.floor(f));
for ( int i = 1; i <= PRECISION; i++)
{
   if (decimal_part > 1.f/(float)(i + 1.0))
   {
      decimal_part -= 1.f/(float)(i + 1.0);
      fixint_value |= (1 << PRECISION - i);
   }
}
return fixint_value;
}

public static double Value(int fixed){
    double dec=(fixed>>FRACBITS);
    double frac=0;char fixint_value=0;

    for ( int i = FRACBITS; i >=0; i--)
    {
       if (((fixed>>FRACBITS)&(0x0001))==1) {
           frac+=1/(2+(FRACBITS-i));
           }
    }
return (dec+frac);
}


}


package testers;

import i.IVideo;

public class IVideoTester {
public static void main(String argv[]){
	IVideo IV=new IVideo();
	IV.InitExpand();
	IV.InitExpand2();
	IV.InitExpand();
}
}

package testers;

import static data.Defines.KEY_BACKSPACE;
import static data.Defines.KEY_DOWNARROW;
import static data.Defines.KEY_ENTER;
import static data.Defines.KEY_EQUALS;
import static data.Defines.KEY_ESCAPE;
import static data.Defines.KEY_F1;
import static data.Defines.KEY_F10;
import static data.Defines.KEY_F11;
import static data.Defines.KEY_F12;
import static data.Defines.KEY_F2;
import static data.Defines.KEY_F3;
import static data.Defines.KEY_F4;
import static data.Defines.KEY_F5;
import static data.Defines.KEY_F6;
import static data.Defines.KEY_F7;
import static data.Defines.KEY_F8;
import static data.Defines.KEY_F9;
import static data.Defines.KEY_LEFTARROW;
import static data.Defines.KEY_MINUS;
import static data.Defines.KEY_PAUSE;
import static data.Defines.KEY_RALT;
import static data.Defines.KEY_RCTRL;
import static data.Defines.KEY_RIGHTARROW;
import static data.Defines.KEY_RSHIFT;
import static data.Defines.KEY_TAB;
import static data.Defines.KEY_UPARROW;

import i.InputListener;

import java.awt.AWTEvent;
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Event;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.image.BufferedImage;
import java.util.LinkedList;

import javax.swing.JFrame;

import doom.event_t;
import doom.evtype_t;

public class CrappyDisplay extends JFrame implements KeyListener{

     /**
	 * 
	 */
	private static final long serialVersionUID = 1L;
		Canvas drawhere;
        BufferedImage[] bi;
        int palette=0;
        Dimension size;
        Image crap;
        //InputListener in;
        Graphics2D g2d;

        public CrappyDisplay(BufferedImage[] bi) {
        	this.setBackground(Color.black);
            this.bi=bi;            
            size = new Dimension();
            size.width = bi[0].getWidth();
            size.height = bi[0].getHeight();
            this.setSize(size);
            drawhere=new Canvas();
            drawhere.setSize(size);
            drawhere.setBounds(0, 0, drawhere.getWidth(),drawhere.getHeight());
            drawhere.setBackground(Color.black);            
            this.add(drawhere);
            this.addKeyListener(this);
            //this.addComponentListener(in);
            this.setVisible(true);
            g2d = (Graphics2D)drawhere.getGraphics();
        }
        
        public CrappyDisplay(BufferedImage bi) {
            this.bi=new BufferedImage[]{bi};
            size = new Dimension();
            size.width = bi.getWidth();
            size.height = bi.getHeight();
            this.setSize(size);
            drawhere=new Canvas();
            drawhere.setSize(size);
            drawhere.setBounds(0, 0, drawhere.getWidth(),drawhere.getHeight());
            drawhere.setBackground(Color.black);
            this.add(drawhere);
            
            //in = new InputListener();
            //this.addComponentListener(in);
            this.setVisible(true);
            g2d = (Graphics2D)drawhere.getGraphics();
        }
        
        public void setPalette(int pal){
            this.palette=pal;
            
        }
        
        public void update(Graphics g) {
           
           //Graphics2D g2d = (Graphics2D)drawhere.getGraphics();
           //voli.getGraphics().drawImage(bi,0,0,null);
           g2d.drawImage(bi[palette],0,0,this);
           
        }
        
        public void update() {
            
            Graphics2D g2d = (Graphics2D)drawhere.getGraphics();
            //voli.getGraphics().drawImage(bi,0,0,null);
            g2d.drawImage(bi[palette],0,0,this);
            
         }
        
        public String processEvents(){
            StringBuffer tmp=new StringBuffer();
            event_t event;
            while ( (event=InputListener.nextEvent()) != null ) {
                tmp.append(event.type.ordinal()+"\n");
            }
            return tmp.toString();
        }

        public int xlatekey(KeyEvent e)
        {

          int rc;

          switch(rc =     e.getKeyCode())
          
              //Event.XKeycodeToKeysym(X_display, X_event.xkey.keycode, 0))

          {
            case KeyEvent.VK_LEFT:    rc = KEY_LEFTARROW; break;
            case KeyEvent.VK_RIGHT:   rc = KEY_RIGHTARROW;    break;
            case KeyEvent.VK_DOWN:    rc = KEY_DOWNARROW; break;
            case KeyEvent.VK_UP:  rc = KEY_UPARROW;   break;
            case KeyEvent.VK_ESCAPE:  rc = KEY_ESCAPE;    break;
            case KeyEvent.VK_ENTER:   rc = KEY_ENTER;     break;
            case KeyEvent.VK_TAB: rc = KEY_TAB;       break;
            case KeyEvent.VK_F1:  rc = KEY_F1;        break;
            case KeyEvent.VK_F2:  rc = KEY_F2;        break;
            case KeyEvent.VK_F3:  rc = KEY_F3;        break;
            case KeyEvent.VK_F4:  rc = KEY_F4;        break;
            case KeyEvent.VK_F5:  rc = KEY_F5;        break;
            case KeyEvent.VK_F6:  rc = KEY_F6;        break;
            case KeyEvent.VK_F7:  rc = KEY_F7;        break;
            case KeyEvent.VK_F8:  rc = KEY_F8;        break;
            case KeyEvent.VK_F9:  rc = KEY_F9;        break;
            case KeyEvent.VK_F10: rc = KEY_F10;       break;
            case KeyEvent.VK_F11: rc = KEY_F11;       break;
            case KeyEvent.VK_F12: rc = KEY_F12;       break;
            
            //case Event.BACK_SPACE:
            case KeyEvent.VK_DELETE:  rc = KEY_BACKSPACE; break;

            case KeyEvent.VK_PAUSE:   rc = KEY_PAUSE;     break;

            case KeyEvent.KEY_PRESSED:
                switch(e.getKeyCode()){
            case (KeyEvent.VK_EQUALS): 
                rc = KEY_EQUALS;    
                break;
                
            case KeyEvent.VK_SUBTRACT: 
            case KeyEvent.VK_MINUS:   
                rc = KEY_MINUS;     
                break;

            case KeyEvent.SHIFT_DOWN_MASK:
            rc = KEY_RSHIFT;
            break;
            
            case KeyEvent.CTRL_DOWN_MASK:
            rc = KEY_RCTRL;
            break;                           
           
            case KeyEvent.ALT_DOWN_MASK:
            rc = KEY_RALT;
            break;
            }
                
            default:
            /*if (rc >= KeyEvent.VK_SPACE && rc <= KeyEvent.VK_DEAD_TILDE)
                rc = (int) (rc - KeyEvent.FOCUS_EVENT_MASK + ' ');*/
            if (rc >= KeyEvent.VK_A && rc <= KeyEvent.VK_Z)
                rc = rc-KeyEvent.VK_A +'a';
            break;
          }
          
          System.out.println("Typed "+e.getKeyCode()+" char "+e.getKeyChar()+" mapped to "+Integer.toHexString(rc));

          return rc;

        }

        public void keyPressed(KeyEvent e) {
            if (!((e.getModifiersEx() & InputEvent.ALT_GRAPH_DOWN_MASK) != 0)) {
                addEvent(new event_t(evtype_t.ev_keydown, xlatekey(e)));
            }
        }

        public void keyReleased(KeyEvent e) {
          //  addEvent(new event_t(evtype_t.ev_keyup,xlatekey(e)));
        }

        public void keyTyped(KeyEvent e) {
         /*   if ((e.getModifiersEx() & InputEvent.ALT_GRAPH_DOWN_MASK) != 0) {
                addEvent(new event_t(evtype_t.ev_keydown, xlatekey(e)));
                addEvent(new event_t(evtype_t.ev_keyup, xlatekey(e)));
            } */       
        }
        
        static void addEvent(event_t ev) {
            synchronized (eventQueue) {
                eventQueue.addLast(ev);
            }
        }

        public static event_t nextEvent() {
            event_t ev;
            synchronized (eventQueue) {
                ev = (!eventQueue.isEmpty())?(event_t)eventQueue.removeFirst():null;
            }
            return ev;
        }
        
     // modifications of eventQueue must be thread safe!
        private static LinkedList<event_t> eventQueue = new LinkedList<event_t>();
    
}

package testers;

import static data.Defines.PU_STATIC;
import static data.Limits.MAXEVENTS;
import n.DummyNetworkHandler;

import hu.HU;
import i.DoomSystem;
import i.IDoomSystem;

import p.Actions;
import p.ILevelLoader;
import p.LevelLoader;

import automap.Map;
import awt.AWTDoom;
import rr.ParallelRenderer;
import rr.SimpleTextureManager;
import rr.SpriteManager;
import s.DummySoundDriver;
import st.StatusBar;
import timing.MilliTicker;

import m.IDoomMenu;
import m.Menu;
import m.DoomRandom;
import v.BufferedRenderer;
import v.DoomVideoRenderer;
import v.GammaTables;
import v.IVideoScale;
import v.VideoScaleInfo;
import w.DoomBuffer;
import w.WadLoader;
import defines.*;
import data.Tables;
import doom.CommandLine;
import doom.DoomMain;
import doom.event_t;
import doom.ticcmd_t;
import doom.wbstartstruct_t;

/** This is a very simple tester for Menu module  */

public class AWTParallelRenderTester1 {

    static IVideoScale VSI=new VideoScaleInfo(4.0f);
    
    public static void main(String[] argv) {
        try {
    
            Tables.InitTables();
            
    // Create a Wad file loader.
    
    WadLoader W=new WadLoader();
    W.InitMultipleFiles(new String[] {"doom1.wad"});
    
    System.out.println("Total lumps read: "+W.numlumps);

    // Read the palette.
    DoomBuffer palette = W.CacheLumpName("PLAYPAL", PU_STATIC);
    // Create a video renderer
    DoomVideoRenderer V=new BufferedRenderer(VSI.getScreenWidth(),VSI.getScreenHeight());
    V.setVideoScale(VSI);
    V.initScaling();
    V.Init();
    byte[] pal=palette.getBuffer().array();
    V.createPalettes(pal, GammaTables.gammatables, 14, 256, 3, 5);
    

    
    IDoomSystem I=new DoomSystem();
    DoomMain DM=new DoomMain();
    CommandLine CM=new CommandLine(argv);
    DM.CM=CM;
    DM.TICK=new MilliTicker();    
    DM.setVideoScale(VSI);
    DM.initScaling();
    DM.singletics=true;
    DM.setTicdup(1);
    DM.DGN=new DummyNetworkHandler();
    
    // Create the frame.
    AWTDoom frame = new AWTDoom(DM,V);
    frame.InitGraphics();

    DM.I=I;
    DM.VI=frame;
    DM.S=new DummySoundDriver();
    frame.DM=DM;
    DM.W=W;
    DM.V=V;
    DM.DM=DM;
    HU HU=new HU(DM);    
    DM.language=Language_t.english;
    HU.Init();
    DM.HU=HU;
    DM.gameepisode=1;
    DM.gamemap=1;
    DM.gamemission=GameMission_t.doom;
    DM.setGameMode(GameMode_t.shareware);
    DM.wminfo=new wbstartstruct_t();
    // Simulate being in the mid of a level.
    DM.usergame=true;
    DM.gamestate=gamestate_t.GS_LEVEL;
    DM.gameskill=skill_t.sk_hard;
    //C2JUtils.initArrayOfObjects(DM.players,player_t.class);
    
    DM.RND=new DoomRandom();
    DM.players[0].cmd=new ticcmd_t();
    DM.players[0].itemcount=1337;
    DM.players[0].killcount=1337;
    DM.players[0].secretcount=1337;
    
    DM.wminfo.plyr[0].in=true;
    DM.wminfo.plyr[0].sitems=1337;
    DM.wminfo.plyr[0].skills=1337;
    DM.wminfo.plyr[0].stime=28595;
    DM.wminfo.plyr[0].ssecret=1337;
    
    DM.playeringame[0]=true;
    DM.wminfo.last=6;
    DM.wminfo.epsd=0;
    DM.wminfo.maxitems=100;
    DM.wminfo.maxkills=100;
    DM.wminfo.maxsecret=100;
    DM.wminfo.partime=28595;
    
   
    IDoomMenu M=DM.M=new Menu(DM);
    Map AM=new Map(DM);
    DM.AM=AM;
    StatusBar ST=(StatusBar) (DM.ST=new StatusBar(DM));
    ILevelLoader LL=DM.LL=new LevelLoader(DM);
    DM.P=new Actions(DM);
    DM.SM=new SpriteManager(DM);
    DM.R=new ParallelRenderer(DM,2,1);    
    DM.TM=new SimpleTextureManager(DM);    
    DM.P.updateStatus(DM);
    LL.updateStatus(DM);
    M.updateStatus(DM);
    ST.updateStatus(DM);
    AM.updateStatus(DM);
    DM.initializeVideoScaleStuff();
    DM.R.Init();
    DM.P.Init();
    DM.players[0].updateStatus(DM);
    DM.players[0].PlayerReborn();
    
    ST.Init();
    M.Init();
    ST.Start();
    LL.SetupLevel(1, 1, 0, skill_t.sk_hard);
    AM.LevelInit();
    AM.Start();
    
    DM.R.SetViewSize(11, 0);
    DM.R.ExecuteSetViewSize();
    DM.TM.setSkyTexture(DM.TM.CheckTextureNumForName("SKY1"));
    long a=System.nanoTime();
    
    DM.menuactive=false;
    DM.automapactive=true;
    DM.R.FillBackScreen();
    DM.R.DrawViewBorder();
    
    
    // Center on "bloody mess" in E1M1
    DM.players[0].mo.y+=420<<16;
    //DM.players[0].mo.x+=1650<<16;
        int pl=0;
        for (int i=0;i<20000;i++){
   
           frame.GetEvent();
           
           for ( ; DM.eventtail != DM.eventhead ; DM.eventtail = (++DM.eventtail)&(MAXEVENTS-1) )
           {
       	event_t ev = DM.events[DM.eventtail];

            //System.out.println(ev);
            if (ev!=null){
            
            AM.Responder(ev);
            M.Responder(ev);
            ST.Responder(ev);
           }
           }
            
       
           
        //V.DrawPatch(0,0,0,help1);
        //M.Ticker();
        //M.Drawer();
        AM.Ticker();
        //AM.Drawer();
        ST.Ticker();        
        DM.players[0].viewz=(40)<<16;
        //DM.players[0].mo.x=ox+(int) ((12864<<16)*Math.cos(2*Math.PI*i/500.0));
        //DM.players[0].mo.y=oy+(int) ((64<<16)*Math.sin(2*Math.PI*i/500.0));
        //DM.players[0].mo.angle= ((long)(0xFFFFFFFFL*(Math.atan2(DM.players[0].mo.y-oy,DM.players[0].mo.x-ox)+Math.PI)/(2*Math.PI)))&0xFFFFFFFFL;
        DM.players[0].mo.angle=(DM.players[0].mo.angle-0x800000)&0xFFFFFFFFL;
        //System.out.println(">>>>>>>>>>>>>>>>>> VIEW ANGLE "+360.0*(DM.players[0].mo.angle>>19)/8192.0); 
       
        DM.R.RenderPlayerView(DM.players[0]);
        //ST.Drawer(false,true);
        //System.out.println("Rendered"+DM.gametic);
        
        DM.gametic++;
        frame.FinishUpdate();
        if (i%200==0){
        	   long b=System.nanoTime();
        	    //frame.setPalette((pl++)%14);
        	    System.out.println((200) +" frames in " +((b-a)/1e09) +" = "+200/((b-a)/1e09) + " fps");
        	    a=b;
        }
        
       // System.out.print(frame.processEvents());
        /*while (ba-DM.I.GetTime()>-1){
            Thread.sleep(10);               
        }*/
        }
            } catch (Exception e){
                e.printStackTrace();
            }
    
    }
    
}

package testers;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.RandomAccessFile;

import demo.VanillaDoomDemo;
import utils.C2JUtils;
import w.*;

public class JaguarFile {
    
    static final String URI="E://jagdoom/doom.wad";

    public static void main(String[] argv) throws IOException {

    DataInputStream dis=new DataInputStream(new FileInputStream("E://jagdoom/doom.wad"));
    DataOutputStream dos=new DataOutputStream(new FileOutputStream("E://jagdoom/jagdoom.wad"));
        
    wadheader_t header=new wadheader_t();
    header.big_endian=true;
    header.read(dis);
    header.big_endian=false;
    header.write(dos);

    System.out.println(header.type);
    System.out.println(header.numentries);
    System.out.println(header.tablepos);
    
    filelump_t[] stuff=new filelump_t[header.numentries];
    C2JUtils.initArrayOfObjects(stuff);
    
    byte[] data=new byte[header.tablepos-wadheader_t.sizeof()];
    dis.read(data);
    dos.write(data);
        
    int marker=dis.readInt();
    dos.writeInt(marker);
    
    for (filelump_t f:stuff){
        f.big_endian=true;
        f.read(dis);
        System.out.printf("%s %s %d %d %s\n",f.name,f.actualname,f.filepos,f.size,f.compressed);
        f.big_endian=false;
        f.write(dos);
    }
    
    dis.close();
    dos.close();
    
    FileOutputStream fos;
    FileInputStream fis =new FileInputStream("E://jagdoom/doom.wad");
    long size= InputStreamSugar.getSizeEstimate(fis, null);
    
    
    int k=0;
    for (filelump_t f:stuff){
        
        //if (k>2) break;
        k++;
        InputStreamSugar.streamSeek(fis, f.filepos, size, URI, null, InputStreamSugar.FILE);
        byte[] input=new byte[(int) Math.min(f.size,fis.available())];
        byte[] output;
        fis.read(input);
        if (f.compressed){
            System.out.printf("Decompressing %s expecting %d\n",f.actualname,f.size);
            output=new byte[(int) f.size];
            JadDecompress.decode(input, output);            
            fos=new FileOutputStream(
                String.format("E://jagdoom/DEC_%s.lmp",f.actualname));
            } else {
                output=input;
                fos=new FileOutputStream(
                    String.format("E://jagdoom/%s.lmp",f.name));
            }
        fos.write(output);
            }
        
    }
    
}

package testers;

import static data.Defines.KEY_F1;
import static data.Defines.PU_STATIC;
import static data.Limits.MAXEVENTS;

import hu.HU;
import i.DoomSystem;
import i.IDoomSystem;
import i.InputListener;

import java.awt.image.BufferedImage;
import java.awt.image.IndexColorModel;

import javax.swing.JFrame;

import p.Actions;
import p.LevelLoader;

import automap.Map;
import awt.OldAWTDoom;

import rr.UnifiedRenderer;
import rr.patch_t;
import s.DummySoundDriver;
import st.StatusBar;

import m.IDoomMenu;
import m.Menu;
import m.DoomRandom;
import utils.C2JUtils;
import v.BufferedRenderer;
import v.SimpleRenderer;
import w.DoomBuffer;
import w.WadLoader;
import data.Defines;
import defines.*;
import doom.DoomContext;
import doom.DoomMain;
import doom.DoomStatus;
import doom.event_t;
import doom.player_t;
import doom.ticcmd_t;
import doom.wbstartstruct_t;

/** This is a very simple tester for Menu module  */

public class AWTAutomapTester {
    public static final int WIDTH=320;
    public static final int HEIGHT=200;
    
    public static void main(String[] argv) {
        try {
        	
    // Create a Wad file loader.
    WadLoader W=new WadLoader();
    W.InitMultipleFiles(new String[] {"doom1.wad"});
    
    System.out.println("Total lumps read: "+W.numlumps);
    patch_t help1=W.CachePatchName("TITLEPIC", PU_STATIC);

    // Read the paletter.
    DoomBuffer palette = W.CacheLumpName("PLAYPAL", PU_STATIC);
    // Create a video renderer
    BufferedRenderer V=new BufferedRenderer(WIDTH,HEIGHT);
    V.Init();
    byte[] pal=palette.getBuffer().array();
    

    
    IDoomSystem I=new DoomSystem();
    DoomMain DM=new DoomMain();
    // Create the frame.
    OldAWTDoom frame = new OldAWTDoom(DM,V,pal);
    frame.InitGraphics();
    DM.I=I;
    DM.VI=frame;
    DM.S=new DummySoundDriver();
    frame.DM=DM;
    DM.W=W;
    DM.V=V;
    DM.DM=DM;
    DM.language=Language_t.english;
    DM.gameepisode=1;
    DM.gamemap=1;
    DM.gamemission=GameMission_t.doom;
    DM.gamemode=GameMode_t.shareware;
    DM.wminfo=new wbstartstruct_t();
    // Simulate being in the mid of a level.
    DM.usergame=true;
    DM.gamestate=gamestate_t.GS_LEVEL;
    DM.gameskill=skill_t.sk_hard;
    //C2JUtils.initArrayOfObjects(DM.players,player_t.class);
    
    DM.RND=new DoomRandom();
    DM.players[0].cmd=new ticcmd_t();
    DM.players[0].itemcount=1337;
    DM.players[0].killcount=1337;
    DM.players[0].secretcount=1337;
    
    DM.wminfo.plyr[0].in=true;
    DM.wminfo.plyr[0].sitems=1337;
    DM.wminfo.plyr[0].skills=1337;
    DM.wminfo.plyr[0].stime=28595;
    DM.wminfo.plyr[0].ssecret=1337;
    DM.playeringame[0]=true;
    DM.wminfo.last=6;
    DM.wminfo.epsd=0;
    DM.wminfo.maxitems=100;
    DM.wminfo.maxkills=100;
    DM.wminfo.maxsecret=100;
    DM.wminfo.partime=28595;
    DM.PlayerReborn(0);
   
    IDoomMenu M=DM.M=new Menu(DM);
    Map AM=(Map) (DM.AM=new Map(DM));
    StatusBar ST=(StatusBar) (DM.ST=new StatusBar(DM));
    LevelLoader LL=DM.LL=new LevelLoader(DM);
    DM.P=new Actions(DM);
    DM.R=new UnifiedRenderer(DM); 
    HU HU=DM.HU=new HU(DM);
    
    DM.P.updateStatus(DM);
    M.updateStatus(DM);
    ST.updateStatus(DM);
    AM.updateStatus(DM);
    HU.updateStatus(DM);
    LL.updateStatus(DM);
    DM.R.Init();
    DM.P.Init();
    HU.Init();
    LL.SetupLevel(1, 2, 0, skill_t.sk_hard);
    ST.Init();
    M.Init();    
    ST.Start();
    AM.LevelInit();
    AM.Start();
    HU.Start();
   // M.StartControlPanel();
    

    long a=System.nanoTime();
    
    DM.menuactive=false;
    DM.automapactive=false;
        for (int i=0;i<100000;i++){
            int ba=DM.I.GetTime();
            while (ba-DM.I.GetTime()==0){
               // Don't do that! frame.setVisible(true);
                Thread.sleep(1);               
            }
           frame.GetEvent();
           
           for ( ; DM.eventtail != DM.eventhead ; DM.eventtail = (++DM.eventtail)&(MAXEVENTS-1) )
           {
       	event_t ev = DM.events[DM.eventtail];

            //System.out.println(ev);
            if (ev!=null){
                AM.Responder(ev);
                M.Responder(ev);
            HU.Responder(ev);
            ST.Responder(ev);
           }
           }
            
       
         
        //V.DrawPatch(0,0,0,help1);
        AM.Ticker();
        AM.Drawer();
        HU.Ticker();
        HU.Drawer();
        ST.Ticker();
        ST.Drawer(false,true);
        M.Ticker();
        M.Drawer();

        DM.gametic++;
        frame.FinishUpdate();
        if (i%100==0){
        	   long b=System.nanoTime();
        	    
        	    System.out.println(i +" frames in " +((b-a)/1e09) +" = "+i/((b-a)/1e09) + " fps");
        }
        System.out.print(frame.processEvents());
        }
            } catch (Exception e){
                e.printStackTrace();
            }
    
    }
    
}

package testers;

import static data.Defines.PU_STATIC;
import static data.Limits.MAXEVENTS;
import n.DummyNetworkHandler;

import hu.HU;
import i.DoomSystem;
import i.IDoomSystem;

import p.Actions;
import p.ILevelLoader;
import p.LevelLoader;

import automap.Map;
import awt.AWTDoom;
import rr.ParallelRenderer2;
import rr.SimpleTextureManager;
import rr.SpriteManager;
import s.DummySoundDriver;
import st.StatusBar;
import timing.MilliTicker;

import m.IDoomMenu;
import m.Menu;
import m.DoomRandom;
import v.BufferedRenderer;
import v.DoomVideoRenderer;
import v.GammaTables;
import v.IVideoScale;
import v.VideoScaleInfo;
import w.DoomBuffer;
import w.WadLoader;
import defines.*;
import data.Tables;
import doom.CommandLine;
import doom.DoomMain;
import doom.event_t;
import doom.ticcmd_t;
import doom.wbstartstruct_t;

/** This is a very simple tester for Menu module  */

public class AWTParallelRenderTester2 {

    static IVideoScale VSI=new VideoScaleInfo(4.0f);
    
    public static void main(String[] argv) {
        try {
    
            Tables.InitTables();
            
    // Create a Wad file loader.
    
    WadLoader W=new WadLoader();
    W.InitMultipleFiles(new String[] {"doom1.wad"});
    
    System.out.println("Total lumps read: "+W.numlumps);

    // Read the palette.
    DoomBuffer palette = W.CacheLumpName("PLAYPAL", PU_STATIC);
    // Create a video renderer
    DoomVideoRenderer<?> V=new BufferedRenderer(VSI.getScreenWidth(),VSI.getScreenHeight());
    V.setVideoScale(VSI);
    V.initScaling();
    V.Init();
    byte[] pal=palette.getBuffer().array();
    V.createPalettes(pal, GammaTables.gammatables, 14, 256, 3, 5);
    

    
    IDoomSystem I=new DoomSystem();
    DoomMain DM=new DoomMain();
    CommandLine CM=new CommandLine(argv);
    DM.CM=CM;
    DM.TICK=new MilliTicker();    
    DM.setVideoScale(VSI);
    DM.initScaling();
    DM.singletics=true;
    DM.setTicdup(1);
    DM.DGN=new DummyNetworkHandler();
    
    // Create the frame.
    AWTDoom frame = new AWTDoom(DM,(DoomVideoRenderer<byte[]>) V);
    frame.InitGraphics();

    DM.I=I;
    DM.VI=frame;
    DM.S=new DummySoundDriver();
    frame.DM=DM;
    DM.W=W;
    DM.V=V;
    DM.DM=DM;
    HU HU=new HU(DM);    
    DM.language=Language_t.english;
    HU.Init();
    DM.HU=HU;
    DM.gameepisode=1;
    DM.gamemap=1;
    DM.gamemission=GameMission_t.doom;
    DM.setGameMode(GameMode_t.shareware);
    DM.wminfo=new wbstartstruct_t();
    // Simulate being in the mid of a level.
    DM.usergame=true;
    DM.gamestate=gamestate_t.GS_LEVEL;
    DM.gameskill=skill_t.sk_hard;
    //C2JUtils.initArrayOfObjects(DM.players,player_t.class);
    
    DM.RND=new DoomRandom();
    DM.players[0].cmd=new ticcmd_t();
    DM.players[0].itemcount=1337;
    DM.players[0].killcount=1337;
    DM.players[0].secretcount=1337;
    
    DM.wminfo.plyr[0].in=true;
    DM.wminfo.plyr[0].sitems=1337;
    DM.wminfo.plyr[0].skills=1337;
    DM.wminfo.plyr[0].stime=28595;
    DM.wminfo.plyr[0].ssecret=1337;
    
    DM.playeringame[0]=true;
    DM.wminfo.last=6;
    DM.wminfo.epsd=0;
    DM.wminfo.maxitems=100;
    DM.wminfo.maxkills=100;
    DM.wminfo.maxsecret=100;
    DM.wminfo.partime=28595;
    
   
    IDoomMenu M=DM.M=new Menu(DM);
    Map AM=new Map(DM);
    DM.AM=AM;
    StatusBar ST=(StatusBar) (DM.ST=new StatusBar(DM));
    ILevelLoader LL=DM.LL=new LevelLoader(DM);
    DM.P=new Actions(DM);
    DM.SM=new SpriteManager(DM);
    DM.R=new ParallelRenderer2(DM,2,1); 
    DM.TM=new SimpleTextureManager(DM);
    
    DM.P.updateStatus(DM);
    LL.updateStatus(DM);
    M.updateStatus(DM);
    ST.updateStatus(DM);
    AM.updateStatus(DM);
    DM.initializeVideoScaleStuff();
    DM.R.Init();
    DM.P.Init();
    DM.players[0].updateStatus(DM);
    DM.players[0].PlayerReborn();
    
    ST.Init();
    M.Init();
    ST.Start();
    LL.SetupLevel(1, 1, 0, skill_t.sk_hard);
    AM.LevelInit();
    AM.Start();
    
    DM.R.SetViewSize(11, 0);
    DM.R.ExecuteSetViewSize();
    DM.TM.setSkyTexture(DM.TM.CheckTextureNumForName("SKY1"));
    long a=System.nanoTime();
    
    DM.menuactive=false;
    DM.automapactive=true;
    DM.R.FillBackScreen();
    DM.R.DrawViewBorder();
    
    
    // Center on "bloody mess" in E1M1
    DM.players[0].mo.y+=420<<16;
    //DM.players[0].mo.x+=1650<<16;
        int pl=0;
        for (int i=0;i<20000;i++){
   
           frame.GetEvent();
           
           for ( ; DM.eventtail != DM.eventhead ; DM.eventtail = (++DM.eventtail)&(MAXEVENTS-1) )
           {
       	event_t ev = DM.events[DM.eventtail];

            //System.out.println(ev);
            if (ev!=null){
            
            AM.Responder(ev);
            M.Responder(ev);
            ST.Responder(ev);
           }
           }
            
       
           
        //V.DrawPatch(0,0,0,help1);
        //M.Ticker();
        //M.Drawer();
        AM.Ticker();
        //AM.Drawer();
        ST.Ticker();        
        DM.players[0].viewz=(40)<<16;
        //DM.players[0].mo.x=ox+(int) ((12864<<16)*Math.cos(2*Math.PI*i/500.0));
        //DM.players[0].mo.y=oy+(int) ((64<<16)*Math.sin(2*Math.PI*i/500.0));
        //DM.players[0].mo.angle= ((long)(0xFFFFFFFFL*(Math.atan2(DM.players[0].mo.y-oy,DM.players[0].mo.x-ox)+Math.PI)/(2*Math.PI)))&0xFFFFFFFFL;
        DM.players[0].mo.angle=(DM.players[0].mo.angle-0x800000)&0xFFFFFFFFL;
        //System.out.println(">>>>>>>>>>>>>>>>>> VIEW ANGLE "+360.0*(DM.players[0].mo.angle>>19)/8192.0); 
       
        DM.R.RenderPlayerView(DM.players[0]);
        //ST.Drawer(false,true);
        //System.out.println("Rendered"+DM.gametic);
        
        DM.gametic++;
        frame.FinishUpdate();
        if (i%200==0){
        	   long b=System.nanoTime();
        	    //frame.setPalette((pl++)%14);
        	    System.out.println((200) +" frames in " +((b-a)/1e09) +" = "+200/((b-a)/1e09) + " fps");
        	    a=b;
        }
        
       // System.out.print(frame.processEvents());
        /*while (ba-DM.I.GetTime()>-1){
            Thread.sleep(10);               
        }*/
        }
            } catch (Exception e){
                e.printStackTrace();
            }
    
    }
    
}

package testers;

import static data.SineCosine.*;
import m.FixedFloat;

/** This is a class that tests what happens with BAM angles used in Doom
 *  Normally, they were supposed to be unsigned 32-bit types, mapping
 *  the [0...360) range to a continuum of 32-bit integer values.
 *  
 *  However, in Java we have no unsigned integer type. 
 *  A workaround is to declare angles as ints, and be careful about what operations we perform.
 *  The general rules are:
 *  
 *  a) Angles below ANG180 (1st and 2nd qdt) will compare just fine.
 *  b) Angles in the 3rd and 4th quadrant will compare OK too.
 *  c) However angles from different semiplanes won't compare correctly: they need to be extended to longs.
 *  d) Alternatively, comparisons can be done with a specialized function -> need to benchmark that.
 *  e) Sums and subtractions of angles will work ok with ints, as far as the bit content is concerned. 
 *     When they need to be compared however, the above limitations apply.
 *  f) Multiplications with work OK with either type.
 *  g) Divisions for cumulative angles between 180 and -0.0 degrees will fail due to signs.
 *  g) So, the best thing would be to expand angles to longs and use the extra value range.
 *  
 *  Some benchmark facts:
 *  1) Addition/subtraction between 32-bit and 64-bit operands are, on average, the same speed.
 *  2) Multiplication is twice as fast with 32-bit operands.
 *  2) Casting to int is usually slightly faster than ANDing with a 32-bit mask and storing as long.
 *  3) Using a special method for division between ints can be almost as 1.5x as fast 
 *     than performing long divisions on 32-bit limited numbers.
 *  4) The only thing that's undeliably faster in 64-bit mode are comparisons between "32 bit unsigned"
 *     numbers (5x as fast), and the overall.
 *     
 *     Using 32-bit ints clearly improves memory bandwidth and storage, but makes comparisons problematic.
 *     If most of the angle code consists of additions and comparisons, I'd say keep BAM angles 64-bit.
 *     Using code that's multiplication or division intensive may benefit from 32-bit BAM angles...
 *     eventally code can be locally optimized depending on what's being done.
 *     
 */

public class TestBAM {
    
    
    public final static int PASSES=2;
    public final static int TESTS=5000000;
    public final static int ANG90=0x40000000;
    public final static int ANG180=0x80000000;
    public final static int ANG270=0xC0000000;
    public final static int ANG45=ANG90/2;
    public final static int ANG135=ANG270/2;
    public final static long LANG90=0x40000000;
    public final static long LANG180=0x80000000;
    public final static long LANG270=0xC0000000L;
    public final static long LANG45=LANG90/2;
    public final static long LANG135=LANG270/2;
    public final static long ANGLETOFINESHIFT=19;
    public final static int BITS31=0x7FFFFFFF;
    
    public static void main(String argv[]){
    System.out.println("ANG90>ANG45: "+(ANG90>ANG45));
    System.out.println("LANG180> ANG90 "+ (toLong(ANG180)>ANG90));
    System.out.println("ANG180>ANG90 "+ (ANG180>ANG90)); // this resolves wrong, because ANG180 is "negative" in signed int notation.
    System.out.println("ANG180>ANG90 with GT"+ GT(ANG180,ANG90)); // this resolves wrong, because ANG180 is "negative" in signed int notation.
    System.out.println("ANG270>ANG180 " + (ANG270>ANG180)); // but this resolves correctly, because ANG270 is negative but "larger".
    System.out.println("ANG270>ANG180 " + GT(ANG270,ANG180)); // but this resolves correctly, because ANG270 is negative but "larger".
    System.out.println(ANG45>ANG270); // this resolves wrong, too. It should be false.
    System.out.println((ANG45>toLong(ANG270))); // this resolves wrong, too. It should be false.
    System.out.println((ANG270-ANG45)>ANG180); // this resolves correctly.
    System.out.println("ZILCH "+((ANG270-ANG45)>ANG90)); // this, however, won't.
    System.out.println((0-ANG45)>ANG180); // this resolves correctly too.
    System.out.println((ANG270-ANG180)>ANG45); // correct.
    System.out.println((ANG270-ANG180)==ANG90); // This is true, and correct. 
    System.out.println((ANG270/2)>ANG90); // This won't work, because it will resolve to a negative.
    System.out.println((ANG270/2)>ANG180); // This won't work either.
    System.out.println((toLong(ANG270)/2)>toLong(ANG180)); // Only this will work.
    
    
    System.out.println(ANG45>>ANGLETOFINESHIFT);
    System.out.println(ANG90>>ANGLETOFINESHIFT);
    
    System.out.println("ANG90 mult 3 == ANG270" +GE(ANG90*5,ANG90));
    System.out.println(ANG180*2);
    System.out.println((long)ANG180/2);
    System.out.println(ANG270*2 == ANG180);
    
    
    System.out.println(FixedFloat.toFloat((int) (ANG45>>>ANGLETOFINESHIFT))+" "+finesine[(int) (ANG45>>>ANGLETOFINESHIFT)]);
    System.out.println(FixedFloat.toFloat((int) (ANG270>>>ANGLETOFINESHIFT))+" "+finesine[(int) (ANG270>>>ANGLETOFINESHIFT)]);
    System.out.println(FixedFloat.toFloat((int) ((ANG45*6)>>>ANGLETOFINESHIFT))+" "+finesine[(int) (ANG45*6)>>>ANGLETOFINESHIFT]);
    System.out.println(FixedFloat.toFloat((int) (ANG45>>>ANGLETOFINESHIFT))+" "+finesine[(int) (ANG45>>>ANGLETOFINESHIFT)]);
    System.out.println(FixedFloat.toFloat((int) (ANG270>>>ANGLETOFINESHIFT))+" "+finesine[(int) (ANG270>>>ANGLETOFINESHIFT)]);
    System.out.println(FixedFloat.toFloat((int) ((ANG45*6)>>>ANGLETOFINESHIFT))+" "+finesine[(int) (ANG45*6)>>>ANGLETOFINESHIFT]);
    
    int[] BAM32=new int[TESTS];
    long[] BAM64=new long[TESTS];
    
    for (int i=0;i<TESTS;i++){
        // Keep only 32 bits.
        BAM64[i]=(Double.doubleToLongBits(Math.random())&0xFFFFFFFFL);
        BAM32[i]=(int) (BAM64[i]);
    }

    int errors=0;
    for (int i=0;i<TESTS-1;i++){
        // Keep only 32 bits.
        if ((BAM64[i]<BAM64[i+1])!=(!GE(BAM32[i],BAM32[i+1]))) errors++;
    }

    System.out.println("Comparison Errors "+ errors);
    
    errors=0;
    for (int i=0;i<TESTS-1;i++){
        // Keep only 32 bits.
        if ((int)(BAM64[i]*BAM64[i+1])!=(BAM32[i]*BAM32[i+1])) errors++;
    }

    System.out.println("Multiplication Errors "+ errors);
    
    errors=0;
    
    // Preventing overflow errors is easy.
    for (int i=0;i<TESTS-1;i++){
        // Keep only 32 bits.
        if ((toLong(BAM32[i])/2)!=((BAM64[i]/2)&0x0FFFFFFFFL)) errors++;
    }
    
    System.out.println("Overflow Errors "+ errors);
    
    errors=0;
    for (int i=0;i<TESTS-1;i++){
        // Keep only 32 bits.
        if ((int)(BAM64[i]/BAM64[i+1])!=(BAM32[i]/BAM32[i+1])) errors++;
    }
    
    System.out.println("Division Errors "+ errors);
    
    errors=0;
    for (int i=1;i<TESTS-1;i++){
        // Keep only 32 bits.
        if ((int)(BAM64[i]/i)!=BAMDiv(BAM32[i],i)) errors++;
        //System.out.println(BAMDiv(BAM32[i],i));
    }
    
    System.out.println("Division with BAMDiv Errors "+ errors);
    
    /*for (int i=0;i<tantoangle.length;i++){
        System.out.println(FixedFloat.toFloat(tantoangle[i]));
    }*/
    for (int p=0;p<PASSES;p++){
    timingTests(BAM32,BAM64);
    }
    }

    private static void timingTests(int[] bAM32, long[] bAM64) {
        
        long a,b;
        int errors;
        int results1[]=new int[TESTS];
        int results2[]=new int[TESTS];
        long results3[]=new long[TESTS];
        a=System.nanoTime();
        for (int i=0;i<TESTS-1;i++){
            // Keep only 32 bits.
            results1[i]=bAM32[i]+bAM32[i+1];
            //System.out.println(BAMDiv(BAM32[i],i));
        }
         b=System.nanoTime();
        
        System.out.println("Time for "+TESTS+" 32-bit additions: "+ (b-a));
        
        a=System.nanoTime();
        for (int i=0;i<TESTS-1;i++){
            // Keep only 32 bits.
            results2[i]=(int) (bAM64[i]+bAM64[i+1]);
            //System.out.println(BAMDiv(BAM32[i],i));
        }
        b=System.nanoTime();
        
        System.out.println("Time for "+TESTS+" 64-bit additions (int storage with cast to 32-bit): "+ (b-a));
        
        a=System.nanoTime();
        for (int i=0;i<TESTS-1;i++){
            // Keep only 32 bits.
            results3[i]=(bAM64[i]+bAM64[i+1])&0xFFFFFFFFL;
            //System.out.println(BAMDiv(BAM32[i],i));
        }
        b=System.nanoTime();
        
        System.out.println("Time for "+TESTS+" 64-bit additions (long storage with 32-bit cutoff): "+ (b-a));
        
        errors=0;
        for (int i=1;i<TESTS-1;i++){
            // Keep only 32 bits.
            if (results1[i]!=results2[i]) errors++;
            //System.out.println(BAMDiv(BAM32[i],i));
        }
        
        System.out.println("Errors "+ errors);
        
        a=System.nanoTime();
        for (int i=0;i<TESTS-1;i++){
            // Keep only 32 bits.
            results1[i]=bAM32[i]*bAM32[i+1];
            //System.out.println(BAMDiv(BAM32[i],i));
        }
         b=System.nanoTime();
         
         System.out.println("Time for "+TESTS+" 32-bit multiplications : "+ (b-a));
         
         a=System.nanoTime();
         for (int i=0;i<TESTS-1;i++){
             // Keep only 32 bits.
             results2[i]=(int)(bAM64[i]*bAM64[i+1]);
             //System.out.println(BAMDiv(BAM32[i],i));
         }
          b=System.nanoTime();
          System.out.println("Time for "+TESTS+" 64-bit multiplications (with result cast to int): "+ (b-a));
          
          errors=0;
          for (int i=1;i<TESTS-1;i++){
              // Keep only 32 bits.
              if (results1[i]!=results2[i]) errors++;
              //System.out.println(BAMDiv(BAM32[i],i));
          }
          
          System.out.println("Errors "+ errors);
          
          a=System.nanoTime();
          for (int i=1;i<TESTS-1;i++){
              // Keep only 32 bits.
              results1[i]=BAMDiv(bAM32[i],50);
              //System.out.println(BAMDiv(BAM32[i],i));
          }
           b=System.nanoTime();
           
           System.out.println("Time for "+TESTS+" 32-bit divisions with BAMDiv: "+ (b-a));
           
           a=System.nanoTime();
           for (int i=1;i<TESTS-1;i++){
               // Keep only 32 bits.
               results2[i]=(int)(bAM64[i]/50);
               //System.out.println(BAMDiv(BAM32[i],i));
           }
            b=System.nanoTime();
            System.out.println("Time for "+TESTS+" 64-bit divisions (with result cast to int): "+ (b-a));
            
            errors=0;
            for (int i=1;i<TESTS-1;i++){
                // Keep only 32 bits.
                results1[i]=(GE(bAM32[i],bAM32[i+1])?1:0);
                //System.out.println(BAMDiv(BAM32[i],i));
            }
            
            System.out.println("Time for "+TESTS+" 32-bit comparisons): "+ (b-a));
            
            a=System.nanoTime();
            for (int i=1;i<TESTS-1;i++){
                // Keep only 32 bits.
                results2[i]=(bAM64[i]>=bAM64[i+1]?1:0);
                //System.out.println(BAMDiv(BAM32[i],i));
            }
             b=System.nanoTime();
             System.out.println("Time for "+TESTS+" 64-bit comparisons (with result cast to int): "+ (b-a));
             
             errors=0;
             for (int i=1;i<TESTS-1;i++){
                 // Keep only 32 bits.
                 if (results1[i]!=results2[i]) errors++;
                 //System.out.println(BAMDiv(BAM32[i],i));
             }
             
             System.out.println("Errors "+ errors);
          
          
    }

    public static final long toLong(int a){
        return(0xFFFFFFFFL&a);
    }
 
    /** Compare BAM angles in 32-bit format 
     *  "Greater or Equal" bam0>bam1
     * */
    
    public static final boolean GE(int bam0, int bam1){
        // Handle easy case.
        if (bam0==bam1) return true;
        
        // bam0 is greater than 180 degrees.
        if (bam0<0 && bam1>=0) return true;
        // bam1 is greater than 180 degrees.
        if (bam0>=0 && bam1<0) return false;
        
        // Both "greater than 180", No other way to compare.
        bam0&=BITS31;
        bam1&=BITS31;        
        return bam0>bam1;
    }
    
    public static final boolean GT(int bam0, int bam1){       
        // bam0 is greater than 180 degrees.
        if (bam0<0 && bam1>=0) return true;
        // bam1 is greater than 180 degrees.
        if (bam0>=0 && bam1<0) return false;
        
        // Both "greater than 180", No other way to compare.
        bam0&=BITS31;
        bam1&=BITS31;        
        return bam0>bam1;
    }
    
    public static final int BAMDiv(int bam0, int bam1){       
        // bam0 is greater than 180 degrees.
        if (bam0>=0) return bam0/bam1;
        // bam0 is greater than 180 degrees.
        // We have to make is so that ANG270 0xC0000000 becomes ANG135, aka 60000000
        if (bam1>=0)
        return (int) ((long)(0x0FFFFFFFFL&bam0)/bam1);
        
        return (int) ((long)(0x0FFFFFFFFL&bam0)/(0x0FFFFFFFFL&bam1));
    }
    
}

package testers;

import demo.VanillaDoomDemo;
import w.*;

public class DemoLumpTester {

    public static void main(String[] argv) {

    WadLoader W=new WadLoader();
    try {
		W.InitMultipleFiles(new String[] {"C://DOOMS/e1m1.wad"});
	} catch (Exception e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}
    //W.AddFile("bitter.wad");
    System.out.println("Total lumps read: "+W.numlumps);
    System.out.println("NUm for DEMO1: "+W.GetNumForName("DEMO1"));
    VanillaDoomDemo demo=(VanillaDoomDemo) W.CacheLumpName("DEMO1",0,VanillaDoomDemo.class);
    System.out.println(demo);
    
    }
    
}

package testers;

import m.DoomRandom;
import m.IRandom;
import s.ClassicDoomSoundDriver;
import s.SpeakerDoomSoundDriver;
import data.sounds.sfxenum_t;
import doom.DoomStatus;
import w.WadLoader;

public class TestClassicSound {
	public static void main(String[] argv) throws Exception{

	DoomStatus DS=new DoomStatus();
	
	
	WadLoader W=new WadLoader();
	IRandom RND=new DoomRandom();
	
	DS.W=W;
	DS.RND=RND;
	W.InitMultipleFiles(new String[]{"C:\\iwads\\doom1.wad"});
	
	SpeakerDoomSoundDriver sound=new SpeakerDoomSoundDriver(DS,4);
	
	sound.InitSound();
	sound.SetChannels(3);
	
	Thread.sleep(1000);
	//sound.StartSound(1, 127, 127, 127, 0);
	for (int i=0;i<1000;i++){
	    
	    Thread.sleep(1000/35);

	    if (i%10==0) sound.StartSound(sfxenum_t.sfx_plpain.ordinal(), 127, 127, 127, 0);
	    //if (i%50==0) sound.StartSound(sfxenum_t.sfx_barexp.ordinal(), 127, 0, 127, 0);
	    //if (i%35==0) sound.StartSound(sfxenum_t.sfx_plpain.ordinal(), 127, 255, 127, 0);
	    //if (i%71==0) sound.StartSound(sfxenum_t.sfx_oof.ordinal(), 127, 192, 127, 0);
	    sound.UpdateSound();
	    
	    sound.SubmitSound();
	    
	       DS.gametic++;
	}
	sound.ShutdownSound();
	}
	
}

package testers;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import w.WadLoader;

public class TestInvoke {

    /**
     * @param args
     * @throws Exception 

     */
    public static void main(String[] args) throws Exception {
        Method shit=WadLoader.class.getDeclaredMethod("InitFile", String.class);
        Method cunt=WadLoader.class.getDeclaredMethod("LumpLength", int.class);
        Method nothing=WadLoader.class.getDeclaredMethod("Reload",null);
        WadLoader crap=new WadLoader();
        shit.invoke(crap, "~DOOM1.WAD");
        System.out.println(cunt.invoke(crap, 0));
        byte[] ahhh=crap.CacheLumpName("PLAYPAL", 0).getBuffer().array();
        System.out.println(ahhh.length);
        ahhh=crap.CacheLumpName("PLAYPAL", 0).getBuffer().array();
        System.out.println(ahhh.length);
        // This should flush the cache
        nothing.invoke(crap, (Object[])null);
        ahhh=crap.CacheLumpName("PLAYPAL", 0).getBuffer().array();
        System.out.println(ahhh.length);
    }

}

package testers;

import static data.Defines.PU_STATIC;
import java.awt.image.BufferedImage;
import java.awt.image.IndexColorModel;

import javax.swing.JFrame;

import m.DoomRandom;
import m.IRandom;
import rr.patch_t;
import v.BufferedRenderer;
import w.DoomBuffer;
import w.WadLoader;
import data.Defines;
import doom.DoomMain;
import f.Wiper;

/** This is a very simple tester for the Automap. Combined with status bar + Level loader. */

public class AssWipeTester {
	
	public static int WIDTH=320;

    public static void main(String[] argv) {
        try {
    DoomMain DC=new DoomMain();
    
    WadLoader W=new WadLoader(DC.I);
    
    IRandom RND=new DoomRandom();
    DC.RND=RND;        
    DC.W=W;
    
    W.InitMultipleFiles(new String[] {"doom1.wad"});
    //W.AddFile("bitter.wad");
    System.out.println("Total lumps read: "+W.numlumps);

    BufferedRenderer V=new BufferedRenderer(640,200);   
    DC.V=V;
    
    // Get the palettes
    DoomBuffer palette = W.CacheLumpName("PLAYPAL", PU_STATIC);
    byte[] pal=palette.getBuffer().array();
    IndexColorModel icm=new IndexColorModel(8, 256,pal, 0, false);    
    V.setIcm(icm);
    V.Init();
    
    
    patch_t titlepic=W.CachePatchName("TITLEPIC", PU_STATIC);
    patch_t credit=W.CachePatchName("HELP1", PU_STATIC);
    
    
    

    
    
    Wiper wipe=new Wiper(DC);
    
    Defines.SCREENWIDTH=640;
    Defines.SCREENHEIGHT=200;
    
    
    
    
    // "Hook" on screen 0.
    BufferedImage bi=((BufferedRenderer)V).screenbuffer[0];
    //BufferedImage[] pals=V.getBufferedScreens(0, icms);
    CrappyDisplay frame = new CrappyDisplay(bi);
    frame.setTitle("MochaDoom");
    

    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.pack();
    frame.setLocationRelativeTo(null);
    //frame.setUndecorated(true);
    frame.setVisible(true);
    frame.setBounds(frame.getX(), frame.getY(), WIDTH, 240);

    int tck=DC.I.GetTime();
    long a=System.nanoTime();
    int TICKS=10;
    int frames=0;
    for (int i=0;i<TICKS;i++){
        V.DrawPatch(0, 0, 0, titlepic);
        wipe.StartScreen(0, 0, Defines.SCREENWIDTH, Defines.SCREENHEIGHT);
        V.DrawPatch(0, 0, 0, credit);
        wipe.EndScreen(0, 0, Defines.SCREENWIDTH, Defines.SCREENHEIGHT);
        
        int wipestart = DC.I.GetTime () - 1;
        int nowtime;
        int tics;
        boolean done;
        
        patch_t tmp=credit;
        credit=titlepic;
        titlepic=tmp;
    //wipe.ScreenWipe(Wiper.wipe.Melt.ordinal(), 0, 0,  Defines.SCREENWIDTH, Defines.SCREENHEIGHT, TICKS-i);

    //ST.Drawer(false,true);
    //Thread.sleep(200);    
    
    do
    {
    do
    {
        nowtime = DC.I.GetTime ();
        tics = nowtime - wipestart;
    } while (tics<1);
    wipestart = nowtime;
    //V.DrawPatch(0, 0, 0, titlepic);
    //V.DrawPatch(320, 0, 0, titlepic);
    done = wipe.ScreenWipe(Wiper.wipe.Melt.ordinal()
                   , 0, 0, Defines.SCREENWIDTH, Defines.SCREENHEIGHT, tics);
    //I_UpdateNoBlit ();
    //M_Drawer ();                            // menu is drawn even on top of wipes
    //System.out.println(i);
    frame.update(null);
    frames++;
   
    
    } while (!done);
    
    long b=System.nanoTime();
    
    System.out.println(frames +" frames in " +((b-a)/1e09) +" = "+frames/((b-a)/1e09) + " fps");
    
    }
        } catch (Exception e){
            e.printStackTrace();
        }
    }
      
}

package testers;

import java.awt.image.BufferedImage;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import javax.imageio.ImageIO;

public class PaletteShower {

    public static final int BLOCKSIZE=16;
    public static final int SHIFT=0;
    
    public static void main(String[] argv) {
        try {
    BufferedInputStream bis;
    bis=new BufferedInputStream(new FileInputStream(argv[0]));
    
    int size=bis.available();
    
    int blocks=size/768;
    
    
    byte[] palbuf=new byte[768];
    
    for (int i=0;i<blocks;i++){
        bis.read(palbuf);
        BufferedImage bim=new BufferedImage(16*BLOCKSIZE,16*BLOCKSIZE,BufferedImage.TYPE_INT_ARGB);
        
            /*
             * for (int j=0;j<256;j++){
            int color=  0xFF000000|(palbuf[j]<<(16+SHIFT))|
                        (palbuf[j]<<(8+SHIFT))|
                        (palbuf[j]<<SHIFT); 
                        }
                        */
            for (int j=0;j<256;j++){
                int colo=  0xFF000000|(0xFF0000&palbuf[j*3]<<(16+SHIFT))|
                            (0xFF00&palbuf[1+j*3]<<(8+SHIFT))|
                            (0xFF&palbuf[2+j*3]<<SHIFT);
            
            for (int x=0;x<BLOCKSIZE;x++){
                for (int y=0;y<BLOCKSIZE;y++){
                    bim.setRGB(BLOCKSIZE*(j%16)+x, BLOCKSIZE*(j/16)+y, colo);
                }
            }
            
        }
        
        FileOutputStream f=new FileOutputStream(String.format("%s-%d.PNG",argv[0],i));
        
        ImageIO.write(bim,"PNG",f);
        }
    
        bis.close();
    
        } catch (Exception e){
            e.printStackTrace();
        }
    }
    
}

package testers;

import static data.SineCosine.finesine;
import static data.Tables.*;
import static m.fixed_t.FRACUNIT;
import static m.fixed_t.FRACBITS;
import static m.fixed_t.FixedDiv;
import static m.fixed_t.FixedMul;
import data.Tables;
import m.fixed_t;

public class TestScale {

    private static int detailshift=0;
    private static long rw_normalangle;
    private static long viewangle;
    private static int projection=0xF0000000;
    private static int rw_distance=0xFF000;
    private static final int divs=10;
    private static int viewx=0, viewy=0;
    private static int dist=256*FRACUNIT;
    
    

    public static void main(String[] argv){
        
        int[][] octants={{dist,0},{dist,dist},{0,dist},{-dist,dist},{-dist,0},{-dist,-dist},{0,-dist},{dist,-dist}};
        for (int i=0;i<octants.length;i++){
            long ang = PointToAngle (octants[i][0],octants[i][1]);
            System.out.print((ang*360.0)/0xFFFFFFFFL);            
            System.out.print(" "+(ang>>FRACBITS));
            
            System.out.print(" "+(Long.toHexString(ang)));
            System.out.print(" "+(Long.toBinaryString(ang+(ANG45*9)/2)));
            int rot = (int) (((ang+(ANG45*9)/2)&BITS32)>>29);
            System.out.print(" "+rot+"\n");
            
        }
        
        /*
        long angle=0;
        projection=0;
        int divisions=(int) (0xFFFFFFFFL/divs);
        for (int i=0;i<divs;i++){
                for (int j=0;j<divs;j++){
            System.out.println(i+" "+j+" "+rw_normalangle+" "+viewangle+" "+ScaleFromGlobalAngle(viewangle));
            viewangle+=divisions;
                }
                rw_normalangle+=divisions;
            }*/
        
        }
    
    /**
     * R_ScaleFromGlobalAngle
     * Returns the texture mapping scale
     *  for the current line (horizontal span)
     *  at the given angle.
     * rw_distance must be calculated first.
     */
    
    public static int ScaleFromGlobalAngle (long visangle)
    {
        int         scale; // fixed_t
        int         anglea;
        int         angleb;
        int         sinea;
        int         sineb;
        int         num; // fixed_t
        int         den;

        // UNUSED
    /*
    {
        fixed_t     dist;
        fixed_t     z;
        fixed_t     sinv;
        fixed_t     cosv;
        
        sinv = finesine[(visangle-rw_normalangle)>>ANGLETOFINESHIFT];   
        dist = FixedDiv (rw_distance, sinv);
        cosv = finecosine[(viewangle-visangle)>>ANGLETOFINESHIFT];
        z = abs(FixedMul (dist, cosv));
        scale = FixedDiv(projection, z);
        return scale;
    }
    */

        anglea = (int) (ANG90 +visangle-viewangle);
        angleb = (int) (ANG90 +visangle-rw_normalangle);

        // both sines are allways positive
        sinea = finesine[anglea>>>ANGLETOFINESHIFT]; 
        sineb = finesine[angleb>>>ANGLETOFINESHIFT];
        num = FixedMul(projection,sineb)<<detailshift;
        den = FixedMul(rw_distance,sinea);

        if (den > num>>16)
        {
        scale = FixedDiv (num, den);

        
        if (scale > 64*FRACUNIT)
            scale = 64*FRACUNIT;
        // Minimum scale.
        else if (scale < 256)
            scale = 256;
        }
        else
        // Top saturation scale
        scale = 64*FRACUNIT;
        
        return scale;
    }
    
    /**
     * R_PointToAngle
     *  To get a global angle from cartesian coordinates,
     *  the coordinates are flipped until they are in
     *  the first octant of the coordinate system, then
     *  the y (<=x) is scaled and divided by x to get a
     *  tangent (slope) value which is looked up in the
     *   tantoangle[] table.
     *   
     *   @param xx (fixed_t)
     *   @param yy (fixed_t)
     */

    public static long
    PointToAngle
    ( int   x,
      int   y )
    {   
        // MAES: note how we don't use &BITS32 here. That is because we know that the maximum possible
        // value of tantoangle is angle
        // This way, we are actually working with vectors emanating from our current position.
        x-= viewx;
        y-= viewy;
        
        if ( (x==0) && (y==0) )
        return 0;

        if (x>= 0)
        {
        // x >=0
        if (y>= 0)
        {
            // y>= 0

            if (x>y)
            {
            // octant 0
            return tantoangle[ SlopeDiv(y,x)];
            }
            else
            {
            // octant 1
            return (ANG90-1-tantoangle[ SlopeDiv(x,y)]);
            }
        }
        else
        {
            // y<0
            y = -y;

            if (x>y)
            {
            // octant 8
            return -tantoangle[SlopeDiv(y,x)];
            }
            else
            {
            // octant 7
            return ANG270+tantoangle[ SlopeDiv(x,y)];
            }
        }
        }
        else
        {
        // x<0
        x = -x;

        if (y>= 0)
        {
            // y>= 0
            if (x>y)
            {
            // octant 3
            return (ANG180-1-tantoangle[ SlopeDiv(y,x)]);
            }
            else
            {
            // octant 2
            return (ANG90+ tantoangle[ SlopeDiv(x,y)]);
            }
        }
        else
        {
            // y<0
            y = -y;

            if (x>y)
            {
            // octant 4
            return (ANG180+tantoangle[ SlopeDiv(y,x)]);
            }
            else
            {
             // octant 5
            return (ANG270-1-tantoangle[ SlopeDiv(x,y)]);
            }
        }
        }
        // This is actually unreachable.
       // return 0;
    }
    

    /** R_PointToDist
     * 
     * @param x fixed_t
     * @param y fixed_t
     * @return
     */
    
    public static int
    PointToDist
    ( int   x,
      int   y )
    {
        int     angle=0;
        int dx;
        int dy;
        int temp;
        int dist;
        
        dx = Math.abs(x - viewx);
        dy = Math.abs(y - viewy);
        
        // Sanity check, else it's going to bomb.
        /* if (dx==0){
            //
            if (dy>0) angle= Tables.toBAMIndex(ANG90);
            else           
            if (dy<0) angle= Tables.toBAMIndex(ANG270);
            else
                      angle= 0;     
        }
        
        else { */
        
        if (dy>dx)
        {
        temp = dx;
        dx = dy;
        dy = temp;
        }
        
        
        /* If both dx and dy are zero, this is going to bomb.
           Fixeddiv will return MAXINT aka 7FFFFFFF, >> DBITS will make it 3FFFFFF,
           addding ANG90 will make if 43FFFFFF and angletofineshi
        */
            
        angle = (int) (((tantoangle[ FixedDiv(dy,dx)>>DBITS ]+ANG90)&BITS32) >> ANGLETOFINESHIFT);
        
        // use as cosine
        dist = FixedDiv (dx, finesine[angle] ); 
        
        return dist;
    }
    
    
}

package testers;

import doom.event_t;
import m.cheatseq_t;

public class TestCheats {

    
    public static void main(String argv[]){
    char  cheat_ammo_seq[] =
    {
        0xb2, 0x26, 0xf2, 0x66, 0xa2, 0xff  // idkfa
    };
    
    cheatseq_t idkfa=new cheatseq_t( cheat_ammo_seq);
    //event_t[] keystokes={new event_t('i'),new event_t('d') ,new event_t('k') ,new event_t('f') ,new event_t('a') };    
    
    //idkfa.sequence=cheat_ammo_seq
    System.out.println(idkfa.CheckCheat('i'));
    System.out.println(idkfa.CheckCheat('d'));
    System.out.println(idkfa.CheckCheat('k'));
    System.out.println(idkfa.CheckCheat('f'));
    System.out.println(idkfa.CheckCheat('a'));
    System.out.println(idkfa.CheckCheat('i'));
    System.out.println(idkfa.CheckCheat('d'));
    System.out.println(idkfa.CheckCheat('k'));
    System.out.println(idkfa.CheckCheat('f'));
    System.out.println(idkfa.CheckCheat('b'));
    System.out.println(idkfa.CheckCheat('i'));
    System.out.println(idkfa.CheckCheat('d'));
    System.out.println(idkfa.CheckCheat('k'));
    System.out.println(idkfa.CheckCheat('f'));
    System.out.println(idkfa.CheckCheat('a'));
    }
    
}

package testers;

import m.FixedFloat;
import m.fixed_t;

class FPBench{         
public static final int PRECISION=16;

    public static void main(String argv[])
{
    final int tests=500000;
 for (int j=0;j<2;j++){   
    float[] a=new float[tests];
    float[] b=new float[tests];
    float[] c=new float[tests];
    fixed_t[] fa=new fixed_t[tests];
    fixed_t[] fb=new fixed_t[tests];
    fixed_t[] fc=new fixed_t[tests];
    
    long tan_0=System.nanoTime();
    for (int i=0;i<tests;i++){           
    a[i]=((float)(Math.random()*65535.0-32678.0));
    b[i]=((float)(Math.random()*65535.0-32678.0));
    }
    long tan_1=System.nanoTime();
    
    System.out.println("Allocate "+tests+" random IEEE floats: \t" + (tan_1-tan_0));
    
    tan_0=System.nanoTime();
    for (int i=0;i<tests;i++){           
    fa[i]=F2F(a[i]);
    fb[i]=F2F(b[i]);
    }
    tan_1=System.nanoTime();
    
    System.out.println("Convert "+tests+" random IEEE floats into fixed_t (creates new): \t" + (tan_1-tan_0));
    
    tan_0=System.nanoTime();
    for (int i=0;i<tests;i++){           
    F2F(a[i],fa[i]);
    F2F(b[i],fb[i]);
    }
    tan_1=System.nanoTime();
    
    System.out.println("Convert "+tests+" random IEEE floats into fixed_t (reuses objects): \t" + (tan_1-tan_0));
    
    tan_0=System.nanoTime();
    for (int i=0;i<tests;i++){           
    fa[i].set(FixedFloat.toFixed(a[i]));
    fb[i].set(FixedFloat.toFixed(b[i]));
    }
    tan_1=System.nanoTime();
    
    System.out.println("Convert "+tests+" random IEEE floats into fixed_t (new method, reuses objects): \t" + (tan_1-tan_0));
    
    tan_0=System.nanoTime();
    for (int i=0;i<tests;i++){
     c[i]=a[i]*b[i];    
    }
    tan_1=System.nanoTime();
    
    System.out.println("Perform "+tests+" random IEEE floats multiplications: \t" + (tan_1-tan_0));
    
    tan_0=System.nanoTime();
    for (int i=0;i<tests;i++){
     fc[i]=new fixed_t(fixed_t.FixedMul(fa[i],fb[i]));    
    }
    tan_1=System.nanoTime();

    System.out.println("Perform "+tests+" random fixed_t multiplications (new objects): \t"+ (tan_1-tan_0));
    
    System.out.println(tan_1-tan_0);
    
    tan_0=System.nanoTime();
    for (int i=0;i<tests;i++){
     fc[i].val=(fixed_t.FixedMulInt(fa[i],fb[i]));    
    }
    tan_1=System.nanoTime();
    
    System.out.println("Perform "+tests+" random fixed_t multiplications (integer return): \t" + (tan_1-tan_0));
    
    tan_0=System.nanoTime();
    for (int i=0;i<tests;i++){
     fixed_t.FixedMul(fa[i],fb[i],fc[i]);    
    }
    tan_1=System.nanoTime();
    
    System.out.println("Perform "+tests+" random fixed_t multiplications (in-place): \t"+ (tan_1-tan_0));

    tan_0=System.nanoTime();
    for (int i=0;i<tests;i++){
     c[i]=a[i]/b[i];    
    }
    tan_1=System.nanoTime();
    
    System.out.println("Perform "+tests+" random IEEE floats divisions: \t" + (tan_1-tan_0));
    
    
    tan_0=System.nanoTime();
    for (int i=0;i<tests;i++){
       // fc[i]=fixed_t.FixedDiv(fa[i],fb[i]);    
    }
    tan_1=System.nanoTime();
    
    System.out.println("Perform "+tests+" random fixed_t divisions (new objects): \t"+ (tan_1-tan_0));
   
    tan_0=System.nanoTime();
    for (int i=0;i<tests;i++){
        //fixed_t.FixedDiv(fa[i],fb[i],fc[i]);    
    }
    tan_1=System.nanoTime();
    
    System.out.println("Perform "+tests+" random fixed_t divisions (in-place): \t"+ (tan_1-tan_0));
    
 }
}


public static fixed_t F2F(float f){
    fixed_t tmp;
    int ing;
    ing=(int)Math.floor(f);
    tmp= new fixed_t((ing<<16)|FixedDecimal(f));
    //System.out.println(Integer.toHexString(tmp.val));    
    return tmp;

}

public static void  F2F(float f, fixed_t tmp){
    int ing;
    ing=(int)Math.floor(f);
    tmp.set((ing<<16)|FixedDecimal(f));
}

public static char FixedDecimal(float f){
         char fixint_value=0;
         float decimal_part= (float) (f-Math.floor(f));
for ( int i = 1; i <= PRECISION; i++)
{
   if (decimal_part >= 1.f/(float)(i + 1))
   {
      decimal_part -= 1.f/(float)(i + 1);
      fixint_value |= (1 << PRECISION - i);
   }
}
return fixint_value;
}

}


package testers;

import m.fixed_t;

public interface Operation {
    public void invoke(fixed_t a,fixed_t b);
    
}

package testers;

import java.util.Arrays;

public class TestPrimitiveClassDetection {

    public static void main(String argv[]) throws InstantiationException, IllegalAccessException{
        Object shit;
        Object shiiit=new double[5];
        shit=shiiit;
        System.out.println(shit.getClass());
        int status=-1;
        double[] something = null;
        
        
         if (shit instanceof double[]) 
             something=(double[])shit;
         
        System.out.println(something.length);
        
    }
}

package testers;

import static data.Defines.PU_STATIC;
import static data.Limits.MAXEVENTS;
import n.DummyNetworkHandler;

import hu.HU;
import i.DoomSystem;
import i.IDoomSystem;
import p.Actions;
import p.ILevelLoader;
import p.LevelLoader;

import automap.Map;
import awt.AWTDoom;

import rr.SimpleTextureManager;
import rr.SpriteManager;
import rr.UnifiedRenderer;
import s.DummySoundDriver;
import st.StatusBar;
import timing.MilliTicker;

import m.IDoomMenu;
import m.Menu;
import m.DoomRandom;
import v.BufferedRenderer16;
import v.DoomVideoRenderer;
import v.GammaTables;
import v.IVideoScale;
import v.VideoScaleInfo;
import w.DoomBuffer;
import w.WadLoader;
import defines.*;
import data.Tables;
import doom.CommandLine;
import doom.DoomMain;
import doom.event_t;
import doom.ticcmd_t;
import doom.wbstartstruct_t;

/** This is a very simple tester for Menu module  */

public class AWTRenderViewTester {

    static IVideoScale VSI=new VideoScaleInfo(3.0f);
    
    public static void main(String[] argv) {
        try {
    
            Tables.InitTables();
            
    // Create a Wad file loader.
    
    WadLoader W=new WadLoader();
    W.InitMultipleFiles(new String[] {"doom1.wad","COLORS15.lmp"});
    
    System.out.println("Total lumps read: "+W.numlumps);

    // Read the palette.
    DoomBuffer palette = W.CacheLumpName("PLAYPAL", PU_STATIC);
    // Create a video renderer
    DoomVideoRenderer<short[]> V=new BufferedRenderer16(VSI.getScreenWidth(),VSI.getScreenHeight());
    V.setVideoScale(VSI);
    V.initScaling();
    V.Init();
    byte[] pal=palette.getBuffer().array();
    V.createPalettes(pal, GammaTables.gammatables, 14, 256, 3, 5);

    
    IDoomSystem I=new DoomSystem();
    DoomMain DM=new DoomMain();
    DM.CM=new CommandLine(argv);
    DM.TICK=new MilliTicker();    
    DM.setVideoScale(VSI);
    DM.initScaling();
    DM.singletics=true;
    DM.setTicdup(1);
    DM.DGN=new DummyNetworkHandler();
    
    // Create the frame.
    AWTDoom frame = new AWTDoom(DM,(DoomVideoRenderer<short[]>) V);
    frame.InitGraphics();
    DM.I=I;
    DM.VI=frame;
    DM.S=new DummySoundDriver();
    frame.DM=DM;
    DM.W=W;
    DM.V=V;
    DM.DM=DM;
    HU HU=new HU(DM);    
    DM.language=Language_t.english;
    HU.Init();
    DM.HU=HU;
    DM.gameepisode=1;
    DM.gamemap=1;
    DM.gamemission=GameMission_t.doom;
    DM.setGameMode(GameMode_t.shareware);
    DM.wminfo=new wbstartstruct_t();
    // Simulate being in the mid of a level.
    DM.usergame=true;
    DM.gamestate=gamestate_t.GS_LEVEL;
    DM.gameskill=skill_t.sk_hard;
    //C2JUtils.initArrayOfObjects(DM.players,player_t.class);
    
    DM.RND=new DoomRandom();
    DM.players[0].cmd=new ticcmd_t();
    DM.players[0].itemcount=1337;
    DM.players[0].killcount=1337;
    DM.players[0].secretcount=1337;
    
    DM.wminfo.plyr[0].in=true;
    DM.wminfo.plyr[0].sitems=1337;
    DM.wminfo.plyr[0].skills=1337;
    DM.wminfo.plyr[0].stime=28595;
    DM.wminfo.plyr[0].ssecret=1337;
    
    DM.playeringame[0]=true;
    DM.wminfo.last=6;
    DM.wminfo.epsd=0;
    DM.wminfo.maxitems=100;
    DM.wminfo.maxkills=100;
    DM.wminfo.maxsecret=100;
    DM.wminfo.partime=28595;
    
   
    IDoomMenu M=DM.M=new Menu(DM);
    Map AM=new Map(DM);
    DM.AM=AM;
    StatusBar ST=(StatusBar) (DM.ST=new StatusBar(DM));
    ILevelLoader LL=DM.LL=new LevelLoader(DM);
    DM.P=new Actions(DM);
    DM.SM=new SpriteManager(DM);    
    DM.R=new UnifiedRenderer(DM);
    DM.TM=new SimpleTextureManager(DM);
    DM.P.updateStatus(DM);
    LL.updateStatus(DM);
    M.updateStatus(DM);
    ST.updateStatus(DM);
    AM.updateStatus(DM);
    //DM.R.updateStatus(DM);
   // Interesting bug: initializing the Unified renderer BEFORE applying
    // scale info, results in VERY dark visplanes.
    // This can't be done as easily with the Parallel  because a lot more
    // stuff will break.
   DM.initializeVideoScaleStuff();    
    DM.R.Init();
    
    DM.P.Init();
    DM.players[0].updateStatus(DM);
    DM.players[0].PlayerReborn();
    
    ST.Init();
    M.Init();
    ST.Start();
    LL.SetupLevel(1, 1, 0, skill_t.sk_hard);
    AM.LevelInit();
    AM.Start();
    
    DM.R.SetViewSize(11, 1);
    DM.R.ExecuteSetViewSize();
    DM.TM.setSkyTexture(DM.TM.CheckTextureNumForName("SKY1"));
    long a=System.nanoTime();
    
    DM.menuactive=false;
    DM.automapactive=true;
    DM.R.FillBackScreen();
    DM.R.DrawViewBorder();
    
    
    // Center on "bloody mess" in E1M1
    DM.players[0].mo.y+=420<<16;
    //DM.players[0].mo.x+=1650<<16;
        int pl=0;
        for (int i=0;i<20000;i++){
   
           frame.GetEvent();
           
           for ( ; DM.eventtail != DM.eventhead ; DM.eventtail = (++DM.eventtail)&(MAXEVENTS-1) )
           {
       	event_t ev = DM.events[DM.eventtail];

            //System.out.println(ev);
            if (ev!=null){
            
            AM.Responder(ev);
            M.Responder(ev);
            ST.Responder(ev);
           }
           }
            
       
           
        //V.DrawPatch(0,0,0,help1);
        //M.Ticker();
        //M.Drawer();
        AM.Ticker();
        //AM.Drawer();
        ST.Ticker();        
        DM.players[0].viewz=(40)<<16;
        //DM.players[0].mo.x=ox+(int) ((12864<<16)*Math.cos(2*Math.PI*i/500.0));
        //DM.players[0].mo.y=oy+(int) ((64<<16)*Math.sin(2*Math.PI*i/500.0));
        //DM.players[0].mo.angle= ((long)(0xFFFFFFFFL*(Math.atan2(DM.players[0].mo.y-oy,DM.players[0].mo.x-ox)+Math.PI)/(2*Math.PI)))&0xFFFFFFFFL;
        DM.players[0].mo.angle=(DM.players[0].mo.angle-0x800000)&0xFFFFFFFFL;
        //System.out.println(">>>>>>>>>>>>>>>>>> VIEW ANGLE "+360.0*(DM.players[0].mo.angle>>19)/8192.0); 
       
        DM.R.RenderPlayerView(DM.players[0]);
        //ST.Drawer(false,true);
        //System.out.println("Rendered"+DM.gametic);
        
        DM.gametic++;
        frame.FinishUpdate();
        if (i%200==0){
        	   long b=System.nanoTime();
        	    //frame.setPalette((pl++)%14);
        	    System.out.println((200) +" frames in " +((b-a)/1e09) +" = "+200/((b-a)/1e09) + " fps");
        	    a=b;
        }
        
       // System.out.print(frame.processEvents());
        /*while (ba-DM.I.GetTime()>-1){
            Thread.sleep(10);               
        }*/
        }
            } catch (Exception e){
                e.printStackTrace();
            }
    
    }
    
}

package testers;

import m.fixed_t;

public class Add
        implements Operation {

    @Override
    public void invoke(fixed_t a, fixed_t b) {
        a.add(b);

    }

}

package testers;

import java.util.HashMap;

import pooling.RoguePatchMap;
import pooling.RoguePatchMap2;

public class TestPatchMaps {
    public static final int TESTS=2000000;
    public static final int SET=30;
    public static final int REPS=50;
    static byte[][][] stuff=new byte[SET][1][1];
    static byte[][][] stuff2=new byte[TESTS][][];
	// A normal hashmap....
	static HashMap<Integer,byte[][]> map1= new HashMap<Integer,byte[][]> ();
	
	// And a NEW and ENHANCED implementation.
	static RoguePatchMap map2=new RoguePatchMap();
	
    public static void main(String[] argv){
    
    	for (int i=0;i<REPS;i++){
    		System.out.printf("Put %d in Hashmap: %d\n",TESTS,testPutHashmap());
    	}

    	for (int i=0;i<REPS;i++){
    		System.out.printf("Put %d in Patchmap: %d\n",TESTS,testPutPatchmap());
    	}

    	
    	for (int i=0;i<REPS;i++){
    		System.out.printf("Get %d from Hashmap: %d\n",TESTS,testGetHashmap());
    	}

    	for (int i=0;i<REPS;i++){
    		System.out.printf("Get %d from Patchmap: %d\n",TESTS,testGetPatchmap());
    	}

    	
    }
    
    private static long testPutHashmap(){
    	
    	long a=System.nanoTime();
    	for (int i=0;i<TESTS;i++){
    		map1.put(i%SET, stuff[i%SET]);
    	}
    	long b=System.nanoTime();
    	
    	return (b-a);
    }
    
    private static long testGetHashmap(){
    	
    	long a=System.nanoTime();
    	for (int i=0;i<TESTS;i++){
    		stuff2[i]=map1.get(i%SET);
    	}
    	long b=System.nanoTime();
    	
    	return (b-a);
    }
    
    private static long testPutPatchmap(){
    	
    	long a=System.nanoTime();
    	for (int i=0;i<TESTS;i++){
    		map2.put(i%SET, stuff[i%SET]);
    	}
    	long b=System.nanoTime();
    	
    	return (b-a);
    }
    
    private static long testGetPatchmap(){
    	
    	long a=System.nanoTime();
    	for (int i=0;i<TESTS;i++){
    		stuff2[i]=map2.get(i%SET);
    	}
    	long b=System.nanoTime();
    	
    	return (b-a);
    }
}

package testers;

import static data.Defines.PU_STATIC;
import static data.Defines.pw_allmap;
import static m.fixed_t.FRACBITS;
import java.awt.image.IndexColorModel;
import automap.DoomAutoMap;
import automap.Map;

import p.LevelLoader;
import p.mobj_t;

import m.DoomRandom;

import st.StatusBar;

import data.Defines;
import defines.*;
import doom.DoomContext;
import doom.DoomMain;
import doom.DoomStatus;
import doom.event_t;
import doom.evtype_t;
import doom.player_t;
import doom.ticcmd_t;
import doom.wbstartstruct_t;
import doom.weapontype_t;

import utils.C2JUtils;
import v.SimpleRenderer;
import w.*;

/** This is a very simple tester for the Automap. Combined with status bar + Level loader. */

public class AutoMapTester {

    public static void main(String[] argv) {
        try {
    WadLoader W=new WadLoader();
    W.InitMultipleFiles(new String[] {"doom1.wad"});
    //W.AddFile("bitter.wad");
    System.out.println("Total lumps read: "+W.numlumps);
    SimpleRenderer V=new SimpleRenderer(320,200);
    V.Init();
    
    DoomBuffer palette = W.CacheLumpName("PLAYPAL", PU_STATIC);
    byte[] pal=palette.getBuffer().array();
    IndexColorModel icm=new IndexColorModel(8, 256,pal, 0, false);
        
    DoomMain DM=new DoomMain();
    DM.gameepisode=1;
    DM.gamemap=1;
    DM.gamemission=GameMission_t.doom;
    DM.gamemode=GameMode_t.shareware;
    DM.wminfo=new wbstartstruct_t();
    C2JUtils.initArrayOfObjects(DM.players,player_t.class);
    Defines.SCREENWIDTH=320;
    Defines.SCREENHEIGHT=200;
    
    DM.DM=(DoomMain) DM;
    DM.W=W;
    DM.V=V;
    DM.RND=new DoomRandom();
    DM.players[0].cmd=new ticcmd_t();
    DM.players[0].itemcount=1337;
    DM.players[0].killcount=1337;
    DM.players[0].secretcount=1337;
    DM.players[0].weaponowned[0]=true;
    DM.players[0].weaponowned[1]=true;
    DM.players[0].weaponowned[2]=true;
    DM.players[0].weaponowned[3]=true;
    DM.players[0].readyweapon=weapontype_t.wp_pistol;
    DM.players[0].health[0]=100;
    DM.players[0].armorpoints[0]=666;
    DM.players[0].ammo[0]=666;
    DM.players[0].maxammo[0]=666;
    DM.players[0].ammo[1]=666;
    DM.players[0].maxammo[1]=666;
    DM.players[0].ammo[2]=666;
    DM.players[0].maxammo[2]=666;
    DM.players[0].ammo[3]=666;
    DM.players[0].maxammo[3]=666;

    
    DM.players[0].cards[0]=true;
    DM.players[0].cards[2]=true;
    DM.players[0].cards[4]=true;
    DM.players[0].mo=new mobj_t();
    DM.players[0].mo.x=1056<<FRACBITS;
    DM.players[0].mo.y=-3616<<FRACBITS;
    
    DM.players[0].powers[pw_allmap]=100;
    DM.deathmatch=false;
    DM.statusbaractive=true;
    
    DM.wminfo.plyr[0].in=true;
    DM.wminfo.plyr[0].sitems=1337;
    DM.wminfo.plyr[0].skills=1337;
    DM.wminfo.plyr[0].stime=28595;
    DM.wminfo.plyr[0].ssecret=1337;
    DM.playeringame[0]=true;
    DM.wminfo.last=6;
    DM.wminfo.epsd=0;
    DM.wminfo.maxitems=100;
    DM.wminfo.maxkills=100;
    DM.wminfo.maxsecret=100;
    DM.wminfo.partime=28595;

    StatusBar ST=(StatusBar) (DM.ST=new StatusBar(DM));
    ST.Start();

    LevelLoader PL=new LevelLoader(DM);
    PL.SetupLevel(1, 1, 0, skill_t.sk_hard);
    DM.LL=PL;
    DM.ST=ST;
    DoomAutoMap AM=DM.AM=new Map(DM);
    AM.Start();
    
    ST.Responder(new event_t('i'));
    ST.Responder(new event_t('d'));
    ST.Responder(new event_t('d'));
    ST.Responder(new event_t('t'));

    AM.Responder(new event_t(Map.AM_FOLLOWKEY));
    AM.Responder(new event_t(Map.AM_ZOOMINKEY));
    AM.Responder(new event_t(Map.AM_GRIDKEY));
	AM.updateStatus(DM);
	ST.updateStatus(DM);
    for (int i=0;i<100;i++){
    	DM.players[0].health[0]--;
    	DM.players[0].ammo[0]=i;
    	DM.players[0].damagecount=1; // if zero, it won't update.
        AM.Responder(new event_t(evtype_t.ev_keyup,Map.AM_MSGENTERED));
    if ((i%20)<10)
        AM.Responder(new event_t(Map.AM_ZOOMINKEY));
       else
       AM.Responder(new event_t(Map.AM_ZOOMOUTKEY));

    if ((i%25)<12) {
        AM.Responder(new event_t(Map.AM_PANUPKEY));
        AM.Responder(new event_t(Map.AM_PANRIGHTKEY));
    }
       else {
       AM.Responder(new event_t(Map.AM_PANDOWNKEY));
       AM.Responder(new event_t(Map.AM_PANLEFTKEY));
       }

    
    AM.Ticker();
    AM.Drawer();
    ST.Ticker();
    ST.Drawer(false,true);
    V.takeScreenShot(0, "tic"+i,icm);    
    //AM.Responder(new event_t(Map.AM_PANLEFTKEY));

    }
    
        } catch (Exception e){
            e.printStackTrace();
        }
    }
    
}

package testers;

import hu.HU;
import m.DummyMenu;
import m.IDoomMenu;
import m.MenuMisc;
import demo.VanillaDoomDemo;
import doom.CommandLine;
import doom.DoomStatus;
import w.*;

public class DefaultsTester {

    public static void main(String[] argv) {

    DoomStatus DS=new DoomStatus();
    m.AbstractDoomMenu dummy= new DummyMenu();
    DS.M=dummy;
    DS.HU=new HU();
    DS.CM=new CommandLine(argv);
    
    MenuMisc.LoadDefaults(DS);
    
    MenuMisc.defaultfile="ghetto.cfg";
    MenuMisc.SaveDefaults(DS);

    
    }
    
}

package testers;

import java.lang.reflect.Method;

import m.FixedFloat;
import m.fixed_t;

import w.WadLoader;

/** A simple benchmark proving how using reflection is slower than both simply switching
 *  on a status flag, or using "invokable" objects. This affected the choice of invokable objects
 *  and simple switching whenever Doom used function pointers.
 *  
 */

public class TestReflection {

    /**
     * @param args
     * @throws Exception
     * @throws
     */
    public static void main(String[] args)
            throws Exception {
        int TESTS = 50000000;

        // "function pointer" to fadd
        Method fadd = fixed_t.class.getDeclaredMethod("add", fixed_t.class);
        Method fmul =
            fixed_t.class.getDeclaredMethod("FixedMul", fixed_t.class);

        Operation fadd2=new Add();
        Operation fmul2=new Mul();
        Operation todo;
        
        fixed_t a = new fixed_t(FixedFloat.toFixed(1.0));
        fixed_t b = new fixed_t(FixedFloat.toFixed(1.1));

        
        
        Method operation = fadd;

        {
            long ta = System.nanoTime();
            for (int i = 0; i < TESTS; i++) {
                operation.invoke(a, b);
            }

            long tb = System.nanoTime();
            System.out.println("Adding with reflection :"+(tb - ta));
        }
        System.out.println(FixedFloat.toDouble(a.val));

        a = new fixed_t(FixedFloat.toFixed(1.0));

        operation = fmul;

        {
            long ta = System.nanoTime();
            for (int i = 0; i < TESTS; i++) {
                operation.invoke(a, b);
            }

            long tb = System.nanoTime();
            System.out.println("Multiplying with reflection :"+(tb - ta));
        }
        System.out.println(FixedFloat.toDouble(a.val));

        /** add=0, mul=1 */
        int op = 0;

        a = new fixed_t(FixedFloat.toFixed(1.0));

        {
            long ta = System.nanoTime();
            for (int i = 0; i < TESTS; i++) {
                switch (op) {
                case 0:
                    a.add(b);
                    break;
                case 1:
                    a.FixedMul(b);
                    break;
                }
            }

            long tb = System.nanoTime();
            System.out.println("Adding with switching :"+(tb - ta));
        }
        System.out.println(FixedFloat.toDouble(a.val));

        
          op=1;
        a = new fixed_t(FixedFloat.toFixed(1.0));

        {
            long ta = System.nanoTime();
            for (int i = 0; i < TESTS; i++) {
                switch (op) {
                case 0:
                    a.add(b);
                case 1:
                    a.FixedMul(b);
                }
            }

            long tb = System.nanoTime();
            System.out.println("Multiplying with switching :"+(tb - ta));
        }
        System.out.println(FixedFloat.toDouble(a.val));
        
        a = new fixed_t(FixedFloat.toFixed(1.0));
        
        todo=fadd2;

        {
            long ta = System.nanoTime();
            for (int i = 0; i < TESTS; i++) {
                todo.invoke(a,b);
            }

            long tb = System.nanoTime();
            System.out.println("Adding with invokable object :"+(tb - ta));
        }
        System.out.println(FixedFloat.toDouble(a.val));
        
        a = new fixed_t(FixedFloat.toFixed(1.0));
        
        todo=fmul2;

        {
            long ta = System.nanoTime();
            for (int i = 0; i < TESTS; i++) {
                todo.invoke(a,b);
            }

            long tb = System.nanoTime();
            System.out.println("Multiplying with invokable object :"+(tb - ta));
        }
        System.out.println(FixedFloat.toDouble(a.val));
        
        operation = fadd;

        {
            long ta = System.nanoTime();
            for (int i = 0; i < TESTS; i++) {
                operation.invoke(a, b);
            }

            long tb = System.nanoTime();
            System.out.println("Adding with reflection 2:"+(tb - ta));
        }
        System.out.println(FixedFloat.toDouble(a.val));

        a = new fixed_t(FixedFloat.toFixed(1.0));

        operation = fmul;

        {
            long ta = System.nanoTime();
            for (int i = 0; i < TESTS; i++) {
                operation.invoke(a, b);
            }

            long tb = System.nanoTime();
            System.out.println("Multiplying with reflection 2:"+(tb - ta));
        }
        System.out.println(FixedFloat.toDouble(a.val));
        
        
    }

}

package testers;

import m.Swap;

public class TestSwap {

    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        short a=(short)0xBEEF;
        char c=(char)0xBABE;
        int b=0XDEADBABE;
        
        System.out.printf("%X\n",a);
        System.out.printf("%X\n",b);
        System.out.printf("%X\n",(int)c);
        a=Swap.SHORT(a);
        b=Swap.LONG(b);
        c=(char)Swap.SHORT(c);
        System.out.printf("%X\n",a);
        System.out.printf("%X\n",b);
        System.out.printf("%X\n",(int)c);
        c=(char)Swap.SHORT(a);
        System.out.printf("%X\n",(int)c);

        char aa=Swap.USHORT((char)a);
        System.out.printf("%X\n",(int)a);
        System.out.printf("%X\n",(int)aa);


    }

}

package testers;

import static data.Defines.PU_STATIC;
import hu.HU;

import java.nio.ByteBuffer;

import m.FixedFloat;

import rr.vertex_t;

import data.mapvertex_t;
import defines.*;
import doom.DoomContext;
import doom.DoomStatus;

import utils.C2JUtils;
import w.*;

/** This is a very simple tester for the WadLoader and HU modules.
 *  We use the same exact methods used in the C source code, only
 *  with a more OO approach.
 * 
 * 
 */

public class WadLoaderTester {

    public static void main(String[] argv) {
        try {
    IWadLoader W=new WadLoader();
    W.InitMultipleFiles(new String[] {"doom1.wad"});
    //W.AddFile("bitter.wad");
    System.out.println("Total lumps read: "+W.NumLumps());
    System.out.println("NUm for E1M1: "+W.GetNumForName("E1M1"));
    System.out.println("NUm for SECTORS: "+W.GetNumForName("SECTORS"));
    System.out.println("NUm for SSECTORS: "+W.GetNumForName("SSECTORS"));
   int lump=W.GetNumForName("VERTEXES");
   System.out.println("NUm for VERTEXES: "+W.GetNumForName("VERTEXES"));
    // We prepare a ByteBuffer to receive a "SECTORS" object. Deserializing it is
    // another matter.
    ByteBuffer bb=W.CacheLumpName("SECTORS", 0).getBuffer();
    System.out.println("Num for THINGS: "+W.GetNumForName("THINGS"));
    DoomStatus ds = new DoomStatus();
    ds.gameepisode=1;
    ds.gamemap=1;
    ds.gamemission=GameMission_t.doom;
    ds.W=W;
    
    // Testing "Heads Up" clases. Notice how we pass "doomstate" and "WadLoader" as instances,
    // instead of globals. OO all the way, baby!
    
    // Determine number of lumps:
    //  total lump length / vertex record length.
    int numvertexes = W.LumpLength (lump) / mapvertex_t.sizeOf();

    // Allocate zone memory for buffer.
    vertex_t[] vertexes;

    // Load data into cache.
    // MAES: we now have a mismatch between memory/disk: in memory, we need an array.
    // On disk, we have a single lump/blob. Thus, we need to find a way to deserialize this...
    vertexes= W.CacheLumpNumIntoArray(lump,numvertexes,vertex_t.class);

   // Copy and convert vertex coordinates,
    // internal representation as fixed.
    for (int i=0 ; i<numvertexes ; i++)
    {
        //vertexes[i].x = ml[i].x<<FRACBITS;
        //vertexes[i].y = ml[i].y<<FRACBITS;
       System.out.println(vertexes[i].x+" , "+vertexes[i].y+" "+FixedFloat.toDouble(vertexes[i].x)+" , "+FixedFloat.toDouble(vertexes[i].y));
        //System.out.println(ml[i].x+" , "+ml[i].y+" "+FPTest.Value(vertexes[i].x)+" , "+FPTest.Value(vertexes[i].y));
    }

    
        } catch (Exception e){
            e.printStackTrace();
        }
    }
    
}

package testers;



public class TestLightLevels {

    public static final int LIGHTLEVELS= 32;
    public static final int MAXLIGHTSCALE =48;
    public static final int NUMCOLORMAPS =32;
    public static final int LIGHTBRIGHT =2;
    public static final int DISTMAP =2;
    
    public static void main(String[] argv){
        for (int i = 0; i < LIGHTLEVELS; i++) {
            int startmap = ((LIGHTLEVELS-LIGHTBRIGHT-i)*2)*NUMCOLORMAPS/LIGHTLEVELS;
            for (int j = 0; j < MAXLIGHTSCALE; j++) {
                int level =
                    startmap - j/ DISTMAP;
                if (level < 0)
                    level = 0;
                if (level >= NUMCOLORMAPS)
                    level = NUMCOLORMAPS - 1;
                System.out.printf("%d ",level);
            }
            System.out.printf("\n");
            }
        }
    }

package testers;
import static data.Defines.KEY_F1;
import static data.Defines.PU_STATIC;
import static data.Defines.pw_allmap;
import static m.fixed_t.FRACBITS;

import i.InputListener;
import i.IDoomSystem;

import java.applet.Applet;
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferByte;
import java.awt.image.IndexColorModel;
import java.io.IOException;

import javax.imageio.ImageIO;

import m.IDoomMenu;
import m.Menu;
import m.DoomRandom;
import p.LevelLoader;
import p.mobj_t;
import rr.patch_t;
import st.StatusBar;
import utils.C2JUtils;
import v.BufferedRenderer;
import w.DoomBuffer;
import w.WadLoader;
import automap.DoomAutoMap;
import automap.Map;
import data.Defines;
import data.Defines.GameMission_t;
import data.Defines.GameMode_t;
import data.Defines.skill_t;
import doom.DoomContext;
import doom.DoomStatus;
import doom.event_t;
import doom.player_t;
import doom.ticcmd_t;
import doom.wbstartstruct_t;
import doom.weapontype_t;
import f.EndLevel;
import f.Wiper;

public class DoomApplet
        extends Applet {
    
    public static final int WIDTH=320;
    public static final int HEIGHT=200;
    public static final int TICKS=5000;
    
    IndexColorModel[] icms;
    Canvas drawhere;
    BufferedImage[] bi;
    int palette=0;
    Dimension size;
    Image crap;
    InputListener in;
    Graphics2D g2d;
    BufferedImage[] pals;
    
    /**
     * 
     */
    private static final long serialVersionUID = 1L;
    
    IWadLoader W;
    BufferedRenderer V;
    DoomStatus ds;
    DoomContext DC;
    StatusBar ST;
    Map AM;
    Menu M;
    EndLevel EL;

    
    private patch_t titlepic;

    private patch_t help1;

    private Wiper wipe;
    
    public void start(){
        
    }
    
    public void doStuff(){
        
       // Do a screen wipe 
       WipeYoAss();
       /*
       DoMenu();
       doAutoMap();
       doEndLevel();*/
    }
    
    private void doAutoMap() {
        
        V.screens[0]=((DataBufferByte)pals[0].getRaster().getDataBuffer()).getData();
        ST.Responder(new event_t('i'));
        ST.Responder(new event_t('d'));
        ST.Responder(new event_t('d'));
        ST.Responder(new event_t('t'));
        
        AM.Responder(new event_t(Map.AM_FOLLOWKEY));
        AM.Responder(new event_t(Map.AM_ZOOMOUTKEY));
        AM.Responder(new event_t(Map.AM_GRIDKEY));
        long a=System.nanoTime();
        for (int i=0;i<TICKS;i++){
            if ((i%20)<10)
                AM.Responder(new event_t(Map.AM_ZOOMINKEY));
               else
               AM.Responder(new event_t(Map.AM_ZOOMOUTKEY)); 

            if ((i%25)<12) {
                AM.Responder(new event_t(Map.AM_PANUPKEY));
                AM.Responder(new event_t(Map.AM_PANRIGHTKEY));
            } 
               else {
               AM.Responder(new event_t(Map.AM_PANDOWNKEY));
               AM.Responder(new event_t(Map.AM_PANLEFTKEY));
               }


            V.DrawPatch(0, 0, 0, titlepic);
            AM.Ticker();
            AM.Drawer();
            ST.Ticker();
            ST.Drawer(false,true);

            this.setPalette((i/(TICKS/14))%14);
            this.processEvents();
            //System.out.println(V.isRasterNull(0));
            this.update();
        
            }
    }

    private void dump(int i) {
        try {
            V.takeScreenShot(0, "C:\\SHIT"+i+".PNG",icms[0]);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        
    }

    public void init(){
        
        // size
        size = new Dimension();
        size.width = WIDTH;
        size.height = HEIGHT;
        this.setSize(size);
        
        // Canvas
        drawhere=new Canvas();
        drawhere.setSize(size);
        drawhere.setBounds(0, 0, drawhere.getWidth(),drawhere.getHeight());
        drawhere.setBackground(Color.black);            
        this.add(drawhere);
        drawhere.setVisible(true);
        
        // Listener
        in = new InputListener();
        this.addComponentListener(in);
        
        // Graphics 
        g2d = (Graphics2D)drawhere.getGraphics();
        
        //this.setSize(new Dimension(WIDTH,));
        this.setVisible(true);

        W=new WadLoader();
        try {
            W.InitMultipleFiles(new String[] {"doom.wad"});
        } catch (Exception e) {
            e.printStackTrace();
        }
        help1=W.CachePatchName("HELP1", PU_STATIC);
        System.out.println(help1.height);
        System.out.println(help1.width);        
        System.out.println(help1);
        System.out.println(W.GetNumForName("TITLEPIC"));
        System.out.println(W.lumpinfo[W.GetNumForName("TITLEPIC")].size);
        titlepic=W.CachePatchName("HELP1", PU_STATIC);
        System.out.println(titlepic.height);
        System.out.println(titlepic.width);

        System.out.println(help1);
        System.out.println(W.GetNumForName("HELP1"));
        System.out.println(W.lumpinfo[W.GetNumForName("HELP1")].size);
        DoomBuffer palette = W.CacheLumpName("PLAYPAL", PU_STATIC);
        byte[] pal=palette.getBuffer().array();

        IndexColorModel icm=new IndexColorModel(8, 256,pal, 0, false);
        Defines.SCREENWIDTH=WIDTH;
        Defines.SCREENHEIGHT=HEIGHT;
        V=new BufferedRenderer(WIDTH,HEIGHT,icm);
        V.Init();
        icms=new IndexColorModel[palette.getBuffer().limit()/768];
        for (int i=0;i<icms.length;i++){
            icms[i]=new IndexColorModel(8, 256,pal, i*768, false);
            }
        pals=new BufferedImage[icms.length];
        pals=V.getBufferedScreens(0, icms);
        
   /*     for (int i=0;i<icms.length;i++){
         icms[i]=new IndexColorModel(8, 256,pal, i*768, false);
             pals[i]=new BufferedImage(icms[i],V.screenbuffer[0].getRaster(), false, null);
            }
            */
        this.Init(pals);
        
        ds=new DoomStatus();
        initDoomState(ds);

        DC=new DoomContext();
        DC.DS=ds;
        DC.W=W;
        DC.V=V;
        DC.RND=new DoomRandom();
        ST=new StatusBar(DC);
        ST.Start();
        LevelLoader PL=new LevelLoader(DC);
        try {
            PL.SetupLevel(1, 1, 0, skill_t.sk_hard);
        } catch (Exception e) {
            e.printStackTrace();
        }
        DC.LL=PL;
        DC.ST=ST;
        AM=new Map(DC);
        AM.Start();
        M= new Menu(DC);
        M.Init();
        wipe=new Wiper(DC);
        EL=new EndLevel(DC);
        EL.Start(DC.DS.wminfo);
        
        doStuff();
    }

    private static void initDoomState(DoomStatus ds) {
        C2JUtils.initArrayOfObjects(ds.players,player_t.class);
        ds.gameepisode=1;
        ds.gamemap=1;
        ds.gamemission=GameMission_t.doom;
        ds.gamemode=GameMode_t.shareware;
        ds.wminfo=new wbstartstruct_t();
        ds.players[0].cmd=new ticcmd_t();
        ds.players[0].itemcount=1337;
        ds.players[0].killcount=1337;
        ds.players[0].secretcount=1337;
        ds.players[0].weaponowned[0]=true;
        ds.players[0].weaponowned[1]=true;
        ds.players[0].weaponowned[2]=true;
        ds.players[0].weaponowned[3]=true;
        ds.players[0].readyweapon=weapontype_t.wp_pistol;
        ds.players[0].health[0]=100;
        ds.players[0].armorpoints[0]=100;
        ds.players[0].ammo[0]=400;
        ds.players[0].maxammo[0]=400;
        ds.players[0].ammo[1]=100;
        ds.players[0].maxammo[1]=100;
        ds.players[0].ammo[2]=100;
        ds.players[0].maxammo[2]=100;
        ds.players[0].ammo[3]=600;
        ds.players[0].maxammo[3]=600;

        
        ds.players[0].cards[0]=true;
        ds.players[0].cards[2]=true;
        ds.players[0].cards[4]=true;
        ds.players[0].mo=new mobj_t();
        ds.players[0].mo.x=1056<<FRACBITS;
        ds.players[0].mo.y=-3616<<FRACBITS;
        
        ds.players[0].powers[pw_allmap]=100;
        ds.deathmatch=false;
        ds.statusbaractive=true;
        
        ds.wminfo.plyr[0].in=true;
        ds.wminfo.plyr[0].sitems=1337;
        ds.wminfo.plyr[0].skills=1337;
        ds.wminfo.plyr[0].stime=28595;
        ds.wminfo.plyr[0].ssecret=1337;
        ds.playeringame[0]=true;
        ds.wminfo.last=6;
        ds.wminfo.epsd=0;
        ds.wminfo.maxitems=100;
        ds.wminfo.maxkills=100;
        ds.wminfo.maxsecret=100;
        ds.wminfo.partime=28595;
    }
       
    /*
    public void paint(Graphics g){
        g2d=(Graphics2D) g;
        g2d.drawImage(bi[palette],0,0,this);        
    }*/
    
    
    public void paint(Graphics g){
        /*
        if (g==null)
            this.update(null);
        else super.paint(g);
        //g2d.drawImage(bi[palette],0,0,this);
         *         
         */
    }

    

    public void Init(BufferedImage[] bi) {
        this.setBackground(Color.black);
        this.bi=bi;            


        
    }
    
    public void Init(BufferedImage bi) {
        this.bi=new BufferedImage[]{bi};
        size = new Dimension();
        size.width = bi.getWidth();
        size.height = bi.getHeight();
        this.setSize(size);
        drawhere=new Canvas();
        drawhere.setSize(size);
        drawhere.setBounds(0, 0, drawhere.getWidth(),drawhere.getHeight());
        drawhere.setBackground(Color.black);
        this.add(drawhere);
        in = new InputListener();
        this.addComponentListener(in);
        g2d = (Graphics2D)drawhere.getGraphics();
    }
    
    public void setPalette(int pal){
        this.palette=pal;
    }
    
    public void update() {
        g2d=(Graphics2D) drawhere.getGraphics();
        g2d.drawImage(bi[palette],0,0,this);
        
        //drawhere.repaint();
        //super.update(this.getGraphics());
      }   
    
    public void update(Graphics g) {
      // g2d.drawImage(bi[palette],0,0,this);       
    }
    
    public String processEvents(){
        StringBuffer tmp=new StringBuffer();
        event_t event;
        while ( (event=InputListener.nextEvent()) != null ) {
            tmp.append(event.type.ordinal()+"\n");
        }
        return tmp.toString();
    }

    public void WipeYoAss(){
        
        int WIPES=1;
        for (int i=0;i<WIPES;i++){
            V.DrawPatch(0, 0, 0, help1);
            // Grab "help"
            wipe.StartScreen(0, 0, Defines.SCREENWIDTH, Defines.SCREENHEIGHT);
                    V.DrawPatch(0, 0, 0, titlepic);
            wipe.EndScreen(0, 0, Defines.SCREENWIDTH, Defines.SCREENHEIGHT);
            
            int wipestart = IDoomSystem.GetTime () - 1;
            int nowtime;
            int tics;
            boolean done;
        
        do
        {
        do
        {
            nowtime = IDoomSystem.GetTime ();
            tics = nowtime - wipestart;
        } while (tics<1);
        wipestart = nowtime;
        //V.DrawPatch(0, 0, 0, titlepic);
        V.DrawPatch(0, 0, 0, help1);
        //V.DrawPatch(320, 0, 0, titlepic);
        done = wipe.ScreenWipe(Wiper.wipe.Melt.ordinal()
                       , 0, 0, Defines.SCREENWIDTH, Defines.SCREENHEIGHT, tics);
        //I_UpdateNoBlit ();
        //M_Drawer ();                            // menu is drawn even on top of wipes
        //System.out.println(V.isRasterNull(0));
        this.update();
       
        
        } while (!done);
    }
}
    public void DoMenu(){
        ds.menuactive=true;        
        for (int i=0;i<600;i++){


            if (i==40){
                System.out.println("Pressing enter");
                M.Responder(new event_t(Defines.KEY_DOWNARROW));
            }

            if (i==60){
                System.out.println("Pressing down");
                M.Responder(new event_t(Defines.KEY_DOWNARROW));
            }
            
            if (i==80){
                System.out.println("Pressing escape");
                M.Responder(new event_t(Defines.KEY_ESCAPE));
            }
            if (i==100){
                System.out.println("Pressing up");
                M.Responder(new event_t(Defines.KEY_UPARROW));
            }
            
            if (i==120){
                System.out.println("Pressing up");
                M.Responder(new event_t(Defines.KEY_UPARROW));
            }
            
            if (i==140){
                System.out.println("Pressing escape");
                M.Responder(new event_t(Defines.KEY_ESCAPE));
            }
            
            if (i==160){
                System.out.println("Pressing up");
                M.Responder(new event_t(Defines.KEY_UPARROW));
            }
            
            if (i==160){
                System.out.println("Pressing up");
                M.Responder(new event_t(Defines.KEY_UPARROW));
            }
            
            if (i==300 || i==500|i==550|i==600){
                System.out.println("Pressing F1");
                M.Responder(new event_t(KEY_F1));
                System.out.println("pressed ");
            }
            
            if (i==400 || i==650){
                System.out.println("Pressing escape");
                M.Responder(new event_t(Defines.KEY_ESCAPE));
            }
            //V.takeScreenShot(0,( "menutic"+i),icm);
            try {
                Thread.sleep(14);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        V.DrawPatch(0,0,0,help1);
        M.Ticker();
        M.Drawer();
        System.out.println(V.isRasterNull(0));
        this.update();
        //System.out.print(frame.processEvents());
        }
    }
    
    public void doEndLevel(){
        int a,b;
        a=IDoomSystem.GetTime();
        b=a;
        for (int i=0;i<2000;i++){
            
        EL.Ticker();
        EL.Drawer();
        this.update();
        if (i==100){
                ds.players[0].cmd.buttons=1; // simulate attack
                ds.players[0].attackdown=false; // simulate attack
            }
            
            if (i==120){
                ds.players[0].cmd.buttons=1; // simulate attack
                ds.players[0].attackdown=false; // simulate attack
            }
       // Do we still have time>
            
            while((b-a)<1) {
                b=IDoomSystem.GetTime();
                }
           a=b;
        }
    }
    
}

package testers;

import v.FastTranspose;

public class TestFastTranspose {

    /**
     * @param args
     */
    public static void main(String[] args) {
        int length=11;
        byte[] x=new byte[1<<(length<<1)];
        byte[] scratch=new byte[1<<(FastTranspose.BL<<1)];

        for(int i=0;i< 1<<(length<<1); i++ ) x[i]=(byte) ((i%256)-128);
        FastTranspose.blocktranspose(x, scratch, length);
    }

}

package testers;

import java.io.IOException;
import java.io.OutputStream;
import java.io.Writer;

import s.QMusToMid;
import s.QMusToMid.Track;

public class QMusToMidTester extends QMusToMid {

	//_D_: used by me to test and fix bugs
	//FileInputStream fisTest;
	//int nbTest = 0;

	public QMusToMidTester() {
		super();
		/*try {
			fisTest = new FileInputStream("C:\\Users\\David\\Desktop\\qmus2mid\\test.mid");
			//fisTest.read(new byte[106]); //debut track 0
			fisTest.read(new byte[6977]); //debut track 1
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}*/

	}
	public static void fwrite(byte[] bytes, int offset, int size, int count, Object file) throws IOException {
		
		//nbTest += size;
		
		if (file instanceof OutputStream) {
			//.getClass(). . isAssignableFrom(OutputStream.class))
			((OutputStream)file).write(bytes, offset, size);
			
			
			for (int i = 0; i < size; i++) {
				//System.out.println(bytes[i]);
				
				//byte b = (byte)fisTest.read();
				//if (b != bytes[offset+i]) {
					//System.out.println("Woawww byte #: "+(offset+i)+" "+b+ " VS "+bytes[offset+i]);
				//}
				
				
			}
			

		}
		if (file instanceof Writer) {
//		if (file.getClass().isAssignableFrom(Writer.class))
			char[] ch = new char[bytes.length];
			for (int i = 0; i < bytes.length; i++) {
				//System.out.println(bytes[i]);
				ch[i] = (char)toUnsigned(bytes[i]);
				
				if ((byte)ch[i] == 0x3F) {
					System.out.println("testtt");
				}
				
				//char c = (char)fisTest.read();
				//if (c != ch[i]) {
				//	System.out.println("Woawww");
				//}
				
				
			}
			
			((Writer)file).write(ch, offset, size);
		}
	}
	
	public void TWriteByte( int MIDItrack, byte byte_, Track track[] ) {
		super.TWriteByte(MIDItrack, byte_, track);
		  //_D_: this was only added by me to test and fix bugs before this worked
		/*try {
			if (MIDItrack == 1) {
			  int b;
			b = fisTest.read();
			  if ((byte)b != byte_) {
				  //System.out.println("Woawww");
			  }
			}

		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}*/
	}
}

package testers;

import javax.swing.*;
import java.awt.event.*;
import java.awt.BorderLayout;
import java.awt.Dimension;

public class KeyEventDemo extends JPanel 
                          implements KeyListener,
                                     ActionListener {
    /**
     * 
     */
    private static final long serialVersionUID = 1L;
    JTextArea displayArea;
    JTextField typingArea;
    static final String newline = "\n";

    public KeyEventDemo() {
        super(new BorderLayout());

        JButton button = new JButton("Clear");
        button.addActionListener(this);

        typingArea = new JTextField(20);
        typingArea.addKeyListener(this);

        //Uncomment this if you wish to turn off focus
        //traversal.  The focus subsystem consumes
        //focus traversal keys, such as Tab and Shift Tab.
        //If you uncomment the following line of code, this
        //disables focus traversal and the Tab events will
        //become available to the key event listener.
        //typingArea.setFocusTraversalKeysEnabled(false);

        displayArea = new JTextArea();
        displayArea.setEditable(false);
        JScrollPane scrollPane = new JScrollPane(displayArea);
        scrollPane.setPreferredSize(new Dimension(375, 125));

        add(typingArea, BorderLayout.PAGE_START);
        add(scrollPane, BorderLayout.CENTER);
        add(button, BorderLayout.PAGE_END);
    }

    /** Handle the key typed event from the text field. */
    public void keyTyped(KeyEvent e) {
        displayInfo(e, "KEY TYPED: ");
    }

    /** Handle the key pressed event from the text field. */
    public void keyPressed(KeyEvent e) {
        displayInfo(e, "KEY PRESSED: ");
    }

    /** Handle the key released event from the text field. */
    public void keyReleased(KeyEvent e) {
        displayInfo(e, "KEY RELEASED: ");
    }

    /** Handle the button click. */
    public void actionPerformed(ActionEvent e) {
        //Clear the text components.
        displayArea.setText("");
        typingArea.setText("");

        //Return the focus to the typing area.
        typingArea.requestFocusInWindow();
    }

    /*
     * We have to jump through some hoops to avoid
     * trying to print non-printing characters 
     * such as Shift.  (Not only do they not print, 
     * but if you put them in a String, the characters
     * afterward won't show up in the text area.)
     */
    protected void displayInfo(KeyEvent e, String s){
        String keyString, modString, tmpString,
               actionString, locationString;

        //You should only rely on the key char if the event
        //is a key typed event.
        int id = e.getID();
        if (id == KeyEvent.KEY_TYPED) {
            char c = e.getKeyChar();
            keyString = "key character = '" + c + "'";
        } else {
            int keyCode = e.getKeyCode();
            keyString = "key code = " + keyCode
                        + " ("
                        + KeyEvent.getKeyText(keyCode)
                        + ")";
        }

        int modifiers = e.getModifiersEx();
        modString = "modifiers = " + modifiers;
        tmpString = KeyEvent.getModifiersExText(modifiers);
        if (tmpString.length() > 0) {
            modString += " (" + tmpString + ")";
        } else {
            modString += " (no modifiers)";
        }

        actionString = "action key? ";
        if (e.isActionKey()) {
            actionString += "YES";
        } else {
            actionString += "NO";
        }

        locationString = "key location: ";
        int location = e.getKeyLocation();
        if (location == KeyEvent.KEY_LOCATION_STANDARD) {
            locationString += "standard";
        } else if (location == KeyEvent.KEY_LOCATION_LEFT) {
            locationString += "left";
        } else if (location == KeyEvent.KEY_LOCATION_RIGHT) {
            locationString += "right";
        } else if (location == KeyEvent.KEY_LOCATION_NUMPAD) {
            locationString += "numpad";
        } else { // (location == KeyEvent.KEY_LOCATION_UNKNOWN)
            locationString += "unknown";
        }

        displayArea.append(s + newline
                           + "    " + keyString + newline
                           + "    " + modString + newline
                           + "    " + actionString + newline
                           + "    " + locationString + newline);
        displayArea.setCaretPosition(displayArea.getDocument().getLength());
    }

    /**
     * Create the GUI and show it.  For thread safety,
     * this method should be invoked from the
     * event-dispatching thread.
     */
    private static void createAndShowGUI() {
        //Make sure we have nice window decorations.
        JFrame.setDefaultLookAndFeelDecorated(true);

        //Create and set up the window.
        JFrame frame = new JFrame("KeyEventDemo");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        //Create and set up the content pane.
        JComponent newContentPane = new KeyEventDemo();
        newContentPane.setOpaque(true); //content panes must be opaque
        frame.setContentPane(newContentPane);

        //Display the window.
        frame.pack();
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        //Schedule a job for the event-dispatching thread:
        //creating and showing this application's GUI.
        javax.swing.SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                createAndShowGUI();
            }
        });
    }
}

package testers;

import static data.Defines.KEY_F1;
import static data.Defines.PU_STATIC;

import hu.HU;
import i.InputListener;

import java.awt.image.BufferedImage;
import java.awt.image.IndexColorModel;

import javax.swing.JFrame;

import rr.patch_t;

import m.IDoomMenu;
import m.Menu;
import m.DoomRandom;
import utils.C2JUtils;
import v.BufferedRenderer;
import v.SimpleRenderer;
import w.DoomBuffer;
import w.WadLoader;
import data.Defines;
import data.Defines.GameMission_t;
import data.Defines.GameMode_t;
import data.Defines.Language_t;
import doom.DoomContext;
import doom.DoomMain;
import doom.DoomStatus;
import doom.event_t;
import doom.player_t;
import doom.ticcmd_t;
import doom.wbstartstruct_t;

/** This is a very simple tester for Menu module  */

public class MenuTester {
    public static final int WIDTH=320;
    public static void main(String[] argv) {
        try {
    WadLoader W=new WadLoader();
    W.InitMultipleFiles(new String[] {"doom1.wad"});
    //W.AddFile("bitter.wad");
    System.out.println("Total lumps read: "+W.numlumps);

    patch_t help1=W.CachePatchName("TITLEPIC", PU_STATIC);
    
    DoomBuffer palette = W.CacheLumpName("PLAYPAL", PU_STATIC);
    byte[] pal=palette.getBuffer().array();

    IndexColorModel icm=new IndexColorModel(8, 256,pal, 0, false);
    Defines.SCREENWIDTH=320;
    Defines.SCREENHEIGHT=200;
    BufferedRenderer V=new BufferedRenderer(320,200,icm);
    V.Init();

    IndexColorModel[] icms=new IndexColorModel[palette.getBuffer().limit()/768];
    BufferedImage[] pals=new BufferedImage[icms.length];
    for (int i=0;i<icms.length;i++){
        icms[i]=new IndexColorModel(8, 256,pal, i*768, false);
            pals[i]=new BufferedImage(icms[i],V.screenbuffer[0].getRaster(), false, null);
           }
        
    DoomMain DM=new DoomMain();


    DM.W=W;
    DM.V=V;
    HU HU=new HU(DM);
    HU.Init();
    DM.HU=HU;
    DM.gameepisode=1;
    DM.gamemap=1;
    DM.gamemission=GameMission_t.doom;
    DM.gamemode=GameMode_t.shareware;
    DM.wminfo=new wbstartstruct_t();
    DM.usergame=true;
    C2JUtils.initArrayOfObjects(DM.players,player_t.class);
    
    DM.RND=new DoomRandom();
    DM.players[0].cmd=new ticcmd_t();
    DM.players[0].itemcount=1337;
    DM.players[0].killcount=1337;
    DM.players[0].secretcount=1337;
    
    DM.wminfo.plyr[0].in=true;
    DM.wminfo.plyr[0].sitems=1337;
    DM.wminfo.plyr[0].skills=1337;
    DM.wminfo.plyr[0].stime=28595;
    DM.wminfo.plyr[0].ssecret=1337;
    DM.playeringame[0]=true;
    DM.wminfo.last=6;
    DM.wminfo.epsd=0;
    DM.wminfo.maxitems=100;
    DM.wminfo.maxkills=100;
    DM.wminfo.maxsecret=100;
    DM.wminfo.partime=28595;
   
    IDoomMenu M=DM.M=new Menu(DM);
    DM.language=Language_t.english;
    M.Init();
    CrappyDisplay frame = new CrappyDisplay(pals);
    frame.setTitle("MochaDoom");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.pack();
    frame.setLocationRelativeTo(null);
    //frame.setUndecorated(true);
    frame.setVisible(true);
    frame.setBounds(frame.getX(), frame.getY(), WIDTH, 240);
    
    DM.menuactive=true;        
        for (int i=0;i<10000;i++){
            int a=DM.I.GetTime();
            while (a-DM.I.GetTime()==0){
                frame.setVisible(true);
                Thread.sleep(1);               
            }
            
           
            
            event_t ev=CrappyDisplay.nextEvent();
            //System.out.println(ev);
            if (ev!=null)
            ((Menu)M).Responder(ev);
            
          /*  if (i==40){
            	System.out.println("Pressing enter");
                M.Responder(new event_t(Defines.KEY_DOWNARROW));
            }

            if (i==60){
            	System.out.println("Pressing down");
                M.Responder(new event_t(Defines.KEY_DOWNARROW));
            }
            
            if (i==80){
            	System.out.println("Pressing escape");
                M.Responder(new event_t(Defines.KEY_ESCAPE));
            }
            if (i==100){
            	System.out.println("Pressing up");
                M.Responder(new event_t(Defines.KEY_UPARROW));
            }
            
            if (i==120){
            	System.out.println("Pressing up");
                M.Responder(new event_t(Defines.KEY_UPARROW));
            }
            
            if (i==140){
            	System.out.println("Pressing escape");
                M.Responder(new event_t(Defines.KEY_ESCAPE));
            }
            
            if (i==160){
            	System.out.println("Pressing up");
                M.Responder(new event_t(Defines.KEY_UPARROW));
            }
            
            if (i==160){
            	System.out.println("Pressing up");
                M.Responder(new event_t(Defines.KEY_UPARROW));
            }
            
            if (i==300 || i==500|i==550|i==600){
            	System.out.println("Pressing F1");
                M.Responder(new event_t(KEY_F1));
                System.out.println("pressed ");
            }
            
            if (i==400 || i==650){
            	System.out.println("Pressing escape");
                M.Responder(new event_t(Defines.KEY_ESCAPE));
            } */
            
            //V.takeScreenShot(0,( "menutic"+i),icm);
         
        V.DrawPatch(0,0,0,help1);
        M.Ticker();
        M.Drawer();
        DM.gametic++;
        frame.update(null);
        System.out.print(frame.processEvents());
        }
            } catch (Exception e){
                e.printStackTrace();
            }
    
    }
    
}

package testers;

import hu.HU;
import p.LevelLoader;
import rr.SimpleTextureManager;
import rr.TextureManager;
import s.DummySoundDriver;
import s.IDoomSound;
import utils.C2JUtils;
import w.WadLoader;
import defines.*;
import doom.DoomContext;
import doom.DoomStatus;
import doom.player_t;
import doom.wbstartstruct_t;

/** This is a very simple tester for the WadLoader and HU modules.
 *  We use the same exact methods used in the C source code, only
 *  with a more OO approach.
 * 
 * 
 */

public class LevelLoaderTester {

    public static void main(String[] argv) {
        try {
    WadLoader W=new WadLoader();
    W.InitMultipleFiles(new String[] {"C:\\DOOMS\\doom1.wad"});
    //W.AddFile("bitter.wad");
    System.out.println("Total lumps read: "+W.numlumps);
    System.out.println("NUm for E1M1: "+W.GetNumForName("E1M1"));
    DoomStatus DS = new DoomStatus();
    DS.gameepisode=1;
    DS.gamemap=1;
    DS.gamemission=GameMission_t.doom;
    DS.gamemode=GameMode_t.shareware;
    IDoomSound S=new DummySoundDriver();            
    DS.S=S;
    DS.W=W;
    LevelLoader LL=new LevelLoader(DS);
    DS.LL=LL;
    TextureManager TM=new SimpleTextureManager(DS);
    DS.TM=TM;
    LL.updateStatus(DS);
    TM.InitFlats();
    TM.InitTextures();
    
    //HU hu=new HU(DS);
    //hu.Init();
    
    LL.SetupLevel(1, 1, 0, skill_t.sk_hard);
    
    
    
        } catch (Exception e){
            e.printStackTrace();
        }
    }
    
}

package testers;

import rr.MultiPatchSynthesizer;
import rr.column_t;

public class TestPatchSynthesis {
    
    public static void main(String[] argv){
        final byte[] crap={1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16};
        final boolean[] shit={true,true,true,
                              false, false, false,
                              true, true,true,
                              false,true,false,
                              true,false,true,
                              true};
        
        column_t fuck=MultiPatchSynthesizer.getColumnStream(crap, shit, crap.length);
        
        System.out.println("FUCK" +fuck);
        
        
    }

}

package testers;

import m.DoomRandom;
import m.IRandom;

/** Another microbenchmark. According to this, casting "unsigned bytes" to chars is 
 * faster when performed with a logical function, rather than with arithmetic or
 * direct logical operations. Inlining perhaps? Furthermore, it has almost no penalty 
 * compared to not casting or to use built-in casting. So it's possible to use a
 * "toUnsigned" function of sorts, whenever you need to treat signed bytes as unsigned
 * without expanding them to chars in memory.
 *
 */

public class Unsigned {

    public static void main(String[] argv){
        int TESTS=100000000;
        IRandom r=new DoomRandom();
        
       byte[] values=new byte[TESTS];
       
       for (int i=0;i<TESTS;i++) {
    	   values[i]=(byte)r.P_Random();
       	}
           
       
       byte ub=(byte) 0xFF;
       char us=(char) ub;
       
       System.out.println((int)us);
       
       us= 0xFFFF;
       ub=(byte) us;
       System.out.println(ub);
       System.out.println((int)((char)ub));
       
       System.out.println((int)us);
       long a=System.nanoTime();
        for (int i=0;i<TESTS;i++) {
        us=(char) (0x00FF&values[i]);
    }
    long b=System.nanoTime();
        System.out.println("Time for "+TESTS+" byte to \"unsigned byte\" casts (with logical ops)"+((b-a)/1e09));

        a=System.nanoTime();
        for (int i=0;i<TESTS;i++) {
        us=unsigned(values[i]);
    }
    b=System.nanoTime();
        System.out.println("Time for "+TESTS+" byte to \"unsigned byte\" casts (with num. function) "+((b-a)/1e09));

        a=System.nanoTime();
        for (int i=0;i<TESTS;i++) {
        us=unsigned2(values[i]);
        }
    b=System.nanoTime();
        System.out.println("Time for "+TESTS+" byte to \"unsigned byte\" casts (with log. function)"+((b-a)/1e09));
        
        a=System.nanoTime();
        for (int i=0;i<TESTS;i++) {
        ub=values[i];
        us= (char) ub;
    }
    b=System.nanoTime();
        System.out.println("Time for "+TESTS+" byte to \"unsigned byte\" casts (no casting)"+((b-a)/1e09));
        
}
    public static final char unsigned(byte b){
        return (char) ((b>0)?b:(-b+128));
    }
    
    public static char unsigned2(byte b){
        return (char) (0x00FF&b);
    }
    }
    
package testers;

import m.DoomRandom;
import m.IRandom;
import s.ClassicDoomSoundDriver;
import s.ClipSFXModule;
import s.SpeakerDoomSoundDriver;
import data.sounds.sfxenum_t;
import doom.DoomStatus;
import w.WadLoader;

public class TestClipSound {
	public static void main(String[] argv) throws Exception{

	DoomStatus DS=new DoomStatus();
	
	
	WadLoader W=new WadLoader();
	IRandom RND=new DoomRandom();
	
	DS.W=W;
	DS.RND=RND;
	W.InitMultipleFiles(new String[]{"doom1.wad"});
	
	ClipSFXModule sound=new ClipSFXModule(DS,4);
	
	sound.InitSound();
	sound.SetChannels(4);
	
	Thread.sleep(1000);
	//sound.StartSound(1, 127, 127, 127, 0);
	for (int i=0;i<1000;i++){
	    
	    Thread.sleep(1000/35);

	    if (i%70==0) sound.StartSound(sfxenum_t.sfx_plpain.ordinal(), 127, 127, 127, 0);
	    if (i%90==0) sound.StartSound(sfxenum_t.sfx_barexp.ordinal(), 127, 0, 127, 0);
	    if (i%35==0) sound.StartSound(sfxenum_t.sfx_plpain.ordinal(), 127, 255, 127, 0);
	    if (i%35==0) sound.StartSound(sfxenum_t.sfx_oof.ordinal(), 127, 192, 127, 0);
	    sound.UpdateSound();
	    
	    sound.SubmitSound();
	    
	       System.out.println(DS.gametic++);
	}
	sound.ShutdownSound();
	}
	
}

package testers;

import p.Actions;
import p.mobj_t;
import pooling.AudioChunkPool;
import pooling.MobjPool;
import s.AudioChunk;

public class TestMobjPooling {
    public static final int TESTS=1000000;
    
    public static void main(String[] argv){
    
    mobj_t[] chunks=new mobj_t[TESTS];
    MobjPool chunkpool=new MobjPool(new Actions());
    
    long a=System.nanoTime();
    
    for (int i=0;i<TESTS;i++){
        chunks[i]=new mobj_t();
    }

    for (int i=0;i<TESTS;i++){
        chunks[i]=new mobj_t();
    }
    
    for (int i=0;i<TESTS;i++){
        chunks[i]=new mobj_t();
    }
    
    for (int i=0;i<TESTS;i++){
        chunks[i]=new mobj_t();
    }
    long b=System.nanoTime();
    
    System.out.println("Time: "+(float)(b-a)/1000000000f);
    
    a=System.nanoTime();
    
    for (int i=0;i<TESTS;i++){
        chunks[i]=chunkpool.checkOut();
    }

    for (int i=0;i<TESTS;i++){
        chunkpool.checkIn(chunks[i]);
        chunks[i]=chunkpool.checkOut();
    }
    
    for (int i=0;i<TESTS;i++){
        chunkpool.checkIn(chunks[i]);
        chunks[i]=chunkpool.checkOut();
    }
    
    for (int i=0;i<TESTS;i++){
        chunkpool.checkIn(chunks[i]);
        chunks[i]=chunkpool.checkOut();
    }
    b=System.nanoTime();
    
    System.out.println("Time: "+(float)(b-a)/1000000000f);
    
    
    }
    

}

package testers;

import java.awt.image.BufferedImage;
import java.awt.image.DataBufferByte;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

import javax.imageio.ImageIO;

public class PaletteShower12 {

    public static final int BLOCKSIZE=16;
    public static final int SHIFT=4;
    
    public static void main(String[] argv) {
        try {
    BufferedInputStream bis;
    bis=new BufferedInputStream(new FileInputStream(argv[0]));
    
    int size=bis.available();
    
    int blocks=size/512;
    
    
    byte[] palbuf=new byte[512];
    ByteBuffer bb= ByteBuffer.wrap(palbuf);
    bb.order(ByteOrder.LITTLE_ENDIAN);
    bb.mark();
    for (int i=0;i<blocks;i++){
        bis.read(palbuf);
        
        BufferedImage bim=new BufferedImage(16*BLOCKSIZE,16*BLOCKSIZE,BufferedImage.TYPE_INT_ARGB);
        
            for (int j=0;j<256;j++){
                short shcol=bb.getShort();
                int r=(0xF000&shcol)>>12;
                int g=(0xF00&shcol)>>8;
                int b=(0xF0&shcol)>>4;
                System.out.printf("%x %d %d %d\n",shcol,r,g,b);
                int colo=  0xFF000000|(r<<(16+SHIFT))|
                            (g<<(8+SHIFT))|
                            (b<<SHIFT);
            
            for (int x=0;x<BLOCKSIZE;x++){
                for (int y=0;y<BLOCKSIZE;y++){
                    bim.setRGB(BLOCKSIZE*(j%16)+x, BLOCKSIZE*(j/16)+y, colo);
                }
            }
            
        }
        
        FileOutputStream f=new FileOutputStream(String.format("%s-%d.PNG",argv[0],i));
        
        ImageIO.write(bim,"PNG",f);
        bb.reset();
        }
    
        bis.close();
    
        } catch (Exception e){
            e.printStackTrace();
        }
    }
    
}

package defines;

/** Mission packs - might be useful for TC stuff?
 * 
 */
public enum GameMission_t 
{
  doom,     // DOOM 1
  doom2,    // DOOM 2
  pack_tnt, // TNT mission pack
  pack_plut,    // Plutonia pack
  none
}

package defines;

/** Move clipping aid for LineDefs. */

public enum slopetype_t
{
ST_HORIZONTAL,
ST_VERTICAL,
ST_POSITIVE,
ST_NEGATIVE

} 

package defines;


public enum skill_t
{
    sk_baby,
    sk_easy,
    sk_medium,
    sk_hard,
    sk_nightmare
}

package defines;

/** The current state of the game: whether we are
    playing, gazing at the intermission screen,    
    the game final animation, or a demo. */ 
public enum gamestate_t
{
 GS_LEVEL,
 GS_INTERMISSION,
 GS_FINALE,
 GS_DEMOSCREEN,
 GS_MINUS_ONE // hack used for the "-1" state
}

package defines;

/**
 *  Key cards.
 */
public enum card_t
{
 it_bluecard,
 it_yellowcard,
 it_redcard,
 it_blueskull,
 it_yellowskull,
 it_redskull,
 
 NUMCARDS
 
}

package defines;

//Ammunition types defined.
public enum ammotype_t
{
    am_clip,    // Pistol / chaingun ammo.
    am_shell,   // Shotgun / double barreled shotgun.
    am_cell,    // Plasma rifle, BFG.
    am_misl,    // Missile launcher.
    NUMAMMO,
    am_noammo   // Unlimited for chainsaw / fist.   

}
package defines;

/** Identify language to use, software localization. */
public enum Language_t {
    english,
    french,
    german,
    unknown
    }


package defines;


/** Game mode handling - identify IWAD version
  to handle IWAD dependend animations etc.
  */
public enum GameMode_t 
{
shareware,    // DOOM 1 shareware, E1, M9
registered,   // DOOM 1 registered, E3, M27
commercial,   // DOOM 2 retail, E1 M34
// DOOM 2 german edition not handled
retail,   // DOOM 1 retail, E4, M36
pack_tnt, // TNT mission pack
pack_plut,    // Plutonia pack
pack_xbla,    // XBLA Doom. How you got hold of it, I don't care :-p
indetermined  // Well, no IWAD found.  
} ;

package defines;

public enum statenum_t {
    S_NULL,
    S_LIGHTDONE,
    S_PUNCH,
    S_PUNCHDOWN,
    S_PUNCHUP,
    S_PUNCH1,
    S_PUNCH2,
    S_PUNCH3,
    S_PUNCH4,
    S_PUNCH5,
    S_PISTOL,
    S_PISTOLDOWN,
    S_PISTOLUP,
    S_PISTOL1,
    S_PISTOL2,
    S_PISTOL3,
    S_PISTOL4,
    S_PISTOLFLASH,
    S_SGUN,
    S_SGUNDOWN,
    S_SGUNUP,
    S_SGUN1,
    S_SGUN2,
    S_SGUN3,
    S_SGUN4,
    S_SGUN5,
    S_SGUN6,
    S_SGUN7,
    S_SGUN8,
    S_SGUN9,
    S_SGUNFLASH1,
    S_SGUNFLASH2,
    S_DSGUN,
    S_DSGUNDOWN,
    S_DSGUNUP,
    S_DSGUN1,
    S_DSGUN2,
    S_DSGUN3,
    S_DSGUN4,
    S_DSGUN5,
    S_DSGUN6,
    S_DSGUN7,
    S_DSGUN8,
    S_DSGUN9,
    S_DSGUN10,
    S_DSNR1,
    S_DSNR2,
    S_DSGUNFLASH1,
    S_DSGUNFLASH2,
    S_CHAIN,
    S_CHAINDOWN,
    S_CHAINUP,
    S_CHAIN1,
    S_CHAIN2,
    S_CHAIN3,
    S_CHAINFLASH1,
    S_CHAINFLASH2,
    S_MISSILE,
    S_MISSILEDOWN,
    S_MISSILEUP,
    S_MISSILE1,
    S_MISSILE2,
    S_MISSILE3,
    S_MISSILEFLASH1,
    S_MISSILEFLASH2,
    S_MISSILEFLASH3,
    S_MISSILEFLASH4,
    S_SAW,
    S_SAWB,
    S_SAWDOWN,
    S_SAWUP,
    S_SAW1,
    S_SAW2,
    S_SAW3,
    S_PLASMA,
    S_PLASMADOWN,
    S_PLASMAUP,
    S_PLASMA1,
    S_PLASMA2,
    S_PLASMAFLASH1,
    S_PLASMAFLASH2,
    S_BFG,
    S_BFGDOWN,
    S_BFGUP,
    S_BFG1,
    S_BFG2,
    S_BFG3,
    S_BFG4,
    S_BFGFLASH1,
    S_BFGFLASH2,
    S_BLOOD1,
    S_BLOOD2,
    S_BLOOD3,
    S_PUFF1,
    S_PUFF2,
    S_PUFF3,
    S_PUFF4,
    S_TBALL1,
    S_TBALL2,
    S_TBALLX1,
    S_TBALLX2,
    S_TBALLX3,
    S_RBALL1,
    S_RBALL2,
    S_RBALLX1,
    S_RBALLX2,
    S_RBALLX3,
    S_PLASBALL,
    S_PLASBALL2,
    S_PLASEXP,
    S_PLASEXP2,
    S_PLASEXP3,
    S_PLASEXP4,
    S_PLASEXP5,
    S_ROCKET,
    S_BFGSHOT,
    S_BFGSHOT2,
    S_BFGLAND,
    S_BFGLAND2,
    S_BFGLAND3,
    S_BFGLAND4,
    S_BFGLAND5,
    S_BFGLAND6,
    S_BFGEXP,
    S_BFGEXP2,
    S_BFGEXP3,
    S_BFGEXP4,
    S_EXPLODE1,
    S_EXPLODE2,
    S_EXPLODE3,
    S_TFOG,
    S_TFOG01,
    S_TFOG02,
    S_TFOG2,
    S_TFOG3,
    S_TFOG4,
    S_TFOG5,
    S_TFOG6,
    S_TFOG7,
    S_TFOG8,
    S_TFOG9,
    S_TFOG10,
    S_IFOG,
    S_IFOG01,
    S_IFOG02,
    S_IFOG2,
    S_IFOG3,
    S_IFOG4,
    S_IFOG5,
    S_PLAY,
    S_PLAY_RUN1,
    S_PLAY_RUN2,
    S_PLAY_RUN3,
    S_PLAY_RUN4,
    S_PLAY_ATK1,
    S_PLAY_ATK2,
    S_PLAY_PAIN,
    S_PLAY_PAIN2,
    S_PLAY_DIE1,
    S_PLAY_DIE2,
    S_PLAY_DIE3,
    S_PLAY_DIE4,
    S_PLAY_DIE5,
    S_PLAY_DIE6,
    S_PLAY_DIE7,
    S_PLAY_XDIE1,
    S_PLAY_XDIE2,
    S_PLAY_XDIE3,
    S_PLAY_XDIE4,
    S_PLAY_XDIE5,
    S_PLAY_XDIE6,
    S_PLAY_XDIE7,
    S_PLAY_XDIE8,
    S_PLAY_XDIE9,
    S_POSS_STND,
    S_POSS_STND2,
    S_POSS_RUN1,
    S_POSS_RUN2,
    S_POSS_RUN3,
    S_POSS_RUN4,
    S_POSS_RUN5,
    S_POSS_RUN6,
    S_POSS_RUN7,
    S_POSS_RUN8,
    S_POSS_ATK1,
    S_POSS_ATK2,
    S_POSS_ATK3,
    S_POSS_PAIN,
    S_POSS_PAIN2,
    S_POSS_DIE1,
    S_POSS_DIE2,
    S_POSS_DIE3,
    S_POSS_DIE4,
    S_POSS_DIE5,
    S_POSS_XDIE1,
    S_POSS_XDIE2,
    S_POSS_XDIE3,
    S_POSS_XDIE4,
    S_POSS_XDIE5,
    S_POSS_XDIE6,
    S_POSS_XDIE7,
    S_POSS_XDIE8,
    S_POSS_XDIE9,
    S_POSS_RAISE1,
    S_POSS_RAISE2,
    S_POSS_RAISE3,
    S_POSS_RAISE4,
    S_SPOS_STND,
    S_SPOS_STND2,
    S_SPOS_RUN1,
    S_SPOS_RUN2,
    S_SPOS_RUN3,
    S_SPOS_RUN4,
    S_SPOS_RUN5,
    S_SPOS_RUN6,
    S_SPOS_RUN7,
    S_SPOS_RUN8,
    S_SPOS_ATK1,
    S_SPOS_ATK2,
    S_SPOS_ATK3,
    S_SPOS_PAIN,
    S_SPOS_PAIN2,
    S_SPOS_DIE1,
    S_SPOS_DIE2,
    S_SPOS_DIE3,
    S_SPOS_DIE4,
    S_SPOS_DIE5,
    S_SPOS_XDIE1,
    S_SPOS_XDIE2,
    S_SPOS_XDIE3,
    S_SPOS_XDIE4,
    S_SPOS_XDIE5,
    S_SPOS_XDIE6,
    S_SPOS_XDIE7,
    S_SPOS_XDIE8,
    S_SPOS_XDIE9,
    S_SPOS_RAISE1,
    S_SPOS_RAISE2,
    S_SPOS_RAISE3,
    S_SPOS_RAISE4,
    S_SPOS_RAISE5,
    S_VILE_STND,
    S_VILE_STND2,
    S_VILE_RUN1,
    S_VILE_RUN2,
    S_VILE_RUN3,
    S_VILE_RUN4,
    S_VILE_RUN5,
    S_VILE_RUN6,
    S_VILE_RUN7,
    S_VILE_RUN8,
    S_VILE_RUN9,
    S_VILE_RUN10,
    S_VILE_RUN11,
    S_VILE_RUN12,
    S_VILE_ATK1,
    S_VILE_ATK2,
    S_VILE_ATK3,
    S_VILE_ATK4,
    S_VILE_ATK5,
    S_VILE_ATK6,
    S_VILE_ATK7,
    S_VILE_ATK8,
    S_VILE_ATK9,
    S_VILE_ATK10,
    S_VILE_ATK11,
    S_VILE_HEAL1,
    S_VILE_HEAL2,
    S_VILE_HEAL3,
    S_VILE_PAIN,
    S_VILE_PAIN2,
    S_VILE_DIE1,
    S_VILE_DIE2,
    S_VILE_DIE3,
    S_VILE_DIE4,
    S_VILE_DIE5,
    S_VILE_DIE6,
    S_VILE_DIE7,
    S_VILE_DIE8,
    S_VILE_DIE9,
    S_VILE_DIE10,
    S_FIRE1,
    S_FIRE2,
    S_FIRE3,
    S_FIRE4,
    S_FIRE5,
    S_FIRE6,
    S_FIRE7,
    S_FIRE8,
    S_FIRE9,
    S_FIRE10,
    S_FIRE11,
    S_FIRE12,
    S_FIRE13,
    S_FIRE14,
    S_FIRE15,
    S_FIRE16,
    S_FIRE17,
    S_FIRE18,
    S_FIRE19,
    S_FIRE20,
    S_FIRE21,
    S_FIRE22,
    S_FIRE23,
    S_FIRE24,
    S_FIRE25,
    S_FIRE26,
    S_FIRE27,
    S_FIRE28,
    S_FIRE29,
    S_FIRE30,
    S_SMOKE1,
    S_SMOKE2,
    S_SMOKE3,
    S_SMOKE4,
    S_SMOKE5,
    S_TRACER,
    S_TRACER2,
    S_TRACEEXP1,
    S_TRACEEXP2,
    S_TRACEEXP3,
    S_SKEL_STND,
    S_SKEL_STND2,
    S_SKEL_RUN1,
    S_SKEL_RUN2,
    S_SKEL_RUN3,
    S_SKEL_RUN4,
    S_SKEL_RUN5,
    S_SKEL_RUN6,
    S_SKEL_RUN7,
    S_SKEL_RUN8,
    S_SKEL_RUN9,
    S_SKEL_RUN10,
    S_SKEL_RUN11,
    S_SKEL_RUN12,
    S_SKEL_FIST1,
    S_SKEL_FIST2,
    S_SKEL_FIST3,
    S_SKEL_FIST4,
    S_SKEL_MISS1,
    S_SKEL_MISS2,
    S_SKEL_MISS3,
    S_SKEL_MISS4,
    S_SKEL_PAIN,
    S_SKEL_PAIN2,
    S_SKEL_DIE1,
    S_SKEL_DIE2,
    S_SKEL_DIE3,
    S_SKEL_DIE4,
    S_SKEL_DIE5,
    S_SKEL_DIE6,
    S_SKEL_RAISE1,
    S_SKEL_RAISE2,
    S_SKEL_RAISE3,
    S_SKEL_RAISE4,
    S_SKEL_RAISE5,
    S_SKEL_RAISE6,
    S_FATSHOT1,
    S_FATSHOT2,
    S_FATSHOTX1,
    S_FATSHOTX2,
    S_FATSHOTX3,
    S_FATT_STND,
    S_FATT_STND2,
    S_FATT_RUN1,
    S_FATT_RUN2,
    S_FATT_RUN3,
    S_FATT_RUN4,
    S_FATT_RUN5,
    S_FATT_RUN6,
    S_FATT_RUN7,
    S_FATT_RUN8,
    S_FATT_RUN9,
    S_FATT_RUN10,
    S_FATT_RUN11,
    S_FATT_RUN12,
    S_FATT_ATK1,
    S_FATT_ATK2,
    S_FATT_ATK3,
    S_FATT_ATK4,
    S_FATT_ATK5,
    S_FATT_ATK6,
    S_FATT_ATK7,
    S_FATT_ATK8,
    S_FATT_ATK9,
    S_FATT_ATK10,
    S_FATT_PAIN,
    S_FATT_PAIN2,
    S_FATT_DIE1,
    S_FATT_DIE2,
    S_FATT_DIE3,
    S_FATT_DIE4,
    S_FATT_DIE5,
    S_FATT_DIE6,
    S_FATT_DIE7,
    S_FATT_DIE8,
    S_FATT_DIE9,
    S_FATT_DIE10,
    S_FATT_RAISE1,
    S_FATT_RAISE2,
    S_FATT_RAISE3,
    S_FATT_RAISE4,
    S_FATT_RAISE5,
    S_FATT_RAISE6,
    S_FATT_RAISE7,
    S_FATT_RAISE8,
    S_CPOS_STND,
    S_CPOS_STND2,
    S_CPOS_RUN1,
    S_CPOS_RUN2,
    S_CPOS_RUN3,
    S_CPOS_RUN4,
    S_CPOS_RUN5,
    S_CPOS_RUN6,
    S_CPOS_RUN7,
    S_CPOS_RUN8,
    S_CPOS_ATK1,
    S_CPOS_ATK2,
    S_CPOS_ATK3,
    S_CPOS_ATK4,
    S_CPOS_PAIN,
    S_CPOS_PAIN2,
    S_CPOS_DIE1,
    S_CPOS_DIE2,
    S_CPOS_DIE3,
    S_CPOS_DIE4,
    S_CPOS_DIE5,
    S_CPOS_DIE6,
    S_CPOS_DIE7,
    S_CPOS_XDIE1,
    S_CPOS_XDIE2,
    S_CPOS_XDIE3,
    S_CPOS_XDIE4,
    S_CPOS_XDIE5,
    S_CPOS_XDIE6,
    S_CPOS_RAISE1,
    S_CPOS_RAISE2,
    S_CPOS_RAISE3,
    S_CPOS_RAISE4,
    S_CPOS_RAISE5,
    S_CPOS_RAISE6,
    S_CPOS_RAISE7,
    S_TROO_STND,
    S_TROO_STND2,
    S_TROO_RUN1,
    S_TROO_RUN2,
    S_TROO_RUN3,
    S_TROO_RUN4,
    S_TROO_RUN5,
    S_TROO_RUN6,
    S_TROO_RUN7,
    S_TROO_RUN8,
    S_TROO_ATK1,
    S_TROO_ATK2,
    S_TROO_ATK3,
    S_TROO_PAIN,
    S_TROO_PAIN2,
    S_TROO_DIE1,
    S_TROO_DIE2,
    S_TROO_DIE3,
    S_TROO_DIE4,
    S_TROO_DIE5,
    S_TROO_XDIE1,
    S_TROO_XDIE2,
    S_TROO_XDIE3,
    S_TROO_XDIE4,
    S_TROO_XDIE5,
    S_TROO_XDIE6,
    S_TROO_XDIE7,
    S_TROO_XDIE8,
    S_TROO_RAISE1,
    S_TROO_RAISE2,
    S_TROO_RAISE3,
    S_TROO_RAISE4,
    S_TROO_RAISE5,
    S_SARG_STND,
    S_SARG_STND2,
    S_SARG_RUN1,
    S_SARG_RUN2,
    S_SARG_RUN3,
    S_SARG_RUN4,
    S_SARG_RUN5,
    S_SARG_RUN6,
    S_SARG_RUN7,
    S_SARG_RUN8,
    S_SARG_ATK1,
    S_SARG_ATK2,
    S_SARG_ATK3,
    S_SARG_PAIN,
    S_SARG_PAIN2,
    S_SARG_DIE1,
    S_SARG_DIE2,
    S_SARG_DIE3,
    S_SARG_DIE4,
    S_SARG_DIE5,
    S_SARG_DIE6,
    S_SARG_RAISE1,
    S_SARG_RAISE2,
    S_SARG_RAISE3,
    S_SARG_RAISE4,
    S_SARG_RAISE5,
    S_SARG_RAISE6,
    S_HEAD_STND,
    S_HEAD_RUN1,
    S_HEAD_ATK1,
    S_HEAD_ATK2,
    S_HEAD_ATK3,
    S_HEAD_PAIN,
    S_HEAD_PAIN2,
    S_HEAD_PAIN3,
    S_HEAD_DIE1,
    S_HEAD_DIE2,
    S_HEAD_DIE3,
    S_HEAD_DIE4,
    S_HEAD_DIE5,
    S_HEAD_DIE6,
    S_HEAD_RAISE1,
    S_HEAD_RAISE2,
    S_HEAD_RAISE3,
    S_HEAD_RAISE4,
    S_HEAD_RAISE5,
    S_HEAD_RAISE6,
    S_BRBALL1,
    S_BRBALL2,
    S_BRBALLX1,
    S_BRBALLX2,
    S_BRBALLX3,
    S_BOSS_STND,
    S_BOSS_STND2,
    S_BOSS_RUN1,
    S_BOSS_RUN2,
    S_BOSS_RUN3,
    S_BOSS_RUN4,
    S_BOSS_RUN5,
    S_BOSS_RUN6,
    S_BOSS_RUN7,
    S_BOSS_RUN8,
    S_BOSS_ATK1,
    S_BOSS_ATK2,
    S_BOSS_ATK3,
    S_BOSS_PAIN,
    S_BOSS_PAIN2,
    S_BOSS_DIE1,
    S_BOSS_DIE2,
    S_BOSS_DIE3,
    S_BOSS_DIE4,
    S_BOSS_DIE5,
    S_BOSS_DIE6,
    S_BOSS_DIE7,
    S_BOSS_RAISE1,
    S_BOSS_RAISE2,
    S_BOSS_RAISE3,
    S_BOSS_RAISE4,
    S_BOSS_RAISE5,
    S_BOSS_RAISE6,
    S_BOSS_RAISE7,
    S_BOS2_STND,
    S_BOS2_STND2,
    S_BOS2_RUN1,
    S_BOS2_RUN2,
    S_BOS2_RUN3,
    S_BOS2_RUN4,
    S_BOS2_RUN5,
    S_BOS2_RUN6,
    S_BOS2_RUN7,
    S_BOS2_RUN8,
    S_BOS2_ATK1,
    S_BOS2_ATK2,
    S_BOS2_ATK3,
    S_BOS2_PAIN,
    S_BOS2_PAIN2,
    S_BOS2_DIE1,
    S_BOS2_DIE2,
    S_BOS2_DIE3,
    S_BOS2_DIE4,
    S_BOS2_DIE5,
    S_BOS2_DIE6,
    S_BOS2_DIE7,
    S_BOS2_RAISE1,
    S_BOS2_RAISE2,
    S_BOS2_RAISE3,
    S_BOS2_RAISE4,
    S_BOS2_RAISE5,
    S_BOS2_RAISE6,
    S_BOS2_RAISE7,
    S_SKULL_STND,
    S_SKULL_STND2,
    S_SKULL_RUN1,
    S_SKULL_RUN2,
    S_SKULL_ATK1,
    S_SKULL_ATK2,
    S_SKULL_ATK3,
    S_SKULL_ATK4,
    S_SKULL_PAIN,
    S_SKULL_PAIN2,
    S_SKULL_DIE1,
    S_SKULL_DIE2,
    S_SKULL_DIE3,
    S_SKULL_DIE4,
    S_SKULL_DIE5,
    S_SKULL_DIE6,
    S_SPID_STND,
    S_SPID_STND2,
    S_SPID_RUN1,
    S_SPID_RUN2,
    S_SPID_RUN3,
    S_SPID_RUN4,
    S_SPID_RUN5,
    S_SPID_RUN6,
    S_SPID_RUN7,
    S_SPID_RUN8,
    S_SPID_RUN9,
    S_SPID_RUN10,
    S_SPID_RUN11,
    S_SPID_RUN12,
    S_SPID_ATK1,
    S_SPID_ATK2,
    S_SPID_ATK3,
    S_SPID_ATK4,
    S_SPID_PAIN,
    S_SPID_PAIN2,
    S_SPID_DIE1,
    S_SPID_DIE2,
    S_SPID_DIE3,
    S_SPID_DIE4,
    S_SPID_DIE5,
    S_SPID_DIE6,
    S_SPID_DIE7,
    S_SPID_DIE8,
    S_SPID_DIE9,
    S_SPID_DIE10,
    S_SPID_DIE11,
    S_BSPI_STND,
    S_BSPI_STND2,
    S_BSPI_SIGHT,
    S_BSPI_RUN1,
    S_BSPI_RUN2,
    S_BSPI_RUN3,
    S_BSPI_RUN4,
    S_BSPI_RUN5,
    S_BSPI_RUN6,
    S_BSPI_RUN7,
    S_BSPI_RUN8,
    S_BSPI_RUN9,
    S_BSPI_RUN10,
    S_BSPI_RUN11,
    S_BSPI_RUN12,
    S_BSPI_ATK1,
    S_BSPI_ATK2,
    S_BSPI_ATK3,
    S_BSPI_ATK4,
    S_BSPI_PAIN,
    S_BSPI_PAIN2,
    S_BSPI_DIE1,
    S_BSPI_DIE2,
    S_BSPI_DIE3,
    S_BSPI_DIE4,
    S_BSPI_DIE5,
    S_BSPI_DIE6,
    S_BSPI_DIE7,
    S_BSPI_RAISE1,
    S_BSPI_RAISE2,
    S_BSPI_RAISE3,
    S_BSPI_RAISE4,
    S_BSPI_RAISE5,
    S_BSPI_RAISE6,
    S_BSPI_RAISE7,
    S_ARACH_PLAZ,
    S_ARACH_PLAZ2,
    S_ARACH_PLEX,
    S_ARACH_PLEX2,
    S_ARACH_PLEX3,
    S_ARACH_PLEX4,
    S_ARACH_PLEX5,
    S_CYBER_STND,
    S_CYBER_STND2,
    S_CYBER_RUN1,
    S_CYBER_RUN2,
    S_CYBER_RUN3,
    S_CYBER_RUN4,
    S_CYBER_RUN5,
    S_CYBER_RUN6,
    S_CYBER_RUN7,
    S_CYBER_RUN8,
    S_CYBER_ATK1,
    S_CYBER_ATK2,
    S_CYBER_ATK3,
    S_CYBER_ATK4,
    S_CYBER_ATK5,
    S_CYBER_ATK6,
    S_CYBER_PAIN,
    S_CYBER_DIE1,
    S_CYBER_DIE2,
    S_CYBER_DIE3,
    S_CYBER_DIE4,
    S_CYBER_DIE5,
    S_CYBER_DIE6,
    S_CYBER_DIE7,
    S_CYBER_DIE8,
    S_CYBER_DIE9,
    S_CYBER_DIE10,
    S_PAIN_STND,
    S_PAIN_RUN1,
    S_PAIN_RUN2,
    S_PAIN_RUN3,
    S_PAIN_RUN4,
    S_PAIN_RUN5,
    S_PAIN_RUN6,
    S_PAIN_ATK1,
    S_PAIN_ATK2,
    S_PAIN_ATK3,
    S_PAIN_ATK4,
    S_PAIN_PAIN,
    S_PAIN_PAIN2,
    S_PAIN_DIE1,
    S_PAIN_DIE2,
    S_PAIN_DIE3,
    S_PAIN_DIE4,
    S_PAIN_DIE5,
    S_PAIN_DIE6,
    S_PAIN_RAISE1,
    S_PAIN_RAISE2,
    S_PAIN_RAISE3,
    S_PAIN_RAISE4,
    S_PAIN_RAISE5,
    S_PAIN_RAISE6,
    S_SSWV_STND,
    S_SSWV_STND2,
    S_SSWV_RUN1,
    S_SSWV_RUN2,
    S_SSWV_RUN3,
    S_SSWV_RUN4,
    S_SSWV_RUN5,
    S_SSWV_RUN6,
    S_SSWV_RUN7,
    S_SSWV_RUN8,
    S_SSWV_ATK1,
    S_SSWV_ATK2,
    S_SSWV_ATK3,
    S_SSWV_ATK4,
    S_SSWV_ATK5,
    S_SSWV_ATK6,
    S_SSWV_PAIN,
    S_SSWV_PAIN2,
    S_SSWV_DIE1,
    S_SSWV_DIE2,
    S_SSWV_DIE3,
    S_SSWV_DIE4,
    S_SSWV_DIE5,
    S_SSWV_XDIE1,
    S_SSWV_XDIE2,
    S_SSWV_XDIE3,
    S_SSWV_XDIE4,
    S_SSWV_XDIE5,
    S_SSWV_XDIE6,
    S_SSWV_XDIE7,
    S_SSWV_XDIE8,
    S_SSWV_XDIE9,
    S_SSWV_RAISE1,
    S_SSWV_RAISE2,
    S_SSWV_RAISE3,
    S_SSWV_RAISE4,
    S_SSWV_RAISE5,
    S_KEENSTND,
    S_COMMKEEN,
    S_COMMKEEN2,
    S_COMMKEEN3,
    S_COMMKEEN4,
    S_COMMKEEN5,
    S_COMMKEEN6,
    S_COMMKEEN7,
    S_COMMKEEN8,
    S_COMMKEEN9,
    S_COMMKEEN10,
    S_COMMKEEN11,
    S_COMMKEEN12,
    S_KEENPAIN,
    S_KEENPAIN2,
    S_BRAIN,
    S_BRAIN_PAIN,
    S_BRAIN_DIE1,
    S_BRAIN_DIE2,
    S_BRAIN_DIE3,
    S_BRAIN_DIE4,
    S_BRAINEYE,
    S_BRAINEYESEE,
    S_BRAINEYE1,
    S_SPAWN1,
    S_SPAWN2,
    S_SPAWN3,
    S_SPAWN4,
    S_SPAWNFIRE1,
    S_SPAWNFIRE2,
    S_SPAWNFIRE3,
    S_SPAWNFIRE4,
    S_SPAWNFIRE5,
    S_SPAWNFIRE6,
    S_SPAWNFIRE7,
    S_SPAWNFIRE8,
    S_BRAINEXPLODE1,
    S_BRAINEXPLODE2,
    S_BRAINEXPLODE3,
    S_ARM1,
    S_ARM1A,
    S_ARM2,
    S_ARM2A,
    S_BAR1,
    S_BAR2,
    S_BEXP,
    S_BEXP2,
    S_BEXP3,
    S_BEXP4,
    S_BEXP5,
    S_BBAR1,
    S_BBAR2,
    S_BBAR3,
    S_BON1,
    S_BON1A,
    S_BON1B,
    S_BON1C,
    S_BON1D,
    S_BON1E,
    S_BON2,
    S_BON2A,
    S_BON2B,
    S_BON2C,
    S_BON2D,
    S_BON2E,
    S_BKEY,
    S_BKEY2,
    S_RKEY,
    S_RKEY2,
    S_YKEY,
    S_YKEY2,
    S_BSKULL,
    S_BSKULL2,
    S_RSKULL,
    S_RSKULL2,
    S_YSKULL,
    S_YSKULL2,
    S_STIM,
    S_MEDI,
    S_SOUL,
    S_SOUL2,
    S_SOUL3,
    S_SOUL4,
    S_SOUL5,
    S_SOUL6,
    S_PINV,
    S_PINV2,
    S_PINV3,
    S_PINV4,
    S_PSTR,
    S_PINS,
    S_PINS2,
    S_PINS3,
    S_PINS4,
    S_MEGA,
    S_MEGA2,
    S_MEGA3,
    S_MEGA4,
    S_SUIT,
    S_PMAP,
    S_PMAP2,
    S_PMAP3,
    S_PMAP4,
    S_PMAP5,
    S_PMAP6,
    S_PVIS,
    S_PVIS2,
    S_CLIP,
    S_AMMO,
    S_ROCK,
    S_BROK,
    S_CELL,
    S_CELP,
    S_SHEL,
    S_SBOX,
    S_BPAK,
    S_BFUG,
    S_MGUN,
    S_CSAW,
    S_LAUN,
    S_PLAS,
    S_SHOT,
    S_SHOT2,
    S_COLU,
    S_STALAG,
    S_BLOODYTWITCH,
    S_BLOODYTWITCH2,
    S_BLOODYTWITCH3,
    S_BLOODYTWITCH4,
    S_DEADTORSO,
    S_DEADBOTTOM,
    S_HEADSONSTICK,
    S_GIBS,
    S_HEADONASTICK,
    S_HEADCANDLES,
    S_HEADCANDLES2,
    S_DEADSTICK,
    S_LIVESTICK,
    S_LIVESTICK2,
    S_MEAT2,
    S_MEAT3,
    S_MEAT4,
    S_MEAT5,
    S_STALAGTITE,
    S_TALLGRNCOL,
    S_SHRTGRNCOL,
    S_TALLREDCOL,
    S_SHRTREDCOL,
    S_CANDLESTIK,
    S_CANDELABRA,
    S_SKULLCOL,
    S_TORCHTREE,
    S_BIGTREE,
    S_TECHPILLAR,
    S_EVILEYE,
    S_EVILEYE2,
    S_EVILEYE3,
    S_EVILEYE4,
    S_FLOATSKULL,
    S_FLOATSKULL2,
    S_FLOATSKULL3,
    S_HEARTCOL,
    S_HEARTCOL2,
    S_BLUETORCH,
    S_BLUETORCH2,
    S_BLUETORCH3,
    S_BLUETORCH4,
    S_GREENTORCH,
    S_GREENTORCH2,
    S_GREENTORCH3,
    S_GREENTORCH4,
    S_REDTORCH,
    S_REDTORCH2,
    S_REDTORCH3,
    S_REDTORCH4,
    S_BTORCHSHRT,
    S_BTORCHSHRT2,
    S_BTORCHSHRT3,
    S_BTORCHSHRT4,
    S_GTORCHSHRT,
    S_GTORCHSHRT2,
    S_GTORCHSHRT3,
    S_GTORCHSHRT4,
    S_RTORCHSHRT,
    S_RTORCHSHRT2,
    S_RTORCHSHRT3,
    S_RTORCHSHRT4,
    S_HANGNOGUTS,
    S_HANGBNOBRAIN,
    S_HANGTLOOKDN,
    S_HANGTSKULL,
    S_HANGTLOOKUP,
    S_HANGTNOBRAIN,
    S_COLONGIBS,
    S_SMALLPOOL,
    S_BRAINSTEM,
    S_TECHLAMP,
    S_TECHLAMP2,
    S_TECHLAMP3,
    S_TECHLAMP4,
    S_TECH2LAMP,
    S_TECH2LAMP2,
    S_TECH2LAMP3,
    S_TECH2LAMP4,
    NUMSTATES   
}
package automap;

import i.DoomStatusAware;
import v.IVideoScaleAware;
import doom.event_t;

public interface IAutoMap<T,V> extends IVideoScaleAware, DoomStatusAware{
 // Used by ST StatusBar stuff.
    public final int AM_MSGHEADER =(('a'<<24)+('m'<<16));
    public final int AM_MSGENTERED= (AM_MSGHEADER | ('e'<<8));
    public final int AM_MSGEXITED= (AM_MSGHEADER | ('x'<<8));

    // Color ranges for automap. Actual colors are bit-depth dependent.
    
    public final int REDRANGE= 16;
    public final int BLUERANGE   =8;
    public final int GREENRANGE  =16;
    public final int GRAYSRANGE  =16;
    public final int BROWNRANGE  =16;
    public final int YELLOWRANGE =1;
    
    public final int YOURRANGE   =0;
    public final int WALLRANGE   =REDRANGE;
    public final int TSWALLRANGE =GRAYSRANGE;
    public final int FDWALLRANGE =BROWNRANGE;
    public final int CDWALLRANGE =YELLOWRANGE;
    public final int THINGRANGE  =GREENRANGE;
    public final int SECRETWALLRANGE =WALLRANGE;
    public final int GRIDRANGE   =0;
    
    // Called by main loop.
    public boolean Responder (event_t ev);

    // Called by main loop.
    public void Ticker ();

    // Called by main loop,
    // called instead of view drawer if automap active.
    public void  Drawer ();

    // Called to force the automap to quit
    // if the level is completed while it is up.
    public void  Stop ();

    public void Start();

    // Should be called in order to set a proper scaled buffer.
    public void Init();
    
}

package automap;

/** used only in automap */

public class mline_t
{
    public mline_t(){
        this(0,0,0,0);
    }
    
    public int ax,ay,bx,by;

    public mline_t(int ax, int ay, int bx, int by) {
        this.ax = ax;
        this.ay = ay;
        this.bx = bx;
        this.by = by;
    }
    
    public mline_t(double ax, double ay, double bx, double by) {
        this.ax = (int) ax;
        this.ay = (int) ay;
        this.bx = (int) bx;
        this.by = (int) by;
    }
    
    /*
    public mline_t(mpoint_t a, mpoint_t b) {
        this.a = a;
        this.b = b;
    }

    public mline_t(int ax,int ay,int bx,int by) {
        this.a = new mpoint_t(ax,ay);
        this.b = new mpoint_t(bx,by);
    }
        
    public mline_t(double ax,double ay,double bx,double by) {
        this.a = new mpoint_t(ax,ay);
        this.b = new mpoint_t(bx,by);
    }
    
    public mpoint_t a, b;
    public int ax;
    
    public String toString(){
        return a.toString()+" - "+ b.toString();
    }
    */
}

package automap;

public class fpoint_t
{
    int x, y;
    
    public fpoint_t(){
        this(0,0);
    }
    
    public fpoint_t(int x, int y){
        this.x=x;
        this.y=y;
    }
    
}

package automap;

import m.fixed_t;

public class mpoint_t
{
  public mpoint_t(fixed_t x, fixed_t y) {
        this.x = x.val;
        this.y = y.val;
    }

  public mpoint_t(int x, int y) {
      this.x = x;
      this.y = y;
  }

  public mpoint_t(double x, double y) {
      this.x = (int) x;
      this.y = (int) y;
  }
  
  public mpoint_t(){
      this.x=0;
      this.y=0;
  }
  
  /** fixed_t */
  public int x,y;
  
  public String toString(){
      return (Integer.toHexString(x)+" , "+Integer.toHexString(y));
  }
};

package automap;

// Emacs style mode select -*- C++ -*-
// -----------------------------------------------------------------------------
//
// $Id: Map.java,v 1.37 2012/09/24 22:36:28 velktron Exp $
//
// Copyright (C) 1993-1996 by id Software, Inc.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//
//
// $Log: Map.java,v $
// Revision 1.37  2012/09/24 22:36:28  velktron
// Map get color
//
// Revision 1.36  2012/09/24 17:16:23  velktron
// Massive merge between HiColor and HEAD. There's no difference from now on, and development continues on HEAD.
//
// Revision 1.34.2.4  2012/09/24 16:58:06  velktron
// TrueColor, Generics.
//
// Revision 1.34.2.3  2012/09/20 14:06:43  velktron
// Generic automap
//
// Revision 1.34.2.2 2011/11/27 18:19:19 velktron
// Configurable colors, more parametrizable.
//
// Revision 1.34.2.1 2011/11/14 00:27:11 velktron
// A barely functional HiColor branch. Most stuff broken. DO NOT USE
//
// Revision 1.34 2011/11/03 18:11:14 velktron
// Fixed long-standing issue with 0-rot vector being reduced to pixels. Fixed
// broken map panning functionality after keymap change.
//
// Revision 1.33 2011/11/01 23:48:43 velktron
// Using FillRect
//
// Revision 1.32 2011/11/01 19:03:10 velktron
// Using screen number constants
//
// Revision 1.31 2011/10/23 18:10:32 velktron
// Generic compliance for DoomVideoInterface
//
// Revision 1.30 2011/10/07 16:08:23 velktron
// Now using g.Keys and line_t
//
// Revision 1.29 2011/09/29 13:25:09 velktron
// Eliminated "intermediate" AbstractAutoMap. Map implements IAutoMap directly.
//
// Revision 1.28 2011/07/28 16:35:03 velktron
// Well, we don't need to know that anymore.
//
// Revision 1.27 2011/06/18 23:16:34 velktron
// Added extreme scale safeguarding (e.g. for Europe.wad).
//
// Revision 1.26 2011/05/30 15:45:44 velktron
// AbstractAutoMap and IAutoMap
//
// Revision 1.25 2011/05/24 11:31:47 velktron
// Adapted to IDoomStatusBar
//
// Revision 1.24 2011/05/23 16:57:39 velktron
// Migrated to VideoScaleInfo.
//
// Revision 1.23 2011/05/17 16:50:02 velktron
// Switched to DoomStatus
//
// Revision 1.22 2011/05/10 10:39:18 velktron
// Semi-playable Techdemo v1.3 milestone
//
// Revision 1.21 2010/12/14 17:55:59 velktron
// Fixed weapon bobbing, added translucent column drawing, separated rendering
// commons.
//
// Revision 1.20 2010/12/12 19:06:18 velktron
// Tech Demo v1.1 release.
//
// Revision 1.19 2010/11/17 23:55:06 velktron
// Kind of playable/controllable.
//
// Revision 1.18 2010/11/12 13:37:25 velktron
// Rationalized the LUT system - now it's 100% procedurally generated.
//
// Revision 1.17 2010/10/01 16:47:51 velktron
// Fixed tab interception.
//
// Revision 1.16 2010/09/27 15:07:44 velktron
// meh
//
// Revision 1.15 2010/09/27 02:27:29 velktron
// BEASTLY update
//
// Revision 1.14 2010/09/23 07:31:11 velktron
// fuck
//
// Revision 1.13 2010/09/13 15:39:17 velktron
// Moving towards an unified gameplay approach...
//
// Revision 1.12 2010/09/08 21:09:01 velktron
// Better display "driver".
//
// Revision 1.11 2010/09/08 15:22:18 velktron
// x,y coords in some structs as value semantics. Possible speed increase?
//
// Revision 1.10 2010/09/06 16:02:59 velktron
// Implementation of palettes.
//
// Revision 1.9 2010/09/02 15:56:54 velktron
// Bulk of unified renderer copyediting done.
//
// Some changes like e.g. global separate limits class and instance methods for
// seg_t and node_t introduced.
//
// Revision 1.8 2010/09/01 15:53:42 velktron
// Graphics data loader implemented....still need to figure out how column
// caching works, though.
//
// Revision 1.7 2010/08/27 23:46:57 velktron
// Introduced Buffered renderer, which makes tapping directly into byte[] screen
// buffers mapped to BufferedImages possible.
//
// Revision 1.6 2010/08/26 16:43:42 velktron
// Automap functional, biatch.
//
// Revision 1.5 2010/08/25 00:50:59 velktron
// Some more work...
//
// Revision 1.4 2010/08/22 18:04:21 velktron
// Automap
//
// Revision 1.3 2010/08/19 23:14:49 velktron
// Automap
//
// Revision 1.2 2010/08/10 16:41:57 velktron
// Threw some work into map loading.
//
// Revision 1.1 2010/07/20 15:52:56 velktron
// LOTS of changes, Automap almost complete. Use of fixed_t inside methods
// severely limited.
//
// Revision 1.1 2010/06/30 08:58:51 velktron
// Let's see if this stuff will finally commit....
//
//
// Most stuff is still being worked on. For a good place to start and get an
// idea of what is being done, I suggest checking out the "testers" package.
//
// Revision 1.1 2010/06/29 11:07:34 velktron
// Release often, release early they say...
//
// Commiting ALL stuff done so far. A lot of stuff is still broken/incomplete,
// and there's still mixed C code in there. I suggest you load everything up in
// Eclpise and see what gives from there.
//
// A good place to start is the testers/ directory, where you can get an idea of
// how a few of the implemented stuff works.
//
//
// DESCRIPTION: the automap code
//
// -----------------------------------------------------------------------------

import static g.Keys.*;
import static data.Limits.*;
import static m.fixed_t.*;
import static doom.englsh.*;
import static data.Tables.*;
import p.AbstractLevelLoader;
import p.mobj_t;
import doom.DoomMain;
import doom.DoomStatus;
import doom.event_t;
import doom.evtype_t;
import doom.player_t;
import rr.patch_t;
import st.IDoomStatusBar;
import utils.C2JUtils;
import v.DoomVideoRenderer;
import static v.DoomVideoRenderer.V_NOSCALESTART;
import v.IVideoScale;
import w.IWadLoader;
import m.cheatseq_t;
import static rr.line_t.*;
import static data.Defines.*;

public abstract class Map<T, V>
        implements IAutoMap<T, V> {

    // ///////////////// Status objects ///////////////////

    IDoomStatusBar ST;

    IWadLoader W;

    DoomMain<T, V> DM;

    DoomVideoRenderer<T,V> V;

    AbstractLevelLoader LL;

    public final String rcsid =
        "$Id: Map.java,v 1.37 2012/09/24 22:36:28 velktron Exp $";

    // For use if I do walls with outsides/insides

    public int REDS, BLUES, GREENS, GRAYS, BROWNS, YELLOWS, BLACK, WHITE;

    // Automap colors
    public int BACKGROUND, YOURCOLORS, WALLCOLORS, TSWALLCOLORS, FDWALLCOLORS,
            CDWALLCOLORS, THINGCOLORS, SECRETWALLCOLORS, GRIDCOLORS,
            XHAIRCOLORS;

    // drawing stuff
    public static final int FB = 0;

    public static final char AM_PANDOWNKEY = KEY_DOWNARROW;

    public static final char AM_PANUPKEY = KEY_UPARROW;

    public static final char AM_PANRIGHTKEY = KEY_RIGHTARROW;

    public static final char AM_PANLEFTKEY = KEY_LEFTARROW;

    public static final char AM_ZOOMINKEY = '=';

    public static final char AM_ZOOMOUTKEY = '-';

    public static final char AM_STARTKEY = KEY_TAB; // KEY_TAB

    public static final char AM_ENDKEY = KEY_TAB; // KEY_TAB

    public static final char AM_GOBIGKEY = '0';

    public static final char AM_FOLLOWKEY = 'f';

    public static final char AM_GRIDKEY = 'g';

    public static final char AM_MARKKEY = 'm';

    public static final char AM_CLEARMARKKEY = 'c';

    public static final int AM_NUMMARKPOINTS = 10;

    // (fixed_t) scale on entry
    public static final int INITSCALEMTOF = (int) (.2 * FRACUNIT);

    // how much the automap moves window per tic in frame-buffer coordinates
    // moves 140 pixels in 1 second
    public static final int F_PANINC = 4;

    // how much zoom-in per tic
    // goes to 2x in 1 second
    public static final int M_ZOOMIN = ((int) (1.02 * FRACUNIT));

    // how much zoom-out per tic
    // pulls out to 0.5x in 1 second
    public static final int M_ZOOMOUT = ((int) (FRACUNIT / 1.02));

    public Map(DoomStatus<T, V> DS) {

        this.updateStatus(DS);
        // Some initializing...
        this.markpoints = new mpoint_t[AM_NUMMARKPOINTS];
        C2JUtils.initArrayOfObjects(markpoints, mpoint_t.class);

        f_oldloc = new mpoint_t();
        m_paninc = new mpoint_t();

        this.plr = DM.players[DM.displayplayer];

    }

    @Override
    public void Init() {

        // Use colormap-specific colors to support extended modes.
        // Moved hardcoding in here. Potentially configurable.

        REDS = 256 - 5 * 16;
        BLUES = 256 - 4 * 16 + 8;
        GREENS = 7 * 16;
        GRAYS = 6 * 16;
        BROWNS = 4 * 16;
        YELLOWS = 256 - 32 + 7;
        BLACK = 0;
        WHITE = 4;

        // Automap colors
        BACKGROUND = BLACK;
        YOURCOLORS = WHITE;
        WALLCOLORS = REDS;
        TSWALLCOLORS = GRAYS;
        FDWALLCOLORS = BROWNS;
        CDWALLCOLORS = YELLOWS;
        THINGCOLORS = GREENS;
        SECRETWALLCOLORS = WALLCOLORS;
        GRIDCOLORS = (GRAYS + GRAYSRANGE / 2);
        XHAIRCOLORS = GRAYS;

        their_colors = new int[] { GREENS, GRAYS, BROWNS, REDS };
    }

    /** translates between frame-buffer and map distances */
    private final int FTOM(int x) {
        return FixedMul(((x) << 16), scale_ftom);
    }

    /** translates between frame-buffer and map distances */
    private final int MTOF(int x) {
        return FixedMul((x), scale_mtof) >> 16;
    }

    /** translates between frame-buffer and map coordinates */
    private final int CXMTOF(int x) {
        return (f_x + MTOF((x) - m_x));
    }

    /** translates between frame-buffer and map coordinates */
    private final int CYMTOF(int y) {
        return (f_y + (f_h - MTOF((y) - m_y)));
    }

    // the following is crap
    public static final short LINE_NEVERSEE = ML_DONTDRAW;

    // This seems to be the minimum viable scale before things start breaking
    // up.
    private static final int MINIMUM_SCALE = (int) (0.7 * FRACUNIT);

    // This seems to be the limit for some maps like europe.wad
    private static final int MINIMUM_VIABLE_SCALE = FRACUNIT >> 5;

    //
    // The vector graphics for the automap.
    /**
     * A line drawing of the player pointing right, starting from the middle.
     */
    protected mline_t[] player_arrow;

    protected int NUMPLYRLINES;

    protected mline_t[] cheat_player_arrow;

    protected int NUMCHEATPLYRLINES;

    protected mline_t[] triangle_guy;

    protected int NUMTRIANGLEGUYLINES;

    protected mline_t[] thintriangle_guy;

    protected int NUMTHINTRIANGLEGUYLINES;

    protected void initVectorGraphics() {

        int R = ((8 * PLAYERRADIUS) / 7);
        player_arrow =
            new mline_t[] {
                    new mline_t(-R + R / 8, 0, R, 0), // -----
                    new mline_t(R, 0, R - R / 2, R / 4), // ----
                    new mline_t(R, 0, R - R / 2, -R / 4),
                    new mline_t(-R + R / 8, 0, -R - R / 8, R / 4), // >---
                    new mline_t(-R + R / 8, 0, -R - R / 8, -R / 4),
                    new mline_t(-R + 3 * R / 8, 0, -R + R / 8, R / 4), // >>--
                    new mline_t(-R + 3 * R / 8, 0, -R + R / 8, -R / 4) };

        NUMPLYRLINES = player_arrow.length;

        cheat_player_arrow =
            new mline_t[] {
                    new mline_t(-R + R / 8, 0, R, 0), // -----
                    new mline_t(R, 0, R - R / 2, R / 6), // ----
                    new mline_t(R, 0, R - R / 2, -R / 6),
                    new mline_t(-R + R / 8, 0, -R - R / 8, R / 6), // >----
                    new mline_t(-R + R / 8, 0, -R - R / 8, -R / 6),
                    new mline_t(-R + 3 * R / 8, 0, -R + R / 8, R / 6), // >>----
                    new mline_t(-R + 3 * R / 8, 0, -R + R / 8, -R / 6),
                    new mline_t(-R / 2, 0, -R / 2, -R / 6), // >>-d--
                    new mline_t(-R / 2, -R / 6, -R / 2 + R / 6, -R / 6),
                    new mline_t(-R / 2 + R / 6, -R / 6, -R / 2 + R / 6, R / 4),
                    new mline_t(-R / 6, 0, -R / 6, -R / 6), // >>-dd-
                    new mline_t(-R / 6, -R / 6, 0, -R / 6),
                    new mline_t(0, -R / 6, 0, R / 4),
                    new mline_t(R / 6, R / 4, R / 6, -R / 7), // >>-ddt
                    new mline_t(R / 6, -R / 7, R / 6 + R / 32, -R / 7 - R / 32),
                    new mline_t(R / 6 + R / 32, -R / 7 - R / 32,
                            R / 6 + R / 10, -R / 7) };

        NUMCHEATPLYRLINES = cheat_player_arrow.length;

        R = (FRACUNIT);
        triangle_guy =
            new mline_t[] { new mline_t(-.867 * R, -.5 * R, .867 * R, -.5 * R),
                    new mline_t(.867 * R, -.5 * R, 0, R),
                    new mline_t(0, R, -.867 * R, -.5 * R) };

        NUMTRIANGLEGUYLINES = triangle_guy.length;

        thintriangle_guy =
            new mline_t[] { new mline_t(-.5 * R, -.7 * R, R, 0),
                    new mline_t(R, 0, -.5 * R, .7 * R),
                    new mline_t(-.5 * R, .7 * R, -.5 * R, -.7 * R) };

        NUMTHINTRIANGLEGUYLINES = thintriangle_guy.length;
    }

    /** Planned overlay mode */
    protected int overlay = 0;

    protected int cheating = 0;

    protected boolean grid = false;

    protected int leveljuststarted = 1; // kluge until AM_LevelInit() is called

    protected int finit_width;

    protected int finit_height;

    // location of window on screen
    protected int f_x;

    protected int f_y;

    // size of window on screen
    protected int f_w;

    protected int f_h;

    /** used for funky strobing effect */
    protected int lightlev;

    /** pseudo-frame buffer */
    protected V fb;

    protected int amclock;

    /** (fixed_t) how far the window pans each tic (map coords) */
    protected mpoint_t m_paninc;

    /** (fixed_t) how far the window zooms in each tic (map coords) */
    protected int mtof_zoommul;

    /** (fixed_t) how far the window zooms in each tic (fb coords) */
    protected int ftom_zoommul;

    /** (fixed_t) LL x,y where the window is on the map (map coords) */
    protected int m_x, m_y;

    /** (fixed_t) UR x,y where the window is on the map (map coords) */
    protected int m_x2, m_y2;

    /** (fixed_t) width/height of window on map (map coords) */
    protected int m_w, m_h;

    /** (fixed_t) based on level size */
    protected int min_x, min_y, max_x, max_y;

    /** (fixed_t) max_x-min_x */
    protected int max_w; //

    /** (fixed_t) max_y-min_y */
    protected int max_h;

    /** (fixed_t) based on player size */
    protected int min_w, min_h;

    /** (fixed_t) used to tell when to stop zooming out */
    protected int min_scale_mtof;

    /** (fixed_t) used to tell when to stop zooming in */
    protected int max_scale_mtof;

    /** (fixed_t) old stuff for recovery later */
    protected int old_m_w, old_m_h, old_m_x, old_m_y;

    /** old location used by the Follower routine */
    protected mpoint_t f_oldloc;

    /** (fixed_t) used by MTOF to scale from map-to-frame-buffer coords */
    protected int scale_mtof = INITSCALEMTOF;

    /** used by FTOM to scale from frame-buffer-to-map coords (=1/scale_mtof) */
    protected int scale_ftom;

    /** the player represented by an arrow */
    protected player_t plr;

    /** numbers used for marking by the automap */
    private patch_t[] marknums = new patch_t[10];

    /** where the points are */
    private mpoint_t[] markpoints;

    /** next point to be assigned */
    private int markpointnum = 0;

    /** specifies whether to follow the player around */
    protected boolean followplayer = true;

    protected char[] cheat_amap_seq = { 0xb2, 0x26, 0x26, 0x2e, 0xff }; // iddt

    protected cheatseq_t cheat_amap = new cheatseq_t(cheat_amap_seq, 0);

    // MAES: STROBE cheat. It's not even cheating, strictly speaking.

    private char cheat_strobe_seq[] = { 0x6e, 0xa6, 0xea, 0x2e, 0x6a, 0xf6,
            0x62, 0xa6, 0xff // vestrobe
        };

    private cheatseq_t cheat_strobe = new cheatseq_t(cheat_strobe_seq, 0);

    private boolean stopped = true;

    // extern boolean viewactive;
    // extern byte screens[][SCREENWIDTH*SCREENHEIGHT];

    /**
     * Calculates the slope and slope according to the x-axis of a line segment
     * in map coordinates (with the upright y-axis n' all) so that it can be
     * used with the brain-dead drawing stuff.
     * 
     * @param ml
     * @param is
     */

    public final void getIslope(mline_t ml, islope_t is) {
        int dx, dy;

        dy = ml.ay - ml.by;
        dx = ml.bx - ml.ax;
        if (dy == 0)
            is.islp = (dx < 0 ? -MAXINT : MAXINT);
        else
            is.islp = FixedDiv(dx, dy);
        if (dx == 0)
            is.slp = (dy < 0 ? -MAXINT : MAXINT);
        else
            is.slp = FixedDiv(dy, dx);

    }

    //
    //
    //
    public final void activateNewScale() {
        m_x += m_w / 2;
        m_y += m_h / 2;
        m_w = FTOM(f_w);
        m_h = FTOM(f_h);
        m_x -= m_w / 2;
        m_y -= m_h / 2;
        m_x2 = m_x + m_w;
        m_y2 = m_y + m_h;
    }

    //
    //
    //
    public final void saveScaleAndLoc() {
        old_m_x = m_x;
        old_m_y = m_y;
        old_m_w = m_w;
        old_m_h = m_h;
    }

    private void restoreScaleAndLoc() {

        m_w = old_m_w;
        m_h = old_m_h;
        if (!followplayer) {
            m_x = old_m_x;
            m_y = old_m_y;
        } else {
            m_x = plr.mo.x - m_w / 2;
            m_y = plr.mo.y - m_h / 2;
        }
        m_x2 = m_x + m_w;
        m_y2 = m_y + m_h;

        // Change the scaling multipliers
        scale_mtof = FixedDiv(f_w << FRACBITS, m_w);
        scale_ftom = FixedDiv(FRACUNIT, scale_mtof);
    }

    /**
     * adds a marker at the current location
     */

    public final void addMark() {
        markpoints[markpointnum].x = m_x + m_w / 2;
        markpoints[markpointnum].y = m_y + m_h / 2;
        markpointnum = (markpointnum + 1) % AM_NUMMARKPOINTS;

    }

    /**
     * Determines bounding box of all vertices, sets global variables
     * controlling zoom range.
     */

    public final void findMinMaxBoundaries() {
        int a; // fixed_t
        int b;

        min_x = min_y = MAXINT;
        max_x = max_y = -MAXINT;

        for (int i = 0; i < LL.numvertexes; i++) {
            if (LL.vertexes[i].x < min_x)
                min_x = LL.vertexes[i].x;
            else if (LL.vertexes[i].x > max_x)
                max_x = LL.vertexes[i].x;

            if (LL.vertexes[i].y < min_y)
                min_y = LL.vertexes[i].y;
            else if (LL.vertexes[i].y > max_y)
                max_y = LL.vertexes[i].y;
        }

        max_w = max_x - min_x;
        max_h = max_y - min_y;

        min_w = 2 * PLAYERRADIUS; // const? never changed?
        min_h = 2 * PLAYERRADIUS;

        a = FixedDiv(f_w << FRACBITS, max_w);
        b = FixedDiv(f_h << FRACBITS, max_h);

        min_scale_mtof = a < b ? a : b;
        if (min_scale_mtof < 0) {
            // MAES: safeguard against negative scaling e.g. in Europe.wad
            // This seems to be the limit.
            min_scale_mtof = MINIMUM_VIABLE_SCALE;
        }
        max_scale_mtof = FixedDiv(f_h << FRACBITS, 2 * PLAYERRADIUS);

    }

    public final void changeWindowLoc() {
        if (m_paninc.x != 0 || m_paninc.y != 0) {
            followplayer = false;
            f_oldloc.x = MAXINT;
        }

        m_x += m_paninc.x;
        m_y += m_paninc.y;

        if (m_x + m_w / 2 > max_x)
            m_x = max_x - m_w / 2;
        else if (m_x + m_w / 2 < min_x)
            m_x = min_x - m_w / 2;

        if (m_y + m_h / 2 > max_y)
            m_y = max_y - m_h / 2;
        else if (m_y + m_h / 2 < min_y)
            m_y = min_y - m_h / 2;

        m_x2 = m_x + m_w;
        m_y2 = m_y + m_h;
    }

    public final void initVariables() {
        int pnum = 0;

        DM.automapactive = true;
        fb = V.getScreen(DoomVideoRenderer.SCREEN_FG);

        f_oldloc.x = MAXINT;
        amclock = 0;
        lightlev = 0;

        m_paninc.x = m_paninc.y = 0;
        ftom_zoommul = FRACUNIT;
        mtof_zoommul = FRACUNIT;

        m_w = FTOM(f_w);
        m_h = FTOM(f_h);

        // find player to center on initially
        if (!DM.playeringame[pnum = DM.consoleplayer])
            for (pnum = 0; pnum < MAXPLAYERS; pnum++) {
                System.out.println(pnum);
                if (DM.playeringame[pnum])
                    break;
            }
        plr = DM.players[pnum];
        m_x = plr.mo.x - m_w / 2;
        m_y = plr.mo.y - m_h / 2;
        this.changeWindowLoc();

        // for saving & restoring
        old_m_x = m_x;
        old_m_y = m_y;
        old_m_w = m_w;
        old_m_h = m_h;

        // inform the status bar of the change
        ST.Responder(st_notify);

    }

    //
    //
    //
    public final void loadPics() {
        int i;
        String namebuf;

        for (i = 0; i < 10; i++) {
            namebuf = ("AMMNUM" + i);
            marknums[i] = W.CachePatchName(namebuf);
        }

    }

    public final void unloadPics() {
        int i;

        for (i = 0; i < 10; i++) {
            W.UnlockLumpNum(marknums[i]);
        }
    }

    public final void clearMarks() {
        int i;

        for (i = 0; i < AM_NUMMARKPOINTS; i++)
            markpoints[i].x = -1; // means empty
        markpointnum = 0;
    }

    /**
     * should be called at the start of every level right now, i figure it out
     * myself
     */
    public final void LevelInit() {
        leveljuststarted = 0;

        f_x = f_y = 0;
        f_w = finit_width;
        f_h = finit_height;

        // scanline=new byte[f_h*f_w];

        this.clearMarks();

        this.findMinMaxBoundaries();
        scale_mtof = FixedDiv(min_scale_mtof, MINIMUM_SCALE);
        if (scale_mtof > max_scale_mtof)
            scale_mtof = min_scale_mtof;
        scale_ftom = FixedDiv(FRACUNIT, scale_mtof);
    }

    //
    //
    //

    protected final event_t st_notify = new event_t(evtype_t.ev_keyup,
            AM_MSGENTERED);

    // MAES: Was a "method static variable"...but what's the point? It's never
    // modified.
    protected final event_t st_notify_ex = new event_t(evtype_t.ev_keyup,
            AM_MSGEXITED);

    public final void Stop() {

        this.unloadPics();
        DM.automapactive = false;
        // This is the only way to notify the status bar responder that we're
        // exiting the automap.
        ST.Responder(st_notify_ex);
        stopped = true;
    }

    //
    //
    //

    // More "static" stuff.
    protected int lastlevel = -1, lastepisode = -1;

    public final void Start() {

        if (!stopped)
            Stop();
        stopped = false;
        if (lastlevel != DM.gamemap || lastepisode != DM.gameepisode) {
            this.LevelInit();
            lastlevel = DM.gamemap;
            lastepisode = DM.gameepisode;
        }
        this.initVectorGraphics();
        this.LevelInit();
        this.initVariables();
        this.loadPics();
    }

    /**
     * set the window scale to the maximum size
     */
    public final void minOutWindowScale() {
        scale_mtof = min_scale_mtof;
        scale_ftom = FixedDiv(FRACUNIT, scale_mtof);
        this.activateNewScale();
    }

    /**
     * set the window scale to the minimum size
     */

    public final void maxOutWindowScale() {
        scale_mtof = max_scale_mtof;
        scale_ftom = FixedDiv(FRACUNIT, scale_mtof);
        this.activateNewScale();
    }

    /** These belong to AM_Responder */
    protected boolean cheatstate = false, bigstate = false;

    /** static char buffer[20] in AM_Responder */
    protected String buffer;

    /** MAES: brought back strobe effect */
    private boolean strobe = false;

    /**
     * Handle events (user inputs) in automap mode
     */

    public final boolean Responder(event_t ev) {

        boolean rc;

        rc = false;

        // System.out.println(ev.data1==AM_STARTKEY);
        if (!DM.automapactive) {
            if ((ev.data1 == AM_STARTKEY) && (ev.type == evtype_t.ev_keyup)) {
                this.Start();
                DM.viewactive = false;
                rc = true;
            }
        }

        else if (ev.type == evtype_t.ev_keydown) {

            rc = true;
            switch (ev.data1) {
            case AM_PANRIGHTKEY: // pan right
                if (!followplayer)
                    m_paninc.x = FTOM(F_PANINC);
                else
                    rc = false;
                break;
            case AM_PANLEFTKEY: // pan left
                if (!followplayer)
                    m_paninc.x = -FTOM(F_PANINC);
                else
                    rc = false;
                break;
            case AM_PANUPKEY: // pan up
                if (!followplayer)
                    m_paninc.y = FTOM(F_PANINC);
                else
                    rc = false;
                break;
            case AM_PANDOWNKEY: // pan down
                if (!followplayer)
                    m_paninc.y = -FTOM(F_PANINC);
                else
                    rc = false;
                break;
            case AM_ZOOMOUTKEY: // zoom out
                mtof_zoommul = M_ZOOMOUT;
                ftom_zoommul = M_ZOOMIN;
                break;
            case AM_ZOOMINKEY: // zoom in
                mtof_zoommul = M_ZOOMIN;
                ftom_zoommul = M_ZOOMOUT;
                break;

            case AM_GOBIGKEY:
                bigstate = !bigstate;
                if (bigstate) {
                    this.saveScaleAndLoc();
                    this.minOutWindowScale();
                } else
                    this.restoreScaleAndLoc();
                break;
            case AM_FOLLOWKEY:
                followplayer = !followplayer;
                f_oldloc.x = MAXINT;
                plr.message = followplayer ? AMSTR_FOLLOWON : AMSTR_FOLLOWOFF;
                break;
            case AM_GRIDKEY:
                grid = !grid;
                plr.message = grid ? AMSTR_GRIDON : AMSTR_GRIDOFF;
                break;
            case AM_MARKKEY:
                buffer = (AMSTR_MARKEDSPOT + " " + markpointnum);
                plr.message = buffer;
                this.addMark();
                break;
            case AM_CLEARMARKKEY:
                this.clearMarks();
                plr.message = AMSTR_MARKSCLEARED;
                break;
            default:
                cheatstate = false;
                rc = false;
            }
            if (!DM.deathmatch && cheat_amap.CheckCheat((char) ev.data1)) {
                rc = false;
                cheating = (cheating + 1) % 3;
            }
            if (cheat_strobe.CheckCheat((char) ev.data1)) {
                strobe = !strobe;
            }
        }

        else if (ev.type == evtype_t.ev_keyup) {
            rc = false;
            switch (ev.data1) {
            case AM_PANRIGHTKEY:
                if (!followplayer)
                    m_paninc.x = 0;
                break;
            case AM_PANLEFTKEY:
                if (!followplayer)
                    m_paninc.x = 0;
                break;
            case AM_PANUPKEY:
                if (!followplayer)
                    m_paninc.y = 0;
                break;
            case AM_PANDOWNKEY:
                if (!followplayer)
                    m_paninc.y = 0;
                break;
            case AM_ZOOMOUTKEY:
            case AM_ZOOMINKEY:
                mtof_zoommul = FRACUNIT;
                ftom_zoommul = FRACUNIT;
                break;
            case AM_ENDKEY:
                bigstate = false;
                DM.viewactive = true;
                this.Stop();
                break;
            }
        }

        return rc;

    }

    /**
     * Zooming
     */
    private final void changeWindowScale() {

        // Change the scaling multipliers
        scale_mtof = FixedMul(scale_mtof, mtof_zoommul);
        scale_ftom = FixedDiv(FRACUNIT, scale_mtof);

        if (scale_mtof < min_scale_mtof)
            this.minOutWindowScale();
        else if (scale_mtof > max_scale_mtof)
            this.maxOutWindowScale();
        else
            this.activateNewScale();

    }

    //
    //
    //
    private final void doFollowPlayer() {

        if (f_oldloc.x != plr.mo.x || f_oldloc.y != plr.mo.y) {
            m_x = FTOM(MTOF(plr.mo.x)) - m_w / 2;
            m_y = FTOM(MTOF(plr.mo.y)) - m_h / 2;
            m_x2 = m_x + m_w;
            m_y2 = m_y + m_h;
            f_oldloc.x = plr.mo.x;
            f_oldloc.y = plr.mo.y;

            // m_x = FTOM(MTOF(plr.mo.x - m_w/2));
            // m_y = FTOM(MTOF(plr.mo.y - m_h/2));
            // m_x = plr.mo.x - m_w/2;
            // m_y = plr.mo.y - m_h/2;

        }

    }

    //
    //
    //

    protected int nexttic = 0;

    protected int[] litelevels = { 0, 4, 7, 10, 12, 14, 15, 15 };

    protected int litelevelscnt = 0;

    private final void updateLightLev() {

        // Change light level
        if (amclock > nexttic) {
            lightlev = litelevels[litelevelscnt++];
            if (litelevelscnt == litelevels.length)
                litelevelscnt = 0;
            nexttic = amclock + 6 - (amclock % 6);
        }

    }

    /**
     * Updates on Game Tick
     */
    public final void Ticker() {

        if (!DM.automapactive)
            return;

        amclock++;

        if (followplayer)
            this.doFollowPlayer();

        // Change the zoom if necessary
        if (ftom_zoommul != FRACUNIT)
            this.changeWindowScale();

        // Change x,y location
        if ((m_paninc.x | m_paninc.y) != 0)
            this.changeWindowLoc();

        // Update light level
        if (strobe)
            updateLightLev();

    }

    // private static int BUFFERSIZE=f_h*f_w;

    /**
     * Automap clipping of lines. Based on Cohen-Sutherland clipping algorithm
     * but with a slightly faster reject and precalculated slopes. If the speed
     * is needed, use a hash algorithm to handle the common cases.
     */
    private int tmpx, tmpy;// =new fpoint_t();

    private final boolean clipMline(mline_t ml, fline_t fl) {

        // System.out.print("Asked to clip from "+FixedFloat.toFloat(ml.a.x)+","+FixedFloat.toFloat(ml.a.y));
        // System.out.print(" to clip "+FixedFloat.toFloat(ml.b.x)+","+FixedFloat.toFloat(ml.b.y)+"\n");
        // These were supposed to be "registers", so they exhibit by-ref
        // properties.
        int outcode1 = 0;
        int outcode2 = 0;
        int outside;

        int dx;
        int dy;
        /*
         * fl.a.x=0; fl.a.y=0; fl.b.x=0; fl.b.y=0;
         */

        // do trivial rejects and outcodes
        if (ml.ay > m_y2)
            outcode1 = TOP;
        else if (ml.ay < m_y)
            outcode1 = BOTTOM;

        if (ml.by > m_y2)
            outcode2 = TOP;
        else if (ml.by < m_y)
            outcode2 = BOTTOM;

        if ((outcode1 & outcode2) != 0)
            return false; // trivially outside

        if (ml.ax < m_x)
            outcode1 |= LEFT;
        else if (ml.ax > m_x2)
            outcode1 |= RIGHT;

        if (ml.bx < m_x)
            outcode2 |= LEFT;
        else if (ml.bx > m_x2)
            outcode2 |= RIGHT;

        if ((outcode1 & outcode2) != 0)
            return false; // trivially outside

        // transform to frame-buffer coordinates.
        fl.ax = CXMTOF(ml.ax);
        fl.ay = CYMTOF(ml.ay);
        fl.bx = CXMTOF(ml.bx);
        fl.by = CYMTOF(ml.by);

        // System.out.println(">>>>>> ("+fl.a.x+" , "+fl.a.y+" ),("+fl.b.x+" , "+fl.b.y+" )");
        outcode1 = DOOUTCODE(fl.ax, fl.ay);
        outcode2 = DOOUTCODE(fl.bx, fl.by);

        if ((outcode1 & outcode2) != 0)
            return false;

        while ((outcode1 | outcode2) != 0) {
            // may be partially inside box
            // find an outside point
            if (outcode1 != 0)
                outside = outcode1;
            else
                outside = outcode2;

            // clip to each side
            if ((outside & TOP) != 0) {
                dy = fl.ay - fl.by;
                dx = fl.bx - fl.ax;
                tmpx = fl.ax + (dx * (fl.ay)) / dy;
                tmpy = 0;
            } else if ((outside & BOTTOM) != 0) {
                dy = fl.ay - fl.by;
                dx = fl.bx - fl.ax;
                tmpx = fl.ax + (dx * (fl.ay - f_h)) / dy;
                tmpy = f_h - 1;
            } else if ((outside & RIGHT) != 0) {
                dy = fl.by - fl.ay;
                dx = fl.bx - fl.ax;
                tmpy = fl.ay + (dy * (f_w - 1 - fl.ax)) / dx;
                tmpx = f_w - 1;
            } else if ((outside & LEFT) != 0) {
                dy = fl.by - fl.ay;
                dx = fl.bx - fl.ax;
                tmpy = fl.ay + (dy * (-fl.ax)) / dx;
                tmpx = 0;
            }

            if (outside == outcode1) {
                fl.ax = tmpx;
                fl.ay = tmpy;
                outcode1 = DOOUTCODE(fl.ax, fl.ay);
            } else {
                fl.bx = tmpx;
                fl.by = tmpy;
                outcode2 = DOOUTCODE(fl.bx, fl.by);
            }

            if ((outcode1 & outcode2) != 0)
                return false; // trivially outside
        }

        return true;
    }

    protected static int LEFT = 1, RIGHT = 2, BOTTOM = 4, TOP = 8;

    /**
     * MAES: the result was supposed to be passed in an "oc" parameter by
     * reference. Not convenient, so I made some changes...
     * 
     * @param mx
     * @param my
     */

    private final int DOOUTCODE(int mx, int my) {
        int oc = 0;
        if ((my) < 0)
            (oc) |= TOP;
        else if ((my) >= f_h)
            (oc) |= BOTTOM;
        if ((mx) < 0)
            (oc) |= LEFT;
        else if ((mx) >= f_w)
            (oc) |= RIGHT;
        return oc;
    }

    /** Not my idea ;-) */
    protected int fuck = 0;

    //
    // Classic Bresenham w/ whatever optimizations needed for speed
    //
    private final void drawFline(fline_t fl, int color) {

        // MAES: wish they were registers...
        int x;
        int y;
        int dx;
        int dy;
        int sx;
        int sy;
        int ax;
        int ay;
        int d;

        // For debugging only

        /*
         * ======= /*if ( fl.ax < 0 || fl.ax >= f_w || fl.ay < 0 || fl.ay >= f_h
         * || fl.bx < 0 || fl.bx >= f_w || fl.by < 0 || fl.by >= f_h) >>>>>>>
         * 1.11 { System.err.println("fuck "+(fuck++)+" \r"); return; <<<<<<<
         * Map.java }
         */

        dx = fl.bx - fl.ax;
        ax = 2 * (dx < 0 ? -dx : dx);
        sx = dx < 0 ? -1 : 1;

        dy = fl.by - fl.ay;
        ay = 2 * (dy < 0 ? -dy : dy);
        sy = dy < 0 ? -1 : 1;

        x = fl.ax;
        y = fl.ay;
        final int c = color;

        if (ax > ay) {
            d = ay - ax / 2;

            while (true) {
                PUTDOT(x, y, c);
                if (x == fl.bx)
                    return;
                if (d >= 0) {
                    y += sy;
                    d -= ax;
                }
                x += sx;
                d += ay;
            }
        } else {
            d = ax - ay / 2;
            while (true) {
                PUTDOT(x, y, c);
                if (y == fl.by)
                    return;
                if (d >= 0) {
                    x += sx;
                    d -= ay;
                }
                y += sy;
                d += ax;
            }
        }
    }

    /** Hopefully inlined */

    protected abstract void PUTDOT(int xx, int yy, int cc);

    /**
     * Clip lines, draw visible parts of lines.
     */
    protected int singlepixel = 0;

    private final void drawMline(mline_t ml, int color) {

        // fl.reset();

        if (this.clipMline(ml, fl)) {
            // if ((fl.a.x==fl.b.x)&&(fl.a.y==fl.b.y)) singlepixel++;
            this.drawFline(fl, color); // draws it on frame buffer using fb
                                       // coords
        }
    }

    private final fline_t fl = new fline_t();

    private final mline_t ml = new mline_t();

    /**
     * Draws flat (floor/ceiling tile) aligned grid lines.
     */
    private final void drawGrid(int color) {
        int x, y; // fixed_t
        int start, end; // fixed_t

        // Figure out start of vertical gridlines
        start = m_x;
        if (((start - LL.bmaporgx) % (MAPBLOCKUNITS << FRACBITS)) != 0)
            start +=
                (MAPBLOCKUNITS << FRACBITS)
                        - ((start - LL.bmaporgx) % (MAPBLOCKUNITS << FRACBITS));
        end = m_x + m_w;

        // draw vertical gridlines
        ml.ay = m_y;
        ml.by = m_y + m_h;
        for (x = start; x < end; x += (MAPBLOCKUNITS << FRACBITS)) {
            ml.ax = x;
            ml.bx = x;
            drawMline(ml, color);
        }

        // Figure out start of horizontal gridlines
        start = m_y;
        if (((start - LL.bmaporgy) % (MAPBLOCKUNITS << FRACBITS)) != 0)
            start +=
                (MAPBLOCKUNITS << FRACBITS)
                        - ((start - LL.bmaporgy) % (MAPBLOCKUNITS << FRACBITS));
        end = m_y + m_h;

        // draw horizontal gridlines
        ml.ax = m_x;
        ml.bx = m_x + m_w;
        for (y = start; y < end; y += (MAPBLOCKUNITS << FRACBITS)) {
            ml.ay = y;
            ml.by = y;
            drawMline(ml, color);
        }

    }

    protected mline_t l = new mline_t();

    /**
     * Determines visible lines, draws them. This is LineDef based, not LineSeg
     * based.
     */

    private final void drawWalls() {

        final int wallcolor = V.getBaseColor(WALLCOLORS + lightlev);
        final int fdwallcolor = V.getBaseColor(FDWALLCOLORS + lightlev);
        final int cdwallcolor = V.getBaseColor(CDWALLCOLORS + lightlev);
        final int tswallcolor = V.getBaseColor(CDWALLCOLORS + lightlev);
        final int secretwallcolor = V.getBaseColor(SECRETWALLCOLORS + lightlev);

        for (int i = 0; i < LL.numlines; i++) {
            l.ax = LL.lines[i].v1x;
            l.ay = LL.lines[i].v1y;
            l.bx = LL.lines[i].v2x;
            l.by = LL.lines[i].v2y;
            if ((cheating | (LL.lines[i].flags & ML_MAPPED)) != 0) {
                if (((LL.lines[i].flags & LINE_NEVERSEE) & ~cheating) != 0)
                    continue;
                if (LL.lines[i].backsector == null) {
                    drawMline(l, wallcolor);
                } else {
                    if (LL.lines[i].special == 39) { // teleporters
                        drawMline(l, WALLCOLORS + WALLRANGE / 2);
                    } else if ((LL.lines[i].flags & ML_SECRET) != 0) // secret
                                                                     // door
                    {
                        if (cheating != 0)
                            drawMline(l, secretwallcolor);
                        else
                            drawMline(l, wallcolor);
                    } else if (LL.lines[i].backsector.floorheight != LL.lines[i].frontsector.floorheight) {
                        drawMline(l, fdwallcolor); // floor level change
                    } else if (LL.lines[i].backsector.ceilingheight != LL.lines[i].frontsector.ceilingheight) {
                        drawMline(l, cdwallcolor); // ceiling level change
                    } else if (cheating != 0) {
                        drawMline(l, tswallcolor);
                    }
                }
            }
            // If we have allmap...
            else if (plr.powers[pw_allmap] != 0) {
                // Some are never seen even with that!
                if ((LL.lines[i].flags & LINE_NEVERSEE) == 0)
                    drawMline(l, GRAYS + 3);
            }
        }

        // System.out.println("Single pixel draws: "+singlepixel+" out of "+P.lines.length);
        // singlepixel=0;
    }

    //
    // Rotation in 2D.
    // Used to rotate player arrow line character.
    //
    private int rotx, roty;

    /**
     * Rotation in 2D. Used to rotate player arrow line character.
     * 
     * @param x
     *        fixed_t
     * @param y
     *        fixed_t
     * @param a
     *        angle_t -> this should be a LUT-ready BAM.
     */

    private final void rotate(int x, int y, int a) {
        // int tmpx;

        rotx = FixedMul(x, finecosine[a]) - FixedMul(y, finesine[a]);

        roty = FixedMul(x, finesine[a]) + FixedMul(y, finecosine[a]);

        // rotx.val = tmpx;
    }

    private final void drawLineCharacter(mline_t[] lineguy, int lineguylines,
            int scale, // fixed_t
            int angle, // This should be a LUT-ready angle.
            int color, int x, // fixed_t
            int y // fixed_t
    ) {
        int i;
        final boolean rotate = (angle != 0);
        mline_t l = new mline_t();

        for (i = 0; i < lineguylines; i++) {
            l.ax = lineguy[i].ax;
            l.ay = lineguy[i].ay;

            if (scale != 0) {
                l.ax = FixedMul(scale, l.ax);
                l.ay = FixedMul(scale, l.ay);
            }

            if (rotate) {
                rotate(l.ax, l.ay, angle);
                // MAES: assign rotations
                l.ax = rotx;
                l.ay = roty;
            }

            l.ax += x;
            l.ay += y;

            l.bx = lineguy[i].bx;
            l.by = lineguy[i].by;

            if (scale != 0) {
                l.bx = FixedMul(scale, l.bx);
                l.by = FixedMul(scale, l.by);
            }

            if (rotate) {
                rotate(l.bx, l.by, angle);
                // MAES: assign rotations
                l.bx = rotx;
                l.by = roty;
            }

            l.bx += x;
            l.by += y;

            drawMline(l, color);
        }
    }

    protected int their_colors[];

    public final void drawPlayers() {
        player_t p;

        int their_color = -1;
        int color;

        // System.out.println(Long.toHexString(plr.mo.angle));

        if (!DM.netgame) {
            if (cheating != 0)
                drawLineCharacter(cheat_player_arrow, NUMCHEATPLYRLINES, 0,
                    toBAMIndex(plr.mo.angle), V.getBaseColor(WHITE), plr.mo.x,
                    plr.mo.y);
            else
                drawLineCharacter(player_arrow, NUMPLYRLINES, 0,
                    toBAMIndex(plr.mo.angle), V.getBaseColor(WHITE), plr.mo.x,
                    plr.mo.y);
            return;
        }

        for (int i = 0; i < MAXPLAYERS; i++) {
            their_color++;
            p = DM.players[i];

            if ((DM.deathmatch && !DM.singledemo) && p != plr)
                continue;

            if (!DM.playeringame[i])
                continue;

            if (p.powers[pw_invisibility] != 0)
                color = 246; // *close* to black
            else
                color = their_colors[their_color];

            drawLineCharacter(player_arrow, NUMPLYRLINES, 0, (int) p.mo.angle,
                V.getBaseColor(color), p.mo.x, p.mo.y);
        }

    }

    public final void drawThings(int colors, int colorrange) {
        mobj_t t;
        int color = V.getBaseColor(colors + lightlev); // Ain't gonna change

        for (int i = 0; i < LL.numsectors; i++) {
            // MAES: get first on the list.
            t = LL.sectors[i].thinglist;
            while (t != null) {
                drawLineCharacter(thintriangle_guy, NUMTHINTRIANGLEGUYLINES,
                    16 << FRACBITS, toBAMIndex(t.angle), color, t.x, t.y);
                t = (mobj_t) t.snext;
            }
        }
    }

    public final void drawMarks() {
        int i, fx, fy, w, h;

        for (i = 0; i < AM_NUMMARKPOINTS; i++) {
            if (markpoints[i].x != -1) {
                w = marknums[i].width;
                h = marknums[i].height;
                // Nothing wrong with v1.9 IWADs, but I wouldn't put my hand on
                // the fire for older ones.
                // w = 5; // because something's wrong with the wad, i guess
                // h = 6; // because something's wrong with the wad, i guess
                fx = CXMTOF(markpoints[i].x);
                fy = CYMTOF(markpoints[i].y);
                if (fx >= f_x && fx <= f_w - w && fy >= f_y && fy <= f_h - h)
                    V.DrawScaledPatch(fx, fy, FB | V_NOSCALESTART, vs,
                        marknums[i]);
            }
        }

    }

    protected abstract void drawCrosshair(int color);

    public final void Drawer() {
        if (!DM.automapactive)
            return;
        // System.out.println("Drawing map");
        if (overlay < 1)
            V.FillRect(BACKGROUND, FB, 0, 0, f_w, f_h); // BACKGROUND
        if (grid)
            drawGrid(V.getBaseColor(GRIDCOLORS));
        drawWalls();
        drawPlayers();
        if (cheating == 2)
            drawThings(THINGCOLORS, THINGRANGE);
        drawCrosshair(V.getBaseColor(XHAIRCOLORS));

        drawMarks();

        V.MarkRect(f_x, f_y, f_w, f_h);

    }

    @Override
    public void updateStatus(DoomStatus<?,?> DC) {
        this.V = (DoomVideoRenderer<T, V>) DC.V;
        this.W = DC.W;
        this.LL = DC.LL;
        this.DM = (DoomMain<T, V>) DC.DM;
        this.ST = DC.ST;
    }

    // //////////////////////////VIDEO SCALE STUFF
    // ////////////////////////////////

    protected int SCREENWIDTH;

    protected int SCREENHEIGHT;

    protected IVideoScale vs;

    public void setVideoScale(IVideoScale vs) {
        this.vs = vs;
    }

    public void initScaling() {
        this.SCREENHEIGHT = vs.getScreenHeight();
        this.SCREENWIDTH = vs.getScreenWidth();

        // Pre-scale stuff.
        finit_width = SCREENWIDTH;
        finit_height = SCREENHEIGHT - 32 * vs.getSafeScaling();
    }

    public static final class HiColor
            extends Map<byte[], short[]> {

        public HiColor(DoomStatus<byte[], short[]> DS) {
            super(DS);
        }

        protected final void PUTDOT(int xx, int yy, int cc) {
            fb[(yy) * f_w + (xx)] = (short) (cc);
        }

        protected final void drawCrosshair(int color) {
            fb[(f_w * (f_h + 1)) / 2] = (short) color; // single point for now
        }
    }

    public static final class Indexed
            extends Map<byte[], byte[]> {

        public Indexed(DoomStatus<byte[], byte[]> DS) {
            super(DS);
        }

        protected final void PUTDOT(int xx, int yy, int cc) {
            fb[(yy) * f_w + (xx)] = (byte) (cc);
        }

        protected final void drawCrosshair(int color) {
            fb[(f_w * (f_h + 1)) / 2] = (byte) color; // single point for now
        }
    }
            
     public static final class TrueColor
            extends Map<byte[], int[]> {

        public TrueColor(DoomStatus<byte[], int[]> DS) {
            super(DS);
        }

        protected final void PUTDOT(int xx, int yy, int cc) {
            fb[(yy) * f_w + (xx)] = (int) (cc);
        }

        protected final void drawCrosshair(int color) {
            fb[(f_w * (f_h + 1)) / 2] = (int) color; // single point for now
        }
    }

}
package automap;

public class islope_t
{
    /** fixed_t */
    int slp, islp;
}

package automap;

public class fline_t
{
 
    /*
     * public fline_t(){
        a=new fpoint_t();
        b=new fpoint_t();
    }
    
    public fline_t(fpoint_t a, fpoint_t b){
        this.a=a;
        this.b=b;
    }
*/    
    public fline_t(int ax, int ay, int bx, int by){
        this.ay=ay;
        this.ax=ax;
        this.by=by;
        this.bx=bx;
    }
    
    public fline_t() {
        // TODO Auto-generated constructor stub
    }

    public int ax,ay,bx,by;
    /*
    public fpoint_t a, b;

    public void reset() {
        this.a.x=0;
        this.a.y=0;
        this.b.x=0;
        this.b.y=0;
        
    }*/
}

package boom;

public class prboom_comp_t {

    public prboom_comp_t(int minver, int maxver, boolean state, String cmd) {
        this.minver = minver;
        this.maxver = maxver;
        this.state = state;
        this.cmd = cmd;
    }

    public int minver;

    public int maxver;

    public boolean state;

    public String cmd;
}
package boom;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import w.CacheableDoomObject;

/** ZDoom node?
 *
 */

public class mapseg_znod_t implements CacheableDoomObject{
    
    public mapseg_znod_t(){
        
    }

  	public int v1,v2; // Those are unsigned :-/
  	public char linedef;
  	public byte side;
   
   public static int sizeOf(){
       return 11;
   }
   
   @Override
   public void unpack(ByteBuffer buf)
           throws IOException {
       buf.order(ByteOrder.LITTLE_ENDIAN);
       this.v1 = buf.getInt();
       this.v2 = buf.getInt();
       this.linedef=buf.getChar();
       this.side=buf.get();
   		}
   
 };

package boom;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import w.CacheableDoomObject;

/** fixed 32 bit gl_vert format v2.0+ (glBsp 1.91) */

public class mapglvertex_t implements CacheableDoomObject{
    public int x, y; // fixed_t

    public static int sizeOf() {
        return 8;
    }

    @Override
    public void unpack(ByteBuffer buf)
            throws IOException {
        buf.order(ByteOrder.LITTLE_ENDIAN);
        x=buf.getInt();
        y=buf.getInt();
    }
}
package boom;

/* Emacs style mode select   -*- C++ -*-
 *-----------------------------------------------------------------------------
 *
 *
 *  PrBoom: a Doom port merged with LxDoom and LSDLDoom
 *  based on BOOM, a modified and improved DOOM engine
 *  Copyright (C) 1999 by
 *  id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman
 *  Copyright (C) 1999-2000 by
 *  Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze
 *  Copyright 2005, 2006 by
 *  Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko
 *
 *  This program is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU General Public License
 *  as published by the Free Software Foundation; either version 2
 *  of the License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 *  02111-1307, USA.
 *
 * DESCRIPTION:
 *
 *-----------------------------------------------------------------------------
 */

public interface E6Y{

/*
#define HU_HUDADDX (HU_HUDX)
#define HU_HUDADDY (HU_HUDY+(-1)*HU_GAPY)
#define HU_CENTERMSGX (320/2)
#define HU_CENTERMSGY ((200-ST_HEIGHT)/2 - 1 - LittleShort(hu_font[0].height))

#define HU_HUDADDX_D (HU_HUDX_LL)
#define HU_HUDADDY_D (HU_HUDY_LL+(-1)*HU_GAPY)

#define HU_MSGCENTERTIMEOUT   (2*TICRATE)
*/

public static final String STSTR_SECRETFOUND =  "A secret is revealed!";

public static final int S_CANT_GL_ARB_MULTITEXTURE= 0x10000000;
public static final int S_CANT_GL_ARB_MULTISAMPLEFACTOR=  0x20000000;

public static final int  GL_COMBINE_ARB  =  0x8570;
public static final int GL_RGB_SCALE_ARB = 0x8573;

public static final char NO_INDEX =0xFFFF;

public static final float FOV_CORRECTION_FACTOR= 1.13776f;
public static final int FOV90 =90;

//public static final double DEG2RAD( a ) ( a * Pi ) / 180.0f;
//#define RAD2DEG( a ) ( a / Pi ) * 180.0f;

public class buf_overrun_item_t
{
  String wadname;
  int map;
  int address;
} 

public class camera_t
{
  long x;
  long y;
  long z;
  long PrevX;
  long PrevY;
  long PrevZ;
  long angle;
  long pitch;
  long PrevAngle;
  long PrevPitch;
  int type;
}

/*

extern int REAL_SCREENWIDTH;
extern int REAL_SCREENHEIGHT;
extern int REAL_SCREENPITCH;

extern dboolean wasWiped;

extern int totalleveltimes;

extern int secretfound;
extern int messagecenter_counter;
extern int demo_skiptics;
extern int demo_tics_count;
extern int demo_curr_tic;
extern int demo_playerscount;
extern char demo_len_st[80];

extern int avi_shot_time;
extern int avi_shot_num;
extern const char *avi_shot_fname;
extern char avi_shot_curr_fname[PATH_MAX];

extern dboolean doSkip;
extern dboolean demo_stoponnext;
extern dboolean demo_stoponend;
extern dboolean demo_warp;

extern int key_speed_up;
extern int key_speed_down;
extern int key_speed_default;
extern int speed_step;
extern int key_nextlevel;
extern int key_demo_jointogame;
extern int key_demo_endlevel;
extern int key_walkcamera;
extern int key_showalive;

extern int hudadd_gamespeed;
extern int hudadd_leveltime;
extern int hudadd_demotime;
extern int hudadd_secretarea;
extern int hudadd_smarttotals;
extern int hudadd_demoprogressbar;
extern int movement_strafe50;
extern int movement_shorttics;
extern int movement_strafe50onturns;
extern int movement_mouselook;
extern int movement_mouseinvert;
extern int movement_maxviewpitch;
extern int mouse_handler;
extern int mouse_doubleclick_as_use;
extern int render_multisampling;
extern int render_paperitems;
extern int render_wipescreen;
extern int mouse_acceleration;
extern int demo_overwriteexisting;

extern int render_fov;
extern int render_aspect;
extern float render_ratio;
extern float render_fovratio;
extern float render_fovy;
extern float render_multiplier;
void M_ChangeAspectRatio(void);
void M_ChangeStretch(void);

extern int misc_fastexit;

extern int palette_ondamage;
extern int palette_onbonus;
extern int palette_onpowers;

extern camera_t walkcamera;

extern fixed_t sidemove_normal[2];
extern fixed_t sidemove_strafe50[2];

extern int PitchSign;
extern int mouseSensitivity_mlook;
extern angle_t viewpitch;
extern float skyscale;
extern float screen_skybox_zplane;
extern float maxNoPitch[];
extern float tan_pitch;
extern float skyUpAngle;
extern float skyUpShift;
extern float skyXShift;
extern float skyYShift;
extern dboolean mlook_or_fov;

extern hu_textline_t  w_hudadd;
extern hu_textline_t  w_centermsg;
extern hu_textline_t  w_precache;
extern char hud_add[80];
extern char hud_centermsg[80];

void e6y_assert(const char *format, ...);

void ParamsMatchingCheck();
void e6y_InitCommandLine(void);

void P_WalkTicker ();
void P_SyncWalkcam(dboolean sync_coords, dboolean sync_sight);
void P_ResetWalkcam(void);

extern dboolean sound_inited_once;
void e6y_I_uSleep(unsigned long usecs);
void G_SkipDemoStart(void);
void G_SkipDemoStop(void);
void G_SkipDemoCheck(void);
int G_GotoNextLevel(void);

#ifdef GL_DOOM
void M_ChangeMouseLook(void);
void M_ChangeMaxViewPitch(void);
void M_ChangeMouseInvert(void);
void M_ChangeFOV(void);
void M_ChangeUseDetail(void);
void M_ChangeMultiSample(void);
void M_ChangeSpriteClip(void);
void M_ChangeAllowBoomColormaps(void);
void M_ChangeTextureUseHires(void);
void M_ChangeAllowFog(void);
void M_ChangeTextureHQResize(void);
#endif
void M_ChangeRenderPrecise(void);
void M_ChangeSpeed(void);
void M_ChangeScreenMultipleFactor(void);
void M_ChangeInterlacedScanning(void);
void M_MouseMLook(int choice);
void M_MouseAccel(int choice);
void M_ChangeCompTranslucency(void);
void CheckPitch(signed int *pitch);
void I_Init2(void);

#ifdef GL_DOOM
dboolean GetMouseLook(void);
dboolean HaveMouseLook(void);
#else
#define GetMouseLook() (0)
#define HaveMouseLook() (0)
#endif

extern float viewPitch;
extern dboolean transparentpresent;

void R_ClearClipSegs (void);
void R_RenderBSPNode(int bspnum);

typedef struct prboom_comp_s
{
  unsigned int minver;
  unsigned int maxver;
  dboolean state;
  const char *cmd;
} prboom_comp_t;

enum
{
  PC_MONSTER_AVOID_HAZARDS,
  PC_REMOVE_SLIME_TRAILS,
  PC_NO_DROPOFF,
  PC_TRUNCATED_SECTOR_SPECIALS,
  PC_BOOM_BRAINAWAKE,
  PC_PRBOOM_FRICTION,
  PC_REJECT_PAD_WITH_FF,
  PC_FORCE_LXDOOM_DEMO_COMPATIBILITY,
  PC_ALLOW_SSG_DIRECT,
  PC_TREAT_NO_CLIPPING_THINGS_AS_NOT_BLOCKING,
  PC_FORCE_INCORRECT_PROCESSING_OF_RESPAWN_FRAME_ENTRY,
  PC_FORCE_CORRECT_CODE_FOR_3_KEYS_DOORS_IN_MBF,
  PC_UNINITIALIZE_CRUSH_FIELD_FOR_STAIRS,
  PC_FORCE_BOOM_FINDNEXTHIGHESTFLOOR,
  PC_ALLOW_SKY_TRANSFER_IN_BOOM,
  PC_APPLY_GREEN_ARMOR_CLASS_TO_ARMOR_BONUSES,
  PC_APPLY_BLUE_ARMOR_CLASS_TO_MEGASPHERE,
  PC_WRONG_FIXEDDIV,
  PC_FORCE_INCORRECT_BOBBING_IN_BOOM,
  PC_BOOM_DEH_PARSER,
  PC_MBF_REMOVE_THINKER_IN_KILLMOBJ,
  PC_DO_NOT_INHERIT_FRIENDLYNESS_FLAG_ON_SPAWN,
  PC_DO_NOT_USE_MISC12_FRAME_PARAMETERS_IN_A_MUSHROOM,
  PC_MAX
};

extern prboom_comp_t prboom_comp[];

int StepwiseSum(int value, int direction, int step, int minval, int maxval, int defval);

enum
{
  TT_ALLKILL,
  TT_ALLITEM,
  TT_ALLSECRET,

  TT_TIME,
  TT_TOTALTIME,
  TT_TOTALKILL,
  TT_TOTALITEM,
  TT_TOTALSECRET,

  TT_MAX
};

typedef struct timetable_s
{
  char map[16];

  int kill[MAXPLAYERS];
  int item[MAXPLAYERS];
  int secret[MAXPLAYERS];
  
  int stat[TT_MAX];
} timetable_t;

#ifdef _WIN32
const char* WINError(void);
#endif

extern int stats_level;
void e6y_G_DoCompleted(void);
void e6y_WriteStats(void);

void e6y_G_DoWorldDone(void);

void I_ProcessWin32Mouse(void);
void I_StartWin32Mouse(void);
void I_EndWin32Mouse(void);
int AccelerateMouse(int val);
void MouseAccelChanging(void);

extern int mlooky;
extern int realtic_clock_rate;

extern dboolean IsDehMaxHealth;
extern dboolean IsDehMaxSoul;
extern dboolean IsDehMegaHealth;
extern dboolean DEH_mobjinfo_bits[NUMMOBJTYPES];

extern int deh_maxhealth;
extern int deh_max_soul;
extern int deh_mega_health;

extern int maxhealthbonus;

void e6y_G_Compatibility(void);

extern dboolean zerotag_manual;
extern int comperr_zerotag;
extern int comperr_passuse;
extern int comperr_hangsolid;

dboolean compbad_get(int *compbad);

dboolean ProcessNoTagLines(line_t* line, sector_t **sec, int *secnum);

#define I_FindName(a)	((a)->Name)
#define I_FindAttr(a)	((a)->Attribs)

typedef struct
{
	unsigned int Attribs;
	unsigned int Times[3*2];
	unsigned int Size[2];
	unsigned int Reserved[2];
	char Name[PATH_MAX];
	char AltName[14];
} findstate_t;

char* PathFindFileName(const char* pPath);
void NormalizeSlashes2(char *str);
unsigned int AfxGetFileName(const char* lpszPathName, char* lpszTitle, unsigned int nMax);
void AbbreviateName(char* lpszCanon, int cchMax, int bAtLeastName);

//extern int viewMaxY;

extern dboolean isskytexture;

void D_AddDehFile (const char *file, wad_source_t source);

extern int levelstarttic;

void I_AfterUpdateVideoMode(void);

extern int force_singletics_to;

dboolean HU_DrawDemoProgress(void);

#ifdef ALL_IN_ONE
unsigned char* GetAllInOneLumpHandle(void);
#endif

#ifdef _MSC_VER
int GetFullPath(const char* FileName, const char* ext, char *Buffer, size_t BufferLength);
#endif

void I_vWarning(const char *message, va_list argList);
void I_Warning(const char *message, ...);

#define PRB_MB_OK                       0x00000000
#define PRB_MB_OKCANCEL                 0x00000001
#define PRB_MB_ABORTRETRYIGNORE         0x00000002
#define PRB_MB_YESNOCANCEL              0x00000003
#define PRB_MB_YESNO                    0x00000004
#define PRB_MB_RETRYCANCEL              0x00000005
#define PRB_MB_DEFBUTTON1               0x00000000
#define PRB_MB_DEFBUTTON2               0x00000100
#define PRB_MB_DEFBUTTON3               0x00000200
#define PRB_IDOK                1
#define PRB_IDCANCEL            2
#define PRB_IDABORT             3
#define PRB_IDRETRY             4
#define PRB_IDIGNORE            5
#define PRB_IDYES               6
#define PRB_IDNO                7
int I_MessageBox(const char* text, unsigned int type);

dboolean SmoothEdges(unsigned char * buffer,int w, int h);

#ifdef _WIN32
extern int mus_extend_volume;
void I_midiOutSetVolumes(int volume);
#endif

#endif
*/

}
package boom;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import w.CacheableDoomObject;

/**
 *  LineSeg, generated by splitting LineDefs
 * using partition lines selected by BSP builder.
 * MAES: this is the ON-DISK structure. The corresponding memory structure, 
 * segs_t, has fixed_t members. 
 */

public class mapseg_v4_t implements CacheableDoomObject{
    
    public mapseg_v4_t(){
        
    }

  	public int v1;
  	public int v2;
  	public char angle;
  	public char linedef;
  	public char side;
  	public char offset;
   
   public static int sizeOf(){
       return 16;
   }
   
   @Override
   public void unpack(ByteBuffer buf)
           throws IOException {
       buf.order(ByteOrder.LITTLE_ENDIAN);
       this.v1 = buf.getInt();
       this.v2 = buf.getInt();
       this.angle=buf.getChar();       
       this.linedef=buf.getChar();
       this.side=buf.getChar();
       this.offset=buf.getChar();
      
   }
   
 };

package boom;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Arrays;

import utils.C2JUtils;
import w.CacheableDoomObject;

public class DeepBSPNodesV4 implements CacheableDoomObject{

	public static final byte[] DeepBSPHeader={
		'x','N','d','4',0,0,0,0
		};
	
	byte[] header=new byte[8];
	mapnode_v4_t[] nodes;
	int numnodes;
	
	public boolean formatOK(){
		return Arrays.equals(header, DeepBSPHeader);
	}

	public mapnode_v4_t[] getNodes(){
		return nodes;
	}
	
	@Override
	public void unpack(ByteBuffer buf) throws IOException {
		int length=buf.capacity();
		
		// Too short, not even header.
		if (length<8) return;
		
		numnodes=(length-8)/mapnode_v4_t.sizeOf();
		
		if (length<1) return;
		
		buf.get(header); // read header
		
		nodes=C2JUtils.createArrayOfObjects(mapnode_v4_t.class, length);
		
		for (int i=0;i<length;i++){
			nodes[i].unpack(buf);
		}
		
	}
	
	
	

}

package boom;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Arrays;

import utils.C2JUtils;
import w.CacheableDoomObject;

public class ZNodeSegs implements CacheableDoomObject{

	private static final byte[] DeepBSPHeader={
		'x','N','d','4',0,0,0,0
		};
	
	byte[] header;
	mapseg_znod_t[] nodes;
	int numnodes;
	
	public boolean formatOK(){
		return Arrays.equals(header, DeepBSPHeader);
	}

	public mapseg_znod_t[] getNodes(){
		return nodes;
	}
	
	@Override
	public void unpack(ByteBuffer buf) throws IOException {
		int length=buf.capacity();
		
		// Too short, not even header.
		if (length<8) return;
		
		numnodes=(length-8)/mapnode_v4_t.sizeOf();
		
		if (length<1) return;
		
		buf.get(header); // read header
		
		nodes=C2JUtils.createArrayOfObjects(mapseg_znod_t.class, length);
		
		for (int i=0;i<length;i++){
			nodes[i].unpack(buf);
		}
		
	}
	
	
	

}

package boom;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

import utils.C2JUtils;
import w.CacheableDoomObject;

public class mapsubsector_znod_t implements CacheableDoomObject{
	
    public long numsegs;
      
  	@Override
	public void unpack(ByteBuffer buf)
        throws IOException {
    buf.order(ByteOrder.LITTLE_ENDIAN);
    this.numsegs = C2JUtils.unsigned(buf.getInt());
	} 

	public static final int sizeOf(){
    return 4;
	}
	
}

package boom;

/** cph - move compatibility levels here so we can use them in d_server.c
 * 
 * @author cph
 *
 */
 
public final class Compatibility {

      /** Doom v1.2 */
      public static final int doom_12_compatibility=0;
      
      public static final int doom_1666_compatibility=1; /* Doom v1.666 */
      public static final int doom2_19_compatibility=2;  /* Doom & Doom 2 v1.9 */
      public static final int ultdoom_compatibility=3;   /* Ultimate Doom and Doom95 */
      public static final int finaldoom_compatibility=4;     /* Final Doom */
      public static final int dosdoom_compatibility=5;     /* DosDoom 0.47 */
      public static final int tasdoom_compatibility=6;     /* TASDoom */
      public static final int boom_compatibility_compatibility=7;      /* Boom's compatibility mode */
      public static final int boom_201_compatibility=8;                /* Boom v2.01 */
      public static final int boom_202_compatibility=9;                /* Boom v2.02 */
      public static final int lxdoom_1_compatibility=10;                /* LxDoom v1.3.2+ */
      public static final int mbf_compatibility=11;                     /* MBF */
      public static final int prboom_1_compatibility=12;                /* PrBoom 2.03beta? */
      public static final int prboom_2_compatibility=13;                /* PrBoom 2.1.0-2.1.1 */
      public static final int prboom_3_compatibility=14;                /* PrBoom 2.2.x */
      public static final int prboom_4_compatibility=15;                /* PrBoom 2.3.x */
      public static final int prboom_5_compatibility=16;              /* PrBoom 2.4.0 */
      public static final int prboom_6_compatibility=17;             /* Latest PrBoom */
      public static final int MAX_COMPATIBILITY_LEVEL=18;           /* Must be last entry */
      /* Aliases follow */
      public static final int boom_compatibility = boom_201_compatibility; /* Alias used by G_Compatibility */
      public static final int best_compatibility = prboom_6_compatibility;
      
      public static final prboom_comp_t[] prboom_comp = {
              new prboom_comp_t(0xffffffff, 0x02020615, false, "-force_monster_avoid_hazards"),
              new prboom_comp_t(0x00000000, 0x02040601, false, "-force_remove_slime_trails"),
              new prboom_comp_t(0x02020200, 0x02040801, false, "-force_no_dropoff"),
              new prboom_comp_t(0x00000000, 0x02040801, false, "-force_truncated_sector_specials"),
              new prboom_comp_t(0x00000000, 0x02040802, false, "-force_boom_brainawake"),
              new prboom_comp_t(0x00000000, 0x02040802, false, "-force_prboom_friction"),
              new prboom_comp_t(0x02020500, 0x02040000, false, "-reject_pad_with_ff"),
              new prboom_comp_t(0xffffffff, 0x02040802, false, "-force_lxdoom_demo_compatibility"),
              new prboom_comp_t(0x00000000, 0x0202061b, false, "-allow_ssg_direct"),
              new prboom_comp_t(0x00000000, 0x02040601, false, "-treat_no_clipping_things_as_not_blocking"),
              new prboom_comp_t(0x00000000, 0x02040803, false, "-force_incorrect_processing_of_respawn_frame_entry"),
              new prboom_comp_t(0x00000000, 0x02040601, false, "-force_correct_code_for_3_keys_doors_in_mbf"),
              new prboom_comp_t(0x00000000, 0x02040601, false, "-uninitialize_crush_field_for_stairs"),
              new prboom_comp_t(0x00000000, 0x02040802, false, "-force_boom_findnexthighestfloor"),
              new prboom_comp_t(0x00000000, 0x02040802, false, "-allow_sky_transfer_in_boom"),
              new prboom_comp_t(0x00000000, 0x02040803, false, "-apply_green_armor_class_to_armor_bonuses"),
              new prboom_comp_t(0x00000000, 0x02040803, false, "-apply_blue_armor_class_to_megasphere"),
              new prboom_comp_t(0x02050001, 0x02050003, false, "-wrong_fixeddiv"),
              new prboom_comp_t(0x02020200, 0x02050003, false, "-force_incorrect_bobbing_in_boom"),
              new prboom_comp_t(0xffffffff, 0x00000000, false, "-boom_deh_parser"),
              new prboom_comp_t(0x00000000, 0x02050007, false, "-mbf_remove_thinker_in_killmobj"),
              new prboom_comp_t(0x00000000, 0x02050007, false, "-do_not_inherit_friendlyness_flag_on_spawn"),
              new prboom_comp_t(0x00000000, 0x02050007, false, "-do_not_use_misc12_frame_parameters_in_a_mushroom")
            };
      
      public static final int PC_MONSTER_AVOID_HAZARDS=0;
      public static final int PC_REMOVE_SLIME_TRAILS=1;
      public static final int PC_NO_DROPOFF=2;
      public static final int PC_TRUNCATED_SECTOR_SPECIALS=3;
      public static final int PC_BOOM_BRAINAWAKE=4;
      public static final int PC_PRBOOM_FRICTION=5;
      public static final int PC_REJECT_PAD_WITH_FF=6;
      public static final int PC_FORCE_LXDOOM_DEMO_COMPATIBILITY=7;
      public static final int PC_ALLOW_SSG_DIRECT=8;
      public static final int PC_TREAT_NO_CLIPPING_THINGS_AS_NOT_BLOCKING=9;
      public static final int PC_FORCE_INCORRECT_PROCESSING_OF_RESPAWN_FRAME_ENTRY=10;
      public static final int PC_FORCE_CORRECT_CODE_FOR_3_KEYS_DOORS_IN_MBF=11;
      public static final int PC_UNINITIALIZE_CRUSH_FIELD_FOR_STAIRS=12;
      public static final int PC_FORCE_BOOM_FINDNEXTHIGHESTFLOOR=13;
      public static final int PC_ALLOW_SKY_TRANSFER_IN_BOOM=14;
      public static final int PC_APPLY_GREEN_ARMOR_CLASS_TO_ARMOR_BONUSES=15;
      public static final int PC_APPLY_BLUE_ARMOR_CLASS_TO_MEGASPHERE=16;
      public static final int PC_WRONG_FIXEDDIV=17;
      public static final int PC_FORCE_INCORRECT_BOBBING_IN_BOOM=18;
      public static final int PC_BOOM_DEH_PARSER=19;
      public static final int PC_MBF_REMOVE_THINKER_IN_KILLMOBJ=20;
      public static final int PC_DO_NOT_INHERIT_FRIENDLYNESS_FLAG_ON_SPAWN=21;
      public static final int PC_DO_NOT_USE_MISC12_FRAME_PARAMETERS_IN_A_MUSHROOM=21;
      public static final int PC_MAX=23;
      
      public enum PC
      {
        PC_MONSTER_AVOID_HAZARDS,
        PC_REMOVE_SLIME_TRAILS,
        PC_NO_DROPOFF,
        PC_TRUNCATED_SECTOR_SPECIALS,
        PC_BOOM_BRAINAWAKE,
        PC_PRBOOM_FRICTION,
        PC_REJECT_PAD_WITH_FF,
        PC_FORCE_LXDOOM_DEMO_COMPATIBILITY,
        PC_ALLOW_SSG_DIRECT,
        PC_TREAT_NO_CLIPPING_THINGS_AS_NOT_BLOCKING,
        PC_FORCE_INCORRECT_PROCESSING_OF_RESPAWN_FRAME_ENTRY,
        PC_FORCE_CORRECT_CODE_FOR_3_KEYS_DOORS_IN_MBF,
        PC_UNINITIALIZE_CRUSH_FIELD_FOR_STAIRS,
        PC_FORCE_BOOM_FINDNEXTHIGHESTFLOOR,
        PC_ALLOW_SKY_TRANSFER_IN_BOOM,
        PC_APPLY_GREEN_ARMOR_CLASS_TO_ARMOR_BONUSES,
        PC_APPLY_BLUE_ARMOR_CLASS_TO_MEGASPHERE,
        PC_WRONG_FIXEDDIV,
        PC_FORCE_INCORRECT_BOBBING_IN_BOOM,
        PC_BOOM_DEH_PARSER,
        PC_MBF_REMOVE_THINKER_IN_KILLMOBJ,
        PC_DO_NOT_INHERIT_FRIENDLYNESS_FLAG_ON_SPAWN,
        PC_DO_NOT_USE_MISC12_FRAME_PARAMETERS_IN_A_MUSHROOM,
        PC_MAX
      };
      
    }

package boom;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

import w.CacheableDoomObject;
import w.DoomBuffer;

/** BSP Node structure on-disk */
public class mapnode_v4_t
        implements CacheableDoomObject {

    public mapnode_v4_t() {
        this.bbox = new short[2][4];
        this.children = new int[2];
    }

    /** Partition line from (x,y) to x+dx,y+dy) */
    public short x, y, dx, dy;

    /** Bounding box for each child, clip against view frustum. */
    public short[][] bbox;

    /** If NF_SUBSECTOR its a subsector, else it's a node of another subtree. 
     *  In simpler words: if the first bit is set, strip it and use the rest
     *  as a subtree index. Else it's a node index.
     * */
    public int[] children = new int[2];

    public static final int sizeOf() {
        return (8 + 16 + 8);
    }

    @Override
    public void unpack(ByteBuffer buf)
            throws IOException {
        buf.order(ByteOrder.LITTLE_ENDIAN);
        this.x = buf.getShort();
        this.y = buf.getShort();
        this.dx = buf.getShort();
        this.dy = buf.getShort();
        DoomBuffer.readShortArray(buf, this.bbox[0], 4);
        DoomBuffer.readShortArray(buf, this.bbox[1], 4);
        DoomBuffer.readIntArray(buf, this.children, 2);
    }

}

package boom;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

import w.CacheableDoomObject;

public class mapsubsector_v4_t implements CacheableDoomObject{
	
    public char     numsegs;
  	/** Index of first one, segs are stored sequentially. */
  	public int     firstseg;
  
  	@Override
	public void unpack(ByteBuffer buf)
        throws IOException {
    buf.order(ByteOrder.LITTLE_ENDIAN);
    this.numsegs = buf.getChar();
    this.firstseg = buf.getInt();        
	} 

	public static int sizeOf(){
    return 6;
	}
	
}

package boom;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

import w.CacheableDoomObject;
import w.DoomBuffer;

public class mapnode_znod_t implements CacheableDoomObject {

    
        public short x;  // Partition line from (x,y) to x+dx,y+dy)
        public short y;
        public short dx;
        public short dy;
        // Bounding box for each child, clip against view frustum.
        public short[][] bbox;
        // If NF_SUBSECTOR its a subsector, else it's a node of another subtree.
        public int[] children;
        
        public mapnode_znod_t(){
            this.bbox = new short[2][4];
            this.children = new int[2];
        }
        
        public static final int sizeOf() {
            return (8 + 16 + 8);
        }

        @Override
        public void unpack(ByteBuffer buf)
                throws IOException {
            buf.order(ByteOrder.LITTLE_ENDIAN);
            this.x = buf.getShort();
            this.y = buf.getShort();
            this.dx = buf.getShort();
            this.dy = buf.getShort();
            DoomBuffer.readShortArray(buf, this.bbox[0], 4);
            DoomBuffer.readShortArray(buf, this.bbox[1], 4);
            DoomBuffer.readIntArray(buf, this.children, 2);
            
        }

}

package pooling;

import doom.think_t;
import p.Actions;
import p.mobj_t;
import s.AudioChunk;

/* The idea is to reuse mobjs...however in practice that 
 * doesn't work out so well, with everything "freezing" after 
 * a while */

public class MobjPool extends ObjectPool<mobj_t>
{

	Actions A;
	
    public MobjPool(Actions A)    
    {
    	// A reasonable time limit for map objects?
    	super(1000L);
    	this.A=A;
    }

    protected mobj_t create()
    {
        /*
        for (int i=0;i<8192;i++){
            locked.push(new mobj_t(A));
        }
        
        return locked.pop();*/
        
        return new mobj_t(A);
    }

    public void expire(mobj_t o)
    {
        o.function=think_t.NOP;
    }

    public boolean validate(mobj_t o)
    {
        return (o.function==think_t.NOP);
    }

}

package pooling;

import s.AudioChunk;

// Referenced classes of package pooling:
//            ObjectPool

public class AudioChunkPool extends ObjectQueuePool<AudioChunk>
{

    public AudioChunkPool()    
    {
    	// A reasonable time limit for Audio chunks
    	super(10000L);
    }

    protected AudioChunk create()
    {
        return new AudioChunk();
    }

    public void expire(AudioChunk o)
    {
        o.free = true;
    }

    public boolean validate(AudioChunk o)
    {
        return o.free;
    }

}

package pooling;

public class RoguePatchMap extends GenericIntMap<byte[][]> {
	
	public RoguePatchMap(){
		super();
		 patches = new byte[DEFAULT_CAPACITY][][];
	}
}

package pooling;

import java.util.Arrays;

public abstract class GenericIntMap<K> {
    protected static final int DEFAULT_CAPACITY = 16;
    
    /** Concrete implementations must allocate patches
     *  
     */
    GenericIntMap() {
    
    	lumps = new int[DEFAULT_CAPACITY];
        // patches = new K[DEFAULT_CAPACITY];
    }
    
    public boolean containsKey(int lump) {
        return indexOf(lump) >= 0;
    }
    
    public K get(int lump) {
        int index = indexOf(lump);
        if (index >= 0) {
            return patches[index];
        } else {
            return null;
        }
    }
    
    public void put(int lump, K patch) {
        int index = indexOf(lump);
        if (index >= 0) {
            patches[index] = patch;
        } else {
            ensureCapacity(numEntries + 1);
            int newIndex = ~index;
            int moveCount = numEntries - newIndex;
            if (moveCount > 0) {
                System.arraycopy(lumps, newIndex, lumps, newIndex+1, moveCount);
                System.arraycopy(patches, newIndex, patches, newIndex+1, moveCount);
            }
            lumps[newIndex] = lump;
            patches[newIndex] = patch;
            ++ numEntries;
        }
    }
    
    protected void ensureCapacity(int cap) {
        while (lumps.length <= cap) {
            lumps =
                Arrays.copyOf(lumps, Math.max(lumps.length * 2, DEFAULT_CAPACITY));
        }
        while (patches.length <= cap) {
            patches =
                Arrays.copyOf(patches, Math.max(patches.length * 2, DEFAULT_CAPACITY));
        }
    }
    protected int indexOf(int lump) {
         return Arrays.binarySearch(lumps, 0, numEntries, lump);
    	//for (int i=0;i<numEntries;i++)
    	//	if (lumps[i]==lump) return i;
    	//
    	//return -1;
    	
    }
    
    
    protected int[] lumps;
    protected int numEntries;
    protected K[] patches;
}

package pooling;

import java.util.Arrays;

public class RoguePatchMap2 {
    private static final int DEFAULT_CAPACITY = 16;
    public RoguePatchMap2() {
        lumps = new int[DEFAULT_CAPACITY];
        patches = new byte[DEFAULT_CAPACITY][][];
    }
    boolean containsKey(int lump) {
        return indexOf(lump) >= 0;
    }
    public byte[][] get(int lump) {
        int index = indexOf(lump);
        if (index >= 0) {
            return patches[index];
        } else {
            return null;
        }
    }
    public void put(int lump, byte[][] patch) {
        int index = indexOf(lump);
        if (index >= 0) {
            patches[index] = patch;
        } else {
            ensureCapacity(numEntries + 1);
            int newIndex = ~index;
            int moveCount = numEntries - newIndex;
            if (moveCount > 0) {
                System.arraycopy(lumps, newIndex, lumps, newIndex+1, moveCount);
                System.arraycopy(patches, newIndex, patches, newIndex+1, moveCount);
            }
            lumps[newIndex] = lump;
            patches[newIndex] = patch;
            ++ numEntries;
        }
    }
    private void ensureCapacity(int cap) {
        while (lumps.length <= cap) {
            lumps =
                Arrays.copyOf(lumps, Math.max(lumps.length * 2, DEFAULT_CAPACITY));
        }
        while (patches.length <= cap) {
            patches =
                Arrays.copyOf(patches, Math.max(patches.length * 2, DEFAULT_CAPACITY));
        }
    }
    private int indexOf(int lump) {
        return Arrays.binarySearch(lumps, 0, numEntries, lump);
    }
    private int[] lumps;
    private int numEntries;
    private byte[][][] patches;
}
package pooling;

import java.util.Stack;

import p.mobj_t;

/** A convenient object pooling class, derived from the stock ObjectPool.
 *  
 *  It's about 50% faster than calling new, and MUCH faster than ObjectPool
 *  because it doesn't do that bullshit object cleanup every so often.
 * 
 */


public abstract class ObjectQueuePool<K>
{

	private static final boolean D=false;
	
    public ObjectQueuePool(long expirationTime)
    {
        locked = new Stack<K>();
        
    }

    protected abstract K create();

    public abstract boolean validate(K obj);

    public abstract void expire(K obj);

    public void drain(){
        locked.clear();
        }
    
    public K checkOut()
    {
        
        K t;
        if(!locked.isEmpty())
        {
            return locked.pop(); 

        }

        t = create();
        return t;
    }

    public void checkIn(K t)
    {
    	if (D) if (t instanceof mobj_t)
    	System.out.printf("Object %s returned to the pool\n",t.toString());
        locked.push(t);
    }

    protected Stack<K> locked;
   // private Hashtable<K,Long> unlocked;
}

package pooling;

import java.util.Enumeration;
import java.util.Hashtable;

import p.mobj_t;

/** A convenient object pooling class. Currently used for AudioChunks, but
 *  could be reused for UI events and other such things. Perhaps reusing it
 *  for mobj_t's is possible, but risky.
 * 
 */


public abstract class ObjectPool<K>
{

	private static final boolean D=false;
	
    public ObjectPool(long expirationTime)
    {
        this.expirationTime = expirationTime;
        locked = new Hashtable<K,Long>();
        unlocked = new Hashtable<K,Long>();
    }

    protected abstract K create();

    public abstract boolean validate(K obj);

    public abstract void expire(K obj);

    public synchronized K checkOut()
    {
        long now = System.currentTimeMillis();
        K t;
        if(unlocked.size() > 0)
        {
            Enumeration<K> e = unlocked.keys();
           // System.out.println((new StringBuilder("Pool size ")).append(unlocked.size()).toString());
            while(e.hasMoreElements()) 
            {
                t = e.nextElement();
                if(now - ((Long)unlocked.get(t)).longValue() > expirationTime)
                {
                	// object has expired
                	if (t instanceof mobj_t)
                	if (D) System.out.printf("Object %s expired\n",t.toString());
                    unlocked.remove(t);
                    expire(t);
                    t = null;
                } else
                {
                    if(validate(t))
                    {
                        unlocked.remove(t);
                        locked.put(t, Long.valueOf(now));
                        if (D) if (t instanceof mobj_t)
                        	System.out.printf("Object %s reused\n",t.toString());
                        return t;
                    }
                    
                    // object failed validation
                    unlocked.remove(t);
                    expire(t);
                    t = null;
                }
            }
        }

        t = create();
        locked.put(t, Long.valueOf(now));
        return t;
    }

    public synchronized void checkIn(K t)
    {
    	if (D) if (t instanceof mobj_t)
    	System.out.printf("Object %s returned to the pool\n",t.toString());
        locked.remove(t);
        unlocked.put(t, Long.valueOf(System.currentTimeMillis()));
    }

    private long expirationTime;
    protected Hashtable<K,Long> locked;
    private Hashtable<K,Long> unlocked;
}

package demo;

import w.IWritableDoomObject;
import defines.skill_t;

public interface IDoomDemo extends IWritableDoomObject{
	
    
    /** Vanilla end demo marker, to append at the end of recorded demos */
    
   public static final int DEMOMARKER =0x80;
   
    /** Get next demo command, in its raw format. Use
     * its own adapters if you need it converted to a 
     * standard ticcmd_t.
     *  
     * @return
     */
    IDemoTicCmd getNextTic();
    
    /** Record a demo command in the IDoomDemo's native format.
     * Use the IDemoTicCmd's objects adaptors to convert it.
     * 
     * @param tic
     */
    void putTic(IDemoTicCmd tic);

    int getVersion();

    void setVersion(int version);

    skill_t getSkill();

    void setSkill(skill_t skill);

    int getEpisode();

    void setEpisode(int episode);

    int getMap();

    void setMap(int map);

    boolean isDeathmatch();

    void setDeathmatch(boolean deathmatch);

    boolean isRespawnparm();
    
    void setRespawnparm(boolean respawnparm);

    boolean isFastparm();

    void setFastparm(boolean fastparm);
    
    boolean isNomonsters();

    void setNomonsters(boolean nomonsters);

    int getConsoleplayer();

    void setConsoleplayer(int consoleplayer);

    boolean[] getPlayeringame();

    void setPlayeringame(boolean[] playeringame);

    void resetDemo();

    


}

package demo;

import java.io.DataOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;

import doom.ticcmd_t;

import utils.C2JUtils;
import w.CacheableDoomObject;
import w.IWritableDoomObject;

/** A more lightweight version of ticcmd_t, which contains
 *  too much crap and redundant data. In order to keep demo
 *  loading and recording easier, this class contains only the
 *  necessary stuff to read/write from/to disk during VANILLA
 *  demos. It can be converted from/to ticcmd_t, if needed.
 * 
 * @author admin
 *
 */
public class VanillaTiccmd implements CacheableDoomObject, IDemoTicCmd,IWritableDoomObject{

    /** *2048 for move */
    public byte forwardmove;

    /** *2048 for move */
    public byte sidemove;

    /** <<16 for angle delta */
    public byte angleturn;
    public byte buttons; 

    /** Special note: the only occasion where we'd ever be interested
     *  in reading ticcmd_t's from a lump is when playing back demos.
     *  Therefore, we use this specialized reading method which does NOT,
     *  I repeat, DOES NOT set all fields and some are read differently.
     *  NOT 1:1 intercangeable with the Datagram methods!  
     * 
     */
    @Override
    public void unpack(ByteBuffer f)
            throws IOException {
        
        // MAES: the original ID code for reference.
        // demo_p++ is a pointer inside a raw byte buffer.
        
     //cmd->forwardmove = ((signed char)*demo_p++); 
     //cmd->sidemove = ((signed char)*demo_p++); 
     //cmd->angleturn = ((unsigned char)*demo_p++)<<8; 
     //cmd->buttons = (unsigned char)*demo_p++; 
        
        forwardmove=f.get();
        sidemove=   f.get();        
        // Even if they use the "unsigned char" syntax, angleturn is signed.
        angleturn=f.get();
        buttons=f.get();
        
    }
    
    /** Ditto, we only pack some of the fields.
     * 
     * @param f
     * @throws IOException
     */
    public void pack(ByteBuffer f)
            throws IOException {
        
        f.put(forwardmove);
        f.put(sidemove);
        f.put(angleturn);
        f.put(buttons);        
    }
    
    private static StringBuilder sb=new StringBuilder();
    
    public String toString(){
        sb.setLength(0);
        sb.append(" forwardmove ");
        sb.append(this.forwardmove); 
        sb.append(" sidemove ");
        sb.append(this.sidemove); 
        sb.append(" angleturn ");
        sb.append(this.angleturn); 
        sb.append(" buttons ");
        sb.append(Integer.toHexString(this.buttons));
        return sb.toString();
    }

    @Override
    public void decode(ticcmd_t dest) {
        // Decode
        dest.forwardmove=this.forwardmove;
        dest.sidemove=this.sidemove;
        dest.angleturn=(short) (this.angleturn<<8);
        dest.buttons=(char) (C2JUtils.toUnsignedByte(this.buttons));
        }

    @Override
    public void encode(ticcmd_t source) {
        // Note: we can get away with a simple copy because
        // Demoticcmds have already been "decoded".
        this.forwardmove=source.forwardmove;
        this.sidemove=source.sidemove;
        // OUCH!!! NASTY PRECISION REDUCTION... but it's the
        // One True Vanilla way.
        this.angleturn=(byte) (source.angleturn>>>8);
        this.buttons=(byte) (source.buttons&0x00FF);
    }

    @Override
    public void write(DataOutputStream f)
            throws IOException {
        f.writeByte(forwardmove);
        f.writeByte(sidemove);
        f.writeByte(angleturn);
        f.writeByte(buttons); 
    }

}

package demo;

import static data.Limits.MAXPLAYERS;

import java.io.DataOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;

import utils.C2JUtils;
import w.CacheableDoomObject;
import w.DoomBuffer;
import w.DoomIO;
import defines.skill_t;

public class VanillaDoomDemo implements IDoomDemo,CacheableDoomObject{

	 // This stuff is in the demo header, in the order it appears
	 // However everything is byte-sized when read from disk or to memory.
	 public int version; 
	 public skill_t skill;
	 public int episode;
	 public int map;
	 public boolean deathmatch;
	 public boolean respawnparm;
	 public boolean fastparm;
	 public boolean nomonsters;
	 public int consoleplayer;
	 public boolean[] playeringame; // normally MAXPLAYERS (4) for vanilla.
	 
	 protected int p_demo;
	 
     //  After that, demos contain a sequence of ticcmd_t's to build dynamically at
     // load time or when recording. This abstraction allows arbitrary demo sizes
     // and easy per-step handling, and even changes/extensions. Just make sure
     // that ticcmd_t's are serializable!
     // Also, the format used in demo lumps is NOT the same as in datagrams/network
     // (e.g. there is no consistency) and their handling is modified.
     
     VanillaTiccmd[] commands;
     List<IDemoTicCmd> demorecorder;
     
     public VanillaDoomDemo(){
         this.demorecorder=new  ArrayList<IDemoTicCmd> ();
     }
     
	 public void unpack(ByteBuffer b){
	     
	     // Just the Header info for vanilla should be 13 bytes.
	     // 1 byte at the end is the end-demo marker
	     // So valid vanilla demos should have sizes that
	     // fit the formula 14+4n, since each vanilla 
	     // demo ticcmd_t is 4 bytes.
	     int lens=(b.limit()-13)/4;	     
	     boolean vanilla=(b.limit()==(14+4*lens));
	     
	     // Minimum valid vanilla demo should be 14 bytes...in theory.
     if (b.limit()<14) {
         // Use skill==null as an indicator that loading didn't go well.
         skill=null;
         return;
     }
     
     version=b.get();		
     
     try {
     skill = skill_t.values()[b.get()];
     } catch (Exception e){
         skill=null;
     }
     episode = b.get(); 
     map = b.get(); 
     deathmatch = b.get()!=0;
     respawnparm = b.get()!=0;
     fastparm = b.get()!=0;
     nomonsters = b.get()!=0;
     consoleplayer = b.get();
     
     playeringame=new boolean[MAXPLAYERS];
     
     for (int i=0 ; i<MAXPLAYERS ; i++) 
     playeringame[i] = b.get()!=0;
     
     
     this.commands=new VanillaTiccmd[lens];
     C2JUtils.initArrayOfObjects(this.commands, VanillaTiccmd.class);
     
     try {
		DoomBuffer.readObjectArray(b, this.commands, lens);
	} catch (IOException e) {
	    skill=null;
	    return;
	}

     }

    @Override
    public IDemoTicCmd getNextTic() {
        if ((commands!=null)&&(p_demo<commands.length)){

        return commands[p_demo++];
        }
        else return null;
    }

    @Override
    public void putTic(IDemoTicCmd tic) {
        demorecorder.add(tic);

    }

    @Override
    public int getVersion() {
        return version;
    }

    @Override
    public void setVersion(int version) {
        this.version = version;
    }

    @Override
    public skill_t getSkill() {
        return skill;
    }

    @Override
    public void setSkill(skill_t skill) {
        this.skill = skill;
    }

    @Override
    public int getEpisode() {
        return episode;
    }

    @Override
    public void setEpisode(int episode) {
        this.episode = episode;
    }

    @Override
    public int getMap() {
        return map;
    }

    @Override
    public void setMap(int map) {
        this.map = map;
    }

    @Override
    public boolean isDeathmatch() {
        return deathmatch;
    }

    @Override
    public void setDeathmatch(boolean deathmatch) {
        this.deathmatch = deathmatch;
    }

    @Override
    public boolean isRespawnparm() {
        return respawnparm;
    }

    @Override
    public void setRespawnparm(boolean respawnparm) {
        this.respawnparm = respawnparm;
    }

    @Override
    public boolean isFastparm() {
        return fastparm;
    }

    @Override
    public void setFastparm(boolean fastparm) {
        this.fastparm = fastparm;
    }

    @Override
    public boolean isNomonsters() {
        return nomonsters;
    }

    @Override
    public void setNomonsters(boolean nomonsters) {
        this.nomonsters = nomonsters;
    }

    @Override
    public int getConsoleplayer() {
        return consoleplayer;
    }

    @Override
    public void setConsoleplayer(int consoleplayer) {
        this.consoleplayer = consoleplayer;
    }
    
    @Override
    public boolean[] getPlayeringame() {
        return playeringame;
    }

    @Override
    public void setPlayeringame(boolean[] playeringame) {
        this.playeringame = playeringame;
    }

    @Override
    public void write(DataOutputStream f)
            throws IOException {
        
        f.writeByte(version);        
        f.writeByte(skill.ordinal()); 
        f.writeByte(episode);
        f.writeByte(map);
        f.writeBoolean(deathmatch);
        f.writeBoolean(respawnparm);
        f.writeBoolean(fastparm);
        f.writeBoolean(nomonsters);
        f.writeByte(consoleplayer);
        DoomIO.writeBoolean(f,this.playeringame,MAXPLAYERS);
        for (IDemoTicCmd i: demorecorder) {            
            i.write(f);
        }
        f.writeByte(DEMOMARKER);
        
        // TODO Auto-generated method stub
        
    }

    @Override
    public void resetDemo() {
        this.p_demo=0;
        
    }
    
    /////////////////////// VARIOUS BORING GETTERS /////////////////////
    
    
	
}

package demo;

import w.IWritableDoomObject;
import doom.ticcmd_t;

/** Demo Tic Commands can be read/written to disk/buffers,
 *  and are not necessarily equal to the in-game ticcmd_t.
 *  Thus, it's necessary for them to implement some
 *  adaptor method (both ways).
 *  
 * @author admin
 *
 */

public interface IDemoTicCmd extends IWritableDoomObject{
    /** Decode this IDemoTicCmd into a standard ticcmd_t. 
     * 
     * @param source
     */
    public void decode(ticcmd_t dest);
    
    /** Encode this IDemoTicCmd from a standard ticcmd_t.
     * 
     * @param dest
     */
    public void encode(ticcmd_t source);
    
}

package m;

import java.io.IOException;

public interface ISyncLogger {

	public void debugStart() throws IOException;
	public void debugEnd();
	public void sync(String format, Object ... args);
}


package m;

import data.mobjtype_t;
import doom.think_t;

public interface IRandom {
	public int P_Random ();
	public int M_Random ();
	public void ClearRandom ();
	public int getIndex();
	public int P_Random(int caller);
	public int P_Random(String message);
	public int P_Random(think_t caller, int sequence);
	public int P_Random(think_t caller, mobjtype_t type,int sequence);
}

package m;

import static data.Limits.*;

/** A fucked-up bounding box class.
 *  Fucked-up  because it's supposed to wrap fixed_t's.... no fucking way I'm doing
 *  this with fixed_t objects.
 *  
 * @author admin
 *
 */

public class BBox {

	public static final int BOXTOP = 0;
	public static final int BOXBOTTOM = 1;
	public static final int BOXLEFT = 2;
	public static final int BOXRIGHT = 3;
	/** (fixed_t) */
	public int[] bbox;

	/** Points of the bbox as an object */

	public BBox() {
		bbox = new int[4];
	}

	// Static method

	public static void ClearBox(fixed_t[] box) {
		box[BOXRIGHT].set(MININT);
		box[BOXTOP].set(MININT);
		box[BOXLEFT].set(MAXINT);
		box[BOXBOTTOM].set(MAXINT);
	}

	// Instance method

	public void ClearBox() {
		bbox[BOXRIGHT]=(MININT);
		bbox[BOXTOP]=(MININT);
		bbox[BOXLEFT]=(MAXINT);
		bbox[BOXBOTTOM]=(MAXINT);
	}

	public static void AddToBox(fixed_t[] box, fixed_t x, fixed_t y) {
		if (x.compareTo(box[BOXLEFT]) < 0)
			box[BOXLEFT].copy(x);
		else if (x.compareTo(box[BOXRIGHT]) > 0)
			box[BOXRIGHT].copy(x);
		if (y.compareTo(box[BOXBOTTOM]) < 0)
			box[BOXBOTTOM] = y;
		else if (y.compareTo(box[BOXTOP]) > 0)
			box[BOXTOP] = y;
	}

	public void AddToBox(fixed_t x, fixed_t y) {
		if (x.compareTo(bbox[BOXLEFT]) < 0)
			bbox[BOXLEFT]=x.val;
		else if (x.compareTo(bbox[BOXRIGHT]) > 0)
			bbox[BOXRIGHT]=x.val;
		if (y.compareTo(bbox[BOXBOTTOM]) < 0)
			bbox[BOXBOTTOM] = y.val;
		else if (y.compareTo(bbox[BOXTOP]) > 0)
			bbox[BOXTOP] = y.val;
	}

	/**
	 * MAES: Keeping with C's type (in)consistency, we also allow to input ints
	 * -_-
	 * 
	 * @param x
	 * @param y
	 */
	public void AddToBox(int x, int y) {
		if (x < bbox[BOXLEFT])
			bbox[BOXLEFT]=(x);
		if (x > bbox[BOXRIGHT])
			bbox[BOXRIGHT]=(x);
		if (y < bbox[BOXBOTTOM])
			bbox[BOXBOTTOM]=(y);
		if (y > bbox[BOXTOP])
			bbox[BOXTOP]=(y);
	}

	/**
	 * R_AddPointToBox Expand a given bbox so that it encloses a given point.
	 * 
	 * @param x
	 * @param y
	 * @param box
	 */

	public static void AddPointToBox(int x, int y, fixed_t[] box) {
		if (x < box[BOXLEFT].val)
			box[BOXLEFT].set(x);
		if (x > box[BOXRIGHT].val)
			box[BOXRIGHT].set(x);
		if (y < box[BOXBOTTOM].val)
			box[BOXBOTTOM].set(y);
		if (y > box[BOXTOP].val)
			box[BOXTOP].set(y);
	}

	/**
	 * R_AddPointToBox Expand this bbox so that it encloses a given point.
	 * 
	 * @param x
	 * @param y
	 * @param box
	 */

	public void AddPointToBox(int x, int y) {
		if (x < bbox[BOXLEFT])
			bbox[BOXLEFT]=x;
		if (x > bbox[BOXRIGHT])
			bbox[BOXRIGHT]=x;
		if (y < bbox[BOXBOTTOM])
			bbox[BOXBOTTOM]=y;
		if (y > bbox[BOXTOP])
			bbox[BOXTOP]=y;
	}

	public int get(int BOXCOORDS){
	    return this.bbox[BOXCOORDS];
	}
	
    public void set(int BOXCOORDS, int val){
        this.bbox[BOXCOORDS]=val;
    }

    public static void ClearBox(int[] bbox) {
        bbox[BOXRIGHT]=(MININT);
        bbox[BOXTOP]=(MININT);
        bbox[BOXLEFT]=(MAXINT);
        bbox[BOXBOTTOM]=(MAXINT);
    }

    public static void AddToBox(int[] box, int x, int y) {
        if (x < box[BOXLEFT])
            box[BOXLEFT]=x;
        if (x > box[BOXRIGHT])
            box[BOXRIGHT]=x;
        if (y < box[BOXBOTTOM])
            box[BOXBOTTOM]=y;
        if (y > box[BOXTOP])
            box[BOXTOP]=y;        
    }
	
}

package m;

import data.Defines;
// Emacs style mode select   -*- C++ -*- 
//-----------------------------------------------------------------------------
//
// $Id: fixed_t.java,v 1.14 2011/10/25 19:52:13 velktron Exp $
//
// Copyright (C) 1993-1996 by id Software, Inc.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
//
// DESCRIPTION:
//	Fixed point implementation.
//
//-----------------------------------------------------------------------------

//
// Fixed point, 32bit as 16.16.
//
// Most functionality of C-based ports is preserved, EXCEPT that there's
// no typedef of ints into fixed_t, and that there's no actual object fixed_t
// type that is actually instantiated in the current codebase, for performance reasons.
// There are still remnants of a full OO implementation that still do work, 
// and the usual FixedMul/FixedDiv etc. methods are still used throughout the codebase,
// but operate on int operants (signed, 32-bit integers).

public class fixed_t implements Comparable<fixed_t>{

    public static final int FRACBITS =       16;
    public static final int FRACUNIT =       (1<<FRACBITS);
    public static final int MAPFRACUNIT =    FRACUNIT/Defines.TIC_MUL;
    public int val;
    
    public fixed_t(){
        this.set(0);
    }
    
    public int get(){
        return val;
    }
    
    public void set(int val){
        this.val=val;
    }

    public void copy(fixed_t a){
        this.set(a.get());
    }

    
    public boolean equals(fixed_t a){
        return (this.get()==a.get())?true:false;
    }

    public static boolean equals(fixed_t a, fixed_t b){
        return (a.get()==b.get())?true:false;
    }    
    
    public fixed_t(int val){
        this.val=val;
    }
    
public fixed_t(fixed_t x) {
        this.val=x.val;
    }

public static final String rcsid = "$Id: fixed_t.java,v 1.14 2011/10/25 19:52:13 velktron Exp $";

/** Creates a new fixed_t object for the result a*b
 * 
 * @param a
 * @param b
 * @return
 */

public static int FixedMul
( fixed_t	a,
  fixed_t	b )
{
    return (int)(((long) a.val * (long ) b.val) >>> FRACBITS);
}

public static int FixedMul
( int   a,
  fixed_t   b )
{
    return (int)(((long) a * (long ) b.val) >>> FRACBITS);
}

public static final int FixedMul
( int   a,
  int   b )
{
    return (int)(((long) a * (long ) b) >>> FRACBITS);
}


/** Returns result straight as an int..
 * 
 * @param a
 * @param b
 * @return
 */

public static int FixedMulInt
( fixed_t   a,
        fixed_t   b )
{
    return (int)(((long) a.val * (long ) b.val) >> FRACBITS);
}

/** In-place c=a*b
 * 
 * @param a
 * @param b
 * @param c
 */

public final static void FixedMul
( fixed_t   a,
        fixed_t   b,
        fixed_t c)
{
    c.set((int)(((long) a.val * (long ) b.val) >> FRACBITS));
}


/** In-place this=this*a
 * 
 * @param a
 * @param b
 * @param c
 */

public final void FixedMul
( fixed_t   a)
{
    this.set((int)(((long) a.val * (long ) this.val) >> FRACBITS));
}


public final static int
FixedDiv
( int   a,
  int   b )
{
	  if ((Math.abs(a) >> 14) >= Math.abs(b))
	    {
		return (a^b) < 0 ? Integer.MIN_VALUE : Integer.MAX_VALUE;
	    }
	    else
	    {
		long result;

		result = ((long) a << 16) / b;

		return (int) result;
	    }
}


public final static int
FixedDiv2
( int   a,
  int   b )
{

    
    int c;
    c = (int)(((long)a<<16) / (long)b);
    return c;
    
    /*
    double c;

    c = ((double)a) / ((double)b) * FRACUNIT;

  if (c >= 2147483648.0 || c < -2147483648.0)
      throw new ArithmeticException("FixedDiv: divide by zero");
 
 return (int)c;*/
}

@Override
public int compareTo(fixed_t o) {
    if (o.getClass()!=fixed_t.class) return -1;
    if (this.val==((fixed_t)(o)).val) return 0;
    if (this.val>((fixed_t)(o)).val) return 1;
    else return -1;
    }

public int compareTo(int o) {
    if (this.val==o) return 0;
    if (this.val>o) return 1;
    else return -1;
    }

public void add(fixed_t a){
    this.val+=a.val;
}

public void sub(fixed_t a){
    this.val-=a.val;
}

public void add(int a){
    this.val+=a;
}

public void sub(int a){
    this.val-=a;
}

/** a+b
 * 
 * @param a
 * @param b
 * @return
 */

public static int add(fixed_t a,fixed_t b){
    return a.val+b.val;
}

/** a-b
 * 
 * @param a
 * @param b
 * @return
 */

public static int sub(fixed_t a,fixed_t b){
    return a.val-b.val;
}

/** c=a+b
 * 
 * @param c
 * @param a
 * @param b
 */

public static void add(fixed_t c, fixed_t a,fixed_t b){
    c.val= a.val+b.val;
}

/** c=a-b
 * 
 * @param c
 * @param a
 * @param b
 */

public static void sub(fixed_t c,fixed_t a,fixed_t b){
    c.val= a.val-b.val;
}


/** Equals Zero
 * 
 * @return
 */

public boolean isEZ() {
    return (this.val==0);
    }

/** Greater than Zero
 * 
 * @return
 */

public boolean isGZ() {
    return (this.val>0);
    }

/** Less than Zero
 * 
 * @return
 */
public boolean isLZ() {
    return (this.val<0);
    }

// These are here to make easier handling all those methods in R 
// that return "1" or "0" based on one result.

public int oneEZ(){
    return (this.val==0)?1:0;
}

public int oneGZ(){
    return (this.val>0)?1:0;
}

public int oneLZ(){
    return (this.val<0)?1:0;
}


}
package m;

// Emacs style mode select -*- C++ -*-
// -----------------------------------------------------------------------------
//
// $Id: cheatseq_t.java,v 1.8 2011/11/01 23:47:50 velktron Exp $
//
// Copyright (C) 1993-1996 by id Software, Inc.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// $Log: cheatseq_t.java,v $
// Revision 1.8  2011/11/01 23:47:50  velktron
// Added constructor method to start from unscrambled strings.
//
// Revision 1.7  2011/05/06 14:00:54  velktron
// More of _D_'s changes committed.
//
// Revision 1.6  2010/12/14 00:53:32  velktron
// Some input sanitizing. Far from perfect but heh...
//
// Revision 1.5  2010/08/25 00:50:59  velktron
// Some more work...
//
// Revision 1.4  2010/07/21 11:41:47  velktron
// Work on menus...
//
// Revision 1.3 2010/07/20 15:52:56 velktron
// LOTS of changes, Automap almost complete. Use of fixed_t inside methods
// severely limited.
//
// Revision 1.2 2010/07/03 23:24:13 velktron
// Added a LOT of stuff, like Status bar code & objects. Now we're cooking with
// gas!
//
// Revision 1.1 2010/06/30 08:58:50 velktron
// Let's see if this stuff will finally commit....
//
//
// Most stuff is still being worked on. For a good place to start and get an
// idea of what is being done, I suggest checking out the "testers" package.
//
// Revision 1.1 2010/06/29 11:07:34 velktron
// Release often, release early they say...
//
// Commiting ALL stuff done so far. A lot of stuff is still broken/incomplete,
// and there's still mixed C code in there. I suggest you load everything up in
// Eclpise and see what gives from there.
//
// A good place to start is the testers/ directory, where you can get an idea of
// how a few of the implemented stuff works.
//
//
// DESCRIPTION:
// Cheat sequence checking.
//
// -----------------------------------------------------------------------------

/**
 * Cheat sequence checking. MAES: all of this stuff used to be in cheat.h and
 * cheat.c, but seeing how all manipulation is done on "cheatseq_t" objects, it
 * makes more sense to move this functionality in here, and go OO all the way.
 * So away with the fugly static methods!!!
 */

public class cheatseq_t {

    // This holds the actual data (was a char*).
    public char[] sequence;

    // This is used as a pointer inside sequence.
    // Was a char*, but in Java it makes more sense to have it as an int.
    public int p;

    public cheatseq_t(char[] sequence, int p) {
        this.sequence = sequence;
        this.p = p;
    }

    public cheatseq_t(char[] sequence) {
        this.sequence = sequence;
        this.p = 0;
    }

    public cheatseq_t(String sequence, boolean prescrambled){
    	if (prescrambled){
    		this.sequence=sequence.toCharArray();
    		p=0;
    	} else {
    		this.sequence=scrambleString(sequence);
    		p=0;
    	}    	
    	}
    
    /**
     * This was in cheat.c, but makes more sense to be used as an
     * initializer/constructor.
     */
    public void GetParam(char[] buffer) {

        // char[] p;
        char c;
        int ptr = 0;

        // p = this.sequence;
        // Increments pointer until the sequence reaches its first internal "1"
        // ???
        while (this.sequence[ptr++] != 1)
            ;
        int bptr = 0;
        // Now it copies the contents of this cheatseq_t into buffer...and nils
        // it???
        do {
            c = this.sequence[ptr];
            buffer[bptr++] = c;
            this.sequence[ptr++] = 0;
        } while ((c != 0) && (this.sequence[ptr] != 0xff));

        if (this.sequence[ptr] == 0xff)
            buffer[bptr] = 0;

    }

    /**
     * Called in st_stuff module, which handles the input. Returns true if the
     * cheat was successful, false if failed. MAES: Let's make this boolean.
     * 
     * @param cht
     * @param key
     * @return
     */

    public boolean CheckCheat(cheatseq_t cht, char key) {
        boolean rc = false;

        if (cht.p < 0)
            cht.p = 0; // initialize if first time

        if (cht.p == 0)
            // This actually points inside "sequence"
            // *(cht->p++) = key;
            cht.sequence[cht.p++] = key;
        else if (cheat_xlate_table[(char) key] == cht.sequence[cht.p])
            cht.p++;
        else
            // Failure: back to the beginning.
            cht.p = 0;

        if (cht.sequence[cht.p] == 1)
            cht.p++;
        else if (cht.sequence[cht.p] == 0xff) // end of sequence character
        {
            cht.p = 0;
            rc = true;
        }

        return rc;
    }

    /**
     * Called in st_stuff module, which handles the input. Returns true if the
     * cheat was successful, false if failed. MAES: Let's make this boolean.
     * 
     * @param key
     * @return
     */

    public boolean CheckCheat(char key) {
        boolean rc = false;



        if (this.p < 0)
            this.p = 0; // initialize if first time

        if (sequence[p] == 0)
            // This actually points inside "sequence"
            // *(cht->p++) = key;
            sequence[p++] = key;
            //p++;  //_D_: this fixed cheat with parm problem (IDCLIP)
        else if (cheat_xlate_table[(char) key] == sequence[p])
            p++;
        else
            // Failure: back to the beginning.
            p = 0;
        if (sequence[p] == 1)
            p++;
        else if (sequence[p] == 0xff) // end of sequence character
        {
            p = 0;
            rc = true;
        }

        return rc;
    }

    /**
     * Scrambles a character. 7 -> 0 6 -> 1 5 -> 5 4 -> 3 3 -> 4 2 -> 2 1 -> 6 0
     * -> 7
     * 
     * @param a
     * @return
     */
    public static char SCRAMBLE(char a) {
        return (char) ((((a) & 1) << 7) + (((a) & 2) << 5) + ((a) & 4)
                + (((a) & 8) << 1) + (((a) & 16) >>> 1) + ((a) & 32)
                + (((a) & 64) >>> 5) + (((a) & 128) >>> 7));
    }
    
    public static char[] scrambleString(String s){
    	
    	char[] tmp=new char[s.length()+1];
    	for (int i=0;i<s.length();i++){
    		tmp[i]=SCRAMBLE(s.charAt(i));
    	}    	
    	tmp[s.length()]=0xff;    	
    	
    	return tmp;    	
    }

    /**
     * These should be common among all instances, unless for soooome reason you
     * need multiple different such tables.
     */
    public static boolean firsttime = true;

    public static char[] cheat_xlate_table = new char[256];

   static {
       if (firsttime) {
           firsttime = false;
           for (char i = 0; i < 256; i++)
               cheat_xlate_table[i] = SCRAMBLE(i);
       }
   }
}

package m;

import doom.DoomStatus;
import doom.IDoomGame;
import hu.HU;
import i.DoomStatusAware;
import i.IDoomSystem;
import rr.RendererState;
import s.IDoomSound;
import timing.ITicker;
import v.DoomVideoRenderer;
import v.IVideoScaleAware;
import w.IWadLoader;

public abstract class AbstractDoomMenu
        implements IDoomMenu {

    ////////////////////// CONTEXT ///////////////////
    
    DoomStatus DM;
    IDoomGame DG;
    IWadLoader W;
    DoomVideoRenderer V;
    HU HU;
    RendererState R;
    IDoomSystem I;
    IDoomSound S;
    ITicker TICK;
    
    @Override
    public void updateStatus(DoomStatus DS) {
           this.DM=DS.DM;
           this.DG=DS.DG;
            this.V=DM.V;
            this.W=DM.W;
            this.HU=DM.HU;
            this.I=DM.I;
            this.S=DM.S;
            this.R=(RendererState) DM.R;
            this.TICK=DM.TICK;
        
    }
    
}

package m;

import i.DoomSystem;

import java.awt.image.BufferedImage;
import java.awt.image.DataBufferByte;
import java.awt.image.DataBufferInt;
import java.awt.image.DataBufferUShort;
import java.awt.image.IndexColorModel;
import java.io.BufferedInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.util.Arrays;
import javax.imageio.ImageIO;

import w.IWritableDoomObject;

// Emacs style mode select   -*- C++ -*- 
//-----------------------------------------------------------------------------
//
// $Id: MenuMisc.java,v 1.29 2012/09/24 17:16:22 velktron Exp $
//
// Copyright (C) 1993-1996 by id Software, Inc.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
//
// DESCRIPTION:
//	Main loop menu stuff.
//	Default Config File.
//	PCX Screenshots.
//
//-----------------------------------------------------------------------------

public abstract class MenuMisc{

    public static final String rcsid = "$Id: MenuMisc.java,v 1.29 2012/09/24 17:16:22 velktron Exp $";
  
    //
    // SCREEN SHOTS
    //
  
    public static boolean WriteFile(String name, byte[] source, int length) {
        OutputStream handle;
        try {
            handle = new  FileOutputStream(name);
            handle.write(source, 0, length);
            handle.close();
        } catch (Exception e) {
            DoomSystem.MiscError("Couldn't write file %s (%s)", name, e.getMessage());
            return false;
        }

        return true;
    }

    public static boolean WriteFile(String name, IWritableDoomObject source) {
        DataOutputStream handle;
        try {
            handle = new DataOutputStream(new FileOutputStream(name));
            source.write(handle);
            handle.close();
        } catch (Exception e) {
            DoomSystem.MiscError("Couldn't write file %s (%s)", name, e.getMessage());
            return false;
        }

        return true;
    }


    /** M_ReadFile 
     *  This version returns a variable-size ByteBuffer, so
     *  we don't need to know a-priori how much stuff to read.
     * 
     */
    public static ByteBuffer ReadFile(String name) {
        BufferedInputStream handle;
        int length;
        // struct stat fileinfo;
        ByteBuffer buf;
        try {
            handle = new BufferedInputStream(new FileInputStream(name));
            length = (int) handle.available();
            buf = ByteBuffer.allocate(length);
            handle.read(buf.array());
            handle.close();
        } catch (Exception e) {
            DoomSystem.MiscError("Couldn't read file %s (%s)", name, e.getMessage());
            return null;
        }

        return buf;
    }

    /** M_ReadFile */
    public static int ReadFile(String name, byte[] buffer) {
    	BufferedInputStream handle;
        int count, length;
        // struct stat fileinfo;
        byte[] buf;
        try {
            handle = new BufferedInputStream(new FileInputStream(name));
            length = (int) handle.available();
            buf = new byte[length];
            count = handle.read(buf);
            handle.close();

            if (count < length)
                throw new Exception("Read only " + count + " bytes out of "
                    + length);

        } catch (Exception e) {
            DoomSystem.MiscError("Couldn't read file %s (%s)", name, e.getMessage());
            return -1;
        }
        System.arraycopy(buf, 0, buffer, 0, Math.min(count,buffer.length));
        return length;
    }

    //
 // WritePCXfile
 //
 public static void
 WritePCXfile
 ( String        filename,
   byte[]     data,
   int       width,
   int       height,
   byte[]     palette )
 {
     int     length;
     pcx_t  pcx;
     byte[]   pack;
     
     pcx = new pcx_t();
     pack=new byte[width*height*2]; // allocate that much data, just in case.

     pcx.manufacturer = 0x0a;       // PCX id
     pcx.version = 5;           // 256 color
     pcx.encoding = 1;          // uncompressed
     pcx.bits_per_pixel = 8;        // 256 color
     pcx.xmin = 0;
     pcx.ymin = 0;
     pcx.xmax = (char) (width-1);
     pcx.ymax = (char) (height-1);
     pcx.hres = (char) width;
     pcx.vres = (char) height;
     // memset (pcx->palette,0,sizeof(pcx->palette));
     pcx.color_planes = 1;      // chunky image
     pcx.bytes_per_line = (char) width;
     pcx.palette_type = 2;   // not a grey scale
     //memset (pcx->filler,0,sizeof(pcx->filler));


     // pack the image
     //pack = &pcx->data;
     int p_pack=0;
     
     for (int i=0 ; i<width*height ; i++)
     {
     if ( (data[i] & 0xc0) != 0xc0)
         pack[p_pack++] = data[i];
     else
     {
         pack[p_pack++] = (byte) 0xc1;
         pack[p_pack++] = data[i];
     }
     }
     
     // write the palette
     pack[p_pack++] = 0x0c; // palette ID byte
     for (int i=0 ; i<768 ; i++)
         pack[p_pack++] = palette[i];
     
     // write output file
     length = p_pack;
     pcx.data=Arrays.copyOf(pack, length);
     
     DataOutputStream f=null;
    try {
        f = new DataOutputStream(new FileOutputStream(filename));
        
    } catch (FileNotFoundException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
     
    try {
        //f.setLength(0);
        pcx.write(f);
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    
     }
    
    public abstract boolean getShowMessages();

    public abstract void setShowMessages(boolean val);

	public static void WritePNGfile(String imagename, short[] linear,
			int width, int height) {
		
		BufferedImage buf=new BufferedImage(width,height,BufferedImage.TYPE_USHORT_555_RGB);
		DataBufferUShort sh=(DataBufferUShort) buf.getRaster().getDataBuffer();
		short[] shd=sh.getData();
		System.arraycopy(linear,0,shd,0,Math.min(linear.length,shd.length));
		try {
			ImageIO.write(buf, "PNG",new File(imagename));
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		// TODO Auto-generated method stub
		
	}

	public static void WritePNGfile(String imagename, int[] linear,
            int width, int height) {
        
        BufferedImage buf=new BufferedImage(width,height,BufferedImage.TYPE_INT_ARGB);
        DataBufferInt sh=(DataBufferInt) buf.getRaster().getDataBuffer();
        int[] shd=sh.getData();
        System.arraycopy(linear,0,shd,0,Math.min(linear.length,shd.length));
        try {
            ImageIO.write(buf, "PNG",new File(imagename));
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        // TODO Auto-generated method stub
        
    }
	
	public static void WritePNGfile(String imagename, byte[] linear,
            int width, int height,IndexColorModel icm) {
        BufferedImage buf=new BufferedImage(width,height,BufferedImage.TYPE_BYTE_INDEXED,icm);
        DataBufferByte sh=(DataBufferByte) buf.getRaster().getDataBuffer();
        byte[] shd=sh.getData();
        System.arraycopy(linear,0,shd,0,Math.min(linear.length,shd.length));
        try {
            ImageIO.write(buf, "PNG",new File(imagename));
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        // TODO Auto-generated method stub
        
    }
	
}

// $Log: MenuMisc.java,v $
// Revision 1.29  2012/09/24 17:16:22  velktron
// Massive merge between HiColor and HEAD. There's no difference from now on, and development continues on HEAD.
//
// Revision 1.28.2.4  2012/09/24 16:57:43  velktron
// Addressed generics warnings.
//
// Revision 1.28.2.3  2012/09/17 15:58:58  velktron
// Defaults loading & handling moved out to variables management subsystem
//
// Revision 1.28.2.2  2011/11/18 21:37:59  velktron
// Saves PNGs now.
//
// Revision 1.28.2.1  2011/11/14 00:27:11  velktron
// A barely functional HiColor branch. Most stuff broken. DO NOT USE
//
// Revision 1.28  2011/10/25 19:52:03  velktron
// Using buffered I/O when possible
//
// Revision 1.27  2011/10/24 02:11:27  velktron
// Stream compliancy
//
// Revision 1.26  2011/07/30 22:04:30  velktron
// Removed unused imports (including one that would cause problems compiling with OpenJDK).
//
// Revision 1.25  2011/07/15 13:53:52  velktron
// Implemented WritePCXFile, at last.
//
// Revision 1.24  2011/06/03 16:37:09  velktron
// Readfile will only read at most as much as the buffer allows.
//
// Revision 1.23  2011/05/31 13:33:54  velktron
// -verbosity
//
// Revision 1.22  2011/05/31 09:57:45  velktron
// Fixed broken parsing of unspaced strings. 
// It's never fun having to come up with your own function for string manipulation!
//
// Revision 1.21  2011/05/30 15:46:50  velktron
// AbstractDoomMenu implemented.
//
// Revision 1.20  2011/05/26 17:54:16  velktron
// Removed some Menu verbosity, better defaults functionality.
//
// Revision 1.19  2011/05/26 13:39:15  velktron
// Now using ICommandLineManager
//
// Revision 1.18  2011/05/24 17:46:03  velktron
// Added vanilla default.cfg loading.
//
package m;

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
import java.util.StringTokenizer;

import doom.ICommandLineManager;

import utils.C2JUtils;

/** Variables manager registers and retrieves variables (or "settings")
 *  For the purposes of Doom, three types will suffice: 
 *  boolean, integer (maybe float?), and string.
 *  
 *  Variables can be registered at startup, through *.cfg files,
 *  and through a (future) console. They can also be deleted 
 *  and updated.
 *  
 *  The variables manager just has the job of managing them. 
 *  Applying/making use of the variables themselves is the job 
 *  of the code that specifically uses them. 
 *  
 *  Proposed way of proceeding: making an "IUseVariables" interface,
 *  which is module-specific, and is called whenever new settings must
 *  be applied.
 *  
 *  E.g. stuff is read from config.cfg, and the the updateSettings()
 *  method of e.g. DoomMain is called, applying whatever changes need 
 *  to be done. Same thing with menus.
 *  
 *  TODO: a similar system was being planned for the CommandLine manager,
 *  but never implemented, wtf.... fix now?
 *  
 */

public class VarsManager implements IVariablesManager {
    
    private final HashMap<String,DoomSetting> settings;
    private final ICommandLineManager CLM;
    
    public VarsManager(ICommandLineManager CLM){
        this.settings=new HashMap<String,DoomSetting>();
        this.CLM=CLM;
    }
    
    @Override
    public DoomSetting getSetting(String name){
        
        DoomSetting tmp=settings.get(name);
        if (tmp!=null) {
            return tmp;
        }
        
        // Good question...what to do here?
        return DoomSetting.NULL_SETTING;
    }
    
    @Override
    public DoomSetting getSetting(Settings name){
        
        DoomSetting tmp=settings.get(name.name());
        if (tmp!=null) {
            return tmp;
        }
        
        // Good question...what to do here?
        return DoomSetting.NULL_SETTING;
    }
    
    @Override
    public boolean isSettingLiteral(String name,String value){
        
        return getSetting(name).getString().equalsIgnoreCase(value);
    }
    
    
    /** Creates a new setting, overriding any existing ones */

    @Override
    public void putSetting(String name,Object value,boolean persist){        
        DoomSetting tmp=new DoomSetting(name,value.toString(),persist);
        settings.put(name,tmp);
    }

    @Override
    public void putSetting(Settings name,Object value){        
        DoomSetting tmp=new DoomSetting(name.name(),value.toString(),true);
        settings.put(name.name(),tmp);
    }
    
    @Override
    public void putSetting(String name,Object value){        
        DoomSetting tmp=new DoomSetting(name,value.toString(),true);
        settings.put(name,tmp);
    }
    
    //
    // M_LoadDefaults
    //

    @Override
    public void LoadDefaults (String defaultfile)
    {
        int     i;
        BufferedReader  in;

        // Set everything to base values. These are persistent.
        int numdefaults = Settings.values().length;
        for (i=0 ; i<numdefaults ; i++){            
            putSetting(Settings.values()[i].name(),Settings.values()[i].value,true);
        }

        try {

            // read the file in, overriding any set defaults
            in = new BufferedReader(new InputStreamReader(new BufferedInputStream(new FileInputStream(defaultfile))));
            if (in!=null)
            {
                String name = null, value = null;
                String s;
                
                // Let's make this the sane way...read a string.
                while (( s=in.readLine())!=null)
                {

                    StringTokenizer tk=new StringTokenizer(s);

                    // They should be exactly two.
                    int tokens=tk.countTokens();
                    if (tokens==2){
                        name=tk.nextToken();
                        value=tk.nextToken();
                        
                        // TODO default value?
                        if (value==null) value="0";
                    } else if (tokens>2){
                        // Not clear what to do in case of multiple values on
                        // the same line. Either introduce multi-value vars (ugh...)
                        // or "join" the tokens, assuming that they are unquoted strings.
                        name=tk.nextToken();
                        value = C2JUtils.unquote(s,'"');
                        if (value==null) continue;
                    }

                        // All var names should be lower case.
                        name=name.toLowerCase();
                        //System.out.printf("NAME: %s VALUE: %s\n",name,value);

                        // Everything read from the file should be marked
                        // as "persistent". There are no "unknown" settings. 
                        // Unusable, maybe.
                        settings.put(name, new DoomSetting(name,value, true));
                        
                } // end-while


                in.close();
            } // not null
        }catch (IOException e){
            // This won't destroy successfully read values, though.
            System.err.printf("I just can't read the settings file %s, will use defaults.\n",defaultfile);
        }
    }

    @Override
    public void applySetting(IUseVariables setme, String name, String value) {
        // TODO Auto-generated method stub
        
    }
    
    //
    // M_SaveDefaults
    //
    
    @Override
    public void SaveDefaults (String defaultfile)
    {
        OutputStream f;
        PrintStream  ps;

        try {
            f = new FileOutputStream(defaultfile);
        } catch (FileNotFoundException e) {
            // can't write the file, but don't complain
            return;
        }

        ps=new PrintStream(f);
        
        Set<Entry<String,DoomSetting>> stuff=settings.entrySet();
        List<DoomSetting> save=new ArrayList<DoomSetting>();
        
        for (Entry<String,DoomSetting> entry :stuff)
        {
            
            DoomSetting set=entry.getValue();
            
            // Save only stuff marked as "persistent"
            if (set.getPersist()){
                save.add(set);
            }
        }
        
        // Nicely sorted alphabetically :-)
        Collections.sort(save);
        
        for (DoomSetting var :save){
            if (C2JUtils.flags(var.getTypeFlag(),DoomSetting.BOOLEAN)){
                ps.printf("%s\t\t%s\n",var.getName(),var.getBoolean());
                continue;
            }
            
            if (C2JUtils.flags(var.getTypeFlag(),DoomSetting.CHAR)){
                ps.printf("%s\t\t\'%c\'\n",var.getName(),var.getChar());
                continue;
            }
            
            if (C2JUtils.flags(var.getTypeFlag(),DoomSetting.INTEGER)){
                ps.printf("%s\t\t%s\n",var.getName(),var.getString());
                continue;
            }
        
            ps.printf("%s\t\t\"%s\"\n",var.getName(),var.getString());
        }

        
        try {
            f.close();}
        catch (IOException e) {
            // Well duh....
            return;
        }
    }
    

    public String getDefaultFile(){
    // check for a custom default file

    int i = CLM.CheckParm("-config");
    if ((i>0) && i<CLM.getArgc()-1)
    {
        return CLM.getArgv(i+1);
        //System.out.printf("   default file: %s\n",defaultfile);
    }
    else
        return Settings.basedefault;
    }

    
}

package m;

import java.io.DataOutputStream;
import java.io.IOException;

import w.IWritableDoomObject;

/** Yeah, this is actually a PCX header implementation, and Mocha Doom
 *  saved PCX screenshots. Implemented it back just to shot that it can be
 *  done (will switch to PNG ASAP though). 
 *  
 *  @author Maes
 * 
 */

public class pcx_t implements IWritableDoomObject{

	//
	// SCREEN SHOTS
	//

		// char -> byte Bytes.
        /** manufacturer byte, must be 10 decimal */
		public byte		manufacturer;
		
		/** PCX version number */
		byte		version;
		
		/** run length encoding byte, must be 1 */
		byte		encoding;
		
		/** number of bits per pixel per bit plane */
		byte		bits_per_pixel;
		
		/** image limits in pixels: Xmin, Ymin, Xmax, Ymax */
		public char	xmin,ymin,xmax,ymax;
	    
		/** horizontal dots per inch when printed (unreliable) */
		char	hres;
		
		/** vertical dots per inch when printed (unreliable) */
		char	vres;

		/** 16-color palette (16 RGB triples between 0-255) 
		 *  UNUSED in Doom. */
		byte[]	palette=new byte[48];
	    
		/** reserved, must be zero */
	    byte		reserved;
	    
	    /** number of bit planes */
	    byte		color_planes;

	    /** video memory bytes per image row */
	    char	bytes_per_line;
	    
	    /** 16-color palette interpretation (unreliable) 0=color/b&w 1=grayscale */
	    char	palette_type;
	    
	    // Seems off-spec. However it's left all zeroed out.
	    byte[]		filler=new byte[58];

	    //unsigned char	data;
	    byte[] data;
	    
		@Override
		public void write(DataOutputStream f) throws IOException {
			// char -> byte Bytes.

			f.writeByte(manufacturer);
			f.writeByte(version);
			f.writeByte(encoding);
			f.writeByte(bits_per_pixel);
			
			// unsigned short -> char
			f.writeChar(Swap.SHORT(xmin));
			f.writeChar(Swap.SHORT(ymin));
			f.writeChar(Swap.SHORT(xmax));
			f.writeChar(Swap.SHORT(ymax));
		    
			f.writeChar(Swap.SHORT(hres));
			f.writeChar(Swap.SHORT(vres));
			f.write(palette);
		    
		    f.writeByte(reserved);
		    f.writeByte(color_planes);
		 // unsigned short -> char
		    f.writeChar(Swap.SHORT(bytes_per_line));
		    f.writeChar(Swap.SHORT(palette_type));
		    
		    f.write(filler);
		    //unsigned char	data;		// unbounded
		    f.write(data);
		}
	} ;

package m;

/** Some utilities for switching between floating and signed 16.16 fixed-point at will.
 *  They use direct bit manipulation with little -if any- looping.
 *  
 *  The methods can probably be generalized but not a priority for now.
 *  They do not handle Infinities, NaNs and unnormalized numbers.
 *  
 * @author Maes
 *
 */

public class FixedFloat {

    // Various bit masks for IEEE-754 floating point 
    public static final int MANTISSA_32=0x007FFFFF;    
    public static final int EXP_32=0x7F800000;
    public static final int IMPLICIT_32=0x00800000;
    public static final int SIGN_32=0x80000000;
    public static final int NONSIGN_32=0x7FFFFFFF;
    public static final long SIGN_64=0x8000000000000000L;
    public static final long EXP_64=0x7FF0000000000000L;
    public static final long IMPLICIT_64=0x0010000000000000L;
    public static final long MANTISSA_64=0x000fffffffffffffL;
    
    public static float toFloat(int fixed){
        if (fixed==0) return (float)(0.0);
        // Remember sign.
        int sign=fixed&SIGN_32;
        if (fixed<0) fixed=-fixed;
        int exp=findShift(fixed);
        // First shift to left to "cancel" bits "above" the first.
        int mantissa=(fixed<<(exp+2))>>>9;
        int result=sign|(((14-exp)+127)<<23)|mantissa;
        /*if (fixed<0) System.out.println(Integer.toBinaryString(fixed) +"\n"+
                                        Integer.toBinaryString(-fixed) +"\n"+
                                        Integer.toBinaryString(result));*/
        return Float.intBitsToFloat(result);
    }
 
    private static int findShift(int fixed){
        // only non-sign bits.
        fixed&=NONSIGN_32;
        // We assume that the MSb after the sign is set.
        int shift=30;
        while((shift>=0)&&(fixed>>>shift)==0)
            // It's not, apparently
            shift--;

        // Positions 0-15 are fractional, anything above 15 is integer.
        // Return two's complement shift.
        return (30-shift);
        
    }
    
    public static double toDouble(int fixed){
        
        
        // Remember sign.
        
        long fx=fixed;
        fx<<=32;
        long sign=(long)fx&SIGN_64;
        
         if (fixed<0) {
             fixed=-fixed;
             fx=-fx;
         }
        long exp=findShift(fixed);
        // First shift to left to "swallow" sign and implicit 1.
        long bits=(fx<<(exp+2))>>>12;
        long result=sign|(((14-exp)+1023)<<52)|bits;
        return Double.longBitsToDouble(result);
    }
    
    public static int toFixed(float fl){
        // Get the raw bits.
        int flbits=Float.floatToRawIntBits(fl);
        // Remember sign.
        int sign=flbits&SIGN_32;
        // Join together: the implcit 1 and the mantissa bits.
        // We now have the "denormalized" value. 
        int denorm=IMPLICIT_32|(flbits&MANTISSA_32);
        // Get exponent...acceptable values are (-15 ~ 15), else wrap around (use only sign and lowest 4 bits).
        int exp=(((flbits&EXP_32)>>23)-127)&0x8000000F;
        /* Remember, leftmost "1" will be at position 23.
         * So for an exponent of 0, we must shift to position 16.
         * For positive exponents in general, we must shift -7 + exp.
         * and for one of 15, to position 30, plus the sign.
         * While there is space for all bits, we can't keep them all, 
         * as some (well, many)numbers can't be represented in fixed point.
         * 
         */
        int result;
        if ((exp-7)>=0)
            result=sign|(denorm<<(exp-7));
        else
            result=sign|(denorm>>>(7-exp));
        return result;
        }
    
    public static int toFixed(double fl){
        
        // Get the raw bits.
        long flbits=Double.doubleToRawLongBits(fl);
        // Remember sign.
        int sign=(int)((flbits&SIGN_64)>>32);
        // Join together: the implcit 1 and the mantissa bits.
        // We now have the "denormalized" value. 
        long denorm=IMPLICIT_64|(flbits&MANTISSA_64);
        //System.out.println("Denorm"+Integer.toBinaryString(denorm));
        // Get exponent...acceptable values are (-15 ~ 15), else wrap around (use only sign and lowest 4 bits).
        int exp=(int)(((flbits&EXP_64)>>52)-1023)&0x8000000F;
        /* Remember, leftmost "1" will be at position 53.
         * So for an exponent of 0, we must shift to position 16.
         * For positive exponents in general, we must shift -37 + exp.
         * and for one of 15, to position 30, plus the sign.
         * While there is space for all bits, we can't keep them all, 
         * as some (well, many)numbers can't be represented in fixed point.
         * 
         */
        int result;
        if ((exp-36)>=0)
            result=(int) (sign|(denorm<<(exp-36)));
        else
            result=(int) (sign|(denorm>>>(36-exp)));
        //int result=sign|(IMPLICIT_32|(mantissa<<(exp-127)))<<8;
        return result;
        }
    
}

package m;

import static g.Keys.*;
import static doom.englsh.*;


/** An anumeration with the most basic default Doom settings their default
 *  values, used if nothing else is available. They are applied first thing, 
 *  and then updated from the .cfg file. 
 *  
 */

public enum Settings {
    mouse_sensitivity("5"),
    sfx_volume("8"),
   music_volume("8"),
   show_messages("1"),
   alwaysrun("1"),
   key_right(KEY_RIGHTARROW),
   key_left(KEY_LEFTARROW),
   key_up('w'),
   key_down('s'),
   key_strafeleft('a'),
   key_straferight('d'),
   key_fire(KEY_CTRL),
   key_use(' '),
   key_strafe(KEY_ALT),
   key_speed(KEY_SHIFT),
   use_mouse(1),
   mouseb_fire(0),
   mouseb_strafe(1),
   mouseb_forward(2),
   use_joystick( 0),
   joyb_fire(0),
   joyb_strafe(1),
   joyb_use(3),
   joyb_speed(2),
   screenblocks(10),
   detaillevel(0),
   snd_channels(6),
   usegamma(0),
   mb_used(2),
   chatmacro0(HUSTR_CHATMACRO0 ),
   chatmacro1(HUSTR_CHATMACRO1 ),
   chatmacro2( HUSTR_CHATMACRO2 ),
   chatmacro3(HUSTR_CHATMACRO3 ),
   chatmacro4( HUSTR_CHATMACRO4 ),
   chatmacro5( HUSTR_CHATMACRO5 ),
   chatmacro6(HUSTR_CHATMACRO6 ),
   chatmacro7( HUSTR_CHATMACRO7 ),
   chatmacro8(HUSTR_CHATMACRO8 ),
   chatmacro9( HUSTR_CHATMACRO9 );

         private Settings(String defaultval){
            this.value=defaultval;
        }
         

         private Settings(int defaultval){
            this.value=Integer.toString(defaultval);
        }
    
    public String value;
    
    /** Normally this is default.cfg, might be .doomrc on lunix??? */
    
    public static String basedefault="default.cfg";      

}
package m;

import java.util.Random;

import data.mobjtype_t;
import doom.think_t;

// Emacs style mode select   -*- C++ -*- 
//-----------------------------------------------------------------------------
//
// $Id: JavaRandom.java,v 1.3 2013/06/03 11:00:03 velktron Exp $
//
// Copyright (C) 1993-1996 by id Software, Inc.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
//
// DESCRIPTION:
//	Random number LUT using java.util.Random
// Don't expect vanilla demo compatibility with THIS!
//
//-----------------------------------------------------------------------------

public class JavaRandom implements IRandom{

protected int	rndindex = 0;
protected int	prndindex = 0;

// Which one is deterministic?
public int P_Random ()
{
	rndindex++;
	return (0xFF&r.nextInt());
	
}

public int M_Random ()
{
	prndindex++;
	return (0xFF&m.nextInt());
}

public void ClearRandom ()
{
	rndindex=prndindex=0;
    r.setSeed(666);
}

public JavaRandom(){
	r=new Random(666);
	m=new Random(666);
    this.ClearRandom();
}

public int getIndex(){
	return rndindex;
}

private Random r;
private Random m;

@Override
public int P_Random(int caller) {    
    // DUMMY
    return P_Random();
}

@Override
public int P_Random(String message) {
    // DUMMY
    return P_Random();
}

@Override
public int P_Random(think_t caller, int sequence) {
    // DUMMY
    return P_Random();
}

@Override
public int P_Random(think_t caller, mobjtype_t type, int sequence) {
    // DUMMY
    return P_Random();
}

}


//$Log: JavaRandom.java,v $
//Revision 1.3  2013/06/03 11:00:03  velktron
//Implements interface without logging.
//
//Revision 1.2  2011/07/27 20:47:46  velktron
//Proper commenting, cleanup.
//
//Revision 1.1  2011/05/29 22:15:32  velktron
//Introduced IRandom interface.


package m;

public class menuitem_t {

	    public menuitem_t(int status, String name, MenuRoutine routine, char alphaKey) {
	    	this.status=status;
	    	this.name=name;
	    	this.routine= routine;
	    	this.alphaKey=alphaKey;
	}	

		public menuitem_t(int status, String name, MenuRoutine routine) {
		     this.status=status;
	            this.name=name;
	            this.routine= routine;
        }

        /** 0 = no cursor here, 1 = ok, 2 = arrows ok */
	    int	status;
	    
	    String	name;
	    
	    // choice = menu item #.
	    // if status = 2,
	    //   choice=0:leftarrow,1:rightarrow
	    // MAES: OK... to probably we need some sort of "MenuRoutine" class for this one.
	    // void	(*routine)(int choice);
	    MenuRoutine routine;
	    
	    /** hotkey in menu */
	    char	alphaKey;			
	} 
package m;

/** menu_t required a function pointer to a (routine)() that drew stuff.
 *  So any class implementing them will implement this interface, and
 *  we can have a single class type for all of them.
 * 
 * @author Maes
 *
 */
public interface DrawRoutine {
	
public void invoke();
}

package m;

import v.IVideoScaleAware;
import i.DoomStatusAware;
import doom.DoomStatus;
import doom.event_t;

// Emacs style mode select -*- C++ -*-
// -----------------------------------------------------------------------------
//
// $Id: IDoomMenu.java,v 1.5 2011/09/29 15:16:23 velktron Exp $
//
// Copyright (C) 1993-1996 by id Software, Inc.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// DESCRIPTION:
// Menu widget stuff, episode selection and such.
//    
// -----------------------------------------------------------------------------

/**
 * 
 */

public interface IDoomMenu extends IVideoScaleAware, DoomStatusAware{

    //
    // MENUS
    //

    /**
     * Called by main loop, saves config file and calls I_Quit when user exits.
     * Even when the menu is not displayed, this can resize the view and change
     * game parameters. Does all the real work of the menu interaction.
     */
    public boolean Responder(event_t ev);

    /**
     * Called by main loop, only used for menu (skull cursor) animation.
     */
    public void Ticker();

    /**
     * Called by main loop, draws the menus directly into the screen buffer.
     */
    public void Drawer();

    /**
     * Called by D_DoomMain, loads the config file.
     */
    public void Init();

    /**
     * Called by intro code to force menu up upon a keypress, does nothing if
     * menu is already up.
     */
    public void StartControlPanel();

    public boolean getShowMessages();

    public void setShowMessages(boolean val);
    
    public int getScreenBlocks();
    
    public void setScreenBlocks(int val);
    
    public int getDetailLevel();

	void ClearMenus();
}
    
package m;

/** General form for a classic, Doom-style menu with a bunch of
 *  items and a drawing routine (menu_t's don't have action callbacks
 *  proper, though).
 * 
 * @author Maes
 *
 */

public class menu_t {
	    public menu_t(int numitems, menu_t prev, menuitem_t[] items,
            DrawRoutine drawroutine, int x, int y, int lastOn) {
	        this.numitems=numitems;
	        this.prevMenu=prev;
	        this.menuitems=items;
	        this.routine=drawroutine;
	        this.x=x;
	        this.y=y;
	        this.lastOn=lastOn;
	        
    }
	    /** # of menu items */	    
        public int		numitems;
        
	    /**  previous menu */
	    public menu_t	prevMenu;

	    /** menu items */
	    public menuitem_t[]		menuitems;	
	    /** draw routine */
	    public DrawRoutine routine;
	    /**  x,y of menu */
	    public int		x,y;
	    /** last item user was on in menu */
	    public int		lastOn;
	} 

package m;

import data.mobjtype_t;
import doom.think_t;

// Emacs style mode select   -*- C++ -*- 
//-----------------------------------------------------------------------------
//
// $Id: DoomRandom.java,v 1.4 2013/06/04 11:29:25 velktron Exp $
//
// Copyright (C) 1993-1996 by id Software, Inc.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// $Log: DoomRandom.java,v $
// Revision 1.4  2013/06/04 11:29:25  velktron
// Dummy implementations
//
// Revision 1.3  2013/06/03 10:53:29  velktron
// Implements the new IRandom.
//
// Revision 1.2.10.3  2013/01/09 19:38:26  velktron
// Printing arbitrary messages
//
// Revision 1.2.10.2  2012/11/20 15:59:20  velktron
// More tooling functions.
//
// Revision 1.2.10.1  2012/11/19 22:11:36  velktron
// Added demo sync tooling.
//
// Revision 1.2  2011/05/30 02:24:30  velktron
// *** empty log message ***
//
// Revision 1.1  2011/05/29 22:15:32  velktron
// Introduced IRandom interface.
//
// Revision 1.4  2010/09/22 16:40:02  velktron
// MASSIVE changes in the status passing model.
// DoomMain and DoomGame unified.
// Doomstat merged into DoomMain (now status and game functions are one).
//
// Most of DoomMain implemented. Possible to attempt a "classic type" start but will stop when reading sprites.
//
// Revision 1.3  2010/09/10 17:35:49  velktron
// DoomGame, Menu, renderers
//
// Revision 1.2  2010/07/06 16:32:38  velktron
// Threw some work in WI, now EndLevel. YEAH THERE'S GONNA BE A SEPARATE EndLevel OBJECT THAT'S HOW PIMP THE PROJECT IS!!!!11!!!
//
// Revision 1.1  2010/06/30 08:58:50  velktron
// Let's see if this stuff will finally commit....
//
//
// Most stuff is still  being worked on. For a good place to start and get an idea of what is being done, I suggest checking out the "testers" package.
//
// Revision 1.1  2010/06/29 11:07:34  velktron
// Release often, release early they say...
//
// Commiting ALL stuff done so far. A lot of stuff is still broken/incomplete, and there's still mixed C code in there. I suggest you load everything up in Eclpise and see what gives from there.
//
// A good place to start is the testers/ directory, where you  can get an idea of how a few of the implemented stuff works.
//
//
// DESCRIPTION:
//	Random number LUT.
//
//-----------------------------------------------------------------------------

public class DoomRandom implements IRandom{

//
// M_Random
// Returns a 0-255 number. Made into shorts for Java, because of their nature.
//
public static short rndtable[] = {
    0,   8, 109, 220, 222, 241, 149, 107,  75, 248, 254, 140,  16,  66 ,
    74,  21, 211,  47,  80, 242, 154,  27, 205, 128, 161,  89,  77,  36 ,
    95, 110,  85,  48, 212, 140, 211, 249,  22,  79, 200,  50,  28, 188 ,
    52, 140, 202, 120,  68, 145,  62,  70, 184, 190,  91, 197, 152, 224 ,
    149, 104,  25, 178, 252, 182, 202, 182, 141, 197,   4,  81, 181, 242 ,
    145,  42,  39, 227, 156, 198, 225, 193, 219,  93, 122, 175, 249,   0 ,
    175, 143,  70, 239,  46, 246, 163,  53, 163, 109, 168, 135,   2, 235 ,
    25,  92,  20, 145, 138,  77,  69, 166,  78, 176, 173, 212, 166, 113 ,
    94, 161,  41,  50, 239,  49, 111, 164,  70,  60,   2,  37, 171,  75 ,
    136, 156,  11,  56,  42, 146, 138, 229,  73, 146,  77,  61,  98, 196 ,
    135, 106,  63, 197, 195,  86,  96, 203, 113, 101, 170, 247, 181, 113 ,
    80, 250, 108,   7, 255, 237, 129, 226,  79, 107, 112, 166, 103, 241 ,
    24, 223, 239, 120, 198,  58,  60,  82, 128,   3, 184,  66, 143, 224 ,
    145, 224,  81, 206, 163,  45,  63,  90, 168, 114,  59,  33, 159,  95 ,
    28, 139, 123,  98, 125, 196,  15,  70, 194, 253,  54,  14, 109, 226 ,
    71,  17, 161,  93, 186,  87, 244, 138,  20,  52, 123, 251,  26,  36 ,
    17,  46,  52, 231, 232,  76,  31, 221,  84,  37, 216, 165, 212, 106 ,
    197, 242,  98,  43,  39, 175, 254, 145, 190,  84, 118, 222, 187, 136 ,
    120, 163, 236, 249
};

protected int	rndindex = 0;
protected int	prndindex = 0;

// Which one is deterministic?
public int P_Random ()
{
    prndindex = (prndindex+1)&0xff;
    return rndtable[prndindex];
}

/** [Maes] I'd rather dispatch the call here, than making IRandom aware of
 * DoomStatus. Replace RND.P_Random calls with DM.P_Random(callerid) etc.
 * 
 * Fixme: this could be made into a proper enum
 * 
 * @param caller
 */

public int P_Random(int caller) {
	int value = P_Random();
	SLY.sync("PR #%d [%d]=%d\n",caller,prndindex,value);
	return value;
}

public int P_Random(String message) {
	int value = P_Random();
	SLY.sync("PR %s [%d]=%d\n", message,
			prndindex, value);
	return value;
}

public int P_Random(think_t caller, int sequence) {
	int value = P_Random();
	/*
	SLY.sync("PR #%d %s_%d [%d]=%d\n", caller.ordinal(),caller,sequence,
			prndindex, value);*/
	return value;
}

public int P_Random(think_t caller, mobjtype_t type,int sequence) {
    int value = P_Random();
    /*
    SLY.sync("PR #%d %s_%d %s [%d]=%d\n", caller.ordinal(),caller,sequence,
        type, prndindex, value);*/
    return value;
}

public int M_Random ()
{
    rndindex = (rndindex+1)&0xff;
    return rndtable[rndindex];
}

public void ClearRandom ()
{
    rndindex = prndindex = 0;
}

public DoomRandom(){
	SLY=null;
}

public int getIndex(){
	return prndindex;
}

public DoomRandom(ISyncLogger SLY){
	this.SLY=SLY;
}

private final ISyncLogger SLY;


}





package m;

// Emacs style mode select   -*- C++ -*- 
//-----------------------------------------------------------------------------
//
// $Id: Swap.java,v 1.2 2011/07/27 20:48:20 velktron Exp $
//
// Copyright (C) 1993-1996 by id Software, Inc.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// DESCRIPTION:
//	Endianess handling, swapping 16bit and 32bit.
//  It's role is much less important than in C-based ports (because of stream
//  built-in endianness settings), but they are still used occasionally.
//
//-----------------------------------------------------------------------------

public final class Swap{


// Swap 16bit, that is, MSB and LSB byte.
public final static short SHORT(short x)
{
    // No masking with 0xFF should be necessary. 
    // MAES: necessary with java due to sign trailing.
    
    return (short) ((short) ((x>>>8)&0xFF) | (x<<8));
}

//Swap 16bit, that is, MSB and LSB byte.
public final static short SHORT(char x)
{
    // No masking with 0xFF should be necessary. 
    // MAES: necessary with java due to sign trailing.
    
    return (short) ((short) ((x>>>8)&0xFF) | (x<<8));
}

//Swap 16bit, that is, MSB and LSB byte.
public final static char USHORT(char x)
{
    // No masking with 0xFF should be necessary. 
    // MAES: necessary with java due to sign trailing.
    
    return (char) ((char) ((x>>>8)&0xFF) | (x<<8));
}


// Swapping 32bit.
// Maes: the "long" here is really 32-bit.
public final static int LONG( int x)
{
    return
	(x>>>24)
	| ((x>>>8) & 0xff00)
	| ((x<<8) & 0xff0000)
	| (x<<24);
}
}

//$Log: Swap.java,v $
//Revision 1.2  2011/07/27 20:48:20  velktron
//Proper commenting, cleanup.
//
//Revision 1.1  2010/06/30 08:58:50  velktron
//Let's see if this stuff will finally commit....

package m;

/** menuitem_t required a function pointer to a (routine)(int choice)
 *  So any class implementing them will implement this interface, and
 *  we can have a single class type for all of them.
 * 
 * @author Velktron
 *
 */
public interface MenuRoutine {
	
	
public void invoke(int choice);
}

package m;

import utils.C2JUtils;

/** A "Doom setting". Based on current experience, it could 
 *  represent an integer value, a string, or a boolean value.
 * 
 *  Therefore, every setting can be interpreted as any of the above, 
 *  based on some rules. Strings that can be interpreted as parseable 
 *  numbers are obvious, and numbers can also be interpreted as strings.
 *  Strings that can't be interpreted as numbers will return "0" as a default
 *  value.
 *  
 *  A numerical value of 1 means "true", any other value is "false".
 *  A string representing the (case insensitive) value "true" will 
 *  be interpreted as a true boolean, false otherwise.
 * 
 * @author velktron
 *
 * 
 */

public class DoomSetting implements Comparable<DoomSetting> {
    
    public static final int BOOLEAN=1;
    public static final int CHAR=2;
    public static final int DOUBLE=4;
    public static final int INTEGER=8;
    public static final int STRING=16;
    
    private String name;
    
    private int typeflag;
    
    // Every setting can be readily interpreted as any of these
    private int int_val;
    private long long_val;
    private char char_val;
    private double double_val;
    private boolean boolean_val;
    private String string_val;
    
    /** Should be saved to file */
    private boolean persist;
    
    public DoomSetting(String name,  String value, boolean persist){
        this.name=name;
        this.typeflag=STRING;
        this.updateValue(value);
        this.persist=persist;
    }
    
    public String getName(){
        return name;
    }
    
    public int getInteger(){
        return int_val;
    }
    
    public long getLong(){
        return long_val;
    }
    
    public char getChar(){
        return (char)int_val;
    }
    
    public String getString(){
        return string_val;
    }
    
    public double getDouble(){
        return double_val;
    }

    public boolean getBoolean(){
        return boolean_val;
    }
    
    public boolean getPersist(){
        return persist;
    }

    public int getTypeFlag(){
        return typeflag;
    }

    
    /** All the gory disambiguation work should go here.
     * 
     * @param value
     */
    
    public void updateValue(String value){

        boolean quoted=false;
        
        if (value.length()>2)        
        if (quoted=C2JUtils.isQuoted(value,'"' ))
            value=C2JUtils.unquote(value, '"');
        else
            if (quoted=C2JUtils.isQuoted(value,'\'' ))
                value=C2JUtils.unquote(value, '\'');

        // String value always available
        this.string_val=value;
       
        // If quoted and sensibly ranged, it gets priority as a "character"        
        
        if (quoted && value.length()==1 && value.charAt(0)>=0 && value.charAt(0)<255){
            char_val=Character.toLowerCase(value.charAt(0));
            int_val=char_val;
            long_val=char_val;
            double_val=char_val;
            typeflag|=CHAR;
            return;
        }
        
        // Not a character, try all other stuff
        
            try {
            this.int_val=Integer.parseInt(value);
            typeflag|=INTEGER;
            } catch (NumberFormatException e) {
                // No nookie
                this.int_val=-1;
            }
            
            try {
                this.long_val=Long.parseLong(value);
                } catch (NumberFormatException e) {
                    try {
                    // Try decoding it as hex, octal, whatever.
                    this.long_val=Long.decode(value);
                    typeflag|=INTEGER;
                    } catch (NumberFormatException h){
                        // If even THAT fails, then no nookie.
                    this.long_val=-1;
                    }
                }
            
            try {
            this.double_val=Double.parseDouble(value);
            typeflag|=DOUBLE;
            } catch (NumberFormatException e) {
                // No nookie
                this.double_val=Double.NaN;
            }            
            
            // Use long value to "trump" smaller ones
            int_val=(int)long_val;
            char_val=(char)int_val;
            
            // Boolean has a few more options;
            // Only mark something explicitly as boolean if the string reads 
            // actually "true" or "false". Numbers such as 0 and 1 might still get
            // interpreted as booleans, but that shouldn't trump the entire number,
            // otherwise everything and the cat is boolean
            
            this.boolean_val=(int_val==1);
            
            if (Boolean.parseBoolean(value) || 
                    (value.compareToIgnoreCase("false")==0)){
                this.boolean_val=(int_val==1) || Boolean.parseBoolean(value);
                this.typeflag|=BOOLEAN;
            }
        }
        
        /** Answer definitively if a setting cannot ABSOLUTELY be 
         *  parsed into a number using simple Integer rules. 
         *  This excludes some special names like "+Inf" and "NaN".
         * 
         * @return
         */
    
        public boolean isIntegerNumeric(){
            
            try {
                this.long_val=Long.parseLong(string_val);
                } catch (NumberFormatException e) {
                    try {
                    // Try decoding it as hex, octal, whatever.
                    Long.decode(string_val);

                    } catch (NumberFormatException h){
                        // If even THAT fails, then no nookie.
                    return false;
                    }
                }
                
                // Everything OK, I presume...
                return true;
        }

        /** Settings are "comparable" to each other by name, so we can save
         *  nicely sorted setting files ;-) 
         *  
         * @param o
         * @return
         */
        
        @Override
        public int compareTo(DoomSetting o) {
            return this.name.compareToIgnoreCase(o.getName());
        }
        
        public String toString(){
            return string_val;
        }

        /** A special setting that returns false, 0 and an empty string, if required.
         * Simplifies handling of nulls A LOT. So code that relies on specific settings
         * should be organized to work only on clear positivies (e.g. use a "fullscreen" setting
         * that must exist and be equal to 1 or true, instead of assuming that a zero/false
         * value enables it. */
        
        public static DoomSetting NULL_SETTING=new DoomSetting("NULL","",false);
        
        static {
            // It's EVERYTHING
            NULL_SETTING.typeflag=0x1F;
            NULL_SETTING.string_val="";
            NULL_SETTING.char_val=0;
            NULL_SETTING.double_val=0;
            NULL_SETTING.boolean_val=false;
            NULL_SETTING.int_val=0;
            NULL_SETTING.long_val=0;            
        }
        
        
    }

package m;

import defines.*;
import static data.Defines.HU_FONTSIZE;
import static data.Defines.HU_FONTSTART;
import static g.Keys.*;
import static data.Defines.PU_CACHE;
import static data.Defines.SAVESTRINGSIZE;
import static data.dstrings.NUM_QUITMESSAGES;
import static data.dstrings.SAVEGAMENAME;
import static data.dstrings.endmsg;
import static doom.englsh.DOSY;
import static doom.englsh.EMPTYSTRING;
import static doom.englsh.ENDGAME;
import static doom.englsh.LOADNET;
import static doom.englsh.MSGOFF;
import static doom.englsh.MSGON;
import static doom.englsh.NETEND;
import static doom.englsh.NEWGAME;
import static doom.englsh.NIGHTMARE;
import static doom.englsh.QLOADNET;
import static doom.englsh.QLPROMPT;
import static doom.englsh.QSAVESPOT;
import static doom.englsh.QSPROMPT;
import static doom.englsh.SAVEDEAD;
import static doom.englsh.SWSTRING;

import java.io.BufferedInputStream;
import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.IOException;

import rr.patch_t;
import utils.C2JUtils;
import v.IVideoScale;
import w.DoomIO;
import data.sounds.sfxenum_t;
import doom.DoomStatus;
import doom.englsh;
import doom.event_t;
import doom.evtype_t;

public class Menu extends AbstractDoomMenu {

	////////////////// CONSTRUCTOR ////////////////
    
    public Menu(DoomStatus DS){
    	this.updateStatus(DS);
    }

    /** The fonts  ... must "peg" them to those from HU */
    patch_t[] hu_font = new patch_t[HU_FONTSIZE];

    /** WTF?! */

    boolean message_dontfuckwithme;
    
    // int mouseSensitivity; // has default

    /** Show messages has default, 0 = off, 1 = on */

    private boolean showMessages=false;

    /**
     * showMessages can be read outside of Menu, but not modified. Menu has the
     * actual C definition (not declaration)
     */
    
    @Override
    public boolean getShowMessages() {
        return showMessages;
    }

    @Override
    public void setShowMessages(boolean val) {
        this.showMessages=val;
    }
    
    /** Blocky mode, has default, 0 = high, 1 = normal */
    int detailLevel;

    int screenblocks=10; // has default

    /** temp for screenblocks (0-9) */
    int screenSize;

    /** -1 = no quicksave slot picked! */
    int quickSaveSlot;

    /** 1 = message to be printed */
    boolean messageToPrint;

    /** ...and here is the message string! */
    String messageString;

    /** message x & y */
    int messx, messy;

    boolean messageLastMenuActive;

    /** timed message = no input from user */
    boolean messageNeedsInput;

    /** Probably I need some MessageRoutine interface at this point? */
    public MenuRoutine messageRoutine;


    /** we are going to be entering a savegame string */
    boolean saveStringEnter;

    int saveSlot; // which slot to save in

    int saveCharIndex; // which char we're editing

    /** old save description before edit */
    char[] saveOldString = new char[SAVESTRINGSIZE];

    boolean inhelpscreens;

    //int menuactive;

    protected static final int SKULLXOFF = -32;

    protected static final int LINEHEIGHT = 16;

    char[][] savegamestrings = new char[10][SAVESTRINGSIZE];

    String endstring = new String();

    //
    // MENU TYPEDEFS
    //

    /** menu item skull is on */
    short itemOn;

    /** skull animation counter */
    short skullAnimCounter;

    /** which skull to draw */
    short whichSkull;

    /**
     * graphic name of skulls warning: initializer-string for array of chars is
     * too long
     */
    private static String[] skullName = { "M_SKULL1", "M_SKULL2" };

    /** current menudef */
    // MAES: pointer? array?
    menu_t currentMenu;

    //
    // DOOM MENU
    //

    // MAES: was an enum called "main_e" used purely as numerals. No need for
    // strong typing.

    /**
     * MenuRoutine class definitions, replacing "function pointers".
     */
    MenuRoutine ChangeDetail, ChangeMessages, ChangeSensitivity, ChooseSkill,
            EndGame, EndGameResponse, Episode, FinishReadThis, LoadGame,
            LoadSelect, MusicVol, NewGame, Options, VerifyNightmare,
            SaveSelect, SfxVol, SizeDisplay, SaveGame, Sound, QuitDOOM,
            QuitResponse, QuickLoadResponse, QuickSaveResponse, ReadThis, ReadThis2;

    /** DrawRoutine class definitions, replacing "function pointers". */

    DrawRoutine DrawEpisode, DrawLoad,DrawMainMenu,DrawNewGame,DrawOptions,
    			DrawReadThis1, DrawReadThis2, DrawSave, DrawSound;

    /** Initialize menu routines first */
    
    private void initMenuRoutines() {




        ChangeMessages = new M_ChangeMessages();
        ChangeDetail = new M_ChangeDetail();
        ChangeSensitivity = new M_ChangeSensitivity();
        ChooseSkill = new M_ChooseSkill();
        EndGame = new M_EndGame();
        EndGameResponse = new M_EndGameResponse();
        Episode = new M_Episode();
        FinishReadThis=new M_FinishReadThis();
        LoadGame=new M_LoadGame();
        LoadSelect=new M_LoadSelect();
        MusicVol=new M_MusicVol();
        NewGame = new M_NewGame();
        Options = new M_Options();


        QuitDOOM = new M_QuitDOOM();
        QuickLoadResponse = new M_QuickLoadResponse();
        QuickSaveResponse= new M_QuickSaveResponse();
        QuitResponse = new M_QuitResponse();
        
        ReadThis = new M_ReadThis();
        ReadThis2 = new M_ReadThis2();
         
        SaveGame=new M_SaveGame();
        SaveSelect= new M_SaveSelect();
        SfxVol=new M_SfxVol();
        SizeDisplay = new M_SizeDisplay();
        Sound = new M_Sound();
        VerifyNightmare = new M_VerifyNightmare();
    }

    /** Then drawroutines */
    
    private void initDrawRoutines() {
        DrawEpisode = new M_DrawEpisode();
        DrawNewGame = new M_DrawNewGame();
        DrawReadThis1 = new M_DrawReadThis1();
        DrawReadThis2 = new M_DrawReadThis2();
        DrawOptions = new M_DrawOptions();
        DrawLoad = new M_DrawLoad();
        DrawSave = new M_DrawSave();
        DrawSound=new M_DrawSound();
        DrawMainMenu = new M_DrawMainMenu();
    }

    /** Menuitem definitions. A "menu" can consist of multiple menuitems */
    menuitem_t[] MainMenu,EpisodeMenu,NewGameMenu, OptionsMenu,ReadMenu1,ReadMenu2,SoundMenu,LoadMenu,SaveMenu;
    
    /** Actual menus. Each can point to an array of menuitems */
    menu_t MainDef, EpiDef,NewDef,OptionsDef,ReadDef1, ReadDef2,SoundDef,LoadDef,SaveDef;
    
    /** First initialize those */
    
    private void initMenuItems(){
    
    MainMenu = new menuitem_t[]
        { new menuitem_t( 1, "M_NGAME", NewGame, 'n'),
                new menuitem_t(1, "M_OPTION", Options, 'o'),
                new menuitem_t(1, "M_LOADG", LoadGame, 'l'),
                new menuitem_t(1, "M_SAVEG", SaveGame, 's'),
                // Another hickup with Special edition.
                new menuitem_t(1, "M_RDTHIS", ReadThis, 'r'),
                new menuitem_t(1, "M_QUITG", QuitDOOM, 'q') };

    MainDef =
        new menu_t(main_end, null, MainMenu, DrawMainMenu, 97, 64, 0);

    //
    // EPISODE SELECT
    //

    EpisodeMenu = new menuitem_t[]
        { new menuitem_t(1, "M_EPI1", Episode, 'k'),
                new menuitem_t(1, "M_EPI2", Episode, 't'),
                new menuitem_t(1, "M_EPI3", Episode, 'i'),
                new menuitem_t(1, "M_EPI4", Episode, 't') };

    EpiDef = new menu_t(ep_end, // # of menu items
            MainDef, // previous menu
            EpisodeMenu, // menuitem_t ->
            DrawEpisode, // drawing routine ->
            48, 63, // x,y
            ep1 // lastOn
        );

    //
    // NEW GAME
    //


     NewGameMenu  = new menuitem_t[]
        { new menuitem_t(1, "M_JKILL", ChooseSkill, 'i'),
                new menuitem_t(1, "M_ROUGH", ChooseSkill, 'h'),
                new menuitem_t(1, "M_HURT", ChooseSkill, 'h'),
                new menuitem_t(1, "M_ULTRA", ChooseSkill, 'u'),
                new menuitem_t(1, "M_NMARE", ChooseSkill, 'n') };

     NewDef = new menu_t(newg_end, // # of menu items
            EpiDef, // previous menu
            NewGameMenu, // menuitem_t ->
            DrawNewGame, // drawing routine ->
            48, 63, // x,y
            hurtme // lastOn
        );

    //
    // OPTIONS MENU
    //

    OptionsMenu = new  menuitem_t[]
        { new menuitem_t(1, "M_ENDGAM", EndGame, 'e'),
                new menuitem_t(1, "M_MESSG", ChangeMessages, 'm'),
                new menuitem_t(1, "M_DETAIL", ChangeDetail, 'g'),
                new menuitem_t(2, "M_SCRNSZ", SizeDisplay, 's'),
                new menuitem_t(-1, "", null),
                new menuitem_t(2, "M_MSENS", ChangeSensitivity, 'm'),
                new menuitem_t(-1, "", null),
                new menuitem_t(1, "M_SVOL", Sound, 's') };

    OptionsDef =
        new menu_t(opt_end, this.MainDef, OptionsMenu, DrawOptions, 60, 37, 0);

    // Read This! MENU 1 

    ReadMenu1 = new menuitem_t[] { new menuitem_t(1, "", ReadThis2, (char) 0) };

    ReadDef1 =
        new menu_t(read1_end, MainDef, ReadMenu1, DrawReadThis1, 280, 185, 0);

    // Read This! MENU 2

    ReadMenu2 = new menuitem_t[] 
        { new menuitem_t(1, "", FinishReadThis, (char) 0) };

    ReadDef2 =
        new menu_t(read2_end, ReadDef1, ReadMenu2, DrawReadThis2, 330, 175, 0);

    //
    // SOUND VOLUME MENU
    //



    SoundMenu = new menuitem_t[]
        { new menuitem_t(2, "M_SFXVOL", SfxVol, 's'),
                new menuitem_t(-1, "", null),
                new menuitem_t(2, "M_MUSVOL", MusicVol, 'm'),
                new menuitem_t(-1, "", null) };

    SoundDef =
        new menu_t(sound_end, OptionsDef, SoundMenu, DrawSound, 80, 64, 0);

    //
    // LOAD GAME MENU
    //

    LoadMenu =new menuitem_t[]
        { new menuitem_t(1, "", LoadSelect, '1'),
                new menuitem_t(1, "", LoadSelect, '2'),
                new menuitem_t(1, "", LoadSelect, '3'),
                new menuitem_t(1, "", LoadSelect, '4'),
                new menuitem_t(1, "", LoadSelect, '5'),
                new menuitem_t(1, "", LoadSelect, '6') };

    LoadDef =
        new menu_t(load_end, MainDef, LoadMenu, DrawLoad, 80, 54, 0);

    //
    // SAVE GAME MENU
    //
    SaveMenu = new menuitem_t[]
        { new menuitem_t(1, "", SaveSelect, '1'),
                new menuitem_t(1, "", SaveSelect, '2'),
                new menuitem_t(1, "", SaveSelect, '3'),
                new menuitem_t(1, "", SaveSelect, '4'),
                new menuitem_t(1, "", SaveSelect, '5'),
                new menuitem_t(1, "", SaveSelect, '6') };

    SaveDef =
        new menu_t(load_end, MainDef, SaveMenu, DrawSave, 80, 54, 0);
    }
    
    /**
     * M_ReadSaveStrings
     * read the strings from the savegame files
     */
    
    public void ReadSaveStrings() {
        DataInputStream handle;
        int count;
        int i;
        String name;

        for (i = 0; i < load_end; i++) {
            if (DM.CM.CheckParm("-cdrom") != 0)
                name = "c:\\doomdata\\" + SAVEGAMENAME + (i) + ".dsg";
            else
                name = SAVEGAMENAME + (i) + ".dsg";

            try {
                handle = new DataInputStream(new BufferedInputStream(new FileInputStream(name)));
                savegamestrings[i] =
                    DoomIO.readString(handle,SAVESTRINGSIZE).toCharArray();
                handle.close();
                LoadMenu[i].status = 1;
            } catch (IOException e) {
                savegamestrings[i][0] = 0x00;
                LoadMenu[i].status = 0;
                continue;
            }

        }
    }

    /**
     * Draw border for the savegame description. This is special in that it's
     * not "invokable" like the other drawroutines, but standalone.
     */
    private void DrawSaveLoadBorder(int x, int y) {
        int i;

        V.DrawScaledPatch(x - 8, y + 7, 0, vs,W.CachePatchName("M_LSLEFT"));

        for (i = 0; i < 24; i++) {
            V.DrawScaledPatch(x, y + 7, 0, vs,W.CachePatchName("M_LSCNTR"));
            x += 8;
        }

        V.DrawScaledPatch(x, y + 7, 0, vs,W.CachePatchName("M_LSRGHT"));
    }

    /** Draws slider rail of a specified width (each notch is 8 base units wide)
     *  and with a slider selector at position thermDot.
     * 
     * @param x
     * @param y
     * @param thermWidth
     * @param thermDot
     */
    
    public void DrawThermo(int x, int y, int thermWidth, int thermDot) {
        int xx;
        int i;

        xx = x;
        V.DrawScaledPatch(xx, y, 0, vs,W.CachePatchName("M_THERML"));
        xx += 8;
        for (i = 0; i < thermWidth; i++) {
            V.DrawScaledPatch(xx, y, 0, vs,W.CachePatchName("M_THERMM"));
            xx += 8;
        }
        V.DrawScaledPatch(xx, y, 0,vs, W.CachePatchName("M_THERMR"));

        V.DrawScaledPatch((x + 8) + thermDot * 8, y, 0,vs, W
                .CachePatchName("M_THERMO"));
    }

    public void DrawEmptyCell(menu_t menu, int item) {
        V.DrawScaledPatch(menu.x - 10, menu.y + item * LINEHEIGHT - 1, 0,vs,
            (patch_t) W.CacheLumpName("M_CELL1", PU_CACHE, patch_t.class));
    }

    public void DrawSelCell(menu_t menu, int item) {
        V.DrawScaledPatch(menu.x - 10, menu.y + item * LINEHEIGHT - 1, 0,vs,
            (patch_t) W.CacheLumpName("M_CELL2", PU_CACHE, patch_t.class));
    }

    //
    // M_SaveGame & Cie.
    //
    public class M_DrawSave implements DrawRoutine {
    	@Override
    	public void invoke(){
        int i;
        V.DrawScaledPatch(72, 28, 0, vs,W.CachePatchName("M_SAVEG"));
        for (i = 0; i < load_end; i++) {
            DrawSaveLoadBorder(LoadDef.x, LoadDef.y + LINEHEIGHT * i);
            WriteText(LoadDef.x, LoadDef.y + LINEHEIGHT * i, savegamestrings[i]);
        }

        if (saveStringEnter) {
            i = StringWidth(savegamestrings[saveSlot]);
            WriteText(LoadDef.x + i, LoadDef.y + LINEHEIGHT * saveSlot, "_");
        }
    	}
    }

    /**
     * M_Responder calls this when user is finished
     * 
     * @param slot
     */

    public void DoSave(int slot) {
        DG.SaveGame(slot, new String(savegamestrings[slot]));
        ClearMenus();

        // PICK QUICKSAVE SLOT YET?
        if (quickSaveSlot == -2)
            quickSaveSlot = slot;
    }

    /**
     * User wants to save. Start string input for M_Responder
     */

    class M_SaveSelect
            implements MenuRoutine {
        @Override
        public void invoke(int choice) {
            // we are going to be intercepting all chars
        	//System.out.println("ACCEPTING typing input");
            saveStringEnter = true;

            saveSlot = choice;
            C2JUtils.strcpy(saveOldString, savegamestrings[choice]);
            if (C2JUtils.strcmp(savegamestrings[choice], EMPTYSTRING))
                savegamestrings[choice][0] = 0;
            saveCharIndex = C2JUtils.strlen(savegamestrings[choice]);
        }
    }

    /**
     * Selected from DOOM menu
     */
    class M_SaveGame
            implements MenuRoutine {
        @Override
        public void invoke(int choice) {
            if (!DM.usergame) {
                StartMessage(SAVEDEAD, null, false);
                return;
            }

            if (DM.gamestate != gamestate_t.GS_LEVEL)
                return;

            SetupNextMenu(SaveDef);
            ReadSaveStrings();
        }
    }

    //
    // M_QuickSave
    //
    private String tempstring;

    class M_QuickSaveResponse
            implements MenuRoutine {
        @Override
        public void invoke(int ch) {
            if (ch == 'y') {
                DoSave(quickSaveSlot);
                S.StartSound(null, sfxenum_t.sfx_swtchx);
            }
        }
    }

    private void QuickSave() {
        if (!DM.usergame) {
            S.StartSound(null, sfxenum_t.sfx_oof);
            return;
        }

        if (DM.gamestate != gamestate_t.GS_LEVEL)
            return;

        if (quickSaveSlot < 0) {
            StartControlPanel();
            ReadSaveStrings();
            SetupNextMenu(SaveDef);
            quickSaveSlot = -2; // means to pick a slot now
            return;
        }
        tempstring = String.format(QSPROMPT,C2JUtils.nullTerminatedString(savegamestrings[quickSaveSlot]));
        StartMessage(tempstring,this.QuickSaveResponse,true);
    }

    //
    // M_QuickLoad
    //
    class M_QuickLoadResponse
            implements MenuRoutine {
        @Override
        public void invoke(int ch) {
            if (ch == 'y') {
                LoadSelect.invoke(quickSaveSlot);
                S.StartSound(null, sfxenum_t.sfx_swtchx);
            }
        }
    }

    class M_QuitResponse
            implements MenuRoutine {
        @Override
        public void invoke(int ch) {
            if (ch != 'y')
                return;
            if (!DM.netgame) {
                if (DM.isCommercial())
                    S.StartSound(null, quitsounds2[(DM.gametic >> 2) & 7]);
                else
                    S.StartSound(null, quitsounds[(DM.gametic >> 2) & 7]);
                // TI.WaitVBL(105);
            }
            I.Quit();
        }
    }

    public void QuickLoad() {
        if (DM.netgame) {
            StartMessage(QLOADNET, null, false);
            return;
        }

        if (quickSaveSlot < 0) {
            StartMessage(QSAVESPOT, null, false);
            return;
        }
        tempstring = String.format(QLPROMPT, C2JUtils.nullTerminatedString(savegamestrings[quickSaveSlot]));
        StartMessage(tempstring, QuickLoadResponse, true);
    }

    class M_Sound
            implements MenuRoutine {

        @Override
        public void invoke(int choice) {

            SetupNextMenu(SoundDef);
        }
    }

    class M_SfxVol
            implements MenuRoutine {

        @Override
        public void invoke(int choice) {
            switch (choice) {
            case 0:
                if (DM.snd_SfxVolume != 0)
                    DM.snd_SfxVolume--;
                break;
            case 1:
                if (DM.snd_SfxVolume < 15)
                    DM.snd_SfxVolume++;
                break;
            }

           S.SetSfxVolume(DM.snd_SfxVolume *8);
        }
    }

    class M_MusicVol
            implements MenuRoutine {

        @Override
        public void invoke(int choice) {
            switch (choice) {
            case 0:
                if (DM.snd_MusicVolume != 0)
                    DM.snd_MusicVolume--;
                break;
            case 1:
                if (DM.snd_MusicVolume < 15)
                    DM.snd_MusicVolume++;
                break;
            }

            S.SetMusicVolume(DM.snd_MusicVolume*8);
        }
    }

    //
    // M_Episode
    //
    private int epi;

    class M_VerifyNightmare
            implements MenuRoutine {

        @Override
        public void invoke(int ch) {
            if (ch != 'y')
                return;

            DG.DeferedInitNew(skill_t.sk_nightmare, epi + 1, 1);
            ClearMenus();
        }
    }

    /**
     * M_ReadThis
     */

    class M_ReadThis
            implements MenuRoutine {

        @Override
        public void invoke(int choice) {
            choice = 0;
            SetupNextMenu(ReadDef1);
        }
    }

    class M_ReadThis2
            implements MenuRoutine {

        @Override
        public void invoke(int choice) {
            choice = 0;
            SetupNextMenu(ReadDef2);
        }
    }

    class M_FinishReadThis
            implements MenuRoutine {

        @Override
        public void invoke(int choice) {
            choice = 0;
            SetupNextMenu(MainDef);
        }
    }

    //
    // M_QuitDOOM
    //

    class M_QuitDOOM
            implements MenuRoutine {
        @Override
        public void invoke(int choice) {
            // We pick index 0 which is language sensitive,
            // or one at random, between 1 and maximum number.
            if (DM.language != Language_t.english)
                endstring = endmsg[0] + "\n\n" + DOSY;
            else
                endstring =
                    endmsg[(DM.gametic % (NUM_QUITMESSAGES - 2)) + 1] + "\n\n"
                            + DOSY;
            StartMessage(endstring, QuitResponse, true);
        }
    }

    class M_QuitGame
            implements MenuRoutine {

        @Override
        public void invoke(int ch) {
            if (ch != 'y')
                return;
            if (!DM.netgame) {
                if (DM.isCommercial())
                S.StartSound(null,quitsounds2[(DM.gametic>>2)&7]);
                else
                S.StartSound(null,quitsounds[(DM.gametic>>2)&7]);
                I.WaitVBL(105);
            }
           I.Quit ();
        }
    }

    class M_SizeDisplay
            implements MenuRoutine {

        @Override
        public void invoke(int choice) {
            switch (choice) {
            case 0:
                if (screenSize > 0) {
                    screenblocks--;
                    screenSize--;
                }
                break;
            case 1:
                if (screenSize < 8) {
                    screenblocks++;
                    screenSize++;
                }
                break;
            }

            R.SetViewSize (screenblocks, detailLevel);
        }

    }

    class M_Options
            implements MenuRoutine {

        @Override
        public void invoke(int choice) {
            SetupNextMenu(OptionsDef);
        }

    }

    class M_NewGame
            implements MenuRoutine {

        @Override
        public void invoke(int choice) {
            if (DM.netgame && !DM.demoplayback) {
                StartMessage(NEWGAME, null, false);
                return;
            }

            if (DM.isCommercial())
                SetupNextMenu(NewDef);
            else
                SetupNextMenu(EpiDef);
        }

    }

    public void StartMessage(String string, MenuRoutine routine, boolean input) {
        messageLastMenuActive = DM.menuactive;
        messageToPrint = true;
        messageString = new String(string);
        messageRoutine = routine;
        messageNeedsInput = input;
        DM.menuactive = true; // "true"
        return;
    }

    public void StopMessage() {
        DM.menuactive = messageLastMenuActive;
        messageToPrint = false;
    }

    /**
     * Find string width from hu_font chars
     */
    public int StringWidth(char[] string) {
        int i;
        int w = 0;
        int c;

        for (i = 0; i < C2JUtils.strlen(string); i++) {
            c = Character.toUpperCase(string[i]) - HU_FONTSTART;
            if (c < 0 || c >= HU_FONTSIZE)
                w += 4;
            else
                w += hu_font[c].width;
        }

        return w;
    }

    /**
     * Find string height from hu_font chars.
     * 
     * Actually it just counts occurences of 'n' and adds height to height.
     */
    private int StringHeight(char[] string) {
        int i;
        int h;
        int height = hu_font[0].height;

        h = height;
        for (i = 0; i < string.length; i++)
            if (string[i] == '\n')
                h += height;

        return h;
    }

    /**
     * Find string height from hu_font chars
     */
    private int StringHeight(String string) {
        return this.StringHeight(string.toCharArray());
    }

    /**
     * Write a string using the hu_font
     */

    private void WriteText(int x, int y, char[] string) {
        int w;
        char[] ch;
        int c;
        int cx;
        int cy;

        ch = string;
        int chptr = 0;
        cx = x;
        cy = y;

        while (chptr<ch.length) {
            c = ch[chptr];
            chptr++;
            if (c == 0)
                break;
            if (c == '\n') {
                cx = x;
                cy += 12;
                continue;
            }
            
            c = Character.toUpperCase(c) - HU_FONTSTART;
            if (c < 0 || c >= HU_FONTSIZE) {
                cx += 4;
                continue;
            }

            w = hu_font[c].width;
            if (cx + w > SCREENWIDTH)
                break;
            
            V.DrawScaledPatch(cx, cy, 0,vs, hu_font[c]);
            cx += w;
        }

    }

    private void WriteText(int x, int y, String string) {
        if (string == null || string.length() == 0)
            return;

        int w;
        int cx;
        int cy;

        int chptr = 0;
        char c;

        cx = x;
        cy = y;

        while (chptr<string.length()) {
            c = string.charAt(chptr++);
            if (c == 0)
                break;
            if (c == '\n') {
                cx = x;
                cy += 12;
                continue;
            }

            c = (char) (Character.toUpperCase(c) - HU_FONTSTART);
            if (c < 0 || c >= HU_FONTSIZE) {
                cx += 4;
                continue;
            }

            w = hu_font[c].width;
            if (cx + w > SCREENWIDTH)
                break;
            V.DrawScaledPatch(cx, cy, 0,vs, hu_font[c]);
            cx += w;
        }

    }

    // These belong to the responder.
    
    private int joywait = 0;

    private int mousewait = 0;

    private int mousey = 0;

    private int lasty = 0;

    private int mousex = 0;

    private int lastx = 0;

    public boolean Responder(event_t ev) {

        char ch;
        int i;
        ch = 0xFFFF;

        //System.out.println("Processing keyevent:" +(ev.type==evtype_t.ev_keydown || ev.type==evtype_t.ev_keyup)+ " value = "+(char)ev.data1);
        
        // Joystick input
        
        if (ev.type == evtype_t.ev_joystick && joywait < TICK.GetTime()) {
            if (ev.data3 == -1) {
                ch = KEY_UPARROW;
                joywait = TICK.GetTime() + 5;
            } else if (ev.data3 == 1) {
                ch = KEY_DOWNARROW;
                joywait = TICK.GetTime() + 5;
            }

            if (ev.data2 == -1) {
                ch = KEY_LEFTARROW;
                joywait = TICK.GetTime() + 2;
            } else if (ev.data2 == 1) {
                ch = KEY_RIGHTARROW;
                joywait = TICK.GetTime() + 2;
            }

            if ((ev.data1 & 1) != 0) {
                ch = KEY_ENTER;
                joywait = TICK.GetTime() + 5;
            }
            if ((ev.data1 & 2) != 0) {
                ch = KEY_BACKSPACE;
                joywait = TICK.GetTime() + 5;
            }
        } else 
        // Mouse input 
        {
            if (ev.type == evtype_t.ev_mouse && mousewait < TICK.GetTime()) {
                mousey += ev.data3;
                if (mousey < lasty - 30) {
                    ch = KEY_DOWNARROW;
                    mousewait = TICK.GetTime() + 5;
                    mousey = lasty -= 30;
                } else if (mousey > lasty + 30) {
                    ch = KEY_UPARROW;
                    mousewait = TICK.GetTime() + 5;
                    mousey = lasty += 30;
                }

                mousex += ev.data2;
                if (mousex < lastx - 30) {
                    ch = KEY_LEFTARROW;
                    mousewait = TICK.GetTime() + 5;
                    mousex = lastx -= 30;
                } else if (mousex > lastx + 30) {
                    ch = KEY_RIGHTARROW;
                    mousewait = TICK.GetTime() + 5;
                    mousex = lastx += 30;
                }

                if ((ev.data1 & 1) != 0) {
                    ch = KEY_ENTER;
                    mousewait = TICK.GetTime() + 15;
                }

                if ((ev.data1 & 2) != 0) {
                    ch = KEY_BACKSPACE;
                    mousewait = TICK.GetTime() + 15;
                }
            } else if (ev.type == evtype_t.ev_keydown) {
                ch = (char) ev.data1;
            }
        }

        if (ch == 0xFFFF)
            return false;

        // Save Game string input
        if (saveStringEnter) {

            switch (ch) {
            case KEY_BACKSPACE:
                if (saveCharIndex > 0) {
                    saveCharIndex--;
                    savegamestrings[saveSlot][saveCharIndex] = 0;
                }
                break;

            case KEY_ESCAPE:
                saveStringEnter = false;
                C2JUtils.strcpy(savegamestrings[saveSlot], saveOldString);
                break;

            case KEY_ENTER:            	
                saveStringEnter = false;
                if (savegamestrings[saveSlot][0] != 0)
                    DoSave(saveSlot);
                break;

            default:
                ch = Character.toUpperCase(ch);
            	if (ch != 32)
                    if (ch - HU_FONTSTART < 0
                            || ch - HU_FONTSTART >= HU_FONTSIZE)
                        break;
                if (ch >= 32
                        && ch <= 127
                        && saveCharIndex < SAVESTRINGSIZE - 1
                        && StringWidth(savegamestrings[saveSlot]) < (SAVESTRINGSIZE - 2) * 8) {
                    savegamestrings[saveSlot][saveCharIndex++] = ch;
                    savegamestrings[saveSlot][saveCharIndex] = 0;
                }
                break;
            }
            return true;
        }

        // Take care of any messages that need input
        if (messageToPrint) {
            if (messageNeedsInput == true
                    && !(ch == ' ' || ch == 'n' || ch == 'y' || ch == KEY_ESCAPE))
                return false;

            DM.menuactive = messageLastMenuActive;
            messageToPrint = false;
            if (messageRoutine != null)
                messageRoutine.invoke(ch);

            DM.menuactive = false; // "false"
            S.StartSound(null, sfxenum_t.sfx_swtchx);
            return true;
        }

        if (DM.devparm && ch == KEY_F1) {
            DG.ScreenShot();
            return true;
        }

        // F-Keys
        if (!DM.menuactive){
            switch (ch) {
            case KEY_MINUS: // Screen size down
                if (DM.automapactive || HU.chat_on[0])
                    return false;
                SizeDisplay.invoke(0);
                S.StartSound(null, sfxenum_t.sfx_stnmov);
                return true;

            case KEY_EQUALS: // Screen size up
                if (DM.automapactive || HU.chat_on[0])
                    return false;
                SizeDisplay.invoke(1);
                S.StartSound(null, sfxenum_t.sfx_stnmov);
                return true;

            case KEY_F1: // Help key
                StartControlPanel();

                if (DM.isRetail() || currentMenu==ReadDef1)
                    currentMenu = ReadDef2;
                else
                    currentMenu = ReadDef1;
                itemOn = 0;
                S.StartSound(null, sfxenum_t.sfx_swtchn);
                return true;

            case KEY_F2: // Save
                StartControlPanel();
                S.StartSound(null, sfxenum_t.sfx_swtchn);
                SaveGame.invoke(0);
                return true;

            case KEY_F3: // Load
                StartControlPanel();
                S.StartSound(null, sfxenum_t.sfx_swtchn);
                LoadGame.invoke(0);
                return true;

            case KEY_F4: // Sound Volume
                StartControlPanel();
                currentMenu = SoundDef;
                itemOn = (short) sfx_vol;
                S.StartSound(null, sfxenum_t.sfx_swtchn);
                return true;

            case KEY_F5: // Detail toggle
                ChangeDetail.invoke(0);
                S.StartSound(null, sfxenum_t.sfx_swtchn);
                return true;

            case KEY_F6: // Quicksave
                S.StartSound(null, sfxenum_t.sfx_swtchn);
                QuickSave();
                return true;

            case KEY_F7: // End game
                S.StartSound(null, sfxenum_t.sfx_swtchn);
                EndGame.invoke(0);
                return true;

            case KEY_F8: // Toggle messages
                ChangeMessages.invoke(0);
                S.StartSound(null, sfxenum_t.sfx_swtchn);
                return true;

            case KEY_F9: // Quickload
                S.StartSound(null, sfxenum_t.sfx_swtchn);
                QuickLoad();
                return true;

            case KEY_F10: // Quit DOOM
                S.StartSound(null, sfxenum_t.sfx_swtchn);
                QuitDOOM.invoke(0);
                return true;

            case KEY_F11: // gamma toggle
                int usegamma = V.getUsegamma();
                usegamma++;
                if (usegamma > 4)
                    usegamma = 0;
                DM.players[DM.consoleplayer].message = gammamsg[usegamma];
                // FIXME: it's pointless to reload the same palette.
                //I.SetPalette (W.CacheLumpName ("PLAYPAL",PU_CACHE));
                DM.VI.SetPalette(0);
                DM.VI.SetGamma(usegamma);
                return true;

            }
    }
        // Pop-up menu?
        if (!DM.menuactive) {
            if (ch == KEY_ESCAPE) {
                StartControlPanel();
                S.StartSound(null, sfxenum_t.sfx_swtchn);
                return true;
            }
            return false;
        }

        // Keys usable within menu
        switch (ch) {
        case KEY_DOWNARROW:
            do {
                if (itemOn + 1 > currentMenu.numitems - 1)
                    itemOn = 0;
                else
                    itemOn++;
                S.StartSound(null, sfxenum_t.sfx_pstop);
            } while (currentMenu.menuitems[itemOn].status == -1);
            return true;

        case KEY_UPARROW:
            do {
                if (itemOn == 0)
                    itemOn = (short) (currentMenu.numitems - 1);
                else
                    itemOn--;
              S.StartSound(null, sfxenum_t.sfx_pstop);
            } while (currentMenu.menuitems[itemOn].status == -1);
            return true;

        case KEY_LEFTARROW:
            if ((currentMenu.menuitems[itemOn].routine != null)
                    && (currentMenu.menuitems[itemOn].status == 2)) {
                S.StartSound(null, sfxenum_t.sfx_stnmov);
                currentMenu.menuitems[itemOn].routine.invoke(0);
            }
            return true;

        case KEY_RIGHTARROW:
            if ((currentMenu.menuitems[itemOn].routine != null)
                    && (currentMenu.menuitems[itemOn].status == 2)) {
            	S.StartSound(null, sfxenum_t.sfx_stnmov);
                currentMenu.menuitems[itemOn].routine.invoke(1);
            }
            return true;

        case KEY_ENTER: {
            if ((currentMenu.menuitems[itemOn].routine != null)
                    && currentMenu.menuitems[itemOn].status != 0) {
                currentMenu.lastOn = itemOn;
                if (currentMenu.menuitems[itemOn].status == 2) {
                    currentMenu.menuitems[itemOn].routine.invoke(1); // right
                    // arrow
                    S.StartSound(null, sfxenum_t.sfx_stnmov);
                } else {
                    currentMenu.menuitems[itemOn].routine.invoke(itemOn);
                    S.StartSound(null, sfxenum_t.sfx_pistol);
                }
            }
        }
            return true;

        case KEY_ESCAPE:
            currentMenu.lastOn = itemOn;
            ClearMenus();
            S.StartSound(null, sfxenum_t.sfx_swtchx);
            return true;

        case KEY_BACKSPACE:
            currentMenu.lastOn = itemOn;
            if (currentMenu.prevMenu != null) {
                currentMenu = currentMenu.prevMenu;
                itemOn = (short) currentMenu.lastOn;
                 S.StartSound(null, sfxenum_t.sfx_swtchn);
            }
            return true;

        default:
            for (i = itemOn + 1; i < currentMenu.numitems; i++)
                if (currentMenu.menuitems[i].alphaKey == ch) {
                    itemOn = (short) i;
                    S.StartSound(null, sfxenum_t.sfx_pstop);
                    return true;
                }
            for (i = 0; i <= itemOn; i++)
                if (currentMenu.menuitems[i].alphaKey == ch) {
                    itemOn = (short) i;
                    S.StartSound(null, sfxenum_t.sfx_pstop);
                    return true;
                }
            break;

        }

        return false;
    }

    /**
     * M_StartControlPanel
     */
    public void StartControlPanel() {
        // intro might call this repeatedly
        if (DM.menuactive)
            return;

        DM.menuactive = true;
        currentMenu = MainDef; // JDC
        itemOn = (short) currentMenu.lastOn; // JDC
    }

    /**
     * M_Drawer Called after the view has been rendered, but before it has been
     * blitted.
     */
    public void Drawer() {

        int x;
        int y;
        int max;
        char[] string = new char[40];
        char[] msstring;
        int start;
        inhelpscreens = false; // Horiz. & Vertically center string and print
        // it.
        if (messageToPrint) {
            start = 0;
            y = 100 - this.StringHeight(messageString) / 2;
            msstring = messageString.toCharArray();
            while(start<messageString.length()) {
                int i=0;
                for ( i = 0; i < messageString.length() - start; i++) 
                    if (msstring[start + i] == '\n') {
                        C2JUtils.memset(string, (char) 0, 40);
                        C2JUtils.strcpy(string, msstring, start, i);
                        start += i + 1;
                        break;
                    }
                
                    if (i == (messageString.length() - start)) {
                        C2JUtils.strcpy(string, msstring,start);
                        start += i;
                    }
                    x = 160 - this.StringWidth(string) / 2;
                    this.WriteText(x, y, string);
                    y += hu_font[0].height;
                }
                return;
            }
            if (!DM.menuactive)
                return;
            if (currentMenu.routine != null){
                currentMenu.routine.invoke(); // call Draw routine
            }
            // DRAW MENU
            x = currentMenu.x;
            y = currentMenu.y;
            max = currentMenu.numitems;
            for (int i = 0; i < max; i++) {
                if (currentMenu.menuitems[i].name != null && currentMenu.menuitems[i].name!="")
                    V.DrawScaledPatch(x, y, 0,vs, W.CachePatchName(
                        currentMenu.menuitems[i].name, PU_CACHE));
                y += LINEHEIGHT;
            }

            // DRAW SKULL
            V.DrawScaledPatch(x + SKULLXOFF, currentMenu.y - 5 + itemOn
                    * LINEHEIGHT, 0, vs,W.CachePatchName(skullName[whichSkull],
                PU_CACHE));
    }

    //
    // M_ClearMenus
    //
    public void ClearMenus() {
        DM.menuactive = false;
        V.clearCaches();
        // MAES: was commented out :-/
        //if (!DM.netgame && DM.usergame && DM.paused)
        //    DM.setPaused(true);
    }

    /**
     * M_SetupNextMenu
     */
    public void SetupNextMenu(menu_t menudef) {
        currentMenu = menudef;
        itemOn = (short) currentMenu.lastOn;
    }

    /**
     * M_Ticker
     */
    public void Ticker() {
        if (--skullAnimCounter <= 0) {
            whichSkull ^= 1;
            skullAnimCounter = 8;
        }
    }

    /**
     * M_Init
     */
    public void Init() {
        
        // Init menus.
        this.initMenuRoutines();
        this.initDrawRoutines();
        this.initMenuItems();
        this.hu_font=HU.getHUFonts();

        currentMenu = MainDef;
        DM.menuactive = false;
        itemOn = (short) currentMenu.lastOn;
        whichSkull = 0;
        skullAnimCounter = 10;
        screenSize = screenblocks - 3;
        messageToPrint = false;
        messageString = null;
        messageLastMenuActive = DM.menuactive;
        quickSaveSlot = -1;

        // Here we could catch other version dependencies,
        // like HELP1/2, and four episodes.

        switch (DM.getGameMode()) {
        case commercial:
        case pack_plut:
        case pack_tnt:
            // This is used because DOOM 2 had only one HELP
            // page. I use CREDIT as second page now, but
            // kept this hack for educational purposes.
            MainMenu[readthis] = MainMenu[quitdoom];
            MainDef.numitems--;
            MainDef.y += 8;
            NewDef.prevMenu = MainDef;
            ReadDef1.routine = DrawReadThis1;
            ReadDef1.x = 330;
            ReadDef1.y = 165;
            ReadMenu1[0].routine = FinishReadThis;
            break;
        case shareware:
            // Episode 2 and 3 are handled,
            // branching to an ad screen.
        case registered:
            // We need to remove the fourth episode.
            EpiDef.numitems--;
            break;
        case retail:
            // We are fine.
        default:
            break;
        }

    }

    

    /**
     * M_DrawText Returns the final X coordinate HU_Init must have been called
     * to init the font. Unused?
     * 
     * @param x
     * @param y
     * @param direct
     * @param string
     * @return
     */

    public int DrawText(int x, int y, boolean direct, String string) {
        int c;
        int w;
        int ptr = 0;
        
        while ((c=string.charAt(ptr)) > 0) {
            c = Character.toUpperCase(c) - HU_FONTSTART;
            ptr++;
            if (c < 0 || c > HU_FONTSIZE) {
                x += 4;
                continue;
            }

            w = hu_font[c].width;
            if (x + w > SCREENWIDTH)
                break;
            if (direct)
                V.DrawScaledPatch(x, y, 0,vs, hu_font[c]);
            else
                V.DrawScaledPatch(x, y, 0,vs, hu_font[c]);
            x += w;
        }

        return x;
    }

    

    // ////////////////////////// DRAWROUTINES
    // //////////////////////////////////

    class M_DrawEpisode
            implements DrawRoutine {

        @Override
        public void invoke() {
            V.DrawScaledPatch(54, 38, 0,vs, W.CachePatchName("M_EPISOD"));
        }

    }

    /**
     * M_LoadGame & Cie.
     */

    class M_DrawLoad
            implements DrawRoutine {
        @Override
        public void invoke() {
            int i;

            V.DrawScaledPatch(72, 28, 0,vs, W.CachePatchName("M_LOADG"));
            for (i = 0; i < load_end; i++) {
                DrawSaveLoadBorder(LoadDef.x, LoadDef.y + LINEHEIGHT * i);
                WriteText(LoadDef.x, LoadDef.y + LINEHEIGHT * i,
                    savegamestrings[i]);
            }

        }
    }

    class M_DrawMainMenu
            implements DrawRoutine {
        @Override
        public void invoke() {
            V.DrawScaledPatch(94, 2, 0,vs, (patch_t) (W.CachePatchName("M_DOOM")));
        }
    }

    class M_DrawNewGame
            implements DrawRoutine {

        @Override
        public void invoke() {
            V.DrawScaledPatch(96, 14, 0, vs,(patch_t) W.CachePatchName("M_NEWG"));
            V.DrawScaledPatch(54, 38, 0, vs,(patch_t) W.CachePatchName("M_SKILL"));
        }
    }

    class M_DrawOptions
            implements DrawRoutine {

        private String detailNames[] = { "M_GDHIGH", "M_GDLOW" };

        private String msgNames[] = { "M_MSGOFF", "M_MSGON" };

        @Override
        public void invoke() {
            V.DrawScaledPatch(108, 15, 0, vs,W.CachePatchName("M_OPTTTL"));

            V.DrawScaledPatch(OptionsDef.x + 175, OptionsDef.y + LINEHEIGHT
                    * detail, 0,vs, W.CachePatchName(detailNames[detailLevel]));

            V.DrawScaledPatch(OptionsDef.x + 120, OptionsDef.y + LINEHEIGHT
                    * messages, 0,vs, W.CachePatchName(msgNames[showMessages?1:0]));

            DrawThermo(OptionsDef.x, OptionsDef.y + LINEHEIGHT
                    * (mousesens + 1), 10, DM.mouseSensitivity);

            DrawThermo(OptionsDef.x,
                OptionsDef.y + LINEHEIGHT * (scrnsize + 1), 9, screenSize);

        }

    }

    /**
     * Read This Menus Had a "quick hack to fix romero bug"
     */

    class M_DrawReadThis1
            implements DrawRoutine {

        @Override
        public void invoke() {
            inhelpscreens = true;
            switch (DM.getGameMode()) {
            case commercial:
                V.DrawPatchSolidScaled(0, 0, 0, vs,W.CachePatchName("HELP"));
                break;
            case shareware:
            case registered:
            case retail:
                V.DrawPatchSolidScaled(0, 0, 0,vs, W.CachePatchName("HELP1"));
                break;
            default:
                break;
            }
            
            // Maes: we need to do this here, otherwide the status bar appears "dirty"
            DM.ST.forceRefresh();
            return;
        }
    }

    /**
     * Read This Menus - optional second page.
     */
    class M_DrawReadThis2
            implements DrawRoutine {

        @Override
        public void invoke() {
            inhelpscreens = true;
            switch (DM.getGameMode()) {
            case retail:
            case commercial:
                // This hack keeps us from having to change menus.
                V.DrawPatchSolidScaled(0, 0, 0, vs,W.CachePatchName("CREDIT"));
                break;
            case shareware:
            case registered:
                V.DrawPatchSolidScaled(0, 0, 0, vs,W.CachePatchName("HELP2"));
                break;
            default:
                break;
            }
            
            // Maes: we need to do this here, otherwide the status bar appears "dirty"
            DM.ST.forceRefresh();
            return;
        }
    }

    /**
     * Change Sfx & Music volumes
     */
    class M_DrawSound
            implements DrawRoutine {

        public void invoke() {
            V.DrawScaledPatch(60, 38, 0,vs, (patch_t) W.CacheLumpName("M_SVOL",
                PU_CACHE, patch_t.class));

            DrawThermo(SoundDef.x, SoundDef.y + LINEHEIGHT * (sfx_vol + 1), 16,
                DM.snd_SfxVolume);

            DrawThermo(SoundDef.x, SoundDef.y + LINEHEIGHT * (music_vol + 1),
                16, DM.snd_MusicVolume);
        }
    }

    // /////////////////////////// MENU ROUTINES
    // ///////////////////////////////////

    class M_ChangeDetail
            implements MenuRoutine {

        @Override
        public void invoke(int choice) {
            choice = 0;
            detailLevel = 1 - detailLevel;

            // FIXME - does not work. Remove anyway?
            //System.err.print("M_ChangeDetail: low detail mode n.a.\n");

            //return;

            R.SetViewSize (screenblocks, detailLevel); 
            if (detailLevel==0) DM.players[DM.consoleplayer].message = englsh.DETAILHI;
             else DM.players[DM.consoleplayer].message = englsh.DETAILLO;
             

        }
    }

    /**
     * Toggle messages on/off
     */
    class M_ChangeMessages
            implements MenuRoutine {

        @Override
        public void invoke(int choice) {
            // warning: unused parameter `int choice'
            //choice = 0;
            showMessages = !showMessages;

            if (!showMessages)
                DM.players[DM.consoleplayer].message = MSGOFF;
            else
                DM.players[DM.consoleplayer].message = MSGON;

            message_dontfuckwithme = true;
        }
    }

    class M_ChangeSensitivity
            implements MenuRoutine {
        @Override
        public void invoke(int choice) {
            switch (choice) {
            case 0:
                if (DM.mouseSensitivity != 0)
                	DM.mouseSensitivity--;
                break;
            case 1:
                if (DM.mouseSensitivity < 9)
                	DM.mouseSensitivity++;
                break;
            }
        }
    }

    class M_ChooseSkill
            implements MenuRoutine {

        @Override
        public void invoke(int choice) {
            if (choice == nightmare) {
                StartMessage(NIGHTMARE, VerifyNightmare, true);
                return;
            }

            DG.DeferedInitNew(skill_t.values()[choice], epi + 1, 1);
            ClearMenus();
        }

    }

    /**
     * M_EndGame
     */

    class M_EndGame
            implements MenuRoutine {

        @Override
        public void invoke(int choice) {
            choice = 0;
            if (!DM.usergame) {
                 S.StartSound(null, sfxenum_t.sfx_oof);
                return;
            }

            if (DM.netgame) {
                StartMessage(NETEND, null, false);
                return;
            }

            StartMessage(ENDGAME, EndGameResponse, true);
        }
    }

    class M_EndGameResponse
            implements MenuRoutine {

        @Override
        public void invoke(int ch) {
            if (ch != 'y')
                return;

            currentMenu.lastOn = itemOn;
            ClearMenus();
            DG.StartTitle();
        }
    }

    class M_Episode
            implements MenuRoutine {

        @Override
        public void invoke(int choice) {

            if (DM.isShareware() && (choice != 0)) {
                StartMessage(SWSTRING, null, false);
                SetupNextMenu(ReadDef2);
                return;
            }

            // Yet another hack...
            if (!DM.isRetail() && (choice > 2)) {
                System.err
                        .print("M_Episode: 4th episode requires UltimateDOOM\n");
                choice = 0;
            }

            epi = choice;
            SetupNextMenu(NewDef);
        }

    }

    /**
     * User wants to load this game
     */
    class M_LoadSelect
            implements MenuRoutine {
        @Override
        public void invoke(int choice) {
            String name;

            if (DM.CM.CheckParm("-cdrom") != 0)
                name = ("c:\\doomdata\\" + SAVEGAMENAME + (choice) + ".dsg");
            else
                name = (SAVEGAMENAME + (choice) + ".dsg");
            DG.LoadGame(name);
            ClearMenus();
        }
    }

    /**
     * Selected from DOOM menu
     */
    class M_LoadGame
            implements MenuRoutine {
        @Override
        public void invoke(int choice) {

            if (DM.netgame) {
                StartMessage(LOADNET, null, false);
                return;
            }

            SetupNextMenu(LoadDef);
            ReadSaveStrings();
        }
    }

    // ////////////////////// VARIOUS CONSTS //////////////////////

    private static final sfxenum_t[] quitsounds =
        { sfxenum_t.sfx_pldeth, sfxenum_t.sfx_dmpain, sfxenum_t.sfx_popain,
                sfxenum_t.sfx_slop, sfxenum_t.sfx_telept, sfxenum_t.sfx_posit1,
                sfxenum_t.sfx_posit3, sfxenum_t.sfx_sgtatk };

    private static final sfxenum_t[] quitsounds2 =
        { sfxenum_t.sfx_vilact, sfxenum_t.sfx_getpow, sfxenum_t.sfx_boscub,
                sfxenum_t.sfx_slop, sfxenum_t.sfx_skeswg, sfxenum_t.sfx_kntdth,
                sfxenum_t.sfx_bspact, sfxenum_t.sfx_sgtatk };

    /** episodes_e enum */
    private static final int ep1 = 0, ep2 = 1, ep3 = 2, ep4 = 3, ep_end = 4;

    /** load_e enum */
    private static final int load1 = 0, load2 = 1, load3 = 2, load4 = 3, load5 = 4,
            load6 = 5, load_end = 6;

    /** options_e enum; */

    private static final int endgame = 0, messages = 1, detail = 2, scrnsize = 3,
            option_empty1 = 4, mousesens = 5, option_empty2 = 6, soundvol = 7,
            opt_end = 8;

    /** main_e enum; */
    private static final int  newgame = 0, options = 1, loadgam = 2, savegame = 3,
            readthis = 4, quitdoom = 5, main_end = 6;

    /** read_e enum */
    private static final int rdthsempty1 = 0, read1_end = 1;

    /** read_2 enum */
    private static final int rdthsempty2 = 0, read2_end = 1;

    /**  newgame_e enum;*/
    public static final int killthings = 0, toorough = 1, hurtme = 2, violence = 3,
            nightmare = 4, newg_end = 5;
    
    private static final String[] gammamsg = { "GAMMALVL0",

        "GAMMALVL1", "GAMMALVL2", "GAMMALVL3", "GAMMALVL4" };

    /** sound_e enum */
    static final int sfx_vol = 0, sfx_empty1 = 1, music_vol = 2, sfx_empty2 = 3,
            sound_end = 4;

    @Override
    public void setScreenBlocks(int val) {
        this.screenblocks=val;
    }
    
	@Override
	public int getScreenBlocks() {
		return this.screenblocks;
	}

	@Override
	public int getDetailLevel() {
		return this.detailLevel;
	}
	
////////////////////////////VIDEO SCALE STUFF ////////////////////////////////

	protected int SCREENWIDTH;
	protected int SCREENHEIGHT;
	protected IVideoScale vs;


	@Override
	public void setVideoScale(IVideoScale vs) {
	    this.vs=vs;
	}

	@Override
	public void initScaling() {
	    this.SCREENHEIGHT=vs.getScreenHeight();
	    this.SCREENWIDTH=vs.getScreenWidth();
	}




}
package m;

public interface IUseVariables {
    
    
    /** Register a variable manager with this module.
     * 
     * @param manager
     */
    public void registerVariableManager(IVariablesManager manager);
    
    
    /** Apply listener-specific variables, asking the manager for them.
     *  Every listener should concern itself with its own variables/settings.
     *  
     *  This method should be called by the manager on every registered
     *  listener. Each listener then "knows" which settings it must update.
     *  
     *  Good for block updates, but maybe a more lightweight mechanism should
     *  be provided, e.g. being able to update just one setting for a listener.
     * 
     */
    public void update();
    
    /** If the variables user makes too many changes, it may be better to 
     * communicate them back to the manager in-block. This shouldn't be needed,
     * if everywhere a certain setting has to be modified tis done through the
     * manager.
     */
    
    public void commit();


}

package m;

public interface IVariablesManager {
    
    /** Does two things: sets a variable, and then calls setme.
     * This way, immediate updates of a single variables user 
     * are possible, avoiding some of the overhead. 
     * 
     * Sadly, users with too many variables will still incur an
     * O(n) penalty.
     * 
     * @param setme
     * @param name
     * @param value
     */
    
    public void applySetting(IUseVariables setme,String name, String value);

    public DoomSetting getSetting(String name);
    
    public DoomSetting getSetting(Settings name);

    public boolean isSettingLiteral(String name,String value);
    
 /** Creates a new setting, overriding any existing ones.
  *   
  * @param name
  * @param value
  * @param persist
  */
    
    public void putSetting(String name, Object value, boolean persist);
    
    /** Puts a new setting or updates an existing one. In this case,
     * the value of the "persist" field is kept unaltered, so that persistent
     * settings are not lost during updates. 
     * 
     * @param name
     * @param value
     */
    
    public void putSetting(String name, Object value);
    public void putSetting(Settings name, Object value);

    void LoadDefaults(String defaultfile);

    void SaveDefaults(String defaultfile);
    
    String getDefaultFile();
    
}

package m;

import v.IVideoScale;
import doom.DoomStatus;
import doom.event_t;

/** A dummy menu, useful for testers that do need a defined
 *  menu object.
 * 
 * @author Maes
 *
 */

public class DummyMenu
        extends AbstractDoomMenu {

    @Override
    public void setVideoScale(IVideoScale vs) {
        // TODO Auto-generated method stub

    }

    @Override
    public void initScaling() {
        // TODO Auto-generated method stub

    }

    @Override
    public boolean Responder(event_t ev) {
        // TODO Auto-generated method stub
        return false;
    }

    @Override
    public void Ticker() {
        // TODO Auto-generated method stub

    }

    @Override
    public void Drawer() {
        // TODO Auto-generated method stub

    }

    @Override
    public void Init() {
        // TODO Auto-generated method stub

    }

    @Override
    public void StartControlPanel() {
        // TODO Auto-generated method stub

    }

    @Override
    public boolean getShowMessages() {
        // TODO Auto-generated method stub
        return false;
    }

    @Override
    public void setShowMessages(boolean val) {
        // TODO Auto-generated method stub

    }

    @Override
    public int getScreenBlocks() {
        // TODO Auto-generated method stub
        return 0;
    }

    @Override
    public void setScreenBlocks(int val) {
        // TODO Auto-generated method stub

    }

    @Override
    public int getDetailLevel() {
        // TODO Auto-generated method stub
        return 0;
    }

    @Override
    public void updateStatus(DoomStatus DS) {
        // TODO Auto-generated method stub

    }

	@Override
	public void ClearMenus() {
		// TODO Auto-generated method stub
		
	}

}

package m;

public class default_t {

    public default_t(String name, int[] location, int defaultvalue) {
        this.name = name;
        this.location = location;
        this.defaultvalue = defaultvalue;
    }

    public String name;

    /** this is supposed to be a pointer */
    public int[] location;

    public int defaultvalue;

    int scantranslate; // PC scan code hack

    int untranslated; // lousy hack
};

package st;

enum st_chatstateenum_t {
	    StartChatState,
	    WaitDestState,
	    GetChatState
}

package st;

// Emacs style mode select -*- C++ -*-
// -----------------------------------------------------------------------------
//
// $Id: StatusBar.java,v 1.47 2011/11/01 23:46:37 velktron Exp $
//
// Copyright (C) 1993-1996 by id Software, Inc.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// DESCRIPTION:
// Status bar code.
// Does the face/direction indicator animatin.
// Does palette indicators as well (red pain/berserk, bright pickup)
//
// -----------------------------------------------------------------------------

import defines.*;
import static data.Limits.MAXPLAYERS;
import static g.Keys.*;
import static data.Defines.*;
import static doom.englsh.*;
import static automap.IAutoMap.*;
import static doom.player_t.*;
import static doom.items.*;
import static data.Tables.*;
import p.mobj_t;
import m.cheatseq_t;
import data.sounds.musicenum_t;
import doom.DoomStatus;
import doom.event_t;
import doom.evtype_t;
import doom.player_t;
import doom.weapontype_t;
import rr.patch_t;
import v.IVideoScale;
import static v.DoomVideoRenderer.*;

public class StatusBar extends AbstractStatusBar   {
    public static final String rcsid =
        "$Id: StatusBar.java,v 1.47 2011/11/01 23:46:37 velktron Exp $";

   
    
    // Size of statusbar.
    // Now sensitive for scaling.

    //
    // STATUS BAR DATA
    //

    // Palette indices.
    // For damage/bonus red-/gold-shifts
    private static int STARTREDPALS = 1;

    private static int STARTBONUSPALS = 9;

    private static int NUMREDPALS = 8;

    private static int NUMBONUSPALS = 4;

    // Radiation suit, green shift.
    private static int RADIATIONPAL = 13;

    // N/256*100% probability
    // that the normal face state will change
    private static int ST_FACEPROBABILITY = 96;

    // For Responder
    private static int ST_TOGGLECHAT = KEY_ENTER;

    // Location of status bar
    private int ST_X = 0;
    private int ST_X2;
    private int ST_FX;
    private int ST_FY;

    // Should be set to patch width
    // for tall numbers later on
    // TODO: private static int ST_TALLNUMWIDTH = (tallnum[0].width);

    // Number of status faces.
    private static int ST_NUMPAINFACES = 5;

    private static int ST_NUMSTRAIGHTFACES = 3;

    private static int ST_NUMTURNFACES = 2;

    private static int ST_NUMSPECIALFACES = 3;

    private static int ST_FACESTRIDE =
        (ST_NUMSTRAIGHTFACES + ST_NUMTURNFACES + ST_NUMSPECIALFACES);

    private static int ST_NUMEXTRAFACES = 2;

    private static int ST_NUMFACES =
        (ST_FACESTRIDE * ST_NUMPAINFACES + ST_NUMEXTRAFACES);

    private static int ST_TURNOFFSET = (ST_NUMSTRAIGHTFACES);

    private static int ST_OUCHOFFSET = (ST_TURNOFFSET + ST_NUMTURNFACES);

    private static int ST_EVILGRINOFFSET = (ST_OUCHOFFSET + 1);

    private static int ST_RAMPAGEOFFSET = (ST_EVILGRINOFFSET + 1);

    private static int ST_GODFACE = (ST_NUMPAINFACES * ST_FACESTRIDE);

    private static int ST_DEADFACE = (ST_GODFACE + 1);

    private int ST_FACESX;

    private int ST_FACESY;

    private static int ST_EVILGRINCOUNT = (2 * TICRATE);

    private static int ST_STRAIGHTFACECOUNT = (TICRATE / 2);

    private static int ST_TURNCOUNT = (1 * TICRATE);

    private static int ST_OUCHCOUNT = (1 * TICRATE);

    private static int ST_RAMPAGEDELAY = (2 * TICRATE);

    private static int ST_MUCHPAIN = 20;

    // Location and size of statistics,
    // justified according to widget type.
    // Problem is, within which space? STbar? Screen?
    // Note: this could be read in by a lump.
    // Problem is, is the stuff rendered
    // into a buffer,
    // or into the frame buffer?

    // AMMO number pos.
    private int ST_AMMOWIDTH;
    private int ST_AMMOX;
    private int ST_AMMOY;

    // HEALTH number pos.
    private int ST_HEALTHWIDTH=3;
    private int ST_HEALTHX;
    private int ST_HEALTHY;

    // Weapon pos.
    private int ST_ARMSX;
    private int ST_ARMSY;
    private int ST_ARMSBGX;
    private int ST_ARMSBGY;
    private int ST_ARMSXSPACE;
    private int ST_ARMSYSPACE;

    // Frags pos.
    private int ST_FRAGSX;
    private int ST_FRAGSY;
    private int ST_FRAGSWIDTH;

    // ARMOR number pos.
    private int ST_ARMORWIDTH = 3;

    private int ST_ARMORX;

    private int ST_ARMORY;

    // Key icon positions.
    private int ST_KEY0WIDTH;
    private int ST_KEY0HEIGHT;
    private int ST_KEY0X;
    private int ST_KEY0Y;

    private int ST_KEY1WIDTH;
    private int ST_KEY1X;
    private int ST_KEY1Y;

    private int ST_KEY2WIDTH;
    private int ST_KEY2X;
    private int ST_KEY2Y;

    // Ammunition counter.
    private int ST_AMMO0WIDTH;
    private int ST_AMMO0HEIGHT;

    private int ST_AMMO0X;
    private int ST_AMMO0Y;

    private int ST_AMMO1WIDTH;

    private int ST_AMMO1X;

    private int ST_AMMO1Y;

    private int ST_AMMO2WIDTH;

    private int ST_AMMO2X;

    private int ST_AMMO2Y;

    private int ST_AMMO3WIDTH;

    private int ST_AMMO3X;

    private int ST_AMMO3Y;

    // Indicate maximum ammunition.
    // Only needed because backpack exists.
    private int ST_MAXAMMO0WIDTH;

    private int ST_MAXAMMO0HEIGHT;

    private int ST_MAXAMMO0X;

    private int ST_MAXAMMO0Y;

    private int ST_MAXAMMO1WIDTH;

    private int ST_MAXAMMO1X;

    private int ST_MAXAMMO1Y;

    private int ST_MAXAMMO2WIDTH;

    private int ST_MAXAMMO2X;

    private int ST_MAXAMMO2Y;

    private int ST_MAXAMMO3WIDTH;

    private int ST_MAXAMMO3X;
    private int ST_MAXAMMO3Y;
    
    // pistol
    private int ST_WEAPON0X;
    private int ST_WEAPON0Y;

    // shotgun
    private int ST_WEAPON1X;
    private int ST_WEAPON1Y;

    // chain gun
    private int ST_WEAPON2X;
    private int ST_WEAPON2Y;

    // missile launcher
    private int ST_WEAPON3X;
    private int ST_WEAPON3Y;

    // plasma gun
    private int ST_WEAPON4X;
    private int ST_WEAPON4Y;

    // bfg
    private int ST_WEAPON5X;
    private int ST_WEAPON5Y;

    // WPNS title
    private int ST_WPNSX;
    private int ST_WPNSY;

    // DETH title
    private int ST_DETHX;
    private int ST_DETHY;

    // Incoming messages window location
    // UNUSED
    // #define ST_MSGTEXTX (viewwindowx)
    // #define ST_MSGTEXTY (viewwindowy+viewheight-18)
    private static int ST_MSGTEXTX = 0;

    private static int ST_MSGTEXTY = 0;

    // Dimensions given in characters.
    private static int ST_MSGWIDTH = 52;

    // Or shall I say, in lines?
    private static int ST_MSGHEIGHT = 1;

    private static int ST_OUTTEXTX = 0;

    private static int ST_OUTTEXTY = 6;

    // Width, in characters again.
    private static int ST_OUTWIDTH = 52;

    // Height, in lines.
    private static int ST_OUTHEIGHT = 1;

    // TODO private static int ST_MAPWIDTH =
    // (mapnames[(gameepisode-1)*9+(gamemap-1)].length));

    // TODO private static int ST_MAPTITLEX = (SCREENWIDTH - ST_MAPWIDTH *
    // ST_CHATFONTWIDTH);

    private static int ST_MAPTITLEY = 0;

    private static int ST_MAPHEIGHT = 1;

    // main player in game
    private player_t plyr;

    // ST_Start() has just been called, OR we want to force an redraw anyway.
    private boolean st_firsttime;
    
    @Override
    public void forceRefresh(){
        st_firsttime=true;
    }

    // used to execute ST_Init() only once
    private int veryfirsttime = 1;

    // lump number for PLAYPAL
    private int lu_palette;

    // used for timing (unsigned int .. maybe long !)
    private long st_clock;

    // used for making messages go away
    int st_msgcounter = 0;

    // used when in chat
    private st_chatstateenum_t st_chatstate;

    // whether in automap or first-person
    private st_stateenum_t st_gamestate;

    /** whether left-side main status bar is active. This fugly hax
     *  (and others like it) are necessary in order to have something
     *  close to a pointer.
     */
    private boolean[] st_statusbaron={false};

    // whether status bar chat is active
    private boolean st_chat;

    // value of st_chat before message popped up
    private boolean st_oldchat;

    // whether chat window has the cursor on
    private boolean[] st_cursoron={false};

    /** !deathmatch */
    private boolean[] st_notdeathmatch={true};

    /** !deathmatch && st_statusbaron */
    private boolean[] st_armson={true};

    /** !deathmatch */
    private boolean[] st_fragson={false};

    // main bar left
    private patch_t sbar;

    // 0-9, tall numbers
    private patch_t[] tallnum = new patch_t[10];

    // tall % sign
    private patch_t tallpercent;

    // 0-9, short, yellow (,different!) numbers
    private patch_t[] shortnum = new patch_t[10];

    // 3 key-cards, 3 skulls
    private patch_t[] keys = new patch_t[NUMCARDS];

    // face status patches
    private patch_t[] faces = new patch_t[ST_NUMFACES];

    // face background
    private patch_t faceback;

    // main bar right
    private patch_t armsbg;

    // weapon ownership patches
    private patch_t[][] arms = new patch_t[6][2];
   
    // // WIDGETS /////

    // ready-weapon widget
    private st_number_t w_ready;

    // in deathmatch only, summary of frags stats
    private st_number_t w_frags;

    // health widget
    private st_percent_t w_health;

    // arms background
    private st_binicon_t w_armsbg;

    // weapon ownership widgets
    private st_multicon_t[] w_arms = new st_multicon_t[6];

    // face status widget
    private st_multicon_t w_faces;

    // keycard widgets
    private st_multicon_t[] w_keyboxes = new st_multicon_t[3];

    // armor widget
    private st_percent_t w_armor;

    // ammo widgets
    private st_number_t[] w_ammo = new st_number_t[4];

    // max ammo widgets
    private st_number_t[] w_maxammo = new st_number_t[4];

    // / END WIDGETS ////

    // number of frags so far in deathmatch
    private int[] st_fragscount={0};

    // used to use appopriately pained face
    private int st_oldhealth = -1;

    // used for evil grin
    private boolean[] oldweaponsowned = new boolean[NUMWEAPONS];

    // count until face changes
    private int st_facecount = 0;

    // current face index, used by w_faces
    private int[] st_faceindex = new int[1];

    // holds key-type for each key box on bar
    private int[] keyboxes = new int[3];

    // a random number per tick
    private int st_randomnumber;
    
    // idmypos toggle mode
    private boolean st_idmypos=false;

    // Massive bunches of cheat shit
    // to keep it from being easy to figure them out.
    // Yeah, right...
    private char cheat_mus_seq[] =
        { 0xb2, 0x26, 0xb6, 0xae, 0xea, 1, 0, 0, 0xff };

    private char cheat_choppers_seq[] =
        { 0xb2, 0x26, 0xe2, 0x32, 0xf6, 0x2a, 0x2a, 0xa6, 0x6a, 0xea, 0xff // id...
        };

    private char cheat_god_seq[] = { 0xb2, 0x26, 0x26, 0xaa, 0x26, 0xff // iddqd
        };

    private char cheat_ammo_seq[] = { 0xb2, 0x26, 0xf2, 0x66, 0xa2, 0xff // idkfa
        };

    private char cheat_ammonokey_seq[] = { 0xb2, 0x26, 0x66, 0xa2, 0xff // idfa
        };

    // Smashing Pumpkins Into Samml Piles Of Putried Debris.
    private char cheat_noclip_seq[] = { 0xb2, 0x26, 0xea, 0x2a, 0xb2, // idspispopd
            0xea, 0x2a, 0xf6, 0x2a, 0x26, 0xff };

    //
    private char cheat_commercial_noclip_seq[] =
        { 0xb2, 0x26, 0xe2, 0x36, 0xb2, 0x2a, 0xff // idclip
        };

    private char cheat_powerup_seq[][] =
        { { 0xb2, 0x26, 0x62, 0xa6, 0x32, 0xf6, 0x36, 0x26, 0x6e, 0xff }, // beholdv
                { 0xb2, 0x26, 0x62, 0xa6, 0x32, 0xf6, 0x36, 0x26, 0xea, 0xff }, // beholds
                { 0xb2, 0x26, 0x62, 0xa6, 0x32, 0xf6, 0x36, 0x26, 0xb2, 0xff }, // beholdi
                { 0xb2, 0x26, 0x62, 0xa6, 0x32, 0xf6, 0x36, 0x26, 0x6a, 0xff }, // beholdr
                { 0xb2, 0x26, 0x62, 0xa6, 0x32, 0xf6, 0x36, 0x26, 0xa2, 0xff }, // beholda
                { 0xb2, 0x26, 0x62, 0xa6, 0x32, 0xf6, 0x36, 0x26, 0x36, 0xff }, // beholdl
                { 0xb2, 0x26, 0x62, 0xa6, 0x32, 0xf6, 0x36, 0x26, 0xff } // behold
        };

    private char cheat_clev_seq[] =
        { 0xb2, 0x26, 0xe2, 0x36, 0xa6, 0x6e, 1, 0, 0, 0xff // idclev
        };

    // my position cheat
    private char cheat_mypos_seq[] =
        { 0xb2, 0x26, 0xb6, 0xba, 0x2a, 0xf6, 0xea, 0xff // idmypos
        };

    // Now what?
    cheatseq_t cheat_mus = new cheatseq_t(cheat_mus_seq, 0);

    cheatseq_t cheat_god = new cheatseq_t(cheat_god_seq, 0);

    cheatseq_t cheat_ammo = new cheatseq_t(cheat_ammo_seq, 0);

    cheatseq_t cheat_ammonokey = new cheatseq_t(cheat_ammonokey_seq, 0);

    cheatseq_t cheat_noclip = new cheatseq_t(cheat_noclip_seq, 0);

    cheatseq_t cheat_commercial_noclip =
        new cheatseq_t(cheat_commercial_noclip_seq, 0);

    cheatseq_t[] cheat_powerup =
        { new cheatseq_t(cheat_powerup_seq[0], 0),
                new cheatseq_t(cheat_powerup_seq[1], 0),
                new cheatseq_t(cheat_powerup_seq[2], 0),
                new cheatseq_t(cheat_powerup_seq[3], 0),
                new cheatseq_t(cheat_powerup_seq[4], 0),
                new cheatseq_t(cheat_powerup_seq[5], 0),
                new cheatseq_t(cheat_powerup_seq[6], 0) };

    cheatseq_t cheat_choppers = new cheatseq_t(cheat_choppers_seq, 0);

    cheatseq_t cheat_clev = new cheatseq_t(cheat_clev_seq, 0);

    cheatseq_t cheat_mypos = new cheatseq_t(cheat_mypos_seq, 0);
    
    cheatseq_t cheat_tnthom= new cheatseq_t("tnthom",false);

    // 
    String[] mapnames;

    //
    // STATUS BAR CODE
    //

    public StatusBar(DoomStatus DC) {
    	this.updateStatus(DC);
    	//this.plyr=DM.players[DM.]
    }

    public void refreshBackground() {

        if (st_statusbaron[0]) {
            V.DrawPatchSolidScaled(ST_X, 0, SAFE_SCALE, SAFE_SCALE, BG, sbar);
            //V.DrawPatch(ST_X, 0, BG, sbar);

            if (DM.netgame)
                V.DrawScaledPatch(ST_FX, 0, BG,vs, faceback);
                //V.DrawPatch(ST_FX, 0, BG, faceback);

            // Buffers the background.
            V.CopyRect(ST_X, 0, BG, ST_WIDTH, ST_HEIGHT, ST_X, ST_Y, FG);
        }

    }

    public void Init() {
        veryfirsttime = 0;
        loadData();
        // MAES: screen(4) of the Video Renderer is actually reserved for the status bar.
        // The "clean" status bar is cached in there, and redrawn only as required.
        
        this.V.setScreen(BG,ST_WIDTH,ST_HEIGHT);
    }

    protected boolean st_stopped = true;

    public void Start() {

        if (!st_stopped)
            Stop();

        initData();
        createWidgets();
        st_stopped = false;

    }

    public void Stop() {
        if (st_stopped)
            return;
        // Reset palette.
        VI.SetPalette (0);

        st_stopped = true;
    }

    public void loadData() {
        lu_palette = W.GetNumForName("PLAYPAL");
        loadGraphics();
    }

    // Respond to keyboard input events,
    // intercept cheats.

    public boolean Responder(event_t ev) {
        int i;

        // Filter automap on/off.
        if (ev.type == evtype_t.ev_keyup
                && ((ev.data1 & 0xffff0000) == AM_MSGHEADER)) {
            switch (ev.data1) {
            case AM_MSGENTERED:
                st_gamestate = st_stateenum_t.AutomapState;
                st_firsttime = true;
                break;

            case AM_MSGEXITED:
                // fprintf(stderr, "AM exited\n");
                st_gamestate = st_stateenum_t.FirstPersonState;
                break;
            }
        }

        // if a user keypress...
        else if (ev.type == evtype_t.ev_keydown) {
            if (!DM.netgame) {
                // b. - enabled for more debug fun.
                // if (gameskill != sk_nightmare) {

                // 'dqd' cheat for toggleable god mode
                if (cheat_god.CheckCheat((char) ev.data1)) {
                    plyr.cheats ^= CF_GODMODE;
                    if ((plyr.cheats & CF_GODMODE) != 0) {
                        if (plyr.mo != null)
                            plyr.mo.health = 100;

                        plyr.health[0] = 100;
                        plyr.message = STSTR_DQDON;
                    } else
                        plyr.message = STSTR_DQDOFF;
                }
                // 'fa' cheat for killer fucking arsenal
                else if (cheat_ammonokey.CheckCheat((char) ev.data1)) {
                    plyr.armorpoints[0] = 200;
                    plyr.armortype = 2;

                    for (i = 0; i < NUMWEAPONS; i++)
                        plyr.weaponowned[i] = true; // true

                    for (i = 0; i < NUMAMMO; i++)
                        plyr.ammo[i] = plyr.maxammo[i];

                    plyr.message = STSTR_FAADDED;
                }
                // 'kfa' cheat for key full ammo
                else if (cheat_ammo.CheckCheat((char) ev.data1)) {
                    plyr.armorpoints[0] = 200;
                    plyr.armortype = 2;

                    for (i = 0; i < NUMWEAPONS; i++)
                        plyr.weaponowned[i] = true; // true

                    for (i = 0; i < NUMAMMO; i++)
                        plyr.ammo[i] = plyr.maxammo[i];

                    for (i = 0; i < NUMCARDS; i++)
                        plyr.cards[i] = true;

                    plyr.message = STSTR_KFAADDED;
                }
                // 'mus' cheat for changing music
                else if (cheat_mus.CheckCheat((char) ev.data1)) {

                    char[] buf = new char[3];
                    int musnum;

                    plyr.message = STSTR_MUS;
                    cheat_mus.GetParam(buf);

                    if (DM.isCommercial()) {
                        musnum =
                            musicenum_t.mus_runnin.ordinal() + (buf[0] - '0')
                                    * 10 + buf[1] - '0' - 1;

                        if (((buf[0] - '0') * 10 + buf[1] - '0') > 35)
                            plyr.message = STSTR_NOMUS;
                        else
                        S.ChangeMusic(musnum, true);
                    } else {
                        musnum =
                            musicenum_t.mus_e1m1.ordinal() + (buf[0] - '1') * 9
                                    + (buf[1] - '1');

                        if (((buf[0] - '1') * 9 + buf[1] - '1') > 31)
                            plyr.message = STSTR_NOMUS;
                        else
                       S.ChangeMusic(musnum, true);
                    }
                }
                // Simplified, accepting both "noclip" and "idspispopd".
                // no clipping mode cheat
                else if (cheat_noclip.CheckCheat((char) ev.data1)
                        || cheat_commercial_noclip.CheckCheat((char) ev.data1)) {
                    plyr.cheats ^= CF_NOCLIP;

                    if ((plyr.cheats & CF_NOCLIP) != 0)
                        plyr.message = STSTR_NCON;
                    else
                        plyr.message = STSTR_NCOFF;
                }
                // 'behold?' power-up cheats
                for (i = 0; i < 6; i++) {
                    if (cheat_powerup[i].CheckCheat((char) ev.data1)) {
                        if (plyr.powers[i] == 0)
                           plyr.GivePower(i);
                        else if (i != pw_strength)
                            plyr.powers[i] = 1;
                        else
                            plyr.powers[i] = 0;

                        plyr.message = STSTR_BEHOLDX;
                    }
                }

                // 'behold' power-up menu
                if (cheat_powerup[6].CheckCheat((char) ev.data1)) {
                    plyr.message = STSTR_BEHOLD;
                }
                // 'choppers' invulnerability & chainsaw
                else if (cheat_choppers.CheckCheat((char) ev.data1)) {
                    plyr.weaponowned[weapontype_t.wp_chainsaw.ordinal()] = true;
                    plyr.powers[pw_invulnerability] = 1; // true
                    plyr.message = STSTR_CHOPPERS;
                }
                // 'mypos' for player position
                else if (cheat_mypos.CheckCheat((char) ev.data1)) {
                    // MAES: made into a toggleable cheat.
                   this.st_idmypos=!st_idmypos;
                }
                else if (cheat_tnthom.CheckCheat((char) ev.data1)) {
                    // MAES: made into a toggleable cheat.
                	plyr.message = (DM.flashing_hom = !DM.flashing_hom) ? "HOM Detection On" :
                	    "HOM Detection Off";
                }
            }

            // 'clev' change-level cheat
            if (cheat_clev.CheckCheat((char) ev.data1)) {
                char[] buf = new char[3];
                int epsd;
                int map;

                cheat_clev.GetParam(buf);

                // This applies to Doom II, Plutonia and TNT.
                if (DM.isCommercial())
                		{
                    epsd = 0;
                    map = (buf[0] - '0') * 10 + buf[1] - '0';
                } else {
                    epsd = buf[0] - '0';
                    map = buf[1] - '0';
                }

                // Catch invalid maps.
                if (epsd < 1 && (!DM.isCommercial()))
                    return false;

                if (map < 1)
                    return false;

                // Ohmygod - this is not going to work.
                if (DM.isRetail()
                        && ((epsd > 4) || (map > 9)))
                    return false;

                // MAES: If it's doom.wad but not ultimate
                if (DM.isRegistered()&& !DM.isRetail()
                        && ((epsd > 3) || (map > 9)))
                    return false;

                if (DM.isShareware()
                        && ((epsd > 1) || (map > 9)))
                    return false;

                if (DM.isCommercial()
                        && ((epsd > 1) || (map > 34)))
                    return false;

                // So be it.
                plyr.message = STSTR_CLEV;
                DM.DeferedInitNew(DM.gameskill, epsd, map);
            }
        }
        return false;
    }

    protected int lastcalc;

    protected int oldhealth = -1;

    public int calcPainOffset() {
        int health = 0;

        health = plyr.health[0] > 100 ? 100 : plyr.health[0];

        if (health != oldhealth) {
            lastcalc =
                ST_FACESTRIDE * (((100 - health) * ST_NUMPAINFACES) / 101);
            oldhealth = health;
        }
        return lastcalc;
    }

    protected int lastattackdown = -1;

    protected int priority = 0;

    /**
     * This is a not-very-pretty routine which handles the face states and their
     * timing. the precedence of expressions is: dead > evil grin > turned head
     * > straight ahead
     */
    public void updateFaceWidget() {
        long badguyangle; // angle_t
        long diffang;

        boolean doevilgrin;

        if (priority < 10) {
            // dead
            if (plyr.health[0] == 0) {
                priority = 9;
                st_faceindex[0] = ST_DEADFACE;
                st_facecount = 1;
            }
        }

        if (priority < 9) {
            if (plyr.bonuscount != 0) {
                // picking up bonus
                doevilgrin = false;

                for (int i = 0; i < NUMWEAPONS; i++) {
                    if (oldweaponsowned[i] != plyr.weaponowned[i]) {
                        doevilgrin = true;
                        oldweaponsowned[i] = plyr.weaponowned[i];
                    }
                }
                if (doevilgrin) {
                    // evil grin if just picked up weapon
                    priority = 8;
                    st_facecount = ST_EVILGRINCOUNT;
                    st_faceindex[0] = calcPainOffset() + ST_EVILGRINOFFSET;
                }
            }

        }

        if (priority < 8) {
            if ((plyr.damagecount != 0) && (plyr.attacker != null)
                    && (plyr.attacker != plyr.mo)) {
                // being attacked
                priority = 7;

                if (plyr.health[0] - st_oldhealth > ST_MUCHPAIN) {
                    st_facecount = ST_TURNCOUNT;
                    st_faceindex[0] = calcPainOffset() + ST_OUCHOFFSET;
                } else {
                    badguyangle =
                        R.PointToAngle2(plyr.mo.x, plyr.mo.y, plyr.attacker.x,
                            plyr.attacker.y);
                    boolean obtuse; // that's another "i"

                    if (badguyangle > plyr.mo.angle) {
                        // whether right or left
                        diffang = badguyangle - plyr.mo.angle;
                        obtuse = diffang > ANG180;
                    } else {
                        // whether left or right
                        diffang = plyr.mo.angle - badguyangle;
                        obtuse = diffang <= ANG180;
                    } // confusing, aint it?

                    st_facecount = ST_TURNCOUNT;
                    st_faceindex[0] = calcPainOffset();

                    if (diffang < ANG45) {
                        // head-on
                        st_faceindex[0] += ST_RAMPAGEOFFSET;
                    } else if (obtuse) {
                        // turn face right
                        st_faceindex[0] += ST_TURNOFFSET;
                    } else {
                        // turn face left
                        st_faceindex[0] += ST_TURNOFFSET + 1;
                    }
                }
            }
        }

        if (priority < 7) {
            // getting hurt because of your own damn stupidity
            if (plyr.damagecount != 0) {
                if (plyr.health[0] - st_oldhealth > ST_MUCHPAIN) {
                    priority = 7;
                    st_facecount = ST_TURNCOUNT;
                    st_faceindex[0] = calcPainOffset() + ST_OUCHOFFSET;
                } else {
                    priority = 6;
                    st_facecount = ST_TURNCOUNT;
                    st_faceindex[0] = calcPainOffset() + ST_RAMPAGEOFFSET;
                }

            }

        }

        if (priority < 6) {
            // rapid firing
            if (plyr.attackdown) {
                if (lastattackdown == -1)
                    lastattackdown = ST_RAMPAGEDELAY;
                else if (--lastattackdown == 0) {
                    priority = 5;
                    st_faceindex[0] = calcPainOffset() + ST_RAMPAGEOFFSET;
                    st_facecount = 1;
                    lastattackdown = 1;
                }
            } else
                lastattackdown = -1;

        }

        if (priority < 5) {
            // invulnerability
            if (((plyr.cheats & CF_GODMODE) != 0)
                    || (plyr.powers[pw_invulnerability] != 0)) {
                priority = 4;

                st_faceindex[0] = ST_GODFACE;
                st_facecount = 1;

            }

        }

        // look left or look right if the facecount has timed out
        if (st_facecount == 0) {
            st_faceindex[0] = calcPainOffset() + (st_randomnumber % 3);
            st_facecount = ST_STRAIGHTFACECOUNT;
            priority = 0;
        }

        st_facecount--;

    }

    protected int largeammo = 1994; // means "n/a"

    /**
     * MAES: this code updated the widgets. Now, due to the way they are
     * constructed, they originally were "hooked" to actual variables using
     * pointers so that they could tap into them directly and self-update.
     * Clearly we can't do that in Java unless said variables are inside an
     * array and we provide both the array AND an index. For other cases, we
     * must simply build ad-hoc hacks.
     * 
     * In any case, only "status" updates are performed here. Actual visual
     * updates are performed by the Drawer.
     * 
     */

    public void updateWidgets() {

        int i;

        // MAES: sticky idmypos cheat that is actually useful
        // TODO: this spams the player message queue at every tic.
        // A direct overlay with a widget would be more useful.
        
        if (this.st_idmypos){
            mobj_t mo = DM.players[DM.consoleplayer].mo;
            plyr.message = String.format("ang= 0x%x; x,y= (%x, %x)",
                        (int)mo.angle,mo.x,mo.y);

        }
        

        // must redirect the pointer if the ready weapon has changed.
        // if (w_ready.data != plyr.readyweapon)
        // {
        if (weaponinfo[plyr.readyweapon.ordinal()].ammo == ammotype_t.am_noammo)
            w_ready.numindex = largeammo;
        else
            w_ready.numindex =
                weaponinfo[plyr.readyweapon.ordinal()].ammo.ordinal();

        w_ready.data = plyr.readyweapon.ordinal();

        // if (*w_ready.on)
        // STlib_updateNum(&w_ready, true);
        // refresh weapon change
        // }

        // update keycard multiple widgets
        for (i = 0; i < 3; i++) {
            keyboxes[i] = plyr.cards[i] ? i : -1;

            if (plyr.cards[i + 3])
                keyboxes[i] = i + 3;
        }

        // refresh everything if this is him coming back to life
        updateFaceWidget();

        // used by the w_armsbg widget
        st_notdeathmatch[0] = !DM.deathmatch;

        // used by w_arms[] widgets
        st_armson[0] = st_statusbaron[0] && !(DM.altdeath||DM.deathmatch);

        // used by w_frags widget
        st_fragson[0] = (DM.altdeath||DM.deathmatch) && st_statusbaron[0];
        st_fragscount[0] = 0;

        for (i = 0; i < MAXPLAYERS; i++) {
            if (i != DM.consoleplayer)
                st_fragscount[0] += plyr.frags[i];
            else
                st_fragscount[0] -= plyr.frags[i];
        }

        // get rid of chat window if up because of message
        if (--st_msgcounter == 0)
            st_chat = st_oldchat;

    }

    public void Ticker() {

        st_clock++;
        st_randomnumber = RND.M_Random();
        updateWidgets();
        st_oldhealth = plyr.health[0];

    }

    static int st_palette = 0;

    public void doPaletteStuff() {

        int palette;
        //byte[] pal;
        int cnt;
        int bzc;

        cnt = plyr.damagecount;

        if (plyr.powers[pw_strength] != 0) {
            // slowly fade the berzerk out
            bzc = 12 - (plyr.powers[pw_strength] >> 6);

            if (bzc > cnt)
                cnt = bzc;
        }

        if (cnt != 0) {
            palette = (cnt + 7) >> 3;

            if (palette >= NUMREDPALS)
                palette = NUMREDPALS - 1;

            palette += STARTREDPALS;
        }

        else if (plyr.bonuscount != 0) {
            palette = (plyr.bonuscount + 7) >> 3;

            if (palette >= NUMBONUSPALS)
                palette = NUMBONUSPALS - 1;

            palette += STARTBONUSPALS;
        }

        else if (plyr.powers[pw_ironfeet] > 4 * 32
                || (plyr.powers[pw_ironfeet] & 8) != 0)
            palette = RADIATIONPAL;
        else
            palette = 0;

        if (palette != st_palette) {
            st_palette = palette;
            VI.SetPalette (palette);
        }

    }

    public void drawWidgets(boolean refresh) {
        int i;

        // used by w_arms[] widgets
        st_armson[0] = st_statusbaron[0] && !(DM.altdeath||DM.deathmatch);

        // used by w_frags widget
        st_fragson[0] = DM.deathmatch && st_statusbaron[0];

        w_ready.update(refresh);

        for (i = 0; i < 4; i++) {
            w_ammo[i].update(refresh);
            w_maxammo[i].update(refresh);
        }

        w_health.update(refresh);
        w_armor.update(refresh);

        w_armsbg.update(refresh);

        for (i = 0; i < 6; i++)
            w_arms[i].update(refresh);

        w_faces.update(refresh);

        for (i = 0; i < 3; i++)
            w_keyboxes[i].update(refresh);

        w_frags.update(refresh);

    }

    public void doRefresh() {

        st_firsttime = false;

        // draw status bar background to off-screen buff
        refreshBackground();

        // and refresh all widgets
        drawWidgets(true);

    }

    public void diffDraw() {
        // update all widgets
        drawWidgets(false);
    }

    public void Drawer(boolean fullscreen, boolean refresh) {

        st_statusbaron[0] = (!fullscreen) || DM.automapactive;
        st_firsttime = st_firsttime || refresh;

        // Do red-/gold-shifts from damage/items
        doPaletteStuff();

        // If just after ST_Start(), refresh all
        if (st_firsttime)
            doRefresh();
        // Otherwise, update as little as possible
        else
            diffDraw();

    }

    public void loadGraphics() {

        int i;
        int j;
        int facenum;

        String namebuf;

        // Load the numbers, tall and short
        for (i = 0; i < 10; i++) {
            namebuf = ("STTNUM" + i);
            tallnum[i] = W.CachePatchName(namebuf, PU_STATIC);

            namebuf = ("STYSNUM" + i);
            shortnum[i] = W.CachePatchName(namebuf, PU_STATIC);

        }

        // Load percent key.
        // Note: why not load STMINUS here, too?
        tallpercent = W.CachePatchName("STTPRCNT", PU_STATIC);
        // MAES: in fact, I do this for sanity. Fuck them. Seriously.
        sttminus= W.CachePatchName("STTMINUS");

        // key cards
        for (i = 0; i < NUMCARDS; i++) {
            namebuf = ("STKEYS" + i);
            keys[i] = W.CachePatchName(namebuf, PU_STATIC);
        }

        // arms background
        armsbg = W.CachePatchName("STARMS", PU_STATIC);

        // arms ownership widgets
        for (i = 0; i < 6; i++) {
            namebuf = ("STGNUM" + (i + 2));

            // gray #
            arms[i][0] = W.CachePatchName(namebuf, PU_STATIC);

            // yellow #
            arms[i][1] = shortnum[i + 2];
        }

        // face backgrounds for different color players
        namebuf = ("STFB" + DM.consoleplayer);
        faceback = W.CachePatchName(namebuf, PU_STATIC);

        // status bar background bits
        sbar = W.CachePatchName("STBAR", PU_STATIC);

        // face states
        facenum = 0;
        for (i = 0; i < ST_NUMPAINFACES; i++) {
            for (j = 0; j < ST_NUMSTRAIGHTFACES; j++) {
                namebuf = ("STFST" + (i) + (j));
                faces[facenum++] = W.CachePatchName(namebuf, PU_STATIC);
            }
            namebuf = "STFTR" + i + "0"; // turn right
            faces[facenum++] = W.CachePatchName(namebuf, PU_STATIC);
            namebuf = "STFTL" + i + "0"; // turn left
            faces[facenum++] = W.CachePatchName(namebuf, PU_STATIC);
            namebuf = "STFOUCH" + i; // ouch!
            faces[facenum++] = W.CachePatchName(namebuf, PU_STATIC);
            namebuf = "STFEVL" + i; // evil grin ;)
            faces[facenum++] = W.CachePatchName(namebuf, PU_STATIC);
            namebuf = "STFKILL" + i; // pissed off
            faces[facenum++] = W.CachePatchName(namebuf, PU_STATIC);
        }
        faces[facenum++] = W.CachePatchName("STFGOD0", PU_STATIC);
        faces[facenum++] = W.CachePatchName("STFDEAD0", PU_STATIC);

    }

    public void unloadGraphics() {
    	
          int i; // unload the numbers, tall and short 
          for (i=0;i<10;i++) {
        	  W.UnlockLumpNum(tallnum[i]);
        	  tallnum[i]=null;
        	  W.UnlockLumpNum(shortnum[i]);
        	  shortnum[i]=null;
          }
        
       // unload tall percent
          W.UnlockLumpNum(tallpercent);
          tallpercent=null;
          	
        	  
         // unload arms background          
          W.UnlockLumpNum(armsbg);
          armsbg=null;
         // unload gray #'s          
          for (i=0;i<6;i++) { 
        	  W.UnlockLumpNum(arms[i][0]);
        	  arms[i][0]=null;
        	  W.UnlockLumpNum(arms[i][1]);
        	  arms[i][1]=null;

          }
          
          // unload the key cards for (i=0;i<NUMCARDS;i++)
          
          for (i=0;i<6;i++) { 
        	  W.UnlockLumpNum(keys[i]);
        	  keys[i]=null;
          }
          
          W.UnlockLumpNum(sbar);
          sbar=null;
          
          W.UnlockLumpNum(faceback);
          faceback=null;
          
           for (i=0;i<ST_NUMFACES;i++){
        	   W.UnlockLumpNum(faces[i]);
        	   faces[i]=null;
           	}
         

        // Note: nobody ain't seen no unloading
        // of stminus yet. Dude.

    }

    public void unloadData() {
        unloadGraphics();
    }

    public void initData() {

        int i;

        st_firsttime = true;
        plyr = DM.players[DM.consoleplayer];

        st_clock = 0;
        st_chatstate = st_chatstateenum_t.StartChatState;
        st_gamestate = st_stateenum_t.FirstPersonState;

        st_statusbaron[0] = true;
        st_oldchat = st_chat = false;
        st_cursoron[0] = false;

        st_faceindex[0] = 0;
        st_palette = -1;

        st_oldhealth = -1;

        for (i = 0; i < NUMWEAPONS; i++)
            oldweaponsowned[i] = plyr.weaponowned[i];

        for (i = 0; i < 3; i++)
            keyboxes[i] = -1;

        Init();

    }

    /**
     * Widgets are created here. Be careful, because their "constructors" used
     * reference to boolean or int variables so that they could be auto-updated
     * by the global refresh functions. We can only do this with some
     * limitations in Java (e.g. passing an array AND an index).
     */

    public void createWidgets() {

        int i;

        // ready weapon ammo

        w_ready =
            new st_number_t(ST_AMMOX, ST_AMMOY, tallnum, plyr.ammo,
                    weaponinfo[plyr.readyweapon.ordinal()].ammo.ordinal(),
                    st_statusbaron, 0,ST_AMMOWIDTH);

        // the last weapon type
        w_ready.data = plyr.readyweapon.ordinal();

        // health percentage
        w_health =
            new st_percent_t(ST_HEALTHX, ST_HEALTHY, tallnum, plyr.health,
                    0, st_statusbaron,0, tallpercent);

        // arms background
        w_armsbg =
            new st_binicon_t(ST_ARMSBGX, ST_ARMSBGY, armsbg, st_notdeathmatch,0,
                    st_statusbaron,0);

        // weapons owned
        for (i = 0; i < 6; i++) {
            w_arms[i] =
                new st_multicon_t(ST_ARMSX + (i % 3) * ST_ARMSXSPACE, ST_ARMSY
                        + (i / 3) * ST_ARMSYSPACE, arms[i], plyr.weaponowned,
                        i + 1, st_armson,0);
        }

        // frags sum
        w_frags =
            new st_number_t(ST_FRAGSX, ST_FRAGSY, tallnum, st_fragscount, 0, // dummy,
                                                                             // we're
                                                                             // passing
                                                                             // an
                                                                             // integer.
                    st_fragson,0, ST_FRAGSWIDTH);

        // faces
        w_faces =
            new st_multicon_t(ST_FACESX, ST_FACESY, faces, st_faceindex, 0,
                    st_statusbaron,0);

        // armor percentage - should be colored later
        w_armor =
            new st_percent_t(ST_ARMORX, ST_ARMORY, tallnum, plyr.armorpoints,
                    0, st_statusbaron, 0,tallpercent);

        // keyboxes 0-2
        w_keyboxes[0] =
            new st_multicon_t(ST_KEY0X, ST_KEY0Y, keys, keyboxes, 0,
                    st_statusbaron,0);

        w_keyboxes[1] =
            new st_multicon_t(ST_KEY1X, ST_KEY1Y, keys, keyboxes, 1,
                    st_statusbaron,0);

        w_keyboxes[2] =
            new st_multicon_t(ST_KEY2X, ST_KEY2Y, keys, keyboxes, 2,
                    st_statusbaron,0);

        // ammo count (all four kinds)

        w_ammo[0] =
            new st_number_t(ST_AMMO0X, ST_AMMO0Y, shortnum, plyr.ammo, 0,
                    st_statusbaron,0, ST_AMMO0WIDTH);

        w_ammo[1] =
            new st_number_t(ST_AMMO1X, ST_AMMO1Y, shortnum, plyr.ammo, 1,
                    st_statusbaron,0, ST_AMMO1WIDTH);

        w_ammo[2] =
            new st_number_t(ST_AMMO2X, ST_AMMO2Y, shortnum, plyr.ammo, 2,
                    st_statusbaron,0, ST_AMMO2WIDTH);

        w_ammo[3] =
            new st_number_t(ST_AMMO3X, ST_AMMO3Y, shortnum, plyr.ammo, 3,
                    st_statusbaron,0, ST_AMMO3WIDTH);

        // max ammo count (all four kinds)
        w_maxammo[0] =
            new st_number_t(ST_MAXAMMO0X, ST_MAXAMMO0Y, shortnum, plyr.maxammo,
                    0, st_statusbaron,0, ST_MAXAMMO0WIDTH);

        w_maxammo[1] =
            new st_number_t(ST_MAXAMMO1X, ST_MAXAMMO1Y, shortnum, plyr.maxammo,
                    1, st_statusbaron,0, ST_MAXAMMO1WIDTH);

        w_maxammo[2] =
            new st_number_t(ST_MAXAMMO2X, ST_MAXAMMO2Y, shortnum, plyr.maxammo,
                    2, st_statusbaron,0, ST_MAXAMMO2WIDTH);

        w_maxammo[3] =
            new st_number_t(ST_MAXAMMO3X, ST_MAXAMMO3Y, shortnum, plyr.maxammo,
                    3, st_statusbaron,0, ST_MAXAMMO3WIDTH);

    }

    /** Binary Icon widget 
     *  This is used for stuff such as keys or weapons, which you either have
     *  or you don't.
     * 
     * */

    class st_binicon_t
            implements StatusBarWidget {

        // center-justified location of icon
        int x;

        int y;

        // last icon value
        boolean oldval;

        /** pointer to current icon status */
        boolean[] val;
        int valindex;

        /** pointer to boolean
            stating whether to update icon */
        boolean[] on;
        int onindex;
        
        patch_t p; // icon

        int data; // user data

        // Binary Icon widget routines

        public st_binicon_t(int x, int y, patch_t i, boolean[] val, int valindex, boolean[] on, int onindex) {
            this.x = x;
            this.y = y;
            this.oldval = false;
            this.val = val;
            this.valindex=valindex;
            this.on = on;
            this.onindex=onindex;
            this.p = i;
            this.val[valindex]=false;;
        }

        @Override
        public void update(boolean refresh) {
            st_binicon_t bi = this;
            int x;
            int y;
            int w;
            int h;

            if (bi.on[onindex] && ((bi.oldval != bi.val[valindex]) || refresh)) {
                x = bi.x - bi.p.leftoffset;
                y = bi.y - bi.p.topoffset;
                w = bi.p.width;
                h = bi.p.height;

                if (y - ST_Y < 0)
                    I.Error("updateBinIcon: y - ST_Y < 0");                    
                if (bi.val[valindex])
                    V.DrawScaledPatch(bi.x, bi.y, V_PREDIVIDE|FG,vs, bi.p);
                else
                    V.CopyRect(x/vs.getScalingX(), y/vs.getScalingY() - ST_Y, BG, w*BEST_X_SCALE, h*BEST_Y_SCALE, x, y, FG);

                bi.oldval = bi.val[valindex];
            }

        }

    }

    /** Icon widget */

    class st_multicon_t
            implements StatusBarWidget {

        // center-justified location of icons
        int x;

        int y;

        // last icon number
        int oldinum;

        /** pointer to current icon, if not an array type. */
        int[] iarray;

        int inum;

        // pointer to boolean stating
        // whether to update icon
        boolean[] on;
        int onindex;

        // list of icons
        patch_t[] p;

        // user data
        int data;

        /** special status 0=boolean[] 1=integer[] -1= unspecified */
        int status = -1;

        protected boolean[] asboolean;

        protected int[] asint;

        public st_multicon_t(int x, int y, patch_t[] il, Object iarray,
                int inum, boolean []on,int onindex) {
            this.x = x;
            this.y = y;
            this.oldinum = -1;
            this.inum = inum;
            this.on = on;
            this.p = il;
            if (iarray instanceof boolean[]) {
                status = 0;
                asboolean = (boolean[]) iarray;
            } else 
                if (iarray instanceof int[]){
                    status = 1;
                asint = (int[]) iarray;               
            }  
        }

        @Override
        public void update(boolean refresh) {

            int w;
            int h;
            int x;
            int y;

            // Actual value to be considered. Poor man's generics!
            int thevalue = -1;
            switch (status) {
            case 0:
                thevalue = asboolean[inum] ? 1 : 0;
                break;
            case 1:
                thevalue = asint[inum];
                break;            
            }

            // Unified treatment of boolean and integer references
            // So the widget will update iff:
            // a) It's on AND
            // b) The new value is different than the old one
            // c) Neither of them is -1
            // d) We actually asked for a refresh.
            if (this.on[onindex] && ((this.oldinum != thevalue) || refresh)
                    && (thevalue != -1)) {
            	// Previous value must not have been -1.
                if (this.oldinum != -1) { 
                    x = this.x - this.p[this.oldinum].leftoffset*BEST_X_SCALE;
                    y = this.y - this.p[this.oldinum].topoffset*BEST_Y_SCALE;
                    w = this.p[this.oldinum].width*BEST_X_SCALE;
                    h = this.p[this.oldinum].height*BEST_Y_SCALE;

                    if (y - ST_Y < 0)
                        I.Error("updateMultIcon: y - ST_Y < 0");
                    //System.out.printf("Restoring at x y %d %d w h %d %d\n",x, y - ST_Y,w,h);
                    V.CopyRect(x, y - ST_Y, BG, w, h, x, y, FG);
                    //V.FillRect(x, y - ST_Y, w, h, FG);
                }
                
                //System.out.printf("Drawing at x y %d %d w h %d %d\n",this.x,this.y,p[thevalue].width,p[thevalue].height);
                V.DrawScaledPatch(this.x,this.y, V_SCALEOFFSET|V_NOSCALESTART|FG, vs,this.p[thevalue]);
                
                this.oldinum = thevalue;
            }
        }
    }

    protected patch_t sttminus;

    /** Number widget */

    class st_number_t
            implements StatusBarWidget {

        /** upper right-hand corner of the number (right-justified) */
        int x, y;

        /** max # of digits in number */
        int width;

        /** last number value */
        int oldnum;

        /**
         * Array in which to point with num. 
         * 
         * Fun fact: initially I tried to use Integer and Boolean, but those are
         * immutable -_-. Fuck that, Java.
         * 
         */
        int[] numarray;

        /** pointer to current value. Of course makes sense only for arrays. */
        int numindex;

        /** pointer to boolean stating whether to update number */
        boolean[] on;
        int onindex;

        /** list of patches for 0-9 */
        patch_t[] p;

        /** user data */
        int data;

        // Number widget routines

        public st_number_t(int x, int y, patch_t[] pl, int[] numarray,
                int numindex, boolean[] on,int onindex, int width) {
                init(x, y, pl, numarray, numindex, on,onindex, width);
                    }

        public void init(int x, int y, patch_t[] pl, int[] numarray,int numindex,
                boolean[] on, int onindex, int width) {
            this.x = x;
            this.y = y;
            this.oldnum = 0;
            this.width = width;
            this.numarray = numarray;
            this.on = on;
            this.onindex=onindex;
            this.p = pl;
            this.numindex=numindex; // _D_ fixed this bug
        }

        // 
        // A fairly efficient way to draw a number
        // based on differences from the old number.
        // Note: worth the trouble?
        //
        public void drawNum(boolean refresh) {

            if (this.numindex==largeammo) return;
            
            //st_number_t n = this;
            int numdigits = this.width; // HELL NO. This only worked while the width happened
            							// to be 3.
            int num = ((int[]) this.numarray)[this.numindex];

            int w = this.p[0].width*BEST_X_SCALE;
            int h = this.p[0].height*BEST_Y_SCALE;
            int x = this.x;

            boolean neg;

            // In this way, num and oldnum are exactly the same. Maybe this
            // should go in the end?
            this.oldnum = num;

            neg = num < 0;

            if (neg) {
                if (numdigits == 2 && num < -9)
                    num = -9;
                else if (numdigits == 3 && num < -99)
                    num = -99;

                num = -num;
            }

            // clear the area
            x = this.x - numdigits * w;

            if (this.y - ST_Y < 0) {
                I.Error("drawNum: n.y - ST_Y < 0");
            }

            // Restore BG from buffer
            //V.FillRect(x+(numdigits-3) * w, y, w*3 , h, FG);
            V.CopyRect(x+(numdigits-3)*w, y- ST_Y, BG, w * 3, h, x+(numdigits-3)*w, y, FG);

            // if non-number, do not draw it
            if (num == 1994)
                return;

            x = this.x;

            // in the special case of 0, you draw 0
            if (num == 0)
                //V.DrawPatch(x - w, n.y, FG, n.p[0]);
                V.DrawScaledPatch(x - w, this.y, FG|V_NOSCALESTART|V_TRANSLUCENTPATCH, vs,p[0]);
                
                
            // draw the new number
            while (((num != 0) && (numdigits-- != 0))) {
                x -= w;
                //V.DrawPatch(x, n.y, FG, n.p[num % 10]);
                V.DrawScaledPatch(x, this.y, FG|V_NOSCALESTART|V_TRANSLUCENTPATCH,vs, p[num % 10]);
                num /= 10;
            }

            // draw a minus sign if necessary
            if (neg)
                V.DrawScaledPatch/*DrawPatch*/(x - 8*BEST_X_SCALE, this.y, FG|V_NOSCALESTART|V_TRANSLUCENTPATCH,vs, sttminus);
                //V.DrawPatch(x - sttminus.width*vs.getScalingX(), n.y, FG, sttminus);
        }

        @Override
        public void update(boolean refresh) {
            if (this.on[onindex])
                drawNum(refresh);
        }

    }

    class st_percent_t
            implements StatusBarWidget {

        // Percent widget ("child" of number widget,
        // or, more precisely, contains a number widget.)
        // number information
        st_number_t n;

        // percent sign graphic
        patch_t p;

        public st_percent_t(int x, int y, patch_t[] pl, int[] numarray,
                int numindex, boolean[] on, int onindex, patch_t percent) {
            n = new st_number_t(x, y, pl, numarray, numindex, on,onindex, 3);
            p = percent;
        }

        @Override
        public void update(boolean refresh) {
            if (refresh && this.n.on[0])
                V.DrawScaledPatch(n.x, n.y, V_PREDIVIDE|FG,vs, p);

            n.update(refresh);
        }

    }

    interface StatusBarWidget {
        public void update(boolean refresh);
    }
	
	// Size of statusbar.
	// Now sensitive for scaling.
	public int ST_HEIGHT;
	public int ST_WIDTH;
	public int ST_Y;

////////////////////////////VIDEO SCALE STUFF ////////////////////////////////

	protected int SCREENWIDTH;
	protected int SCREENHEIGHT;
	protected int SAFE_SCALE;
	protected int BEST_X_SCALE;
	protected int BEST_Y_SCALE;
	protected IVideoScale vs;


	@Override
	public void setVideoScale(IVideoScale vs) {
	    this.vs=vs;
	}

	@Override
	public void initScaling() {
	    SCREENHEIGHT=vs.getScreenHeight();
	    SCREENWIDTH=vs.getScreenWidth();
	    SAFE_SCALE=vs.getSafeScaling();
	    BEST_X_SCALE=vs.getScalingX();
	    BEST_Y_SCALE=vs.getScalingY();
	    
	    // Pre-scale stuff.
	    ST_HEIGHT =32*SAFE_SCALE;
	    ST_WIDTH  =SCREENWIDTH;
	    ST_Y      =(SCREENHEIGHT - ST_HEIGHT);
	    ST_X2 = (int) (104*SAFE_SCALE);
	    ST_FX = (int) (143*SAFE_SCALE);
	    ST_FY = (int) (169*SAFE_SCALE);
	    ST_FACESX = (int) (143*SAFE_SCALE);

	    ST_FACESY = (int) (168*SAFE_SCALE);
	    
	     // AMMO number pos.
	     ST_AMMOWIDTH= 3;	    
	     ST_AMMOX = (int) (44*SAFE_SCALE);
	     ST_AMMOY = (int) (171*SAFE_SCALE);

	     // HEALTH number pos
	     ST_HEALTHWIDTH= 3;
	     ST_HEALTHX = (int) (90*SAFE_SCALE);
	     ST_HEALTHY = (int) (171*SAFE_SCALE);

	    // Weapon pos.
	     ST_ARMSX = (int) (111*SAFE_SCALE);
	     ST_ARMSY = (int) (172*SAFE_SCALE);
	     ST_ARMSBGX = (int) (104*SAFE_SCALE);
	     ST_ARMSBGY = (int) (168*SAFE_SCALE);
	     ST_ARMSXSPACE = 12*SAFE_SCALE;;
	     ST_ARMSYSPACE = 10*SAFE_SCALE;;
	     
	     // Frags pos.
	     ST_FRAGSX = (int) (138*SAFE_SCALE);
	     ST_FRAGSY = (int) (171*SAFE_SCALE);
	     ST_FRAGSWIDTH=2;
	     
	     //
	     

	     
	     ST_ARMORX = (int) (221*SAFE_SCALE);

	     ST_ARMORY = (int) (171*SAFE_SCALE);

	     // Key icon positions.
	     ST_KEY0WIDTH = 8*SAFE_SCALE;;
	     ST_KEY0HEIGHT = 5*SAFE_SCALE;;
	     
	     ST_KEY0X = (int) (239*SAFE_SCALE);
	     ST_KEY0Y = (int) (171*SAFE_SCALE);

	     ST_KEY1WIDTH = ST_KEY0WIDTH;
	     ST_KEY1X = (int) (239*SAFE_SCALE);
	     ST_KEY1Y = (int) (181*SAFE_SCALE);

	     ST_KEY2WIDTH = ST_KEY0WIDTH;
	     ST_KEY2X = (int) (239*SAFE_SCALE);
	     ST_KEY2Y = (int) (191*SAFE_SCALE);

	    // Ammunition counter.
	    ST_AMMO0WIDTH = 3*SAFE_SCALE;
	    ST_AMMO0HEIGHT = 6*SAFE_SCALE;

	     ST_AMMO0X = (int) (288*SAFE_SCALE);

	     ST_AMMO0Y = (int) (173*SAFE_SCALE);

	    ST_AMMO1WIDTH = ST_AMMO0WIDTH;

	     ST_AMMO1X = (int) (288*SAFE_SCALE);

	     ST_AMMO1Y = (int) (179*SAFE_SCALE);

	    ST_AMMO2WIDTH = ST_AMMO0WIDTH;

	     ST_AMMO2X = (int) (288*SAFE_SCALE);

	     ST_AMMO2Y = (int) (191*SAFE_SCALE);

	    ST_AMMO3WIDTH = ST_AMMO0WIDTH;

	     ST_AMMO3X = (int) (288*SAFE_SCALE);

	     ST_AMMO3Y = (int) (185*SAFE_SCALE);

	    // Indicate maximum ammunition.
	    // Only needed because backpack exists.
	    ST_MAXAMMO0WIDTH = 3*SAFE_SCALE;
	    ST_MAXAMMO0HEIGHT = 5*SAFE_SCALE;

	     ST_MAXAMMO0X = (int) (314*SAFE_SCALE);
	     ST_MAXAMMO0Y = (int) (173*SAFE_SCALE);

	    ST_MAXAMMO1WIDTH = ST_MAXAMMO0WIDTH;
	    ST_MAXAMMO1X = 314*SAFE_SCALE;
	     ST_MAXAMMO1Y = (int) (179*SAFE_SCALE);

	    ST_MAXAMMO2WIDTH = ST_MAXAMMO0WIDTH;
	     ST_MAXAMMO2X = (int) (314*SAFE_SCALE);
	     ST_MAXAMMO2Y = (int) (191*SAFE_SCALE);

	    ST_MAXAMMO3WIDTH = ST_MAXAMMO0WIDTH;
	     ST_MAXAMMO3X = (int) (314*SAFE_SCALE);
	     ST_MAXAMMO3Y = (int) (185*SAFE_SCALE);

	    // pistol
	     ST_WEAPON0X = (int) (110*SAFE_SCALE);
	     ST_WEAPON0Y = (int) (172*SAFE_SCALE);

	    // shotgun
	     ST_WEAPON1X = (int) (122*SAFE_SCALE);
	     ST_WEAPON1Y = (int) (172*SAFE_SCALE);

	    // chain gun
	     ST_WEAPON2X = (int) (134*SAFE_SCALE);

	     ST_WEAPON2Y = (int) (172*SAFE_SCALE);

	    // missile launcher
	     ST_WEAPON3X = (int) (110*SAFE_SCALE);

	     ST_WEAPON3Y = (int) (181*SAFE_SCALE);

	    // plasma gun
	     ST_WEAPON4X = (int) (122*SAFE_SCALE);

	     ST_WEAPON4Y = (int) (181*SAFE_SCALE);

	    // bfg
	     ST_WEAPON5X = (int) (134*SAFE_SCALE);

	     ST_WEAPON5Y = (int) (181*SAFE_SCALE);

	    // WPNS title
	     ST_WPNSX = (int) (109*SAFE_SCALE);

	     ST_WPNSY = (int) (191*SAFE_SCALE);

	    // DETH title
	     ST_DETHX = (int) (109*SAFE_SCALE);

	     ST_DETHY = (int) (191*SAFE_SCALE);

	    
	}

    @Override
    public int getHeight() {
        return this.ST_HEIGHT;
    }

	
}

//$Log: StatusBar.java,v $
//Revision 1.47  2011/11/01 23:46:37  velktron
//Added TNTHOM cheat.
//
//Revision 1.46  2011/10/23 15:57:08  velktron
//BG reference
//
//Revision 1.45  2011/10/07 16:07:14  velktron
//Now using g.Keys for key input stuff.
//
//Revision 1.44  2011/08/23 16:15:30  velktron
//Got rid of Z remnants.
//
//Revision 1.43  2011/07/28 10:29:46  velktron
//Added hack for forcing full redraw of status bar after help screen drawing.
//
//Revision 1.42  2011/07/22 15:37:16  velktron
//Sticky idmypos cheat
//
//Revision 1.41  2011/06/23 17:17:04  velktron
//Using BG constant.
//
//Revision 1.40  2011/06/13 21:03:48  velktron
//Fixed Ultimate Doom clev bug
//
//Revision 1.39  2011/06/02 14:20:45  velktron
//Implemented unloading code....kind of pointless, really.
//
//Revision 1.38  2011/06/01 18:13:37  velktron
//Fixed idmypos crash.
//
//Revision 1.37  2011/06/01 00:07:26  velktron
//Moved status, fixed soundinterface.
//
//Revision 1.36  2011/05/31 13:32:03  velktron
//Added V_SCALEOFFSET flag to doomguy's mug.
//
//Revision 1.35  2011/05/31 12:39:31  velktron
//Face centering fixed.
//
//Revision 1.34  2011/05/30 15:47:34  velktron
//AbstractStatusBar introduced.
//
//Revision 1.33  2011/05/30 10:34:20  velktron
//Fixed binicon refresh bug...
//
//Revision 1.32  2011/05/30 02:21:08  velktron
//Fixed number widget diffdraw
//
//Revision 1.31  2011/05/29 22:15:32  velktron
//Introduced IRandom interface.
//
//Revision 1.30  2011/05/29 20:54:43  velktron
//Fixed status bar scaling
//
//Revision 1.29  2011/05/24 13:42:22  velktron
//Fidgeting around with the STBar refresh
//
//Revision 1.28  2011/05/24 11:31:23  velktron
//Got rid of a whole bunch of useless interfaces.
//
//Revision 1.27  2011/05/23 16:59:02  velktron
//Migrated to VideoScaleInfo.
//
//Revision 1.26  2011/05/21 15:00:14  velktron
//Adapted to use new gamemode system.
//
//Revision 1.25  2011/05/18 16:57:21  velktron
//Changed to DoomStatus

package st;

// States for the chat code.

enum st_enumstatcodes_t {

	    StartChatState,
	    WaitDestState,
	    GetChatState
	    
	}
package st;

import m.IRandom;
import doom.DoomMain;
import doom.DoomStatus;
import i.DoomVideoInterface;
import i.IDoomSystem;
import rr.Renderer;
import s.IDoomSound;
import v.DoomVideoRenderer;
import v.IVideoScaleAware;
import w.IWadLoader;

public abstract class AbstractStatusBar implements IDoomStatusBar, IVideoScaleAware{

	 // /// STATUS //////////

    protected DoomVideoRenderer<?,?> V;

    protected IWadLoader W;

    protected Renderer<?,?> R;

    protected DoomMain<?,?> DM;

    protected IRandom RND;
    
    protected IDoomSystem I;
    
    protected DoomVideoInterface<?> VI;

    protected IDoomSound S;
    
	@Override
	public void updateStatus(DoomStatus<?,?> DC) {
        this.DM=DC.DM;
        this.V=DC.V;
        this.W=DC.W;
        this.RND=DC.RND;
        this.R= DC.R;
        this.VI=DC.VI;
        this.I=DC.I;
        this.S=DC.S;
    }
	
}

package st;
// Emacs style mode select   -*- C++ -*- 
//-----------------------------------------------------------------------------
//
// $Id: IDoomStatusBar.java,v 1.4 2012/09/24 17:16:23 velktron Exp $
//
// Copyright (C) 1993-1996 by id Software, Inc.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// DESCRIPTION:
//	Status bar code.
//	Does the face/direction indicator animatin.
//	Does palette indicators as well (red pain/berserk, bright pickup)
//
//-----------------------------------------------------------------------------

import i.DoomStatusAware;
import v.IVideoScaleAware;
import doom.event_t;

public interface IDoomStatusBar extends IVideoScaleAware,DoomStatusAware{

    
    
    /** Points to "screen 4" which is treated as a buffer */
    static final int BG =4;

    /** Points to "screen 0" which is what you actually see */
     static final int FG =0;

    //
    // STATUS BAR
    //

    /** Called by main loop. */
    public boolean Responder (event_t ev);

    /** Called by main loop. */
    public void Ticker ();

    /** Called by main loop.*/
    public void Drawer (boolean fullscreen, boolean refresh);

    /** Called when the console player is spawned on each level. */
    public void Start ();

    /** Called by startup code. */
    public void Init ();

    /** Used externally to determine window scaling. 
     *  This means that drawing transparent status bars is possible, but
     *  it will look fugly because of the solid windowing (and possibly
     *  HOMS).
     */
    public int getHeight();
    
    /** Forces a full refresh for reasons not handled otherwise, e.g. after full-page
     *  draws of help screens, which normally don't trigger a complete redraw even if
     *  they should, really.
     */
    
    void forceRefresh();

}
package st;

//States for status bar code.

public enum st_stateenum_t {

	
		    AutomapState,
	    FirstPersonState
	    
	} 
package tools;

import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;

import rr.line_t;
import data.maplinedef_t;
import data.mapsector_t;
import data.mapsidedef_t;
import data.mapthing_t;
import data.mapvertex_t;
import doom64.MapLights;
import doom64.MapLinedef;
import doom64.MapSector;
import doom64.MapSidedef;
import doom64.MapThing;
import doom64.MapVertex;
import utils.C2JUtils;
import w.*;


public class D64Converter {	    
	    
	    static final String[] MAPS={"MAP01" ,"MAP02","MAP03","MAP04","MAP05",
	    							"MAP06","MAP07","MAP08","MAP09","MAP10",
	    							"MAP11","MAP12","MAP13","MAP14","MAP15",
	    							"MAP16","MAP17","MAP18","MAP19","MAP20",
	    							"MAP21","MAP22","MAP23","MAP24","MAP25",
	    							"MAP26","MAP27","MAP28","MAP29","MAP30",
	    							"MAP31","MAP32","MAP33"};
	    static final String DOOM64="d://doom//iwads//doom64.wad";
	    
	    static final String[] COPY_LUMPS_1={"SEGS","SSECTORS","NODES"};
	    static final String[] COPY_LUMPS_2={"REJECT","BLOCKMAP"};
	    static final String VOID_TEXTURE="-";
	    WadLoader W,WAD64;
	    String[] TEXTURES,FLATS;
	    HashMap<Integer,String> TexHash,FlatHash;

	    /* ResourceManager::getTextureHash
	     * Returns the Doom64 hash of a given texture name, computed using
	     * the same hash algorithm as Doom64 EX itself
	     *******************************************************************/
	    public static int getTextureHash(String name)
	    {
	    	char[] str=new char[8];
	    	
	    	Arrays.fill(str, (char)0);
	    	
	    	 for (int c = 0; c < name.length() && c < 8; c++)
	    		str[c] = name.charAt(c);

	    	long hash = 1315423911;
	    	for(int i = 0; i < 8 && str[i] != '\0'; ++i){
	    		hash ^= ((hash << 5)+ Character.toUpperCase(str[i]) + (hash >> 2));
	    		hash&=0x00FFFFFFFFL;
	    	}
	    	
	    	hash %= 65536;
	    	return (int) hash;
	    }
	    
	    public mapsector_t[] ConvertSectors(int lump, MapLights[] lights) throws IOException
	    {
	        MapSector[] data;
	        mapsector_t[] sectors;
	        MapSector    d64;
	        mapsector_t  doom;
	        
	        
	        int numsectors = WAD64.LumpLength (lump) / MapSector.sizeOf();
	        
	       
	        // Read "mapsectors"
	        data=WAD64.CacheLumpNumIntoArray(lump,numsectors,MapSector.class);
	        sectors = C2JUtils.createArrayOfObjects(mapsector_t.class,numsectors);
	        
	        
	        for (int i=0 ; i<numsectors ; i++)
	        {
	        	d64 = data[i];
	        	doom = sectors[i];
	        	doom.floorheight =d64.floorheight;
	        	doom.ceilingheight = d64.ceilingheight;
	        	doom.floorpic=FlatHash.get(new Integer(d64.floorpic));
	        	doom.ceilingpic=FlatHash.get(new Integer(d64.ceilingpic));
	        	
	        	if (doom.floorpic==null) doom.floorpic=TexHash.get(new Integer(d64.floorpic));
	        	if (doom.floorpic==null) System.err.printf("Warning: Sector %d floorpic hash %d has null mapping!\n",i,(int)d64.floorpic);

	        	if (doom.ceilingpic==null) doom.ceilingpic=TexHash.get(new Integer(d64.ceilingpic));
	        	if (doom.ceilingpic==null) System.err.printf("Warning: Sector %d ceilingpic hash %d has null mapping!\n",i,(int)d64.ceilingpic);
	        	
	        	if(doom.floorpic.equals("?")) doom.floorpic=VOID_TEXTURE;
	        	if(doom.ceilingpic.equals("?")) doom.ceilingpic=VOID_TEXTURE;
	        	
	        	// System.out.printf("Sector %d mapped to %s %s\n",i,dooms.floorpic,dooms.ceilingpic);
	        	doom.lightlevel=lights[d64.thingcolor].greyscaleLightLevel();
	        	//dooms.floorpic = FLATS[d64s.floorpic];
	        	//dooms.ceilingpic = FLATS[d64s.ceilingpic];
	        	//dooms.lightlevel = ms.lightlevel;
	        	doom.special = d64.special;
	        	doom.tag = d64.tag;
	        }
	        
	        return sectors;

	    }
	    
	    /**
	     * P_LoadThings
	   * @throws IOException 
	     */
	    public mapthing_t[] ConvertThings(int lump) throws IOException
	    {
	        MapThing[]       data;
	        mapthing_t[] things;
	        MapThing d64thing;
	        mapthing_t     mt;
	        int         numthings;
     
	        
	        numthings = WAD64.LumpLength (lump) / MapThing.sizeOf();
	        
	        data=WAD64.CacheLumpNumIntoArray(lump,numthings,MapThing.class);
	        things = C2JUtils.createArrayOfObjects(mapthing_t.class,numthings);
	        
	        for (int i=0 ; i<numthings ; i++)   {
	        	mt=things[i];
	        	d64thing=data[i];
	        	mt.x=d64thing.x;
	        	mt.y=d64thing.y;
	        	mt.angle=d64thing.angle;
	        	mt.options=d64thing.flags;
	        	mt.type=d64thing.type;
	        }        
	        
	        return things;
	        
	    }

	    public maplinedef_t[] ConvertLinedefs(int lump) throws IOException
	    {
	        MapLinedef[]       data;
	        maplinedef_t[]       lines;
	        MapLinedef d64l;
	        maplinedef_t   mld;
	        
	        int numlines = WAD64.LumpLength (lump) / MapLinedef.sizeOf();
	        lines = C2JUtils.createArrayOfObjects(maplinedef_t.class,numlines);

	        // read "maplinedefs"
	        data = WAD64.CacheLumpNumIntoArray(lump,numlines,MapLinedef.class);
	        
	        for (int i=0 ; i<numlines ; i++)
	        {
	            d64l=data[i];
	        	mld=lines[i];      	        
	        	
	        	mld.v1=(char) d64l.v1;
	        	mld.v2=(char) d64l.v2;
	        	// TODO: adapt flags
	        	mld.flags = (short) (d64l.flags&0xFFFF);
	        	mld.special = (short) (d64l.special&0xFF);
	        	mld.tag = d64l.tag;
	        	mld.sidenum[0]=(char) d64l.left;
	        	mld.sidenum[1]=(char) d64l.right;
	        	
	        }
	        
	        return lines;	        

	     }
	
	    public mapsidedef_t[] ConvertSidedefs(int lump) throws IOException
	    {
	        MapSidedef[] data;
	        mapsidedef_t[] sides;
	        MapSidedef    d64;
	        mapsidedef_t  doom;	        
	        
	        int numsides = WAD64.LumpLength (lump) / MapSidedef.sizeOf();
	        
	       
	        // Read "mapsectors"
	        data=WAD64.CacheLumpNumIntoArray(lump,numsides,MapSidedef.class);
	        sides = C2JUtils.createArrayOfObjects(mapsidedef_t.class,numsides);
	        
	        
	        for (int i=0 ; i<numsides ; i++)
	        {
	        	d64 = data[i];
	        	doom = sides[i];
	        	doom.textureoffset=d64.xoffset;
	        	doom.rowoffset=d64.yoffset;
	        	doom.sector=d64.sector;
	        	doom.bottomtexture=TexHash.get(new Integer(d64.bottomtexture));
	        	doom.midtexture=TexHash.get(new Integer(d64.midtexture));
	        	doom.toptexture=TexHash.get(new Integer(d64.toptexture));
	        		
        		if (doom.bottomtexture==null)	doom.bottomtexture=FlatHash.get(new Integer(d64.bottomtexture));
	        	if (doom.bottomtexture==null) System.err.printf("Warning: Sidedef %d bottomtexture hash %d has null mapping!\n",i,(int)d64.bottomtexture);
	        	
	        	if (doom.midtexture==null) doom.midtexture=FlatHash.get(new Integer(d64.midtexture));
	        	if (doom.midtexture==null) System.err.printf("Warning: Sidedef %d midtexture hash %d has null mapping!\n",i,(int)d64.midtexture);
	        	
	        	if (doom.toptexture==null) doom.toptexture=FlatHash.get(new Integer(d64.toptexture));
	        	if (doom.toptexture==null) System.err.printf("Warning: Sidedef %d toptexture hash %d has null mapping!\n",i,(int)d64.toptexture);
	        	
	        	if(doom.bottomtexture.equals("?")) doom.bottomtexture=VOID_TEXTURE;
	        	if(doom.midtexture.equals("?")) doom.midtexture=VOID_TEXTURE;
	        	if(doom.toptexture.equals("?")) doom.toptexture=VOID_TEXTURE;
	        	
	        	// System.out.printf("Sidedef %d mapped to %s %s %s\n",i,doom.bottomtexture,doom.midtexture,doom.toptexture);
	        }
	        
	        return sides;

	    }
	    
	    public mapvertex_t[] ConvertVertexes(int lump) throws IOException
	    {
	        MapVertex[] data;
	        mapvertex_t[] vertexes;
	        MapVertex    d64;
	        mapvertex_t  doom;	        
	        
	        int numvertexes = WAD64.LumpLength (lump) / MapVertex.sizeOf();	        
	       
	        data=WAD64.CacheLumpNumIntoArray(lump,numvertexes,MapVertex.class);
	        vertexes = C2JUtils.createArrayOfObjects(mapvertex_t.class,numvertexes);	        
	        
	        for (int i=0 ; i<numvertexes ; i++)
	        {
	        	d64 = data[i];
	        	doom = vertexes[i];
	        	doom.x= (short) (d64.x>>16);
	        	doom.y= (short) (d64.y>>16);
	        }
	        
	        return vertexes;

	    }	    	    
	    
	    
		private MapLights[] ConvertLights(int lump) {
			MapLights[] lights;
			MapLights[] data;
	        
	        int numlights = WAD64.LumpLength (lump) / MapLights.sizeOf();
	        
	        // First 256 lights are auto-generated
	        lights=new MapLights[256+numlights];
	        
	        for (int i=0;i<256;i++){
	        	lights[i]=new MapLights((short) i);	        	
	        }
	        	       
	        // Read extra lights
	        data=WAD64.CacheLumpNumIntoArray(lump,numlights,MapLights.class);
	        
	        for (int i=0;i<numlights;i++)	lights[256+i]=data[i];
	        return lights;
		}
	    
	    
	    public static void main(String[] argv) throws Exception {	    	
	    	D64Converter conv=new D64Converter();
	    	conv.gogogo();
	    }
	    
	    public void gogogo() throws Exception{	    	
		    DataInputStream dis=new DataInputStream(new FileInputStream(DOOM64));
	    
		    W=new WadLoader(DOOM64);
		    
		    System.out.println(W.numlumps);
		    
		    int T_START=W.GetNumForName("T_START");
		    int T_END=W.GetNumForName("T_END");
		    
	    
		    // Find "?" textures. The first texture and the first flat will both be "?"
		    
		    int[] suck_markers=new int[2];
		    int mark=0;
		    
		    for (int lump=T_START+1;lump<T_END;lump++){
		    	if (W.GetNameForNum(lump).equals("?")) suck_markers[mark++]=lump;
		    	if (mark>1) break;
		    }
		    
		    // Can't separate between flats and textures, not enough "?"
		    if (mark<2) return;
		    
		    TEXTURES=new String[suck_markers[1]-suck_markers[0]];
		    FLATS=new String[T_END-suck_markers[1]];
		    
		    TexHash=new HashMap<Integer,String>();
		    FlatHash=new HashMap<Integer,String>();
		    
		    
		    int k=0;
		    for (int lump=suck_markers[0];lump<suck_markers[1];lump++,k++){
		    	TEXTURES[k]=W.GetNameForNum(lump);
		    	System.out.printf("Texture %d %s hash %d\n",k,TEXTURES[k],getTextureHash(TEXTURES[k]));
		    	TexHash.put(getTextureHash(TEXTURES[k]),TEXTURES[k]);
		    }
		    
		    k=0;
		    for (int lump=suck_markers[1];lump<T_END;lump++,k++){
		    	FLATS[k]=W.GetNameForNum(lump);
		    	System.out.printf("Flat %d %s hash %d\n",k,FLATS[k],getTextureHash(FLATS[k]));
		    	FlatHash.put(getTextureHash(FLATS[k]),FLATS[k]);
		    }
		    
		    WadBuilder WB=new WadBuilder("Doom64conv.wad", "PWAD");
		    WB.start();
		    
		    for (String map: MAPS){
		    		System.err.println(map);
	    	
		            byte[] mapdata=W.CacheLumpNameAsRawBytes(map,0);
		            File f=new File(String.format("%s.d64.wad",map));
		            BufferedOutputStream bos=new BufferedOutputStream(new FileOutputStream(f));
		            bos.write(mapdata);
		            bos.close();
		            
		            WAD64=new WadLoader(f.getName());
		            System.out.printf("%s %d\n",f.getName(),WAD64.numlumps);

		            // We have no use for the LEAFS and MACROS nodes, but we do have some use for the LIGHTS node,
		            // since it's the only way to deduct some lighting information.
		            
		            int lump=WAD64.GetNumForName("LIGHTS");
		            MapLights[] lights=ConvertLights(lump);
		            
		            lump=WAD64.GetNumForName("THINGS");
		            mapthing_t[] things=ConvertThings(lump);

		            lump=WAD64.GetNumForName("LINEDEFS");
		            maplinedef_t[] lines=ConvertLinedefs(lump);		            
		            
		            lump=WAD64.GetNumForName("SIDEDEFS");
		            mapsidedef_t[] sides=ConvertSidedefs(lump);
		            
		            ProcessLinesSides(lines,sides);
	            
		            lump=WAD64.GetNumForName("SECTORS");
		            mapsector_t[] sectors=ConvertSectors(lump,lights);
		            
		            lump=WAD64.GetNumForName("VERTEXES");
		            mapvertex_t[] vertexes=ConvertVertexes(lump);
	            
		            WB.add(map);
		            WB.add("THINGS", things);
		            WB.add("LINEDEFS", lines);
		            WB.add("SIDEDEFS", sides);
		            WB.add("VERTEXES", vertexes);
		            
		            // SEGS, SSECTORS and NODES are the same as vanilla
		            WB.add(WAD64,COPY_LUMPS_1);
		            
		            WB.add("SECTORS", sectors);
		            
		            // REJECT and BLOCKMAP are the same as vanilla...
		            WB.add(WAD64,COPY_LUMPS_2);	            
		            }
		    
            	WB.close();
	    }

		private void ProcessLinesSides(maplinedef_t[] lines,
				mapsidedef_t[] sides) {

			for (int k=0;k<lines.length;k++){
				//  Samuel: there's a flag in D64 maps that is "render mid-texture"
				// if that flag is not set but there is a midtexture present, it should be nullified to no texture
				// note that the nullification should only occur on two-sided lines, one-sided lines should be left alone in that regard
				//
				
				if (C2JUtils.flags(lines[k].flags,line_t.ML_TWOSIDED) &&
						!C2JUtils.flags(lines[k].flags,MapLinedef.D64_ML_RENDERMIDDLE)) {
					int front=lines[k].sidenum[0];
					int back=lines[k].sidenum[1];
						
					if(front!=0xFFFF && !sides[front].midtexture.equals(VOID_TEXTURE)){
	        			System.err.printf("Linedef %d Sidedef %d (front) Flags %x Mid %s =>",k,front,lines[k].flags,sides[front].midtexture);
	        			sides[front].midtexture=VOID_TEXTURE;
	        			System.err.printf(" %s\n",sides[front].midtexture);
	        		}
					
					if(back!=0xFFFF && !sides[back].midtexture.equals(VOID_TEXTURE)){
	        			System.err.printf("Linedef %d Sidedef %d (front) Flags %x Mid %s =>",k,back,lines[k].flags,sides[back].midtexture);
	        			sides[back].midtexture=VOID_TEXTURE;
	        			System.err.printf(" %s\n",sides[back].midtexture);
	        		}

					
	        	}

			}
		
		}

}

package v;

import java.awt.image.BufferedImage;
import java.awt.image.DataBufferByte;
import java.awt.image.DataBufferInt;
import java.awt.image.DataBufferUShort;
import m.BBox;

public class BufferedRenderer32 extends SoftwareVideoRenderer32 {
	
static final String rcsid = "$Id: BufferedRenderer32.java,v 1.4 2016/07/04 07:52:26 velktron Exp $";

/** Buffered Renderer has a bunch of images "pegged" to the underlying arrays */

public BufferedImage[] screenbuffer=new BufferedImage[5];

public BufferedRenderer32(int w, int h) {
    super(w,h);
}

@Override
public final void Init () 
{ 
 int		i;
 for (i=0 ; i<4 ; i++){
	//screens[i] = new byte[this.getHeight()*this.getWidth()];
     this.setScreen(i, this.width, this.height);     
	}
     dirtybox=new BBox();
}

/** This implementation will "tie" a bufferedimage to the underlying byte raster.
 * 
 * NOTE: this relies on the ability to "tap" into a BufferedImage's backing array,
 * in order to have fast writes without setpixel/getpixel. If that is not possible,
 * then we'll need to use a special renderer.
 * 
 */
@Override
public final void setScreen(int index, int width, int height){

	if (screens[index]==null){
		screenbuffer[index]=new BufferedImage(width,height,BufferedImage.TYPE_INT_ARGB);
    
    	screens[index]=((DataBufferInt)screenbuffer[index].getRaster().getDataBuffer()).getData();
		}
}

public void setPalette(int palette){
    this.usepalette=palette%maxpalettes;

    // Invalidate cached graphics, otherwise older colormaps
    // will persist.
    this.clearCaches();

    
    // Tint the current set of colormaps.
    getCachedCmap(palette);

    
	//this.currentpal=palette%maxpalettes;
	this.currentscreen=this.screenbuffer[0];
	
}

@Override
public void setUsegamma(int gamma) {
	this.usegamma=gamma%maxgammas;

   // Invalidate palette cache.
    super.clearPalettes();

	
	// Re-synthesize current palette.
	setPalette(usepalette);
}

public void setCurrentScreen(int screen){
	  super.setCurrentScreen(screen);
	  this.currentscreen=this.screenbuffer[0];
}

@Override
protected final void specificPaletteCreation(byte[] paldata,
        short[][] gammadata, 
        final int palettes, 
        final int colors,
        final int stride,
        final int gammalevels){

      System.out.printf("Enough data for %d palettes",maxpalettes);
      System.out.printf("Enough data for %d gamma levels",maxgammas);
      
      //this.palettes=new int[maxpalettes*maxgammas][];
      this.palettes=new int[maxpalettes][];
      
      // Apply gammas a-posteriori, not a-priori.
      // Initial palette can be neutral or based upon "gamma 0",
      // which is actually a bit biased and distorted
      
      for (int z=0;z<1;z++){
          
          // For each palette
          for (int y=0;y<maxpalettes;y++){
              this.palettes[z*maxpalettes+y]=new int[colors];
              
              for (int x=0;x<colors;x++){
                  int r=gammadata[z][0xFF&paldata[y*colors*stride+stride*x]]; // R
                  int g=gammadata[z][0xFF&paldata[1+y*colors*stride+stride*x]]; // G
                  int b=gammadata[z][0xFF&paldata[2+y*colors*stride+stride*x]]; // B
                  int color=0xFF000000|r<<16|g<<8|b;
                  this.palettes[z*maxpalettes+y][x]=color;
                }
            }
      }
      
      // Set base colormap
      
      cmap_base=PaletteGenerator.RF_BuildLights24(this.palettes[0], NUMLIGHTS);
      cmap_work=PaletteGenerator.RF_BuildLights24(this.palettes[0], NUMLIGHTS);

}

public int getBaseColor(int color){
	return cmap_work[0][color];
}

}

package v;

import doom.ICommandLineManager;
import static utils.C2JUtils.*;

public class VisualSettings {

    /** Default video scale is "triple vanilla: 3 x (320 x 200) */
    public final static IVideoScale vanilla=new VideoScaleInfo(1.0f);
    public final static IVideoScale double_vanilla=new VideoScaleInfo(2.0f);
    public final static IVideoScale triple_vanilla=new VideoScaleInfo(3.0f);
    public final static IVideoScale default_scale=triple_vanilla;
    
    /** Parses the command line for resolution-specific commands, and creates
     *  an appropriate IVideoScale object.
     *  
     * @param CM
     * @return
     */
    
    public final static IVideoScale parse(ICommandLineManager CM){
        
        int width=-1;
        int height=-1;
        
        // -multiply parameter defined from linux doom.
        // It gets priority over all others, if present.
        int p=CM.CheckParm("-multiply");
        if (eval(p)) {            
            try {
                width=Integer.parseInt(CM.getArgv(p+1));
            } catch (NumberFormatException e){
                // We failed. Mark the occasion.
                width=-1;
            }
        }
        
        // If -multiply was successful, trump any others.
        // Implied to be a solid multiple of the vanilla resolution.
        if (width>0 && width<=5) return new VideoScaleInfo(width); 
        
        // Width defined?
        p=CM.CheckParm("-width");
        if (eval(p)) {            
            try {
                width=Integer.parseInt(CM.getArgv(p+1));
            } catch (NumberFormatException e){
                // We failed. Mark the occasion.
                width=-1;
            }
        }
        
        p=CM.CheckParm("-height");
        if (eval(p)) {
            try {
                height=Integer.parseInt(CM.getArgv(p+1));
            } catch (NumberFormatException e){
                // We failed. Mark the occasion.
                height=-1;
            }
        }
    
        // Nothing to do?
        if (height==-1 && width==-1) return default_scale;

        // At least one of them is not a dud.
        int mulx, muly,mulf;
        
        // Break them down to the nearest multiple of the base width or height.
        mulx=Math.round((float)width/IVideoScale.BASE_WIDTH);
        muly=Math.round((float)height/IVideoScale.BASE_HEIGHT);
        
        // Do not accept zero or sub-vanilla resolutions
        if (mulx>0 || muly>0){
        
        // Use the maximum multiplier. We don't support skewed
        // aspect ratios yet.
        mulf=Math.max(mulx,muly);
        if (mulf>=1 && mulf<=5) return new VideoScaleInfo(mulf);
        }
        
        // In all other cases...
        return default_scale;
        
        
        
    }
    
}

package v;

import java.awt.Image;
import java.awt.image.IndexColorModel;
import java.io.IOException;

import rr.patch_t;

/** DoomVideoSystem is now an interface, that all "video drivers" (wheter do screen, disk, etc.)
 *  must implement. 
 *  
 *  23/10/2011: Made into a generic type, which affects the underlying raw screen data
 *  type. This should make -in theory- true colour or super-indexed (>8 bits) video modes
 *  possible. The catch is that everything directly meddling with the renderer must also
 *  be aware of the underlying implementation. E.g. the various screen arrays will not be
 *  necessarily byte[].
 * 
 * @author Maes
 *
 */

public interface DoomVideoRenderer<T,V> extends IVideoScaleAware {
    
    //flags hacked in scrn (not supported by all functions (see src))
    // Added by _D_. Unsure if I should use VSI objects instead, as they
    // already carry scaling information which doesn't need to be repacked...
    public static final int V_NOSCALESTART =      0x010000;   // dont scale x,y, start coords
    public static final int V_SCALESTART        = 0x020000;   // scale x,y, start coords
    public static final int V_SCALEPATCH        = 0x040000;   // scale patch
    public static final int V_NOSCALEPATCH      = 0x080000;   // don't scale patch
    public static final int V_WHITEMAP          = 0x100000;   // draw white (for v_drawstring)    
    public static final int V_FLIPPEDPATCH      = 0x200000;   // flipped in y
    public static final int V_TRANSLUCENTPATCH  = 0x400000;   // draw patch translucent    
    public static final int V_PREDIVIDE  = 0x800000;   // pre-divide by best x/y scale.    
    public static final int V_SCALEOFFSET = 0x1000000; // Scale the patch offset
    
    public static final int SCREEN_FG = 0; // Foreground screen
    public static final int SCREEN_BG= 1; // Used for endlevel/finale BG
    public static final int SCREEN_WS = 2; // Wipe start screen, also used for screenshots
    public static final int SCREEN_WE = 3; // Wipe end screen
    public static final int SCREEN_SB = 4; // Used for status bar
    
    // Allocates buffer screens, call before R_Init.
    public void Init();

    public void CopyRect(int srcx, int srcy, int srcscrn, int width,
            int height, int destx, int desty, int destscrn);
    
    public void FillRect(int srcx, int srcy, int width,
            int height,int destscrn);

    public void DrawPatch(int x, int y, int scrn, patch_t patch);
    
    public void DrawPatchFlipped ( int      x,   int        y,    int       scrn,  patch_t  patch );

    public void DrawPatchDirect(int x, int y, int scrn, patch_t patch);

    /** V_DrawPatch
     * Draws a SOLID (non-masked) patch to the screen with integer scaling
     * m and n.
     * Useful for stuff such as help screens, titlepic and status bar. Not 
     * very useful for menus, though.
     * desttop, dest and source were byte
     */ 

    public void DrawPatchSolidScaled ( int x, int y,int m, int n, int scrn, patch_t patch );
    
    public void DrawPatchSolidScaled ( int x, int y,  int scrn, IVideoScale vs, patch_t patch );
    
    // Draw a linear block of pixels into the view buffer.
    public void DrawBlock(int x, int y, int scrn, int width, int height,
            T src);

    // Draw a linear block of pixels into the view buffer.
    public void DrawBlock(int x, int y, int scrn, int width, int height,
            T src,int offset);
    
    // Reads a linear block of pixels into the view buffer.
    public void GetBlock(int x, int y, int scrn, int width, int height,
            V dest);

    public void MarkRect(int x, int y, int width, int height);

    public V getScreen(int index);
    
    public void setScreen(int index, int width, int height);
    
    public int getUsegamma();    
    
    public void takeScreenShot(int screen, String imagefile, IndexColorModel icm) throws IOException;
    
    public int getWidth();
    
    public int getHeight();
    
    /** Shamelessly ripped from Doom Legacy (for menus, etc) by _D_ ;-)
     * It uses FLAGS (see above) hacked into the scrn parameter, to be
     * parsed afterwards.
     */
    public void DrawScaledPatch(int x, int y, int scrn, IVideoScale VSI,  // hacked flags in it...
            patch_t patch);

    void DrawPatchColScaled(int x,  patch_t patch,int col, IVideoScale vs,
            int screen);
    
    /** Perform any action necessary so that palettes get modified according to specified gamma.
     *  Consider this a TIME CONSUMING operation, so don't call it unless really necessary.
     * 
     * @param gammalevel
     * 
     */
    void setUsegamma(int gammalevel);
    
    /** Perform any action necessary so that the screen output uses the specified palette
     * Consider this a TIME CONSUMING operation, so don't call it unless really necessary.
     * 
     * @param palette
     */
    
    void setPalette(int palette);
    
    
    /** Perform any action necessary so that palettes and gamma tables are created, e.g. by reading
     * from on-disk resources or from somewhere else.
     * 
     */
    void createPalettes(byte[] paldata, short[][] gammadata, final int palettes, final int colors, final int stride,final int gammalevels);
    
    
    /** No matter how complex/weird/arcane palette manipulations you do internally, the AWT module
     *  must always be able to "tap" into what's the current, "correct" screen after all manipulation and
     *  color juju was applied. Call after a palette/gamma change.
     * 
     */
    Image getCurrentScreen();
    
    /** Final call before updating a particular screen. 
     *  In case that e.g. manual palette application or additonal
     *  rendering must be performed on a screen.
     * 
     */
    
    void update();
    
    
    /** Which of the internal screens you want to display next time you call getCurrentScreen
     * 
     * @param screen
     */
    void setCurrentScreen(int screen);

    void FillRect(int color, int screen, int x, int y, int width, int height);
    
    V[] getColorMaps();

    /** Get the value corresponding to a base color (0-255).
     *  Depending on the implementation this might be indexed,
     *  RGB etc. Use whenever you need "absolute" colors.
     * 
     * @return
     */
    int getBaseColor(int color);
    
    /** Clear any byte-to-short or byte-to-int post or flat caches generated 
     *  during e.g. extended color blits or OpenGL acceleration.
     *  
     *  Good moments to call this function include:
     *  *After starting a new level.
     *  *After Menu, Finale, Endlevel screens or Wipers have been deactivated.
     *  *In general, after anything that might use fixed graphics has completed.
     *  
     *  This is necessary because the cache keeps references to 
     */
    
	void clearCaches();
	
	/** Return current palette. Only works for 8-bit renderer, others return null */	
	
	IndexColorModel getPalette();

    
}

package v;

import static data.Defines.RANGECHECK;

import i.DoomStatusAware;
import i.IDoomSystem;

import java.awt.Image;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferByte;
import java.awt.image.IndexColorModel;

import doom.DoomStatus;

import m.BBox;
import rr.LightsAndColors;
import rr.patch_t;

public abstract class SoftwareVideoRenderer<T,V>
        implements DoomVideoRenderer<T,V>, IVideoScaleAware, DoomStatusAware{
    
    /** Light levels. Binded to the colormap subsystem */
    public static final int NUMLIGHTS=LightsAndColors.LIGHTLEVELS;
    
    /** Colormap used for static graphics (menu etc.) */
    public static final int CMAP_FIXED=0;

    protected IDoomSystem I;
    protected Image currentscreen;
    
    public Image getCurrentScreen(){
    	return currentscreen;
    }
    
    public final void updateStatus(DoomStatus<?,?> DM){
    	this.I=DM.I;
    }
    
    protected int width;
    protected int height;

    /** Each screen is [SCREENWIDTH*SCREENHEIGHT]; 
     *  This is what the various modules (menu, automap,
     *  renderer etc.) get to manipulate at the pixel
     *  level. To go beyond 8 bit displays, these must be extended */ 
    protected V[]         screens;  
     
    //MAES: maybe this should be a bbox?

    public BBox             dirtybox=new BBox();
    
    /** Colormaps are now part of the base software renderer. This 
     *  allows some flexibility over manipulating them.
     *  
     *  Use base as immutable, use work for applying effects.
     *  
     */
    
    protected V[] cmap_base,cmap_work;
    
    /** PLAYPAL-read palettes, used to build dynamic color maps 
     *  Use [z*maxpalettes+y] form, where z=gamme, y=palette
     * */
    
    protected int[][] palettes;
    
    
    public SoftwareVideoRenderer(){
        // Defaults
        width=SCREENWIDTH;
        height=SCREENHEIGHT;
    }

    public SoftwareVideoRenderer(int w,int h){
        // Defaults
        width=w;
        height=h;
    }
    
    protected int  usegamma=0;
    protected int  usepalette=0;
    protected int maxpalettes;
    protected int maxgammas;
    protected int currentpal;
    protected int currentgamma;
    protected int usescreen=0;
    
    public final int getUsegamma() {
        return usegamma;
    }
    
    /** V_Markrect:
     *  Apparently, video.c had its own "dirtybox" bbox, and this was a crude method for 
     *  expanding it.
     * 
     */
    
    public final void MarkRect ( int      x,  int     y,  int     width,  int     height ) 
    { 
        dirtybox.AddToBox(x, y); 
        dirtybox.AddToBox(x+width-1, y+height-1); 
    } 
    
  /**
   *  V_CopyRect 
   */

  public final void CopyRect (int       srcx,
          int     srcy,
          int     srcscrn,
          int     width,
          int     height,
          int     destx,
          int     desty,
          int     destscrn ) 
  { 
      // These are pointers inside an array.
      final V  src=screens[srcscrn];
      final V  dest=screens[destscrn]; 

      if  (RANGECHECK) {
          if (srcx<0
                  ||srcx+width >this.width
                  || srcy<0
                  || srcy+height>SCREENHEIGHT 
                  ||destx<0||destx+width >this.width
                  || desty<0
                  || desty+height>SCREENHEIGHT 
                  || srcscrn>4
                  || destscrn>4)
          {
              I.Error ("Bad V_CopyRect");
          }
      } 
      this.MarkRect (destx, desty, width, height); 


      // MAES: these were pointers to a specific position inside the screen.
      int srcPos = this.width*srcy+srcx; 
      int destPos = this.width*desty+destx; 

      for ( ; height>0 ; height--) 
      { 
          System.arraycopy(src,srcPos, dest, destPos, width);
          //memcpy (dest, src, width); 
          srcPos += this.width; 
          destPos += this.width; 
      }

  }
   
  protected final boolean doRangeCheck(int x, int y,patch_t patch, int scrn){
    return      (x<0
                  ||x+patch.width >this.width
                  || y<0
                  || y+patch.height>this.height 
                  || scrn>4);
  }

  protected final boolean doRangeCheck(int x, int y, int scrn){
      return      (x<0
                    ||x>this.width
                    || y<0
                    || y>this.height 
                    || scrn>4);
    }

  public void DrawPatchSolidScaled ( int x, int y,  int scrn, IVideoScale vs, patch_t patch ){
      this.DrawPatchSolidScaled(x, y, vs.getScalingX(), vs.getScalingY(),scrn, patch);
  }
  
  public final int getHeight() {
      return this.height;
  } 
  public final int getWidth() {
      return this.width;
  } 
  
  public final void DrawPatchDirect(int x, int y, int scrn, patch_t patch) {
      this.DrawPatch(x, y, scrn, patch);
      
  }

  public final V getScreen(int index) {
     return screens[index];
  }
  /*
  public final boolean isRasterNull(int screen){
      for (int i=0;i<screens[screen].length;i++){
          if (screens[screen][i]!=0) return false;
      }
      return true;
  } */
  
  public void setCurrentScreen(int screen){
	  this.usescreen=screen;
  }
  
  public void update(){
	// Override only if there's something else to be done, e.g. map palette to truecolor buffer  
  }
  
  public void report(BufferedImage[] b){
	    System.out.println("Main video buffer "+screens[0]);
	    for (int i=0;i<b.length;i++){
	    System.out.println(((Object)b[i].getRaster()).toString()+" "+b[i].getRaster().hashCode()+" "+((DataBufferByte)(b[i].getRaster().getDataBuffer())).getData());
	    }
	}
  
////////////////////////////VIDEO SCALE STUFF ////////////////////////////////

  protected int SCREENWIDTH=320;
  protected int SCREENHEIGHT=200;
  protected IVideoScale vs;


  @Override
  public void setVideoScale(IVideoScale vs) {
      this.vs=vs;
  }

  @Override
  public void initScaling() {
      this.SCREENHEIGHT=vs.getScreenHeight();
      this.SCREENWIDTH=vs.getScreenWidth();
  }

  /** Built-in method for recovering from palette disasters.
   * Uses PaletteGenerator class to generate Doom's palettes with only the data of
   * the first palette.
   * 
   */
protected final void paletteRecovery() {
	createPalettes(PaletteGenerator.generatePalette(PaletteGenerator.playpal, 256,ColorTint.tints), GammaTables.gammatables, 14, 256, 3, 5);
	
}

/** Internal method for setting up palettes (and gamma tables)
 * 
 */

public void createPalettes(byte[] paldata, short[][] gammadata, final int palettes, final int colors, final int stride,final int gammalevels){
	
	// Sanity check on supplied data length. If there is not enough data to create the specified palettes,
	// their number will be limited.
	
	if (paldata!=null) 	// As many as are likely contained
		maxpalettes=paldata.length/(colors*stride);
	else
		maxpalettes=0; // Do some default action on null palette.

	if (gammadata!=null) 	// As many as are likely contained
		maxgammas=gammadata.length;
	else
		maxgammas=0; // Do some default action on null gamma tables.
	
	if (maxgammas==0){
		gammadata=GammaTables.gammatables;
		maxgammas=GammaTables.gammatables.length;
	}
	

	// Enough data for all palettes. 
	// Enough data for all palettes. 
	if (maxpalettes>0 && maxgammas>0)
			specificPaletteCreation(paldata,gammadata,palettes,colors,stride,gammalevels);
		 else 
			 paletteRecovery();
    	  
      }
    
/** Override this in extending classes to perform specific actions depending on the
 *  type of renderer. It's better not to assign a default action, nor make assumptions
 *  on the underlying types of actual palettes
 * 
 * @param paldata
 * @param gammadata
 * @param palettes
 * @param colors
 * @param stride
 * @param gammalevels
 */

protected abstract void specificPaletteCreation(byte[] paldata,
		short[][] gammadata, 
		final int palettes, 
		final int colors,
		final int stride,
		final int gammalevels);

protected int lastcolor=-1;
protected byte[] scanline;

public int getBaseColor(int color){
    return color;
}

/** Override if there's something special to do for colormap caches, e.g. during
 *  palette or gamma changes. Not required for indexed.
 */

public void clearCaches(){
    // Does nothing for indexed.
}

/** Should return colormaps, if you ever move their management in here.
 * 
 */

public V[] getColorMaps(){
    return null;
}

public void setColorMaps(V[] colormaps,int num){
    // Dummy
}

public IndexColorModel getPalette(){
    return null;
    }
}

package v;

import static data.Defines.RANGECHECK;

import i.DoomStatusAware;
import i.IDoomSystem;

import java.awt.Image;
import java.awt.image.BufferedImage;
import java.awt.image.IndexColorModel;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;

import javax.imageio.ImageIO;

import doom.DoomStatus;

import m.BBox;

import rr.column_t;
import rr.patch_t;
import utils.C2JUtils;

public abstract class SoftwareVideoRenderer16 extends SoftwareVideoRenderer<byte[],short[]> {


	// MAES: maybe this should be a bbox?

	public BBox dirtybox = new BBox();

	public SoftwareVideoRenderer16() {
		// Defaults
	    super();
		screens = new short[5][];
	}

	public SoftwareVideoRenderer16(int w, int h) {
		super(w,h);
		screens = new short[5][];
	}

	/**
	 * V_Fillrect
	 */
	@Override
	public void FillRect(int srcx, int srcy, int width, int height, int destscrn) {
		// These are pointers inside an array.
		final short[] dest = screens[destscrn];

		if (RANGECHECK) {
			if (srcx < 0 || srcx + width > this.width || srcy < 0
					|| srcy + height > SCREENHEIGHT || destscrn > 4) {
				I.Error("Bad V_FillRect");
			}
		}
		this.MarkRect(srcx, srcy, width, height);

		// MAES: these were pointers to a specific position inside the screen.
		int srcPos = this.width * srcy + srcx;

		for (; height > 0; height--) {
			for (int i = 0; i < width; i++) {
				dest[srcPos + i] = 0;
			}
			// memcpy (dest, src, width);
			srcPos += this.width;
		}

	}

	/**
	 * V_DrawPatch Masks a column based masked pic to the screen. desttop, dest
	 * and source were byte*
	 */

	public final void DrawPatch(int x, int y, int scrn, patch_t patch) {

		column_t column;
		int desttop;
		final short[] dest = screens[scrn];
		int w;

		y -= patch.topoffset;
		x -= patch.leftoffset;
		if (RANGECHECK)
			if (doRangeCheck(x, y, patch, scrn)) {
				System.err.print("Patch at " + x + "," + y + " exceeds LFB\n");
				// No I_Error abort - what is up with TNT.WAD?
				System.err.print("V_DrawPatch: bad patch (ignored)\n");
				return;
			}

		if (scrn == 0)
			this.MarkRect(x, y, patch.width, patch.height);

		w = patch.width;
		desttop = x + this.width * y;
		// For each column..
		int destPos;
		int ptr = 0;
		for (int col = 0; col < w; desttop++, col++, x++) {
			// This points at a "column" object.
			// column = (column_t *)((byte *)patch +
			// LONG(patch->columnofs[col]));
			column = patch.columns[col];
			final short[] data=getShortVersion(column.data);
			// For each post...
			for (int i = 0; i < column.posts; i++) {
				// Get pointer to post offset.
				ptr = column.postofs[i];
				// Get post delta
				short delta = column.postdeltas[i];
				// We skip delta, len and padding.
				// ptr+=3; NOT NEEDED< pre-skipped at parsing.

				// Skip transparent rows...
				if (delta == 0xFF)
					break;

				destPos = desttop + delta * this.width;

				// These lengths are already correct.
				for (int j = 0; j < column.postlen[i]; j++) {
					dest[destPos] = data[ptr++];
					destPos += this.width;
				}
			}
		}

	}

	/**
	 * V_DrawPatchSolidScaled Draws a SOLID (non-masked) patch to the screen
	 * with integer scaling m and n. Useful for stuff such as help screens,
	 * titlepic and status bar. Not very useful for menus, though. desttop, dest
	 * and source were byte
	 */

	public final void DrawPatchSolidScaled(int x, int y, int m, int n,
			int scrn, patch_t patch) {

		if ((m == 1) && (n == 1)) {
			DrawPatch(x, y, scrn, patch);
			return;
		}

		column_t column;
		short[] data;
		int desttop;
		final short[] dest = screens[scrn];
		int w;

		y = y * n - patch.topoffset;
		x = x * m - patch.leftoffset;
		if (RANGECHECK)
			if (doRangeCheck(x, y, patch, scrn)) {
				System.err.print("Patch at " + x + "," + y + " exceeds LFB\n");
				// No I_Error abort - what is up with TNT.WAD?
				System.err.print("V_DrawPatch: bad patch (ignored)\n");
				return;
			}

		if (scrn == 0)
			this.MarkRect(x, y, patch.width, patch.height);

		w = patch.width;
		desttop = m * x + this.width * y;
		// For each column..
		int destPos;
		int ptr = 0;
		// x increases by m.

		// Some unrolling...

		if (m == 2) {
			// desttop=2*desttop;
			for (int col = 0; col < w; desttop += 2, col++)

			{

				// This points at a "column" object.
				// column = (column_t *)((byte *)patch +
				// LONG(patch->columnofs[col]));
				column = patch.columns[col];
				data=this.getShortVersion(column.data);
				
				// For each post...
				for (int i = 0; i < column.posts; i++) {
					// Get pointer to post offset.
					ptr = column.postofs[i];
					// Get post delta
					short delta = column.postdeltas[i];
					// We skip delta, len and padding.
					// ptr+=3;

					// Skip transparent rows...
					if (delta == 0xFF)
						break;

					destPos = desttop + n * delta * this.width;

					// These lengths are already correct.
					for (int j = 0; j < column.postlen[i]; j++) {
						dest[destPos] = data[ptr++];
						dest[destPos + 1] = dest[destPos];
						destPos += n * this.width;
					}
				}
			}
		} else if (m == 3) {
			// desttop=3*desttop;
			for (int col = 0; col < w; desttop += 3, col++)

			{

				column = patch.columns[col];
				data=this.getShortVersion(column.data);
				
				// For each post...
				for (int i = 0; i < column.posts; i++) {
					// Get pointer to post offset.
					ptr = column.postofs[i];
					// Get post delta
					short delta = column.postdeltas[i];
					// We skip delta, len and padding.
					// ptr+=3;

					// Skip transparent rows...
					if (delta == 0xFF)
						break;

					destPos = desttop + n * delta * this.width;

					// These lengths are already correct.
					for (int j = 0; j < column.postlen[i]; j++) {
						dest[destPos] = data[ptr++];
						dest[destPos + 1] = dest[destPos];
						dest[destPos + 2] = dest[destPos];
						destPos += n * this.width;
					}

				}
			}
		} else if (m == 4) {
			// desttop=4*desttop;
			for (int col = 0; col < w; desttop += 4, col++)

			{

				column = patch.columns[col];
				data=this.getShortVersion(column.data);
				
				// For each post...
				for (int i = 0; i < column.posts; i++) {
					// Get pointer to post offset.
					ptr = column.postofs[i];
					// Get post delta
					short delta = column.postdeltas[i];
					// We skip delta, len and padding.
					// ptr+=3;

					// Skip transparent rows...
					if (delta == 0xFF)
						break;

					destPos = desttop + n * delta * this.width;

					// These lengths are already correct.
					for (int j = 0; j < column.postlen[i]; j++) {
						dest[destPos] = data[ptr++];
						dest[destPos + 1] = dest[destPos];
						dest[destPos + 2] = dest[destPos];
						dest[destPos + 3] = dest[destPos];
						destPos += n * this.width;
					}
				}
			}
		} else {
			// desttop=m*desttop;
			for (int col = 0; col < w; desttop += m, col++)

			{

				// This points at a "column" object.
				// column = (column_t *)((byte *)patch +
				// LONG(patch->columnofs[col]));
				column = patch.columns[col];
				// For each post...
				for (int i = 0; i < column.posts; i++) {
					// Get pointer to post offset.
					ptr = column.postofs[i];
					// Get post delta
					short delta = column.postdeltas[i];
					// We skip delta, len and padding.
					// ptr+=3;

					// Skip transparent rows...
					if (delta == 0xFF)
						break;

					destPos = desttop + n * delta * this.width;

					// These lengths are already correct.
					for (int j = 0; j < column.postlen[i]; j++) {
						for (int k = 0; k < m; k++)
							dest[destPos + k] = column.data[ptr];
						ptr++;
						destPos += n * this.width;
					}
				}
			}

		}

		scaleSolid(m, n, scrn, m * patch.width);
	}

	/**
	 * Pretty crude in-place scaling. It's fast, but only works full-screen
	 * Width needs to be specific, height is implied.
	 * */

	protected final void scaleSolid(int m, int n, int screen, int width) {
		int height = screens[screen].length / width;
		for (int i = 0; i < height; i += n) {

			for (int j = 0; j < n - 1; j++) {

				System.arraycopy(screens[screen], (i + j) * width,
						screens[screen], (i + j + 1) * width, width);
			}
		}

	}

	/**
	 * V_DrawPatchFlipped Masks a column based masked pic to the screen. Flips
	 * horizontally, e.g. to mirror face.
	 * 
	 * Currently UNUSED, as any occurence to it is also scaled and best served
	 * by another function.
	 * 
	 * 
	 */

	public final void DrawPatchFlipped(int x, int y, int scrn, patch_t patch) {

		column_t column;
		int desttop;
		short[] dest = screens[scrn];
		int w;

		y -= patch.topoffset;
		x -= patch.leftoffset;

		if (RANGECHECK)
			if (doRangeCheck(x, y, patch, scrn)) {
				{
					System.err.print("Patch origin " + x + "," + y
							+ " exceeds LFB\n");
					// No I_Error abort - what is up with TNT.WAD?
					I.Error("Bad V_DrawPatch in V_DrawPatchFlipped");
				}
			}

		if (scrn == 0)
			this.MarkRect(x, y, patch.width, patch.height);

		// Set x and y coords inside dest array.

		w = patch.width;
		desttop = y * this.width + x;
		// For each column..
		for (int col = 0; col < w; desttop++, col++) {

			// This points at a "column" object.
			// Notice the flipping on this one.
			// column = (column_t *)((byte *)patch +
			// LONG(patch->columnofs[w-1-col]));
			column = patch.columns[w - 1 - col];

			// For each post...
			// System.out.println("Column"+(w-1-col));
			for (int i = 0; i < column.posts; i++) {
				// Get pointer to post offset.
				int ptr = column.postofs[i];
				// Get post delta
				int delta = 0xFF & column.data[ptr];
				// We skip delta, len and padding.
				// ptr+=3;
				if (delta == 0xFF)
					break;
				int destPos = desttop + delta * this.width;
				// count = column.length;

				// These lengths are already correct.
				for (int j = 0; j < column.postlen[i]; j++, destPos += this.width) {
					dest[destPos] = column.data[ptr + j];
					// next line

				}
			}
		}

	}

	/**
	 * V_DrawScaledPatch like V_DrawPatch, but scaled 2,3,4 times the original
	 * size and position this is used for menu and title screens, with high
	 * resolutions
	 * 
	 * added:05-02-98: default params : scale patch and scale start
	 * 
	 * Iniially implemented for Mocha Doom by _D_ (shamelessly ripped from
	 * Eternity Engine ;-), adapted to scale based on a scaling info object
	 * (VSI).
	 * 
	 * Unless overriden by flags, starting x and y are automatically scaled
	 * (implied V_SCALESTART)
	 * 
	 */
	@Override
	public void DrawScaledPatch(int x, int y, int scrn, IVideoScale VSI,
			patch_t patch) {
		int col;
		column_t column;
		int desttop;
		final short[] dest = screens[scrn & 0xFF];
		// byte[] source;

		int dupx, dupy;
		int colfrac, rowfrac;

		// System.out.printf("V_DrawScaledPatch %d %d \n",x,y);
		// draw an hardware converted patch
		/*
		 * #ifdef HWRENDER if (rendermode != render_soft) {
		 * HWR_DrawPatch((GlidePatch_t *) patch, x, y, scrn); return; } #endif
		 */

		// A very common operation, eliminates the need to pre-divide.
		if (C2JUtils.flags(scrn, V_PREDIVIDE)) {
			x /= vs.getScalingX();
			y /= vs.getScalingY();
		}

		if (C2JUtils.flags(scrn, V_NOSCALEPATCH))
			dupx = dupy = 1;
		else {
			dupx = VSI.getScalingX();
			dupy = VSI.getScalingY();
		}

		// Eliminates.
		// MAES: added this fix so that non-zero patch offsets can be
		// taken into account, regardless of whether we use pre-scaled
		// coords or not. Only Doomguy's face needs this hack for now.

		if (C2JUtils.flags(scrn, V_SCALEOFFSET)) {
			y -= patch.topoffset * dupx;
			x -= patch.leftoffset * dupy;
		} else {
			y -= patch.topoffset;
			x -= patch.leftoffset;
		}

		colfrac = dupx;
		rowfrac = dupy;

		// desttop = screens[scrn & 0xFF];
		if (C2JUtils.flags(scrn, V_NOSCALESTART))
			desttop = (y * this.width) + x;
		else
			desttop = (y * dupy * this.width) + (x * dupx) /* + scaledofs */;
		// destend = desttop + /*SHORT(*/patch.width/*)*/ * dupx;

		int w = patch.width * dupx;

		int colInc = 1;
		col = 0;
		if (C2JUtils.flags(scrn, V_FLIPPEDPATCH)) {
			colInc = -1;
			col = w - 1;// (/*SHORT(*/patch.width/*)*/ << fixed_t.FRACBITS) +
						// colfrac;
		}

		for (; col >= 0 && col < w/* ; desttop < destend */; col += colInc, desttop++) {
			// column = (column_t *) ((byte *) patch + LONG(patch.columnofs[col
			// >> FRACBITS]));
			column = patch.columns[col / colfrac];
			final short[] data=getShortVersion(column.data);

			int destPos;
			int ptr = 0;
			int ptrOfs;

			// while (column.topdelta != 0xff)
			for (int i = 0; i < column.posts; i++) {
				{
					ptrOfs = column.postofs[i];// +3;
					ptr = 0;
					short delta = column.postdeltas[i];

					// Skip transparent rows...
					if (delta == 0xFF)
						break;

					destPos = desttop + delta * dupy * this.width;
					// dest = desttop + column.topdelta * dupy * this.width;

					// ofs = 0;
					// while (count-- > 0)
					for (int j = 0; j < column.postlen[i] * dupy; j++) {
						dest[destPos] = data[ptrOfs + ptr / rowfrac];
						destPos += this.width;

						ptr++;
						// ofs += rowfrac;
					}

					// column = (column_t *) ((byte *) column + column.length +
					// 4);
				}
			}
		}
	}

	@Override
	public final void DrawBlock(int x, int y, int scrn, int width, int height,
			byte[] src) {
		DrawBlock(x,y,scrn,width,height,src,0);
	}

	@Override
	public final void DrawBlock(int x, int y, int scrn, int width, int height,
			byte[] src,int offset) {
		// This is "screens[scrn]"
		final short[] dest = screens[scrn];
		final short[] data=getShortVersion(src);
		
		if (doRangeCheck(x, y, scrn)) {
			I.Error("Bad V_DrawBlock");
		}

		this.MarkRect(x, y, width, height);

		int destPos = /* screens[scrn] + */y * this.width + x;
		// MAES: making an assumption here. A BIIIIG one.
		int srcPos = offset;
		while ((height--) > 0) {
			// A real dog. It's good that this ain't used
			// so often.
			for (int xx=0;xx<width;xx++)
				dest[destPos+xx]=data[srcPos+xx];
				//System.arraycopy(src, srcPos, dest, destPos, width);
			srcPos += width;
			destPos += this.width;
		}
	}
	

	/**
	 * Replaces DrawPatchCol for bunny scrolled in Finale.
	 * 
	 * 
	 */

	@Override
	public final void DrawPatchColScaled(int x, patch_t patch, int col,
			IVideoScale vs, int screen) {
		column_t column;
		int source;
		final short[] dest;
		final short[] data;
		int desttop;
		final int scale = vs.getScalingX();

		column = patch.columns[col];
		data=this.getShortVersion(column.data);
		desttop = x * scale; // Scale X position.
		dest = screens[screen];
		// step through the posts in a column

		for (int i = 0; i < column.posts; i++) {
			// Get pointer to post offset.
			source = column.postofs[i];
			// Get post delta
			short delta = column.postdeltas[i];
			// We skip delta, len and padding.
			// source+=3; NOT NEEDED ANYMORE, skipped by parsing.

			// Skip transparent rows...
			if (delta == 0xFF)
				break;

			// Replicate each column scale times vertically,
			// with spaced pixels.
			final int startsource = source;
			for (int kl = 0; kl < scale; kl++) {
				int destPos = desttop + (delta + kl) * SCREENWIDTH;
				for (int j = 0; j < column.postlen[i]; j++) {
					final short datap = data[source++];
					// replicate each column's pixel horizontally and
					// vertically.
					for (int k = 0; k < scale; k++)
						dest[destPos + k] = datap;
					destPos += scale * SCREENWIDTH;
				}
				source = startsource;
			}
		}
	}

	public void setScreen(int index, int width, int height) {
		this.screens[index] = new short[width * height];
	}

	public final void takeScreenShot(int screen, String imagefile,
			IndexColorModel icm) throws IOException {

		BufferedImage b = new BufferedImage(this.getWidth(), this.getHeight(),
				BufferedImage.TYPE_BYTE_INDEXED, icm);

		int[] tmp = new int[this.screens[screen].length];
		for (int i = 0; i < this.screens[screen].length; i++) {
			tmp[i] = this.screens[screen][i];
		}

		b.getRaster().setPixels(0, 0, this.getWidth(), this.getHeight(), tmp);

		File outputFile = new File(imagefile + ".png");
		ImageIO.write(b, "PNG", outputFile);

	}

	
	/** Internal method for setting up palettes (and gamma tables)
	 * 
	 */

	public void createPalettes(byte[] paldata, short[][] gammadata, final int palettes, final int colors, final int stride,final int gammalevels){
	    
	    // Sanity check on supplied data length. If there is not enough data to create the specified palettes,
	    // their number will be limited.
	    
	    if (paldata!=null)  // As many as are likely contained
	        maxpalettes=paldata.length/(colors*stride);
	    else
	        maxpalettes=0; // Do some default action on null palette.

	    if (gammadata!=null)    // As many as are likely contained
	        maxgammas=gammadata.length;
	    else
	        maxgammas=0; // Do some default action on null gamma tables.
	    
	    if (maxgammas==0){
	        gammadata=GammaTables.gammatables;
	        maxgammas=GammaTables.gammatables.length;
	    }
	    

	    // Enough data for all palettes. 
	    // Enough data for all palettes. 
	    if (maxpalettes>0 && maxgammas>0)
	            specificPaletteCreation(paldata,gammadata,palettes,colors,stride,gammalevels);
	         else 
	             paletteRecovery();
	          
	      }
	
	/** Override this in extending classes to perform specific actions depending on the
	 *  type of renderer. It's better not to assign a default action, nor make assumptions
	 *  on the underlying types of actual palettes
	 * 
	 * @param paldata
	 * @param gammadata
	 * @param palettes
	 * @param colors
	 * @param stride
	 * @param gammalevels
	 */

	protected abstract void specificPaletteCreation(byte[] paldata,
	        short[][] gammadata, 
	        final int palettes, 
	        final int colors,
	        final int stride,
	        final int gammalevels);
	
	
	/**
	 * Clear automap frame buffer or fill solid color
	 * MAES: optimized for efficiency, seen the lack of a proper "memset" in Java.
	 * 
	 */

	@Override
	public final void FillRect(int color, int screen, int x, int y, int width,
			int height) {
		short[] arr = screens[screen];

		// Do a "per scanline" copy.
		int fromIndex = x + y * SCREENWIDTH;
		int toIndex = x + (y + height - 1) * SCREENWIDTH;

		final short c=(short) getBaseColor(color);
		
		// First scanline.
		for (int i = 0; i < width; i++)
			arr[fromIndex + i] = c;

		for (; fromIndex < toIndex; fromIndex += SCREENWIDTH) {
			System.arraycopy(arr, fromIndex, arr, fromIndex + SCREENWIDTH,
					width);
		}
	}

	public final void GetBlock(int x, int y, int scrn, int width, int height,
			short[] dest) {
		final short[] src = screens[scrn];

		if (RANGECHECK) {
			if (doRangeCheck(x, y, scrn)) {
				I.Error("Bad V_DrawBlock");
			}

		}
		int srcPos = y * this.width + x;
		int destPos = 0;

		while ((height--) > 0) {
			System.arraycopy(src, srcPos, dest, destPos, width);
			// memcpy (dest, src, width);
			srcPos += width;
			destPos += this.width;
		}
	}
	
	public final void setColorMaps(short[] stuff, int num){
		// For HiCOlor, load COLORS15 lump

		this.cmap_base=new short[num][256];		
		this.cmap_work=new short[num][256];       
		
		for (int i = 0; i < cmap_base.length; i++) {
			System.arraycopy(stuff, i * 256, cmap_base[i], 0, 256);
			System.arraycopy(stuff, i * 256, cmap_work[i], 0, 256);
		}
	}
	
	public final void setColorMaps(int[] stuff, int num){
		// For HiCOlor, load COLORS15 lump

        this.cmap_base=new short[num][256];     
        this.cmap_work=new short[num][256];    
		
		for (int i = 0; i < cmap_base.length; i++) {
			for (int j=0;j<256;j++){
			    cmap_base[i][j]=PaletteGenerator.rgb888to555(stuff[i*256+j]);
			    cmap_work[i][j]=cmap_base[i][j];
			}
		}
	}
	
	public short[][] getColorMaps(){
		return cmap_work;
	}
	
	///// MEGA HACK FOR SUPER-8BIT MODES
	
	private final HashMap<Integer,short[]> colcache=new HashMap<Integer,short[]>();
	
	private final short[] getShortVersion(byte[] data){
		if (!colcache.containsKey(data.hashCode())){
			//System.out.printf("Generated cache for %d\n",data.hashCode());
			short[] stuff=new short[data.length];
			for (int i=0;i<stuff.length;i++){
				stuff[i]=cmap_work[CMAP_FIXED][0xFF&data[i]];
			}
			colcache.put(data.hashCode(),stuff);
			
		}
		return colcache.get(data.hashCode());
		
	}
	
	@Override
	public final void clearCaches(){
		this.colcache.clear();
	}
	
	///// MODIFIED COLORMAP CACHING //////////
	
	private final HashMap<Integer,short[][]> cmapcache=new HashMap<Integer,short[][]>();
	
	/** Cache colormaps in order to avoid expensive re-computations 
	 *  Invalidate if gamma is changed. Changes are applied on-the-fly
	 *  to working_colormap.
	 * 
	 * */
	
	   protected final void getCachedCmap(int palette){
	       
	        short[][] stuff=cmapcache.get(palette);	       
	        
	        if (stuff==null){
	            // Generate the full range of colormap for a given palette effect	            
	            
	            //stuff=
	            //PaletteGenerator.RF_BuildLights15(palettes[usegamma*maxpalettes+usepalette], NUMLIGHTS);

	            // Tinting more faithful to the original, thanks to Sodaholic's input.
                stuff=new short[cmap_base.length][cmap_base[0].length];
                for (int i = 0; i < stuff.length; i++) { 
                  PaletteGenerator.tintColormap(cmap_base[i], stuff[i], 256, ColorTint.tints[palette%14],GammaTables.gammatables[usegamma]);
                }
                  

	            cmapcache.put(palette,stuff);	            
	        }
	        
	        // Use fast arraycopy to copy data to working colormap.
	        for (int i = 0; i < stuff.length; i++) {
	            System.arraycopy(stuff[i], 0, cmap_work[i], 0, 256);
	        }	        
	    }
	   
	    protected final void clearPalettes(){
	        this.cmapcache.clear();
	    }
	
}

package v;

import static data.Defines.RANGECHECK;

import i.DoomStatusAware;
import i.IDoomSystem;

import java.awt.Image;
import java.awt.image.BufferedImage;
import java.awt.image.IndexColorModel;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;

import javax.imageio.ImageIO;

import doom.DoomStatus;

import m.BBox;

import rr.column_t;
import rr.patch_t;
import utils.C2JUtils;

public abstract class SoftwareVideoRenderer32 extends SoftwareVideoRenderer<byte[],int[]> {

    // MAES: maybe this should be a bbox?

    public BBox dirtybox = new BBox();

    public SoftwareVideoRenderer32() {
        // Defaults
        super();
        screens = new int[5][];
    }

    public SoftwareVideoRenderer32(int w, int h) {
        super(w,h);
        screens = new int[5][];
    }

    /**
     * V_Fillrect
     */
    @Override
    public void FillRect(int srcx, int srcy, int width, int height, int destscrn) {
        // These are pointers inside an array.
        final int[] dest = screens[destscrn];

        if (RANGECHECK) {
            if (srcx < 0 || srcx + width > this.width || srcy < 0
                    || srcy + height > SCREENHEIGHT || destscrn > 4) {
                I.Error("Bad V_FillRect");
            }
        }
        this.MarkRect(srcx, srcy, width, height);

        // MAES: these were pointers to a specific position inside the screen.
        int srcPos = this.width * srcy + srcx;

        for (; height > 0; height--) {
            for (int i = 0; i < width; i++) {
                dest[srcPos + i] = 0;
            }
            // memcpy (dest, src, width);
            srcPos += this.width;
        }

    }

    /**
     * V_DrawPatch Masks a column based masked pic to the screen. desttop, dest
     * and source were byte*
     */

    public final void DrawPatch(int x, int y, int scrn, patch_t patch) {

        column_t column;
        int desttop;
        final int[] dest = screens[scrn];
        int w;

        y -= patch.topoffset;
        x -= patch.leftoffset;
        if (RANGECHECK)
            if (doRangeCheck(x, y, patch, scrn)) {
                System.err.print("Patch at " + x + "," + y + " exceeds LFB\n");
                // No I_Error abort - what is up with TNT.WAD?
                System.err.print("V_DrawPatch: bad patch (ignored)\n");
                return;
            }

        if (scrn == 0)
            this.MarkRect(x, y, patch.width, patch.height);

        w = patch.width;
        desttop = x + this.width * y;
        // For each column..
        int destPos;
        int ptr = 0;
        for (int col = 0; col < w; desttop++, col++, x++) {
            // This points at a "column" object.
            // column = (column_t *)((byte *)patch +
            // LONG(patch->columnofs[col]));
            column = patch.columns[col];
            final int[] data=getShortVersion(column.data);
            // For each post...
            for (int i = 0; i < column.posts; i++) {
                // Get pointer to post offset.
                ptr = column.postofs[i];
                // Get post delta
                short delta = column.postdeltas[i];
                // We skip delta, len and padding.
                // ptr+=3; NOT NEEDED< pre-skipped at parsing.

                // Skip transparent rows...
                if (delta == 0xFF)
                    break;

                destPos = desttop + delta * this.width;

                // These lengths are already correct.
                for (int j = 0; j < column.postlen[i]; j++) {
                    dest[destPos] = data[ptr++];
                    destPos += this.width;
                }
            }
        }

    }

    /**
     * V_DrawPatchSolidScaled Draws a SOLID (non-masked) patch to the screen
     * with integer scaling m and n. Useful for stuff such as help screens,
     * titlepic and status bar. Not very useful for menus, though. desttop, dest
     * and source were byte
     */

    public final void DrawPatchSolidScaled(int x, int y, int m, int n,
            int scrn, patch_t patch) {

        if ((m == 1) && (n == 1)) {
            DrawPatch(x, y, scrn, patch);
            return;
        }

        column_t column;
        int[] data;
        int desttop;
        final int[] dest = screens[scrn];
        int w;

        y = y * n - patch.topoffset;
        x = x * m - patch.leftoffset;
        if (RANGECHECK)
            if (doRangeCheck(x, y, patch, scrn)) {
                System.err.print("Patch at " + x + "," + y + " exceeds LFB\n");
                // No I_Error abort - what is up with TNT.WAD?
                System.err.print("V_DrawPatch: bad patch (ignored)\n");
                return;
            }

        if (scrn == 0)
            this.MarkRect(x, y, patch.width, patch.height);

        w = patch.width;
        desttop = m * x + this.width * y;
        // For each column..
        int destPos;
        int ptr = 0;
        // x increases by m.

        // Some unrolling...

        if (m == 2) {
            // desttop=2*desttop;
            for (int col = 0; col < w; desttop += 2, col++)

            {

                // This points at a "column" object.
                // column = (column_t *)((byte *)patch +
                // LONG(patch->columnofs[col]));
                column = patch.columns[col];
                data=this.getShortVersion(column.data);
                
                // For each post...
                for (int i = 0; i < column.posts; i++) {
                    // Get pointer to post offset.
                    ptr = column.postofs[i];
                    // Get post delta
                    short delta = column.postdeltas[i];
                    // We skip delta, len and padding.
                    // ptr+=3;

                    // Skip transparent rows...
                    if (delta == 0xFF)
                        break;

                    destPos = desttop + n * delta * this.width;

                    // These lengths are already correct.
                    for (int j = 0; j < column.postlen[i]; j++) {
                        dest[destPos] = data[ptr++];
                        dest[destPos + 1] = dest[destPos];
                        destPos += n * this.width;
                    }
                }
            }
        } else if (m == 3) {
            // desttop=3*desttop;
            for (int col = 0; col < w; desttop += 3, col++)

            {

                column = patch.columns[col];
                data=this.getShortVersion(column.data);
                
                // For each post...
                for (int i = 0; i < column.posts; i++) {
                    // Get pointer to post offset.
                    ptr = column.postofs[i];
                    // Get post delta
                    short delta = column.postdeltas[i];
                    // We skip delta, len and padding.
                    // ptr+=3;

                    // Skip transparent rows...
                    if (delta == 0xFF)
                        break;

                    destPos = desttop + n * delta * this.width;

                    // These lengths are already correct.
                    for (int j = 0; j < column.postlen[i]; j++) {
                        dest[destPos] = data[ptr++];
                        dest[destPos + 1] = dest[destPos];
                        dest[destPos + 2] = dest[destPos];
                        destPos += n * this.width;
                    }

                }
            }
        } else if (m == 4) {
            // desttop=4*desttop;
            for (int col = 0; col < w; desttop += 4, col++)

            {

                column = patch.columns[col];
                data=this.getShortVersion(column.data);
                
                // For each post...
                for (int i = 0; i < column.posts; i++) {
                    // Get pointer to post offset.
                    ptr = column.postofs[i];
                    // Get post delta
                    short delta = column.postdeltas[i];
                    // We skip delta, len and padding.
                    // ptr+=3;

                    // Skip transparent rows...
                    if (delta == 0xFF)
                        break;

                    destPos = desttop + n * delta * this.width;

                    // These lengths are already correct.
                    for (int j = 0; j < column.postlen[i]; j++) {
                        dest[destPos] = data[ptr++];
                        dest[destPos + 1] = dest[destPos];
                        dest[destPos + 2] = dest[destPos];
                        dest[destPos + 3] = dest[destPos];
                        destPos += n * this.width;
                    }
                }
            }
        } else {
            // desttop=m*desttop;
            for (int col = 0; col < w; desttop += m, col++)

            {

                // This points at a "column" object.
                // column = (column_t *)((byte *)patch +
                // LONG(patch->columnofs[col]));
                column = patch.columns[col];
                // For each post...
                for (int i = 0; i < column.posts; i++) {
                    // Get pointer to post offset.
                    ptr = column.postofs[i];
                    // Get post delta
                    short delta = column.postdeltas[i];
                    // We skip delta, len and padding.
                    // ptr+=3;

                    // Skip transparent rows...
                    if (delta == 0xFF)
                        break;

                    destPos = desttop + n * delta * this.width;

                    // These lengths are already correct.
                    for (int j = 0; j < column.postlen[i]; j++) {
                        for (int k = 0; k < m; k++)
                            dest[destPos + k] = column.data[ptr];
                        ptr++;
                        destPos += n * this.width;
                    }
                }
            }

        }

        scaleSolid(m, n, scrn, m * patch.width);
    }

    /**
     * Pretty crude in-place scaling. It's fast, but only works full-screen
     * Width needs to be specific, height is implied.
     * */

    protected final void scaleSolid(int m, int n, int screen, int width) {
        int height = screens[screen].length / width;
        for (int i = 0; i < height; i += n) {

            for (int j = 0; j < n - 1; j++) {

                System.arraycopy(screens[screen], (i + j) * width,
                        screens[screen], (i + j + 1) * width, width);
            }
        }

    }

    /**
     * V_DrawPatchFlipped Masks a column based masked pic to the screen. Flips
     * horizontally, e.g. to mirror face.
     * 
     * Currently UNUSED, as any occurence to it is also scaled and best served
     * by another function.
     * 
     * 
     */

    public final void DrawPatchFlipped(int x, int y, int scrn, patch_t patch) {

        column_t column;
        int desttop;
        int[] dest = screens[scrn];
        int w;

        y -= patch.topoffset;
        x -= patch.leftoffset;

        if (RANGECHECK)
            if (doRangeCheck(x, y, patch, scrn)) {
                {
                    System.err.print("Patch origin " + x + "," + y
                            + " exceeds LFB\n");
                    // No I_Error abort - what is up with TNT.WAD?
                    I.Error("Bad V_DrawPatch in V_DrawPatchFlipped");
                }
            }

        if (scrn == 0)
            this.MarkRect(x, y, patch.width, patch.height);

        // Set x and y coords inside dest array.

        w = patch.width;
        desttop = y * this.width + x;
        // For each column..
        for (int col = 0; col < w; desttop++, col++) {

            // This points at a "column" object.
            // Notice the flipping on this one.
            // column = (column_t *)((byte *)patch +
            // LONG(patch->columnofs[w-1-col]));
            column = patch.columns[w - 1 - col];

            // For each post...
            // System.out.println("Column"+(w-1-col));
            for (int i = 0; i < column.posts; i++) {
                // Get pointer to post offset.
                int ptr = column.postofs[i];
                // Get post delta
                int delta = 0xFF & column.data[ptr];
                // We skip delta, len and padding.
                // ptr+=3;
                if (delta == 0xFF)
                    break;
                int destPos = desttop + delta * this.width;
                // count = column.length;

                // These lengths are already correct.
                for (int j = 0; j < column.postlen[i]; j++, destPos += this.width) {
                    dest[destPos] = column.data[ptr + j];
                    // next line

                }
            }
        }

    }

    /**
     * V_DrawScaledPatch like V_DrawPatch, but scaled 2,3,4 times the original
     * size and position this is used for menu and title screens, with high
     * resolutions
     * 
     * added:05-02-98: default params : scale patch and scale start
     * 
     * Iniially implemented for Mocha Doom by _D_ (shamelessly ripped from
     * Eternity Engine ;-), adapted to scale based on a scaling info object
     * (VSI).
     * 
     * Unless overriden by flags, starting x and y are automatically scaled
     * (implied V_SCALESTART)
     * 
     */
    @Override
    public void DrawScaledPatch(int x, int y, int scrn, IVideoScale VSI,
            patch_t patch) {
        int col;
        column_t column;
        int desttop;
        final int[] dest = screens[scrn & 0xFF];
        // byte[] source;

        int dupx, dupy;
        int colfrac, rowfrac;

        // System.out.printf("V_DrawScaledPatch %d %d \n",x,y);
        // draw an hardware converted patch
        /*
         * #ifdef HWRENDER if (rendermode != render_soft) {
         * HWR_DrawPatch((GlidePatch_t *) patch, x, y, scrn); return; } #endif
         */

        // A very common operation, eliminates the need to pre-divide.
        if (C2JUtils.flags(scrn, V_PREDIVIDE)) {
            x /= vs.getScalingX();
            y /= vs.getScalingY();
        }

        if (C2JUtils.flags(scrn, V_NOSCALEPATCH))
            dupx = dupy = 1;
        else {
            dupx = VSI.getScalingX();
            dupy = VSI.getScalingY();
        }

        // Eliminates.
        // MAES: added this fix so that non-zero patch offsets can be
        // taken into account, regardless of whether we use pre-scaled
        // coords or not. Only Doomguy's face needs this hack for now.

        if (C2JUtils.flags(scrn, V_SCALEOFFSET)) {
            y -= patch.topoffset * dupx;
            x -= patch.leftoffset * dupy;
        } else {
            y -= patch.topoffset;
            x -= patch.leftoffset;
        }

        colfrac = dupx;
        rowfrac = dupy;

        // desttop = screens[scrn & 0xFF];
        if (C2JUtils.flags(scrn, V_NOSCALESTART))
            desttop = (y * this.width) + x;
        else
            desttop = (y * dupy * this.width) + (x * dupx) /* + scaledofs */;
        // destend = desttop + /*SHORT(*/patch.width/*)*/ * dupx;

        int w = patch.width * dupx;

        int colInc = 1;
        col = 0;
        if (C2JUtils.flags(scrn, V_FLIPPEDPATCH)) {
            colInc = -1;
            col = w - 1;// (/*SHORT(*/patch.width/*)*/ << fixed_t.FRACBITS) +
                        // colfrac;
        }

        for (; col >= 0 && col < w/* ; desttop < destend */; col += colInc, desttop++) {
            // column = (column_t *) ((byte *) patch + LONG(patch.columnofs[col
            // >> FRACBITS]));
            column = patch.columns[col / colfrac];
            final int[] data=getShortVersion(column.data);

            int destPos;
            int ptr = 0;
            int ptrOfs;

            // while (column.topdelta != 0xff)
            for (int i = 0; i < column.posts; i++) {
                {
                    ptrOfs = column.postofs[i];// +3;
                    ptr = 0;
                    short delta = column.postdeltas[i];

                    // Skip transparent rows...
                    if (delta == 0xFF)
                        break;

                    destPos = desttop + delta * dupy * this.width;
                    // dest = desttop + column.topdelta * dupy * this.width;

                    // ofs = 0;
                    // while (count-- > 0)
                    for (int j = 0; j < column.postlen[i] * dupy; j++) {
                        dest[destPos] = data[ptrOfs + ptr / rowfrac];
                        destPos += this.width;

                        ptr++;
                        // ofs += rowfrac;
                    }

                    // column = (column_t *) ((byte *) column + column.length +
                    // 4);
                }
            }
        }
    }

    @Override
    public final void DrawBlock(int x, int y, int scrn, int width, int height,
            byte[] src) {
        DrawBlock(x,y,scrn,width,height,src,0);
    }

    @Override
    public final void DrawBlock(int x, int y, int scrn, int width, int height,
            byte[] src,int offset) {
        // This is "screens[scrn]"
        final int[] dest = screens[scrn];
        final int[] data=getShortVersion(src);
        
        if (doRangeCheck(x, y, scrn)) {
            I.Error("Bad V_DrawBlock");
        }

        this.MarkRect(x, y, width, height);

        int destPos = /* screens[scrn] + */y * this.width + x;
        // MAES: making an assumption here. A BIIIIG one.
        int srcPos = offset;
        while ((height--) > 0) {
            // A real dog. It's good that this ain't used
            // so often.
            for (int xx=0;xx<width;xx++)
                dest[destPos+xx]=data[srcPos+xx];
                //System.arraycopy(src, srcPos, dest, destPos, width);
            srcPos += width;
            destPos += this.width;
        }
    }
    

    /**
     * Replaces DrawPatchCol for bunny scrolled in Finale.
     * 
     * 
     */

    @Override
    public final void DrawPatchColScaled(int x, patch_t patch, int col,
            IVideoScale vs, int screen) {
        column_t column;
        int source;
        final int[] dest;
        final int[] data;
        int desttop;
        final int scale = vs.getScalingX();

        column = patch.columns[col];
        data=this.getShortVersion(column.data);
        desttop = x * scale; // Scale X position.
        dest = screens[screen];
        // step through the posts in a column

        for (int i = 0; i < column.posts; i++) {
            // Get pointer to post offset.
            source = column.postofs[i];
            // Get post delta
            short delta = column.postdeltas[i];
            // We skip delta, len and padding.
            // source+=3; NOT NEEDED ANYMORE, skipped by parsing.

            // Skip transparent rows...
            if (delta == 0xFF)
                break;

            // Replicate each column scale times vertically,
            // with spaced pixels.
            final int startsource = source;
            for (int kl = 0; kl < scale; kl++) {
                int destPos = desttop + (delta + kl) * SCREENWIDTH;
                for (int j = 0; j < column.postlen[i]; j++) {
                    final int datap = data[source++];
                    // replicate each column's pixel horizontally and
                    // vertically.
                    for (int k = 0; k < scale; k++)
                        dest[destPos + k] = datap;
                    destPos += scale * SCREENWIDTH;
                }
                source = startsource;
            }
        }
    }

    public void setScreen(int index, int width, int height) {
        this.screens[index] = new int[width * height];
    }

    public final void takeScreenShot(int screen, String imagefile,
            IndexColorModel icm) throws IOException {

        BufferedImage b = new BufferedImage(this.getWidth(), this.getHeight(),
                BufferedImage.TYPE_BYTE_INDEXED, icm);

        int[] tmp = new int[this.screens[screen].length];
        for (int i = 0; i < this.screens[screen].length; i++) {
            tmp[i] = this.screens[screen][i];
        }

        b.getRaster().setPixels(0, 0, this.getWidth(), this.getHeight(), tmp);

        File outputFile = new File(imagefile + ".png");
        ImageIO.write(b, "PNG", outputFile);

    }

    /** Internal method for setting up palettes (and gamma tables)
     * 
     */

    public void createPalettes(byte[] paldata, short[][] gammadata, final int palettes, final int colors, final int stride,final int gammalevels){
        
        // Sanity check on supplied data length. If there is not enough data to create the specified palettes,
        // their number will be limited.
        
        if (paldata!=null)  // As many as are likely contained
            maxpalettes=paldata.length/(colors*stride);
        else
            maxpalettes=0; // Do some default action on null palette.

        if (gammadata!=null)    // As many as are likely contained
            maxgammas=gammadata.length;
        else
            maxgammas=0; // Do some default action on null gamma tables.
        
        if (maxgammas==0){
            gammadata=GammaTables.gammatables;
            maxgammas=GammaTables.gammatables.length;
        }
        

        // Enough data for all palettes. 
        // Enough data for all palettes. 
        if (maxpalettes>0 && maxgammas>0)
                specificPaletteCreation(paldata,gammadata,palettes,colors,stride,gammalevels);
             else 
                 paletteRecovery();
              
          }
    
    /** Override this in extending classes to perform specific actions depending on the
     *  type of renderer. It's better not to assign a default action, nor make assumptions
     *  on the underlying types of actual palettes
     * 
     * @param paldata
     * @param gammadata
     * @param palettes
     * @param colors
     * @param stride
     * @param gammalevels
     */

    protected abstract void specificPaletteCreation(byte[] paldata,
            short[][] gammadata, 
            final int palettes, 
            final int colors,
            final int stride,
            final int gammalevels);
    
    
    /**
     * Clear automap frame buffer or fill solid color
     * MAES: optimized for efficiency, seen the lack of a proper "memset" in Java.
     * 
     */

    @Override
    public final void FillRect(int color, int screen, int x, int y, int width,
            int height) {
        int[] arr = screens[screen];
        final int c=getBaseColor(color);

        // Do a "per scanline" copy.
        int fromIndex = x + y * SCREENWIDTH;
        int toIndex = x + (y + height - 1) * SCREENWIDTH;

        // First scanline.
        for (int i = 0; i < width; i++)
            arr[fromIndex + i] = c;

        for (; fromIndex < toIndex; fromIndex += SCREENWIDTH) {
            System.arraycopy(arr, fromIndex, arr, fromIndex + SCREENWIDTH,
                    width);
        }
    }

    public final void GetBlock(int x, int y, int scrn, int width, int height,
            int[] dest) {
        final int[] src = screens[scrn];

        if (RANGECHECK) {
            if (doRangeCheck(x, y, scrn)) {
                I.Error("Bad V_DrawBlock");
            }

        }
        int srcPos = y * this.width + x;
        int destPos = 0;

        while ((height--) > 0) {
            System.arraycopy(src, srcPos, dest, destPos, width);
            // memcpy (dest, src, width);
            srcPos += width;
            destPos += this.width;
        }
    }
    
    public final void setColorMaps(short[] stuff, int num){
        // For HiCOlor, load COLORS15 lump

        this.cmap_base=new int[num][256];     
        this.cmap_work=new int[num][256];       
        
        for (int i = 0; i < cmap_base.length; i++) {
            System.arraycopy(stuff, i * 256, cmap_base[i], 0, 256);
            System.arraycopy(stuff, i * 256, cmap_work[i], 0, 256);
        }
    }
    
    public final void setColorMaps(int[] stuff, int num){
        // For HiCOlor, load COLORS15 lump

        this.cmap_base=new int[num][256];     
        this.cmap_work=new int[num][256];    
        
        for (int i = 0; i < cmap_base.length; i++) {
            for (int j=0;j<256;j++){
                cmap_base[i][j]=PaletteGenerator.rgb888to555(stuff[i*256+j]);
                cmap_work[i][j]=cmap_base[i][j];
            }
        }
    }
    
    public int[][] getColorMaps(){
        return cmap_work;
    }
    
    ///// MEGA HACK FOR SUPER-8BIT MODES
    
    private final HashMap<Integer,int[]> colcache=new HashMap<Integer,int[]>();
    
    private final int[] getShortVersion(byte[] data){
        if (!colcache.containsKey(data.hashCode())){
            //System.out.printf("Generated cache for %d\n",data.hashCode());
            int[] stuff=new int[data.length];
            for (int i=0;i<stuff.length;i++){
                stuff[i]=cmap_work[CMAP_FIXED][0xFF&data[i]];
            }
            colcache.put(data.hashCode(),stuff);
            
        }
        return colcache.get(data.hashCode());
        
    }
    
    @Override
    public final void clearCaches(){
        this.colcache.clear();
    }
    
    ///// MODIFIED COLORMAP CACHING //////////
    
    private final HashMap<Integer,int[][]> cmapcache=new HashMap<Integer,int[][]>();
    
    /** Cache colormaps in order to avoid expensive re-computations 
     *  Invalidate if gamma is changed. Changes are applied on-the-fly
     *  to working_colormap.
     * 
     * */
    
       protected final void getCachedCmap(int palette){
           
            int[][] stuff=cmapcache.get(palette);        
            
            if (stuff==null){
                // Generate the full range of colormap for a given palette effect               
                
                // Results in different tinting behavior than vanilla.
                //stuff=
                //PaletteGenerator.RF_BuildLights24(palettes[usegamma*maxpalettes+usepalette], NUMLIGHTS);
                
                // Tinting more faithful to the original, thanks to Sodaholic's input.
                stuff=new int[cmap_base.length][cmap_base[0].length];
                for (int i = 0; i < stuff.length; i++) 
                  PaletteGenerator.tintColormap(cmap_base[i], stuff[i], 256, ColorTint.tints[palette%14],GammaTables.gammatables[usegamma]);
                cmapcache.put(palette,stuff);               
            }
            
            // Use fast arraycopy to copy data to working colormap.
            for (int i = 0; i < stuff.length; i++) {
                System.arraycopy(stuff[i], 0, cmap_work[i], 0, 256);
            }           
        }
       
        protected final void clearPalettes(){
            this.cmapcache.clear();
        }
    
}

package v;

// Emacs style mode select   -*- C++ -*- 
//-----------------------------------------------------------------------------
//
//$Id: GammaTables.java,v 1.2 2016/07/04 07:52:26 velktron Exp $
//
//Copyright (C) 1993-1996 by id Software, Inc.
//
//This program is free software; you can redistribute it and/or
//modify it under the terms of the GNU General Public License
//as published by the Free Software Foundation; either version 2
//of the License, or (at your option) any later version.
//
//This program is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//GNU General Public License for more details.
//
//
//DESCRIPTION:
//	Gamma correction LUT stuff.
//

public class GammaTables {
	 // Now where did these came from? /*[5][256]*/
    public static final short[][] gammatables =
    {
        {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,
         17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,
         33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,
         49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,
         65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,
         81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,
         97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,
         113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,
         128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,
         144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,
         160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,
         176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,
         192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,
         208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,
         224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,
         240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255},

        {2,4,5,7,8,10,11,12,14,15,16,18,19,20,21,23,24,25,26,27,29,30,31,
         32,33,34,36,37,38,39,40,41,42,44,45,46,47,48,49,50,51,52,54,55,
         56,57,58,59,60,61,62,63,64,65,66,67,69,70,71,72,73,74,75,76,77,
         78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,
         99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,
         115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,129,
         130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,
         146,147,148,148,149,150,151,152,153,154,155,156,157,158,159,160,
         161,162,163,163,164,165,166,167,168,169,170,171,172,173,174,175,
         175,176,177,178,179,180,181,182,183,184,185,186,186,187,188,189,
         190,191,192,193,194,195,196,196,197,198,199,200,201,202,203,204,
         205,205,206,207,208,209,210,211,212,213,214,214,215,216,217,218,
         219,220,221,222,222,223,224,225,226,227,228,229,230,230,231,232,
         233,234,235,236,237,237,238,239,240,241,242,243,244,245,245,246,
         247,248,249,250,251,252,252,253,254,255},

        {4,7,9,11,13,15,17,19,21,22,24,26,27,29,30,32,33,35,36,38,39,40,42,
         43,45,46,47,48,50,51,52,54,55,56,57,59,60,61,62,63,65,66,67,68,69,
         70,72,73,74,75,76,77,78,79,80,82,83,84,85,86,87,88,89,90,91,92,93,
         94,95,96,97,98,100,101,102,103,104,105,106,107,108,109,110,111,112,
         113,114,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,
         129,130,131,132,133,133,134,135,136,137,138,139,140,141,142,143,144,
         144,145,146,147,148,149,150,151,152,153,153,154,155,156,157,158,159,
         160,160,161,162,163,164,165,166,166,167,168,169,170,171,172,172,173,
         174,175,176,177,178,178,179,180,181,182,183,183,184,185,186,187,188,
         188,189,190,191,192,193,193,194,195,196,197,197,198,199,200,201,201,
         202,203,204,205,206,206,207,208,209,210,210,211,212,213,213,214,215,
         216,217,217,218,219,220,221,221,222,223,224,224,225,226,227,228,228,
         229,230,231,231,232,233,234,235,235,236,237,238,238,239,240,241,241,
         242,243,244,244,245,246,247,247,248,249,250,251,251,252,253,254,254,
         255},

        {8,12,16,19,22,24,27,29,31,34,36,38,40,41,43,45,47,49,50,52,53,55,
         57,58,60,61,63,64,65,67,68,70,71,72,74,75,76,77,79,80,81,82,84,85,
         86,87,88,90,91,92,93,94,95,96,98,99,100,101,102,103,104,105,106,107,
         108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,
         125,126,127,128,129,130,131,132,133,134,135,135,136,137,138,139,140,
         141,142,143,143,144,145,146,147,148,149,150,150,151,152,153,154,155,
         155,156,157,158,159,160,160,161,162,163,164,165,165,166,167,168,169,
         169,170,171,172,173,173,174,175,176,176,177,178,179,180,180,181,182,
         183,183,184,185,186,186,187,188,189,189,190,191,192,192,193,194,195,
         195,196,197,197,198,199,200,200,201,202,202,203,204,205,205,206,207,
         207,208,209,210,210,211,212,212,213,214,214,215,216,216,217,218,219,
         219,220,221,221,222,223,223,224,225,225,226,227,227,228,229,229,230,
         231,231,232,233,233,234,235,235,236,237,237,238,238,239,240,240,241,
         242,242,243,244,244,245,246,246,247,247,248,249,249,250,251,251,252,
         253,253,254,254,255},

        {16,23,28,32,36,39,42,45,48,50,53,55,57,60,62,64,66,68,69,71,73,75,76,
         78,80,81,83,84,86,87,89,90,92,93,94,96,97,98,100,101,102,103,105,106,
         107,108,109,110,112,113,114,115,116,117,118,119,120,121,122,123,124,
         125,126,128,128,129,130,131,132,133,134,135,136,137,138,139,140,141,
         142,143,143,144,145,146,147,148,149,150,150,151,152,153,154,155,155,
         156,157,158,159,159,160,161,162,163,163,164,165,166,166,167,168,169,
         169,170,171,172,172,173,174,175,175,176,177,177,178,179,180,180,181,
         182,182,183,184,184,185,186,187,187,188,189,189,190,191,191,192,193,
         193,194,195,195,196,196,197,198,198,199,200,200,201,202,202,203,203,
         204,205,205,206,207,207,208,208,209,210,210,211,211,212,213,213,214,
         214,215,216,216,217,217,218,219,219,220,220,221,221,222,223,223,224,
         224,225,225,226,227,227,228,228,229,229,230,230,231,232,232,233,233,
         234,234,235,235,236,236,237,237,238,239,239,240,240,241,241,242,242,
         243,243,244,244,245,245,246,246,247,247,248,248,249,249,250,250,251,
         251,252,252,253,254,254,255,255}
    };
}

package v;

import java.awt.Color;

/**
 * Palette generation failsafe. Uses only data from the first palette, and
 * generates the rest by tinting according to the Doom wiki specs. Uses info
 * from: http://doom.wikia.com/wiki/PLAYPAL
 * 
 * Now extended to contain many extended color synthesizing tools, used
 * in super-indexed and truecolor software rendered displays.
 * 
 * @author Maes
 */
public class PaletteGenerator {
    public final static int playpal[] = { 0x00, 0x00, 0x00, 0x1F, 0x17, 0x0B,
            0x17, 0x0F, 0x07, 0x4B, 0x4B, 0x4B, 0xFF, 0xFF, 0xFF, 0x1B, 0x1B,
            0x1B, 0x13, 0x13, 0x13, 0x0B, 0x0B, 0x0B, 0x07, 0x07, 0x07, 0x2F,
            0x37, 0x1F, 0x23, 0x2B, 0x0F, 0x17, 0x1F, 0x07, 0x0F, 0x17, 0x00,
            0x4F, 0x3B, 0x2B, 0x47, 0x33, 0x23, 0x3F, 0x2B, 0x1B, 0xFF, 0xB7,
            0xB7, 0xF7, 0xAB, 0xAB, 0xF3, 0xA3, 0xA3, 0xEB, 0x97, 0x97, 0xE7,
            0x8F, 0x8F, 0xDF, 0x87, 0x87, 0xDB, 0x7B, 0x7B, 0xD3, 0x73, 0x73,
            0xCB, 0x6B, 0x6B, 0xC7, 0x63, 0x63, 0xBF, 0x5B, 0x5B, 0xBB, 0x57,
            0x57, 0xB3, 0x4F, 0x4F, 0xAF, 0x47, 0x47, 0xA7, 0x3F, 0x3F, 0xA3,
            0x3B, 0x3B, 0x9B, 0x33, 0x33, 0x97, 0x2F, 0x2F, 0x8F, 0x2B, 0x2B,
            0x8B, 0x23, 0x23, 0x83, 0x1F, 0x1F, 0x7F, 0x1B, 0x1B, 0x77, 0x17,
            0x17, 0x73, 0x13, 0x13, 0x6B, 0x0F, 0x0F, 0x67, 0x0B, 0x0B, 0x5F,
            0x07, 0x07, 0x5B, 0x07, 0x07, 0x53, 0x07, 0x07, 0x4F, 0x00, 0x00,
            0x47, 0x00, 0x00, 0x43, 0x00, 0x00, 0xFF, 0xEB, 0xDF, 0xFF, 0xE3,
            0xD3, 0xFF, 0xDB, 0xC7, 0xFF, 0xD3, 0xBB, 0xFF, 0xCF, 0xB3, 0xFF,
            0xC7, 0xA7, 0xFF, 0xBF, 0x9B, 0xFF, 0xBB, 0x93, 0xFF, 0xB3, 0x83,
            0xF7, 0xAB, 0x7B, 0xEF, 0xA3, 0x73, 0xE7, 0x9B, 0x6B, 0xDF, 0x93,
            0x63, 0xD7, 0x8B, 0x5B, 0xCF, 0x83, 0x53, 0xCB, 0x7F, 0x4F, 0xBF,
            0x7B, 0x4B, 0xB3, 0x73, 0x47, 0xAB, 0x6F, 0x43, 0xA3, 0x6B, 0x3F,
            0x9B, 0x63, 0x3B, 0x8F, 0x5F, 0x37, 0x87, 0x57, 0x33, 0x7F, 0x53,
            0x2F, 0x77, 0x4F, 0x2B, 0x6B, 0x47, 0x27, 0x5F, 0x43, 0x23, 0x53,
            0x3F, 0x1F, 0x4B, 0x37, 0x1B, 0x3F, 0x2F, 0x17, 0x33, 0x2B, 0x13,
            0x2B, 0x23, 0x0F, 0xEF, 0xEF, 0xEF, 0xE7, 0xE7, 0xE7, 0xDF, 0xDF,
            0xDF, 0xDB, 0xDB, 0xDB, 0xD3, 0xD3, 0xD3, 0xCB, 0xCB, 0xCB, 0xC7,
            0xC7, 0xC7, 0xBF, 0xBF, 0xBF, 0xB7, 0xB7, 0xB7, 0xB3, 0xB3, 0xB3,
            0xAB, 0xAB, 0xAB, 0xA7, 0xA7, 0xA7, 0x9F, 0x9F, 0x9F, 0x97, 0x97,
            0x97, 0x93, 0x93, 0x93, 0x8B, 0x8B, 0x8B, 0x83, 0x83, 0x83, 0x7F,
            0x7F, 0x7F, 0x77, 0x77, 0x77, 0x6F, 0x6F, 0x6F, 0x6B, 0x6B, 0x6B,
            0x63, 0x63, 0x63, 0x5B, 0x5B, 0x5B, 0x57, 0x57, 0x57, 0x4F, 0x4F,
            0x4F, 0x47, 0x47, 0x47, 0x43, 0x43, 0x43, 0x3B, 0x3B, 0x3B, 0x37,
            0x37, 0x37, 0x2F, 0x2F, 0x2F, 0x27, 0x27, 0x27, 0x23, 0x23, 0x23,
            0x77, 0xFF, 0x6F, 0x6F, 0xEF, 0x67, 0x67, 0xDF, 0x5F, 0x5F, 0xCF,
            0x57, 0x5B, 0xBF, 0x4F, 0x53, 0xAF, 0x47, 0x4B, 0x9F, 0x3F, 0x43,
            0x93, 0x37, 0x3F, 0x83, 0x2F, 0x37, 0x73, 0x2B, 0x2F, 0x63, 0x23,
            0x27, 0x53, 0x1B, 0x1F, 0x43, 0x17, 0x17, 0x33, 0x0F, 0x13, 0x23,
            0x0B, 0x0B, 0x17, 0x07, 0xBF, 0xA7, 0x8F, 0xB7, 0x9F, 0x87, 0xAF,
            0x97, 0x7F, 0xA7, 0x8F, 0x77, 0x9F, 0x87, 0x6F, 0x9B, 0x7F, 0x6B,
            0x93, 0x7B, 0x63, 0x8B, 0x73, 0x5B, 0x83, 0x6B, 0x57, 0x7B, 0x63,
            0x4F, 0x77, 0x5F, 0x4B, 0x6F, 0x57, 0x43, 0x67, 0x53, 0x3F, 0x5F,
            0x4B, 0x37, 0x57, 0x43, 0x33, 0x53, 0x3F, 0x2F, 0x9F, 0x83, 0x63,
            0x8F, 0x77, 0x53, 0x83, 0x6B, 0x4B, 0x77, 0x5F, 0x3F, 0x67, 0x53,
            0x33, 0x5B, 0x47, 0x2B, 0x4F, 0x3B, 0x23, 0x43, 0x33, 0x1B, 0x7B,
            0x7F, 0x63, 0x6F, 0x73, 0x57, 0x67, 0x6B, 0x4F, 0x5B, 0x63, 0x47,
            0x53, 0x57, 0x3B, 0x47, 0x4F, 0x33, 0x3F, 0x47, 0x2B, 0x37, 0x3F,
            0x27, 0xFF, 0xFF, 0x73, 0xEB, 0xDB, 0x57, 0xD7, 0xBB, 0x43, 0xC3,
            0x9B, 0x2F, 0xAF, 0x7B, 0x1F, 0x9B, 0x5B, 0x13, 0x87, 0x43, 0x07,
            0x73, 0x2B, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xDB, 0xDB, 0xFF, 0xBB,
            0xBB, 0xFF, 0x9B, 0x9B, 0xFF, 0x7B, 0x7B, 0xFF, 0x5F, 0x5F, 0xFF,
            0x3F, 0x3F, 0xFF, 0x1F, 0x1F, 0xFF, 0x00, 0x00, 0xEF, 0x00, 0x00,
            0xE3, 0x00, 0x00, 0xD7, 0x00, 0x00, 0xCB, 0x00, 0x00, 0xBF, 0x00,
            0x00, 0xB3, 0x00, 0x00, 0xA7, 0x00, 0x00, 0x9B, 0x00, 0x00, 0x8B,
            0x00, 0x00, 0x7F, 0x00, 0x00, 0x73, 0x00, 0x00, 0x67, 0x00, 0x00,
            0x5B, 0x00, 0x00, 0x4F, 0x00, 0x00, 0x43, 0x00, 0x00, 0xE7, 0xE7,
            0xFF, 0xC7, 0xC7, 0xFF, 0xAB, 0xAB, 0xFF, 0x8F, 0x8F, 0xFF, 0x73,
            0x73, 0xFF, 0x53, 0x53, 0xFF, 0x37, 0x37, 0xFF, 0x1B, 0x1B, 0xFF,
            0x00, 0x00, 0xFF, 0x00, 0x00, 0xE3, 0x00, 0x00, 0xCB, 0x00, 0x00,
            0xB3, 0x00, 0x00, 0x9B, 0x00, 0x00, 0x83, 0x00, 0x00, 0x6B, 0x00,
            0x00, 0x53, 0xFF, 0xFF, 0xFF, 0xFF, 0xEB, 0xDB, 0xFF, 0xD7, 0xBB,
            0xFF, 0xC7, 0x9B, 0xFF, 0xB3, 0x7B, 0xFF, 0xA3, 0x5B, 0xFF, 0x8F,
            0x3B, 0xFF, 0x7F, 0x1B, 0xF3, 0x73, 0x17, 0xEB, 0x6F, 0x0F, 0xDF,
            0x67, 0x0F, 0xD7, 0x5F, 0x0B, 0xCB, 0x57, 0x07, 0xC3, 0x4F, 0x00,
            0xB7, 0x47, 0x00, 0xAF, 0x43, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
            0xD7, 0xFF, 0xFF, 0xB3, 0xFF, 0xFF, 0x8F, 0xFF, 0xFF, 0x6B, 0xFF,
            0xFF, 0x47, 0xFF, 0xFF, 0x23, 0xFF, 0xFF, 0x00, 0xA7, 0x3F, 0x00,
            0x9F, 0x37, 0x00, 0x93, 0x2F, 0x00, 0x87, 0x23, 0x00, 0x4F, 0x3B,
            0x27, 0x43, 0x2F, 0x1B, 0x37, 0x23, 0x13, 0x2F, 0x1B, 0x0B, 0x00,
            0x00, 0x53, 0x00, 0x00, 0x47, 0x00, 0x00, 0x3B, 0x00, 0x00, 0x2F,
            0x00, 0x00, 0x23, 0x00, 0x00, 0x17, 0x00, 0x00, 0x0B, 0x00, 0x00,
            0x00, 0xFF, 0x9F, 0x43, 0xFF, 0xE7, 0x4B, 0xFF, 0x7B, 0xFF, 0xFF,
            0x00, 0xFF, 0xCF, 0x00, 0xCF, 0x9F, 0x00, 0x9B, 0x6F, 0x00, 0x6B,
            0xA7, 0x6B, 0x6B };

    public final static int greypal[] = new int[768];


    public static byte[] generatePalette(int[] data, int colors,
            ColorTint[] tints) {

        final int palstride = colors * 3;
        final byte[] tmp = new byte[palstride * tints.length];
        final int[] rgb = new int[3];
        final int[] rgb2 = new int[3];

        for (int i = 0; i < colors; i++) {
            rgb[0] = data[3 * i];
            rgb[1] = data[1 + 3 * i];
            rgb[2] = data[2 + 3 * i];

            for (int t = 0; t < tints.length; t++) {
                tintRGB(tints[t], rgb, rgb2);
                tmp[palstride * t + 3 * i] = (byte) rgb2[0];
                tmp[palstride * t + 3 * i + 1] = (byte) rgb2[1];
                tmp[palstride * t + 3 * i + 2] = (byte) rgb2[2];
            }

        }

        return tmp;

    }

    /**
     * Tint a part of a RGB_888 colormap.
     * 
     * @param original
     *        The original reference colormap. DO NOT modify!
     * @param modified
     * @param colors
     *        Usually 256.
     * @param tint
     * @param gamma
     */

    public static void tintColormap(final int[] original, int[] modified,
            int colors, ColorTint tint,short[] gamma) {

        final int[] rgb = new int[4];
        final int[] rgb2 = new int[4];

        for (int i = 0; i < colors; i++) {
            final int rgba=original[i];
            rgb[0] = getRed(rgba);
            rgb[1] = getGreen(rgba);
            rgb[2] = getBlue(rgba);            

            tintRGB(tint, rgb, rgb2);
            
            // Apply gamma correction.
            rgb2[0]=gamma[rgb2[0]];
            rgb2[1]=gamma[rgb2[1]];
            rgb2[2]=gamma[rgb2[2]];
            
            modified[i] = getARGB(rgb2[0],rgb2[1],rgb2[2]);
        }

    }

    public static void tintColormap(final short[] original, short[] modified,
            int colors, ColorTint tint,short[] gamma) {

        final int[] rgb = new int[3];
        final int[] rgb2 = new int[3];

        for (int i = 0; i < colors; i++) {
            final int rgba = rgb555to888(original[i]);
            rgb[0] = getRed(rgba);
            rgb[1] = getGreen(rgba);
            rgb[2] = getBlue(rgba);

            tintRGB(tint, rgb, rgb2);
            
            // Apply gamma correction.
            rgb2[0]=gamma[rgb2[0]];
            rgb2[1]=gamma[rgb2[1]];
            rgb2[2]=gamma[rgb2[2]];
            modified[i] = rgb888to555(rgb2[0],rgb2[1],rgb2[2]);
        }

    }

    public static final void tintRGB(final ColorTint tint, final int[] rgb,
            int[] rgb2) {
        rgb2[0] = (int) (rgb[0] * (1 - tint.tint) + tint.r * tint.tint);
        rgb2[1] = (int) (rgb[1] * (1 - tint.tint) + tint.g * tint.tint);
        rgb2[2] = (int) (rgb[2] * (1 - tint.tint) + tint.b * tint.tint);
        if (rgb2[0] > 255)
            rgb2[0] = 255;
        if (rgb2[1] > 255)
            rgb2[1] = 255;
        if (rgb2[2] > 255)
            rgb2[2] = 255;

    }

    /**
     * ColorShiftPalette - lifted from dcolors.c Operates on RGB888 palettes in
     * separate bytes. at shift = 0, the colors are normal at shift = steps, the
     * colors are all the given rgb
     */

    public static final void ColorShiftPalette(byte[] inpal, byte[] outpal,
            int r, int g, int b, int shift, int steps) {
        int i;
        int dr, dg, db;
        int in_p, out_p;

        in_p = 0;
        out_p = 0;

        for (i = 0; i < 256; i++) {
            dr = r - inpal[in_p + 0];
            dg = g - inpal[in_p + 1];
            db = b - inpal[in_p + 2];

            outpal[out_p + 0] = (byte) (inpal[in_p + 0] + dr * shift / steps);
            outpal[out_p + 1] = (byte) (inpal[in_p + 1] + dg * shift / steps);
            outpal[out_p + 2] = (byte) (inpal[in_p + 2] + db * shift / steps);

            in_p += 3;
            out_p += 3;
        }
    }
  
    /** Get ARGB_8888 from RGB_555, with proper higher-bit
     *  replication.
     * 
     * @param rgb
     * @return
     */
    
    protected static final int rgb555to888(short rgb) {
        int ri, gi, bi;
        int bits;

        // .... .... .... ....
        // 111 11 = 7C00
        // 11 111 = 03E0
        // 1F= 1 1111
        ri = (0x7C00 & rgb) >> 7;
        gi = (0x03E0 & rgb) >> 2;
        bi = (0x001F & rgb) << 3;

        // replicate 3 higher bits
        bits = (ri & 0xE0) >> 5;
        ri = ri + bits;

        bits = (gi & 0xE0) >> 5;
        gi = gi + bits;

        bits = (bi & 0xE0) >> 5;
        bi = bi + bits;

        // ARGB 8888 packed
        
        return 0xFF000000+(ri << 16) + (gi << 8) + (bi);
    }

    protected static final short rgb4444to555(short rgb) {
        int ri, gi, bi;
        int bits;

        // .... .... .... ....
        // 1111

        ri = (0xF000 & rgb) >> 11;
        gi = (0x0F00 & rgb) >> 7;
        bi = (0x00F0 & rgb) >> 3;

        bits = (ri & 0x10) >> 4;
        ri = ri + bits;

        bits = (gi & 0x10) >> 4;
        gi = gi + bits;

        bits = (bi & 0x10) >> 4;
        bi = bi + bits;

        // RGBA 555 packed for NeXT

        return (short) ((ri << 10) + (gi << 5) + (bi));
    }

    /** Get RGB_555 from packed ARGB_8888.
     * 
     * @param argb
     * @return
     */
    
    protected static final short rgb888to555(int rgb) {
        int ri, gi, bi;

        ri = (0xFF0000 & rgb) >> 19;
        gi = (0x00FF00 & rgb) >> 11;
        bi = (0x0000FF & rgb) >> 3;

        return (short) ((ri << 10) + (gi << 5) + (bi));
    }

    /** Get packed RGB_555 word from individual 8-bit RGB components. 
     * 
     *  WARNING: there's no sanity/overflow check for performance reasons.
     * 
     * @param r
     * @param g
     * @param b
     * @return
     */
    
    protected static final short rgb888to555(int r,int g,int b) {

        return (short) (((r>>3) << 10) + ((g>>3) << 5) + (b>>3));
    }
    
    /** Get red from packed argb long word.
     * 
     * @param argb
     * @return
     */
    protected final static int getRed(int argb) {
        return (0xFF0000 & argb) >> 16;
    }

    /** Get green from packed argb long word.
     * 
     * @param argb
     * @return
     */
    
    protected final static int getGreen(int argb) {
        return (0x00FF00 & argb) >> 8;
    }

    /** Get blue from packed argb long word.
     * 
     * @param argb
     * @return
     */
    
    protected final static int getBlue(int argb) {
        return (0x0000FF & argb);
    }

    protected final static int getARGB(int r,int g, int b){
        return 0xFF000000+(r << 16) + (g << 8) + (b);
    }
    
    protected static final short getRGB555(int red,int green,int blue){
        int ri,gi,bi;
        
        ri = (((red+4)>255?255:red+4))>>3;
        ri = ri > 31 ? 31 : ri;
        gi = (((green+4)>255?255:green+4))>>3;
        gi = gi > 31 ? 31 : gi;
        bi = (((blue+4)>255?255:blue+4))>>3;
        bi = bi > 31 ? 31 : bi;

        // RGB555 for HiColor
        return (short) ((ri<<10) + (gi<<5) + bi);
    }
    
    protected static final short getRGB555(int rgb){
        return getRGB555(getRed(rgb),getGreen(rgb),getBlue(rgb));
    }
    
    /**RF_BuildLights lifted from dcolors.c
     * 
     * Used to compute extended-color colormaps even in absence of the
     * COLORS15 lump. Must be recomputed if gamma levels change, since 
     * they actually modify the RGB envelopes.
     * 
     * @author John Carmack
     * @author Velktron
     * @param palette A packed ARGB 256-entry int palette, eventually tinted.
     * @param NUMLIGHTS Number of light levels to synth. Usually 32.
     */

    public static final short[][] RF_BuildLights15 (int[] palette,int NUMLIGHTS)
    {
        int     l,c;
        int     red,green,blue;
        short[][] stuff=new short[NUMLIGHTS+1][256];

        for (l=0;l<NUMLIGHTS;l++)
        {
            for (c=0;c<256;c++)
            {
                red = getRed(palette[c]);
                green = getGreen(palette[c]);
                blue = getBlue(palette[c]);

                red = (red*(NUMLIGHTS-l)+NUMLIGHTS/2)/NUMLIGHTS;
                green = (green*(NUMLIGHTS-l)+NUMLIGHTS/2)/NUMLIGHTS;
                blue = (blue*(NUMLIGHTS-l)+NUMLIGHTS/2)/NUMLIGHTS;

                // RGB555 for HiColor
                stuff[l][c] = getRGB555(red,green,blue);
            }
        }
        
        // Build special map for invulnerability
        BuildSpecials15(stuff[NUMLIGHTS],palette); 
        
        return stuff;
    }
    
    private static final void BuildSpecials15 (short[] stuff, int[] palette)
    {
        int     c,gray,best;
        int   red, green, blue;;

        for (c=0;c<256;c++)
        {
            red = getRed(palette[c]);
            green = getGreen(palette[c]);
            blue = getBlue(palette[c]);

            gray = (int) (255*(1.0-((float)red*0.299/256.0 + 
            						(float)green*0.587/256.0 +
            						(float)blue*0.114/256.0)));            
            
            // We are not done. Because of the grayscaling, the all-white cmap
            
            best=palette[BestColor(gray,gray,gray,palette,0,255)];
            stuff[c] = getRGB555(best);
            
        }
        

        // will lack tinting.
        
        
    }
    
    private static final void BuildSpecials24 (int[] stuff, int[] palette)
    {
        int     c,gray,best;
        int   red, green, blue;;

        for (c=0;c<256;c++)
        {
            red = getRed(palette[c]);
            green = getGreen(palette[c]);
            blue = getBlue(palette[c]);

            gray = (int) (255*(1.0-((float)red*0.299/256.0 + 
                                    (float)green*0.587/256.0 +
                                    (float)blue*0.114/256.0)));            
            
            // We are not done. Because of the grayscaling, the all-white cmap
            
            //best=palette[BestColor(gray,gray,gray,palette,0,255)];
            stuff[c] = new Color(gray,gray,gray).getRGB();
            
        }
        

        // will lack tinting.
        
        
    }
    
    public static final int BestColor (int r, int g, int b, int[] palette, int rangel, int rangeh)
	{
		int	i;
		long	dr, dg, db;
		long	bestdistortion, distortion;
		int	bestcolor;
		int	pal;

	//
	// let any color go to 0 as a last resort
	//
		bestdistortion = ( (long)r*r + (long)g*g + (long)b*b )*2;
		bestcolor = 0;

		for (i=rangel ; i<= rangeh ; i++)
		{
			dr = r - getRed(palette[i]);
			dg = g - getGreen(palette[i]);
			db = b - getBlue(palette[i]);
			distortion = dr*dr + dg*dg + db*db;
			if (distortion < bestdistortion)
			{
				if (distortion==0)
					return i;		// perfect match

				bestdistortion = distortion;
				bestcolor = i;
			}
		}

		return bestcolor;
	}
    
    /** Variation that produces true-color lightmaps
     * 
     * @author John Carmack
     * @param palette A packed ARGB 256-entry int palette, eventually tinted.
     * @param NUMLIGHTS Number of light levels to synth. Usually 32.
     */

    public static final int[][] RF_BuildLights24 (int[] palette,int NUMLIGHTS)
    {
        int     l,c;
        int     red,green,blue;
        int[][] stuff=new int[NUMLIGHTS+1][256];

        for (l=0;l<NUMLIGHTS;l++)
        {
            for (c=0;c<256;c++)
            {
                red = getRed(palette[c]);
                green = getGreen(palette[c]);
                blue = getBlue(palette[c]);

                red = (red*(NUMLIGHTS-l)+NUMLIGHTS/2)/NUMLIGHTS;
                green = (green*(NUMLIGHTS-l)+NUMLIGHTS/2)/NUMLIGHTS;
                blue = (blue*(NUMLIGHTS-l)+NUMLIGHTS/2)/NUMLIGHTS;

                // Full-quality truecolor.
                stuff[l][c] = new Color(red,green,blue).getRGB();
            }
        }
        
        BuildSpecials24(stuff[NUMLIGHTS],palette); 
        
        return stuff;
    }
    
    public static final int[][] BuildLights24 (int[] palette,int NUMLIGHTS)
    {
        int     l,c;
        int     red,green,blue;
        int[][] stuff=new int[NUMLIGHTS+1][256];

        for (l=0;l<NUMLIGHTS;l++)
        {
            for (c=0;c<256;c++)
            {
                red = getRed(palette[c]);
                green = getGreen(palette[c]);
                blue = getBlue(palette[c]);

                red = (red*(NUMLIGHTS-l)+NUMLIGHTS/2)/NUMLIGHTS;
                green = (green*(NUMLIGHTS-l)+NUMLIGHTS/2)/NUMLIGHTS;
                blue = (blue*(NUMLIGHTS-l)+NUMLIGHTS/2)/NUMLIGHTS;

                // Full-quality truecolor.
                stuff[l][c] = new Color(red,green,blue).getRGB();
            }
        }
        
        BuildSpecials24(stuff[NUMLIGHTS],palette); 
        
        return stuff;
    }
    
    static {
        for (int i = 0; i < 256; i++) {
            greypal[3 * i] = i;
            greypal[3 * i + 1] = i;
            greypal[3 * i + 2] = i;
        }

    }
    
}

package v;

public interface IVideoScaleAware {    
    
    
    /** Set the video scale for a certain object. Setting
     * does NOT (re)initialize an object yet. This is only done
     * by calling the init() method at a safe moment.
     * 
     * @param vs
     */
    public void setVideoScale(IVideoScale vs);
    
    /** Initialize an object according to the current video scale
     * settings. This should adapt multipliers, static constants,
     * etc. and should be set before the object is first used
     * or after a dynamic (if ever implemented) resolution change.
     * 
     * The proposed method is to initialize everything en-bloc
     * before entering the display loop, and after initializing
     * 
     */    
    public void initScaling();

}

package v;

public class ColorTint {
    public ColorTint(int r, int g, int b, float tint) {
        super();
        this.r = r;
        this.g = g;
        this.b = b;
        this.tint = tint;
    }

    public int r, g, b;

    public float tint;

    public static final ColorTint[] tints = { new ColorTint(0, 0, 0, .0f), // 0
                                                                           // Normal
            new ColorTint(255, 2, 3, 0.11f), // 1 Unused. 11% red tint of
                                             // RGB(252, 2, 3).
            new ColorTint(255, 0, 0, 0.22f), // 2
            new ColorTint(255, 0, 0, 0.33f), // 3
            new ColorTint(255, 0, 0, 0.44f), // 4
            new ColorTint(255, 0, 0, 0.55f), // 5
            new ColorTint(255, 0, 0, 0.66f), // 6
            new ColorTint(255, 0, 0, 0.77f), // 7
            new ColorTint(255, 0, 0, 0.88f), // 8
            new ColorTint(215, 185, 68, 0.12f), // 9
            new ColorTint(215, 185, 68, 0.25f), // 10
            new ColorTint(215, 185, 68, 0.375f), // 11
            new ColorTint(215, 185, 68, 0.50f), // 12
            new ColorTint(3, 253, 3, 0.125f) // 13

        };
}
package v;

import static data.Defines.RANGECHECK;

import i.DoomStatusAware;
import i.IDoomSystem;

import java.awt.Image;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferByte;
import java.awt.image.IndexColorModel;
import java.io.File;
import java.io.IOException;

import javax.imageio.ImageIO;

import doom.DoomStatus;

import m.BBox;

import rr.column_t;
import rr.patch_t;
import utils.C2JUtils;

public abstract class SoftwareVideoRenderer8 extends SoftwareVideoRenderer<byte[],byte[]> {

    public Image getCurrentScreen(){
    	return currentscreen;
    }

    public SoftwareVideoRenderer8(){
        super();
        screens=new byte[5][];
    }

    public SoftwareVideoRenderer8(int w,int h){
        super(w,h);
        screens=new byte[5][];
    }
   
  /** V_DrawPatch
   * Masks a column based masked pic to the screen. 
   *  desttop, dest and source were byte*
   */ 

  public final void DrawPatch
  ( int       x,
  int     y,
  int     scrn,
  patch_t patch ) 
  { 

   column_t   column; 
   int    desttop;
   final  byte[] dest=screens[scrn];
   int        w; 
       
   y -= patch.topoffset; 
   x -= patch.leftoffset; 
  if (RANGECHECK) 
   if (doRangeCheck(x,y,patch,scrn))
   {
     System.err.print("Patch at "+x+","+y+" exceeds LFB\n");
     // No I_Error abort - what is up with TNT.WAD?
     System.err.print("V_DrawPatch: bad patch (ignored)\n");
     return;
   }

   if (scrn==0)
      this.MarkRect (x, y, patch.width, patch.height); 

       
   w = patch.width; 
   desttop = x+this.width*y; 
   // For each column..
   int destPos;
   int ptr=0;
   for (int col=0 ; col<w ; desttop++, col++,x++)
   { 
      // This points at a "column" object.     
      //column = (column_t *)((byte *)patch + LONG(patch->columnofs[col])); 
      column=patch.columns[col];
      // For each post...
      for (int i=0;i<column.posts;i++){
          // Get pointer to post offset.
          ptr=column.postofs[i];
          // Get post delta
          short delta=column.postdeltas[i];
          // We skip delta, len and padding.
          // ptr+=3; NOT NEEDED< pre-skipped at parsing. 
          
          // Skip transparent rows...
          if (delta==0xFF) break;

          destPos = desttop + delta*this.width;  
          
          // These lengths are already correct.
          for (int j=0;j<column.postlen[i];j++){
                 dest[destPos] = column.data[ptr++];
                 destPos += this.width;
          }
      }
   }
      
      
  }

  /** V_DrawPatchSolidScaled
   * Draws a SOLID (non-masked) patch to the screen with integer scaling
   * m and n.
   * Useful for stuff such as help screens, titlepic and status bar. Not 
   * very useful for menus, though.
   * desttop, dest and source were byte
   */ 

  public final void DrawPatchSolidScaled
  ( int       x,
  int     y,int m, int n,
  int     scrn,
  patch_t patch ) 
  { 

   if ((m==1)&&(n==1)){
       DrawPatch(x,y,scrn,patch);
       return;
   }
      
   column_t   column; 
   int    desttop;
   final byte[] dest=screens[scrn];
   int        w; 
       
   y =y*n- patch.topoffset; 
   x =x*m- patch.leftoffset; 
  if (RANGECHECK) 
   if (doRangeCheck(x,y,patch,scrn))
   {
     System.err.print("Patch at "+x+","+y+" exceeds LFB\n");
     // No I_Error abort - what is up with TNT.WAD?
     System.err.print("V_DrawPatch: bad patch (ignored)\n");
     return;
   }

   if (scrn==0)
      this.MarkRect (x, y, patch.width, patch.height); 

       
   w = patch.width; 
   desttop = m*x+this.width*y; 
   // For each column..
   int destPos;
   int ptr=0;
   // x increases by m.
   
   // Some unrolling...
   
   if (m==2) {
       //desttop=2*desttop;
       for (int col=0 ; col<w ; desttop+=2, col++)

       { 

           // This points at a "column" object.     
           //column = (column_t *)((byte *)patch + LONG(patch->columnofs[col])); 
           column=patch.columns[col];
           // For each post...
           for (int i=0;i<column.posts;i++){
               // Get pointer to post offset.
               ptr=column.postofs[i];
               // Get post delta
               short delta=column.postdeltas[i];
               // We skip delta, len and padding.
               //ptr+=3; 

               // Skip transparent rows...
               if (delta==0xFF) break;

               destPos = desttop+ n*delta*this.width;  

               // These lengths are already correct.
               for (int j=0;j<column.postlen[i];j++){
                   dest[destPos] = column.data[ptr++];
                   dest[destPos+1] = dest[destPos];
                   destPos += n*this.width;
               }
           }
       }
   } else  if (m==3) {
      // desttop=3*desttop;
       for (int col=0 ; col<w ; desttop+=3, col++)

       { 

           // This points at a "column" object.     
           //column = (column_t *)((byte *)patch + LONG(patch->columnofs[col])); 
           column=patch.columns[col];
           // For each post...
           for (int i=0;i<column.posts;i++){
               // Get pointer to post offset.
               ptr=column.postofs[i];
               // Get post delta
               short delta=column.postdeltas[i];
               // We skip delta, len and padding.
               //ptr+=3; 

               // Skip transparent rows...
               if (delta==0xFF) break;

               destPos = desttop + n*delta*this.width;  

               // These lengths are already correct.
               for (int j=0;j<column.postlen[i];j++){
                   dest[destPos] = column.data[ptr++];
                   dest[destPos+1] = dest[destPos];
                   dest[destPos+2] = dest[destPos];
                   destPos += n*this.width;    
               }
               
           }
       }
   }else  if (m==4) {
           //desttop=4*desttop;
           for (int col=0 ; col<w ; desttop+=4, col++)

           { 

               // This points at a "column" object.     
               //column = (column_t *)((byte *)patch + LONG(patch->columnofs[col])); 
               column=patch.columns[col];
               // For each post...
               for (int i=0;i<column.posts;i++){
                   // Get pointer to post offset.
                   ptr=column.postofs[i];
                   // Get post delta
                   short delta=column.postdeltas[i];
                   // We skip delta, len and padding.
                   //ptr+=3; 

                   // Skip transparent rows...
                   if (delta==0xFF) break;

                   destPos = desttop + n*delta*this.width;  

                   // These lengths are already correct.
                   for (int j=0;j<column.postlen[i];j++){
                       dest[destPos] = column.data[ptr++];
                       dest[destPos+1] = dest[destPos];
                       dest[destPos+2] = dest[destPos];
                       dest[destPos+3] = dest[destPos];
                       destPos += n*this.width;
                   }
               }
           }
       }
           else  {
              // desttop=m*desttop;
               for (int col=0 ; col<w ; desttop+=m, col++)

               { 

                   // This points at a "column" object.     
                   //column = (column_t *)((byte *)patch + LONG(patch->columnofs[col])); 
                   column=patch.columns[col];
                   // For each post...
                   for (int i=0;i<column.posts;i++){
                       // Get pointer to post offset.
                       ptr=column.postofs[i];
                       // Get post delta
                       short delta=column.postdeltas[i];
                       // We skip delta, len and padding.
                       //ptr+=3; 

                       // Skip transparent rows...
                       if (delta==0xFF) break;

                       destPos = desttop + n*delta*this.width;  

                       // These lengths are already correct.
                       for (int j=0;j<column.postlen[i];j++){
                           for (int k=0;k<m;k++)
                           dest[destPos+k] = column.data[ptr];
                           ptr++;
                           destPos += n*this.width;
                       }
                   }
               }
       
       
   }
   
   
    scaleSolid(m,n, scrn,m*patch.width);  
  }
  

  /** Pretty crude in-place scaling. It's fast, but only works full-screen 
   * Width needs to be specific, height is implied.
   *  */
  
  protected final void scaleSolid(int m, int n, int screen,int width){
      int height=screens[screen].length/width;
      for (int i=0;i<height;i+=n){
           
          for (int j=0;j<n-1;j++){

              System.arraycopy(screens[screen], (i+j)*width, screens[screen],(i+j+1)*width,width);
          }
      }
      
  }

  public void DrawPatchSolidScaled ( int x, int y,  int scrn, IVideoScale vs, patch_t patch ){
      this.DrawPatchSolidScaled(x, y, vs.getScalingX(), vs.getScalingY(),scrn, patch);
  }
  
  /**
   * V_DrawPatchFlipped 
   * Masks a column based masked pic to the screen.
   * Flips horizontally, e.g. to mirror face.
   * 
   * Currently UNUSED, as any occurence to it is also scaled and
   * best served by another function.
   * 
   * 
   */

  public final void DrawPatchFlipped ( int      x,   int        y,    int       scrn,  patch_t  patch ) 
  { 

      column_t    column; 
      int desttop;
      final byte[]  dest=screens[scrn];
      int     w; 
       
      y -= patch.topoffset; 
      x -= patch.leftoffset;
      

      if (RANGECHECK) 
          if (doRangeCheck(x,y,patch,scrn))
          {
      {
          System.err.print("Patch origin "+x+","+y +" exceeds LFB\n" );
          // No I_Error abort - what is up with TNT.WAD?
          I.Error("Bad V_DrawPatch in V_DrawPatchFlipped");
          }
      }
   
      if (scrn==0)
          this.MarkRect (x, y, patch.width, patch.height); 

   // Set x and y coords inside dest array.
    
        
      w = patch.width; 
      desttop = y*this.width+x;     
   // For each column..
      for (int col=0 ; col<w ; desttop++,col++)
      {         

          // This points at a "column" object.     
         // Notice the flipping on this one.
         // column = (column_t *)((byte *)patch + LONG(patch->columnofs[w-1-col]));  
         column=patch.columns[w-1-col];
         
         // For each post...
         //System.out.println("Column"+(w-1-col));
         for (int i=0;i<column.posts;i++){
             // Get pointer to post offset.
             int ptr=column.postofs[i];
             // Get post delta
             int delta=0xFF&column.data[ptr];
             // We skip delta, len and padding.
             //ptr+=3;
             if (delta==0xFF) break;
             int destPos = desttop+delta*this.width;
             //count = column.length; 

             // These lengths are already correct.
             for (int j=0;j<column.postlen[i];j++,   destPos += this.width){
                    dest[destPos] = column.data[ptr+j];
                    // next line
                  
             }
         }
      }
      
  } 
   
/**
 * V_DrawScaledPatch
 * like V_DrawPatch, but scaled 2,3,4 times the original size and position
 * this is used for menu and title screens, with high resolutions
 * 
 *added:05-02-98:
 *default params : scale patch and scale start
 *
 * Iniially implemented for Mocha Doom by _D_ (shamelessly ripped from 
 * Eternity Engine ;-), adapted to scale based on a scaling info object (VSI).
 * 
 * Unless overriden by flags, starting x and y are automatically
 * scaled (implied V_SCALESTART)
 *
 */
@Override
public void DrawScaledPatch(int x, int y, int scrn, IVideoScale VSI, patch_t patch)
{
    int col;
    column_t column;
    int desttop;
    final byte[] dest = screens[scrn&0xFF];
    //byte[] source;

    int dupx, dupy;
    int colfrac, rowfrac;

   // System.out.printf("V_DrawScaledPatch %d %d \n",x,y);
    // draw an hardware converted patch
    /*#ifdef HWRENDER
  if (rendermode != render_soft)
  {
      HWR_DrawPatch((GlidePatch_t *) patch, x, y, scrn);
      return;
  }
#endif*/

    // A very common operation, eliminates the need to pre-divide.
    if (C2JUtils.flags(scrn,V_PREDIVIDE)){
        x/=vs.getScalingX();
        y/=vs.getScalingY();
    }
    
    if (C2JUtils.flags(scrn, V_NOSCALEPATCH))
        dupx = dupy = 1;
    else
    {
        dupx = VSI.getScalingX();
        dupy = VSI.getScalingY();
    }

    // Eliminates.
    // MAES: added this fix so that non-zero patch offsets can be 
    // taken into account, regardless of whether we use pre-scaled
    // coords or not. Only Doomguy's face needs this hack for now.
    
    if (C2JUtils.flags(scrn, V_SCALEOFFSET)) {
    y -= patch.topoffset*dupx;
    x -= patch.leftoffset*dupy;
    } else {
        y -= patch.topoffset;
        x -= patch.leftoffset;
    }
    

    colfrac = dupx;
    rowfrac = dupy;

    //desttop = screens[scrn & 0xFF];
    if (C2JUtils.flags(scrn, V_NOSCALESTART))
        desttop = (y * this.width) + x;
    else
        desttop = (y * dupy * this.width) + (x * dupx) /*+ scaledofs*/;
    //destend = desttop + /*SHORT(*/patch.width/*)*/ * dupx;

    int w = patch.width*dupx;

    int colInc = 1;
    col = 0;
    if (C2JUtils.flags(scrn, V_FLIPPEDPATCH))
    {
        colInc = -1;
        col = w-1;//(/*SHORT(*/patch.width/*)*/ << fixed_t.FRACBITS) + colfrac;
    }
    
    for (; col >= 0 && col<w/*; desttop < destend*/; col += colInc, desttop++)
    {
        //column = (column_t *) ((byte *) patch + LONG(patch.columnofs[col >> FRACBITS]));
        column=patch.columns[col/colfrac];

        int destPos;
        int ptr = 0;
        int ptrOfs;

        //while (column.topdelta != 0xff)
        for (int i=0;i<column.posts;i++){
            {
                ptrOfs = column.postofs[i];//+3;
                ptr = 0;
                short delta = column.postdeltas[i];

                // Skip transparent rows...
                if (delta==0xFF) break;

                destPos = desttop + delta*dupy*this.width;  
                //dest = desttop + column.topdelta * dupy * this.width;

                //ofs = 0;
                //while (count-- > 0)
                for (int j=0;j<column.postlen[i]*dupy;j++)
                {
                    dest[destPos] = column.data[ptrOfs+ptr/rowfrac];
                    destPos += this.width;

                    ptr++;
                    //ofs += rowfrac;
                }

                //column = (column_t *) ((byte *) column + column.length + 4);
            }
        }
    }
}

  @Override
  public final void
  DrawBlock
  ( int       x,
    int       y,
    int       scrn,
    int       width,
    int       height,
    byte[]        src ) 
  { 
      // This is "screens[scrn]"
	  final  byte[]  dest=screens[scrn];  
       
  if (doRangeCheck(x, y, scrn))
      {
      I.Error("Bad V_DrawBlock");
      }

   
      this.MarkRect(x, y, width, height); 
   
      int destPos = /*screens[scrn] +*/ y*this.width +x;
      // MAES: making an assumption here. A BIIIIG one.
      int srcPos=0;
      while ((height--)>0) 
      { 
          //  memcpy (dest, src, width);
      System.arraycopy(src, srcPos, dest, destPos, width);
      srcPos += width; 
      destPos += this.width; 
      } 
  }
  
  @Override
  public final void DrawBlock(int x, int y, int scrn, int width, int height,
          byte[] src,int offset) {
      // This is "screens[scrn]"
      final byte[] dest = screens[scrn];
      
      if (doRangeCheck(x, y, scrn)) {
          I.Error("Bad V_DrawBlock");
      }

      this.MarkRect(x, y, width, height);

      int destPos = /* screens[scrn] + */y * this.width + x;
      // MAES: making an assumption here. A BIIIIG one.
      int srcPos = offset;
      while ((height--) > 0) {
              System.arraycopy(src, srcPos, dest, destPos, width);
          srcPos += width;
          destPos += this.width;
      }
  }

   

  /**
   * V_GetBlock
   * Gets a linear block of pixels from the view buffer.
   */


  public final void
  GetBlock
  ( int       x,
    int       y,
    int       scrn,
    int       width,
    int       height,
    byte[]        dest ) 
  { 
	  final byte[]  src=screens[scrn]; 
       
  if (RANGECHECK){
      if (doRangeCheck(x,y,scrn)){    
      I.Error ("Bad V_DrawBlock");
      }
      
   }
      int srcPos = y*this.width+x;
      int destPos=0;

      while ((height--)>0) 
      { 
      System.arraycopy(src, srcPos, dest, destPos, width);
      //memcpy (dest, src, width); 
      srcPos += width; 
      destPos += this.width; 
      } 
  }
  
  
  /** Replaces DrawPatchCol for bunny scrolled in Finale.
   * 
   * 
   */
  
  @Override
  public final void
  DrawPatchColScaled
  ( int       x, patch_t  patch,int col, 
          IVideoScale vs, int       screen )
  {
      column_t   column;
      int   source;
      final byte[]   dest;
      int   desttop;
      final int scale=vs.getScalingX();
      
      column = patch.columns[col];
      desttop = x*scale; // Scale X position.
      dest=screens[screen];
      // step through the posts in a column
      
      
      for (int i=0;i<column.posts;i++){
          // Get pointer to post offset.
          source=column.postofs[i];
          // Get post delta
          short delta=column.postdeltas[i];
          // We skip delta, len and padding.
          //source+=3;  NOT NEEDED ANYMORE, skipped by parsing.
          
          // Skip transparent rows...
          if (delta==0xFF) break;

            
          
          // Replicate each column scale times vertically,
          // with spaced pixels.
          final int startsource=source;
          for (int kl=0;kl<scale;kl++){
              int destPos = desttop + (delta+kl)*SCREENWIDTH;
          for (int j=0;j<column.postlen[i];j++){
              final byte data=column.data[source++];
              // replicate each column's pixel horizontally and vertically.
                  for (int k=0;k<scale;k++)
                  dest[destPos+k] =data; 
                 destPos += scale*SCREENWIDTH;
              }
          source=startsource;
          }
      }
  }

  public void setScreen(int index, int width, int height){
      this.screens[index]=new byte[width*height];
  }
  
  public final void takeScreenShot(int screen, String imagefile, IndexColorModel icm) throws IOException {
      
      BufferedImage b=new BufferedImage(this.getWidth(),this.getHeight(), BufferedImage.TYPE_BYTE_INDEXED, icm);
      
      
      int[] tmp=new int[this.screens[screen].length];
      for (int i=0;i<this.screens[screen].length;i++){
          tmp[i]=this.screens[screen][i];
      }
      
      b.getRaster().setPixels(0, 0, this.getWidth(),this.getHeight(), tmp);
      
      File outputFile =
          new File(
              imagefile+".png");
      ImageIO.write(b, "PNG", outputFile);
      
  }
  
  /*
  public final boolean isRasterNull(int screen){
      for (int i=0;i<screens[screen].length;i++){
          if (screens[screen][i]!=0) return false;
      }
      return true;
  } */
  
  public void setCurrentScreen(int screen){
	  this.usescreen=screen;
  }
  
  public void update(){
	// Override only if there's something else to be done, e.g. map palette to truecolor buffer  
  }
  
  public void report(BufferedImage[] b){
	    System.out.println("Main video buffer "+screens[0]);
	    for (int i=0;i<b.length;i++){
	    System.out.println(((Object)b[i].getRaster()).toString()+" "+b[i].getRaster().hashCode()+" "+((DataBufferByte)(b[i].getRaster().getDataBuffer())).getData());
	    }
	}
  
/** Internal method for setting up palettes (and gamma tables)
 * 
 */

public void createPalettes(byte[] paldata, short[][] gammadata, final int palettes, final int colors, final int stride,final int gammalevels){
	
	// Sanity check on supplied data length. If there is not enough data to create the specified palettes,
	// their number will be limited.
	
	if (paldata!=null) 	// As many as are likely contained
		maxpalettes=paldata.length/(colors*stride);
	else
		maxpalettes=0; // Do some default action on null palette.

	if (gammadata!=null) 	// As many as are likely contained
		maxgammas=gammadata.length;
	else
		maxgammas=0; // Do some default action on null gamma tables.
	
	if (maxgammas==0){
		gammadata=GammaTables.gammatables;
		maxgammas=GammaTables.gammatables.length;
	}
	

	// Enough data for all palettes. 
	// Enough data for all palettes. 
	if (maxpalettes>0 && maxgammas>0)
			specificPaletteCreation(paldata,gammadata,palettes,colors,stride,gammalevels);
		 else 
			 paletteRecovery();
    	  
      }
    
/** Override this in extending classes to perform specific actions depending on the
 *  type of renderer. It's better not to assign a default action, nor make assumptions
 *  on the underlying types of actual palettes
 * 
 * @param paldata
 * @param gammadata
 * @param palettes
 * @param colors
 * @param stride
 * @param gammalevels
 */

protected abstract void specificPaletteCreation(byte[] paldata,
		short[][] gammadata, 
		final int palettes, 
		final int colors,
		final int stride,
		final int gammalevels);

protected int lastcolor=-1;
protected byte[] scanline;

/**
 * Clear automap frame buffer or fi
 * MAES: optimized for efficiency, seen the lack of a proper "memset" in Java.
 * 
 */

@Override
public final void FillRect(int color,int screen, int x,int y,int width, int height) {
    
    if  (RANGECHECK) {
        if (x<0
                ||x+width >this.width
                || y<0
                || y+height>SCREENHEIGHT 
                || screen>4)
        {
            I.Error ("Bad V_FillRect");
        }
    }
    
    byte[] arr=screens[screen];
    
    // Do a "per scanline" copy. 
    int fromIndex=x+y*SCREENWIDTH;
    int toIndex=x+(y+height-1)*SCREENWIDTH;
    
    // First scanline.
    for (int i=0;i<width;i++)
    	arr[fromIndex+i]=(byte) color;
    
    for (;fromIndex<toIndex;fromIndex+=SCREENWIDTH){
    		System.arraycopy(arr,fromIndex,arr,fromIndex+SCREENWIDTH,width);
        }
}

/**
 * V_Fillrect
 */
@Override
public void FillRect(int srcx, int srcy, int width, int height, int destscrn) {
    // These are pointers inside an array.
    final byte[] dest = screens[destscrn];

    if (RANGECHECK) {
        if (srcx < 0 || srcx + width > this.width || srcy < 0
                || srcy + height > SCREENHEIGHT || destscrn > 4) {
            I.Error("Bad V_FillRect");
        }
    }
    this.MarkRect(srcx, srcy, width, height);

    // MAES: these were pointers to a specific position inside the screen.
    int srcPos = this.width * srcy + srcx;

    for (; height > 0; height--) {
        for (int i = 0; i < width; i++) {
            dest[srcPos + i] = 0;
        }
        // memcpy (dest, src, width);
        srcPos += this.width;
    }

}

/** Should return colormaps, if you ever move their management in here.
 * 
 */

public final byte[][] getColorMaps(){
    return null;
}


}

package v;

import java.awt.image.BufferedImage;
import java.awt.image.DataBufferByte;
import java.awt.image.DataBufferUShort;
import static rr.LightsAndColors.LIGHTLEVELS;
import m.BBox;

public class BufferedRenderer16 extends SoftwareVideoRenderer16 {
	
static final String rcsid = "$Id: BufferedRenderer16.java,v 1.5 2016/07/04 07:52:26 velktron Exp $";

/** Buffered Renderer has a bunch of images "pegged" to the underlying arrays */

public BufferedImage[] screenbuffer=new BufferedImage[5];

public BufferedRenderer16(int w, int h) {
    super(w,h);
}

@Override
public final void Init () 
{ 
 int		i;
 for (i=0 ; i<4 ; i++){
	//screens[i] = new byte[this.getHeight()*this.getWidth()];
     this.setScreen(i, this.width, this.height);     
	}
     dirtybox=new BBox();
}

/** This implementation will "tie" a bufferedimage to the underlying byte raster.
 * 
 * NOTE: this relies on the ability to "tap" into a BufferedImage's backing array,
 * in order to have fast writes without setpixel/getpixel. If that is not possible,
 * then we'll need to use a special renderer.
 * 
 */
@Override
public final void setScreen(int index, int width, int height){

	if (screens[index]==null){
		screenbuffer[index]=new BufferedImage(width,height,BufferedImage.TYPE_USHORT_555_RGB);
    
    	screens[index]=((DataBufferUShort)screenbuffer[index].getRaster().getDataBuffer()).getData();
		}
}

public void setPalette(int palette){
    this.usepalette=palette%maxpalettes;

    // Invalidate cached graphics, otherwise older colormaps
    // will persist.
    this.clearCaches();

    
    // Tint the current set of colormaps.
    getCachedCmap(palette);

    
	//this.currentpal=palette%maxpalettes;
	this.currentscreen=this.screenbuffer[0];
	
}

@Override
public void setUsegamma(int gamma) {
	this.usegamma=gamma%maxgammas;

   // Invalidate palette cache.
    super.clearPalettes();

	
	// Re-synthesize current palette.
	setPalette(usepalette);
}

public void setCurrentScreen(int screen){
	  super.setCurrentScreen(screen);
	  this.currentscreen=this.screenbuffer[0];
}

@Override
protected final void specificPaletteCreation(byte[] paldata,
        short[][] gammadata, 
        final int palettes, 
        final int colors,
        final int stride,
        final int gammalevels){

      System.out.printf("Enough data for %d palettes",maxpalettes);
      System.out.printf("Enough data for %d gamma levels",maxgammas);
      
      //this.palettes=new int[maxpalettes*maxgammas][];
      this.palettes=new int[maxpalettes][];
      
      // Apply gammas a-posteriori, not a-priori.
      // Initial palette can be neutral or based upon "gamma 0",
      // which is actually a bit biased and distorted
      
      for (int z=0;z<1;z++){
          
          // For each palette
          for (int y=0;y<maxpalettes;y++){
              this.palettes[z*maxpalettes+y]=new int[colors];
              
              for (int x=0;x<colors;x++){
                  int r=gammadata[z][0xFF&paldata[y*colors*stride+stride*x]]; // R
                  int g=gammadata[z][0xFF&paldata[1+y*colors*stride+stride*x]]; // G
                  int b=gammadata[z][0xFF&paldata[2+y*colors*stride+stride*x]]; // B
                  int color=0xFF000000|r<<16|g<<8|b;
                  this.palettes[z*maxpalettes+y][x]=color;
                }
            }
      }
      
      // Set base colormap
      
      cmap_base=PaletteGenerator.RF_BuildLights15(this.palettes[0], NUMLIGHTS);
      cmap_work=PaletteGenerator.RF_BuildLights15(this.palettes[0], NUMLIGHTS);

}

public int getBaseColor(int color){
	return cmap_work[0][color];
}

}

package v;

import static data.Defines.RANGECHECK;

import java.awt.image.BufferedImage;
import java.awt.image.DataBufferInt;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

import rr.column_t;
import rr.patch_t;
import m.BBox;

/** N.B. this renderer just uses 8-bit resources and 256 color, but renders directly onto a 
 *  24-bit canvas, so technically it should be a byte[],byte[] renderer. It's not a "true color" 
 *  renderer in the sense of using extended colormaps etc. 
 * 
 * @author velktron
 *
 */

public class ParallelTrueColorRenderer extends SoftwareVideoRenderer<byte[],byte[]> {
	
static final String rcsid = "$Id: ParallelTrueColorRenderer.java,v 1.3 2016/06/06 14:27:55 velktron Exp $";


/* With a truecolour raster, some things are indeed easier */
protected int[][] palettes;
protected int[] raster;
protected final int nrOfProcessors;
protected Runnable[] paletteThreads;
protected final Executor executor;

public ParallelTrueColorRenderer(){
super();
Runtime runtime = Runtime.getRuntime();
nrOfProcessors = runtime.availableProcessors();
updateBarrier=new CyclicBarrier(nrOfProcessors+1);
paletteThreads=new PaletteThread[nrOfProcessors];
int len=this.getHeight()*this.getWidth();
int chunk=len/nrOfProcessors;
for (int i=0;i<nrOfProcessors;i++){
	paletteThreads[i]=new PaletteThread(i*chunk,(i+1)*chunk);
}
this.executor=Executors.newFixedThreadPool(nrOfProcessors);
}

public ParallelTrueColorRenderer(int w,int h){
    // Defaults
    super(w,h);
    Runtime runtime = Runtime.getRuntime();
    nrOfProcessors = runtime.availableProcessors();
    updateBarrier=new CyclicBarrier(nrOfProcessors+1);
    paletteThreads=new PaletteThread[nrOfProcessors];
    int len=w*h;
    int chunk=len/nrOfProcessors;
    for (int i=0;i<nrOfProcessors;i++){
    	paletteThreads[i]=new PaletteThread(i*chunk,(i+1)*chunk);
    }
    this.executor=Executors.newFixedThreadPool(nrOfProcessors);
}

public void Init () 
{ 
 int		i;
 for (i=0 ; i<screens.length ; i++){
	screens[i] = new byte[this.getHeight()*this.getWidth()];
	}
     dirtybox=new BBox();
     
  // Using ARGB is half the speed, WTF? While RGB is almost as fast as indexed. Go figure.
  this.currentscreen=new BufferedImage(width,height, BufferedImage.TYPE_INT_RGB);
  this.mapInternalRasterToBufferedImage((BufferedImage) currentscreen);
}

@Override
public void setUsegamma(int gammalevel) {
	this.usegamma=gammalevel%maxgammas;
	
}

@Override
public void setPalette(int palette) {
	this.usepalette=palette%maxpalettes;
}

@Override
protected final void specificPaletteCreation(byte[] paldata,
		short[][] gammadata, 
		final int palettes, 
		final int colors,
		final int stride,
		final int gammalevels){

	  System.out.printf("Enough data for %d palettes",maxpalettes);
	  System.out.printf("Enough data for %d gamma levels",maxgammas);
	  
	  this.palettes=new int[maxgammas*maxpalettes][];
	  
	  for (int z=0;z<maxgammas;z++){
		  
		  // For each palette
		  for (int y=0;y<maxpalettes;y++){
			  this.palettes[z*maxpalettes+y]=new int[colors];
			  
			  for (int x=0;x<colors;x++){
				  int r=gammadata[z][0xFF&paldata[y*colors*stride+stride*x]]; // R
				  int g=gammadata[z][0xFF&paldata[1+y*colors*stride+stride*x]]; // G
				  int b=gammadata[z][0xFF&paldata[2+y*colors*stride+stride*x]]; // B
				  int color=0xFF000000|r<<16|g<<8|b;
				  this.palettes[z*maxpalettes+y][x]=color;
			  	}
	  		}
	  }

}

/** Hotlinks a 32-bit "canvas" (the raster int[] array) to an externally supplied
 *  buffered image. Now whatever we write into raster, will appear in the image as well,
 *  without using drawing primitives. Necessary for fast rendering.
 *  
 * @param b
 */

private void mapInternalRasterToBufferedImage(BufferedImage b){
    raster=((DataBufferInt)(b.getRaster().getDataBuffer())).getData();
    
}

/** Update "canvas" to one of the internal screens.
 *  
 * @param screen
 * @param b
 */

@Override
public  final void update()  {
	
	for (int i=0;i<this.nrOfProcessors;i++){
		executor.execute(paletteThreads[i]);
	}
	try {
		updateBarrier.await();
	} catch (InterruptedException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	} catch (BrokenBarrierException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}
	
	/*
    final byte[] scr=this.screens[usescreen];
    final int length=scr.length; 
    final int[] pal=this.palettes[usegamma*maxpalettes+usepalette];
    for (int i=0;i<length;i+=16){
        raster[i]=pal[0xFF&scr[i]];
        raster[i+1]=pal[0xFF&scr[i+1]];
        raster[i+2]=pal[0xFF&scr[i+2]];
        raster[i+3]=pal[0xFF&scr[i+3]];
        raster[i+4]=pal[0xFF&scr[i+4]];
        raster[i+5]=pal[0xFF&scr[i+5]];
        raster[i+6]=pal[0xFF&scr[i+6]];
        raster[i+7]=pal[0xFF&scr[i+7]];
        raster[i+8]=pal[0xFF&scr[i+8]];
        raster[i+9]=pal[0xFF&scr[i+9]];
        raster[i+10]=pal[0xFF&scr[i+10]];
        raster[i+11]=pal[0xFF&scr[i+11]];
        raster[i+12]=pal[0xFF&scr[i+12]];
        raster[i+13]=pal[0xFF&scr[i+13]];
        raster[i+14]=pal[0xFF&scr[i+14]];
        raster[i+15]=pal[0xFF&scr[i+15]];

    } */
}

protected final CyclicBarrier updateBarrier;

private class PaletteThread implements Runnable{

	private final int start;
	private final int stop;
	
	public PaletteThread(int start, int stop){
		this.start=start;
		this.stop=stop;
	}
	
	@Override
	public void run() {
		
    	final byte[] scr=screens[usescreen];
    	final int[] pal=palettes[usegamma*maxpalettes+usepalette];
	    for (int i=start;i<stop;i+=16){
	    	raster[i]=pal[0xFF&scr[i]];
	        raster[i+1]=pal[0xFF&scr[i+1]];
	        raster[i+2]=pal[0xFF&scr[i+2]];
	        raster[i+3]=pal[0xFF&scr[i+3]];
	        raster[i+4]=pal[0xFF&scr[i+4]];
	        raster[i+5]=pal[0xFF&scr[i+5]];
	        raster[i+6]=pal[0xFF&scr[i+6]];
	        raster[i+7]=pal[0xFF&scr[i+7]];
	        raster[i+8]=pal[0xFF&scr[i+8]];
	        raster[i+9]=pal[0xFF&scr[i+9]];
	        raster[i+10]=pal[0xFF&scr[i+10]];
	        raster[i+11]=pal[0xFF&scr[i+11]];
	        raster[i+12]=pal[0xFF&scr[i+12]];
	        raster[i+13]=pal[0xFF&scr[i+13]];
	        raster[i+14]=pal[0xFF&scr[i+14]];
	        raster[i+15]=pal[0xFF&scr[i+15]];

	    }
        try {
			updateBarrier.await();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (BrokenBarrierException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
}

public final void DrawPatch
( int       x,
int     y,
int     scrn,
patch_t patch ) 
{ 

 column_t   column; 
 int    desttop;
 final  byte[] dest=screens[scrn];
 int        w; 
     
 y -= patch.topoffset; 
 x -= patch.leftoffset; 
if (RANGECHECK) 
 if (doRangeCheck(x,y,patch,scrn))
 {
   System.err.print("Patch at "+x+","+y+" exceeds LFB\n");
   // No I_Error abort - what is up with TNT.WAD?
   System.err.print("V_DrawPatch: bad patch (ignored)\n");
   return;
 }

 if (scrn==0)
    this.MarkRect (x, y, patch.width, patch.height); 

     
 w = patch.width; 
 desttop = x+this.width*y; 
 // For each column..
 int destPos;
 int ptr=0;
 for (int col=0 ; col<w ; desttop++, col++,x++)
 { 
    // This points at a "column" object.     
    //column = (column_t *)((byte *)patch + LONG(patch->columnofs[col])); 
    column=patch.columns[col];
    // For each post...
    for (int i=0;i<column.posts;i++){
        // Get pointer to post offset.
        ptr=column.postofs[i];
        // Get post delta
        short delta=column.postdeltas[i];
        // We skip delta, len and padding.
        // ptr+=3; NOT NEEDED< pre-skipped at parsing. 
        
        // Skip transparent rows...
        if (delta==0xFF) break;

        destPos = desttop + delta*this.width;  
        
        // These lengths are already correct.
        for (int j=0;j<column.postlen[i];j++){
               dest[destPos] = column.data[ptr++];
               destPos += this.width;
        }
    }
 }
    
    
}

}


package v;

import java.awt.image.BufferedImage;
import java.awt.image.DataBufferByte;
import java.awt.image.IndexColorModel;
import java.awt.image.RasterOp;
import java.awt.image.WritableRaster;

import m.BBox;

public class BufferedRenderer extends SoftwareVideoRenderer8 {
	
static final String rcsid = "$Id: BufferedRenderer.java,v 1.18 2012/09/24 17:16:23 velktron Exp $";

/** Buffered Renderer has a bunch of images "pegged" to the underlying arrays */

private BufferedImage[] screenbuffer;

/** Indexed renderers keep separate color models for each colormap (intended as gamma levels) and 
 * palette levels */
private IndexColorModel[][] cmaps;

public BufferedRenderer(int w, int h, IndexColorModel icm) {
    super(w,h);
    this.setIcm(icm);
}

/** Normally, you only have the palettes available ONLY after you read the palette from disk.
 *  So use the super contructor, and then this when the palettes are available.
 *  
 * @param icm2
 */

public void setIcm(IndexColorModel icm2) {
    this.icm=icm2;
    
}

public BufferedRenderer(int w, int h) {
    super(w,h);
}

// Used only for internal status.
private IndexColorModel icm;

@Override
public final void Init () 
{ 
 int		i;
 for (i=0 ; i<4 ; i++){
	//screens[i] = new byte[this.getHeight()*this.getWidth()];
     this.setScreen(i, this.width, this.height);     
	}
     dirtybox=new BBox();
}

/** This actually creates a raster with a fixed underlying array, but NOT the images
 *  themselves. So it's possible to have "imageless" rasters (unless you specifically
 *  request to make them visible, of course).
 *  
 */

@Override
public final void setScreen(int index, int width, int height){
	
    // We must FIRST initialize the image, so that the (immutable) color model will be set.
    if (this.icm==null){
    	final byte[] dummy=new byte[256];
    	for (int i=0;i<dummy.length;i++)
    		dummy[i]=(byte) i;    	
    	icm=new IndexColorModel(8,256,dummy,dummy,dummy);
    }
    
    r[index]=icm.createCompatibleWritableRaster(width,height);
    
    // Only create non-visible data, pegged to the raster. Create visible images
    // only on-demand.
    
    screens[index]=((DataBufferByte)r[index].getDataBuffer()).getData();

}


/** We only call this once we have a stable WritableRaster, and we only want
 *  a different colormodel (e.g. after changing gamma). It's slower than keepings
 *  severerl BufferedImages ready, so it's only used when changing gamma. The
 *  backing screen, array etc. should not have changed at this moment.
 * 
 * @param index
 * @param r
 */

private final BufferedImage createScreen(int index,IndexColorModel icm, WritableRaster r){
    return new BufferedImage(icm,r,false,null);
}

/*
public BufferedImage mapBufferedImageToScreen(int screen, IndexColorModel icm){
    // Map databuffer to one of the screens.
    DataBufferByte dbb=new DataBufferByte(screens[screen],screens[screen].length);
    BufferedImage b=new BufferedImage(this.getWidth(),this.getHeight(),BufferedImage.TYPE_BYTE_INDEXED,icm);
    WritableRaster r=WritableRaster.createPackedRaster(dbb,b.getWidth(), b.getHeight(), 8,
        new Point(0,0));
    b.setData(r);
    
    return b;
    
} */

/*
public BufferedImage cloneScreen(int screen, IndexColorModel icm){
    BufferedImage b=new BufferedImage(this.getWidth(),this.getHeight(),BufferedImage.TYPE_BYTE_INDEXED,icm);
    b.setData(screenbuffer[0].getRaster());
    return b;
    
} */




public final void changePalette(int pal){
    this.usepalette=(pal<<8);//+0x00FF;
    //this.usepalette=/*(pal<<8)+*/0xFF;
    
}

/** Get a bunch of BufferedImages "pegged" on the same output screen of this
 *  Doom Video Renderer, but with different palettes, defined in icms[].
 *  This is VERY speed efficient assuming that an IndexedColorModel will be used,
 *  rather than a 32-bit canvas, and memory overhead is minimal. Call this ONLY
 *  ONCE when initializing the video renderer, else it will invalidate pretty much
 *  everything in an ongoing game.
 * 
 *  NOTE: this will actually CREATE a new byte array for the screen, so it's important
 *  that this is called BEFORE anything else taps into it.
 * 
 * @param screen
 * @param icms
 * @return
 */

private BufferedImage[] createScreenForPalettes(int screen,IndexColorModel[] icms) {
        
		// These screens represent a complete range of palettes for a specific gamma
		// and specific screen
        BufferedImage[] b=new BufferedImage[icms.length];
        
        
        // MEGA hack: all images share the same raster data as screenbuffer[screen]
        // If this is the first time we called this method, the actually backing array
        // will be actually created. If not...

        // Create the first of the screens.
        this.icm=icms[0];
        
        if (r[screen]==null){
        	// This will create the first buffered image (and its data array)/
            // as screenbuffer[0]	   	
        	setScreen(screen,this.getWidth(),this.getHeight());
       		}
        
        // This is the base image for this set of palettes (usually index 0).
       
       
        // Create the rest of the screens (with different palettes) on the same raster.
        for (int i=0;i<icms.length;i++){
        	 b[i]=createScreen(screen,icms[i],r[screen]);
        }
        
        return b;
        
    }

protected final void specificPaletteCreation(byte[] paldata,
		short[][] gammadata, 
		final int palettes, 
		final int colors,
		final int stride,
		final int gammalevels){

	  System.out.printf("Enough data for %d palettes",maxpalettes);
	  System.out.printf("Enough data for %d gamma levels",maxgammas);
	  
	  // Create as gamma levels as specified.
	  cmaps=new IndexColorModel[maxgammas][];
	  
	  // First set of palettes, normal gamma.
	  cmaps[0]=new IndexColorModel[maxpalettes];

	  // Now we have our palettes.
	  for (int i=0;i<maxpalettes;i++){
		  cmaps[0][i]=new IndexColorModel(8, colors,paldata, i*stride*colors, false);
	  		}
  
  // Wire the others according to the gamma table.
	  byte[] tmpcmap=new byte[colors*stride];
	  
	  // For each gamma value...
	  for (int j=1;j<maxgammas;j++){
		  
		  cmaps[j]=new IndexColorModel[maxpalettes];
		  
		  // For each palette
		  for (int i=0;i<maxpalettes;i++){
			  
			  for (int k=1;k<256;k++){
				  tmpcmap[3*k]=(byte) gammadata[j][0x00FF&paldata[i*colors*stride+stride*k]]; // R
				  tmpcmap[3*k+1]=(byte) gammadata[j][0x00FF&paldata[1+i*colors*stride+stride*k]]; // G
				  tmpcmap[3*k+2]=(byte) gammadata[j][0x00FF&paldata[2+i*colors*stride+stride*k]]; // B
			  	}

			  cmaps[j][i]=new IndexColorModel(8, 256,tmpcmap, 0, false);
	  		}
	  }

}

private WritableRaster[] r=new WritableRaster[5];

public void setPalette(int palette){
	this.currentpal=palette%maxpalettes;
	this.currentscreen=this.screenbuffer[currentpal];
}

@Override
public void setUsegamma(int gamma) {
	this.usegamma=gamma%maxgammas;
	// Changing gamma also "fixes" the screens!
	this.setCurrentScreen(0);
}

public void setCurrentScreen(int screen){
	  super.setCurrentScreen(screen);
	  this.screenbuffer=this.createScreenForPalettes(usescreen, cmaps[usegamma]);
	  this.currentscreen=this.screenbuffer[currentpal];
}

public IndexColorModel getPalette(){
    return cmaps[0][this.usepalette];
    }

}

//$Log: BufferedRenderer.java,v $
//Revision 1.18  2012/09/24 17:16:23  velktron
//Massive merge between HiColor and HEAD. There's no difference from now on, and development continues on HEAD.
//
//Revision 1.17.2.3  2012/09/24 16:56:06  velktron
//New hierarchy, less code repetition.
//

package v;

/** Interface for an object that conveys screen resolution/scaling
 * information, meant to replace the static declarations in Defines.
 * 
 * Classes that rely on resolution changes should implement an interface
 * called "IVideoScaleAware", which should support
 * 
 * @author admin
 *
 */

public interface IVideoScale {
    
  //It is educational but futile to change this
  //scaling e.g. to 2. Drawing of status bar,
  //menues etc. is tied to the scale implied
  //by the graphics.

  public static double INV_ASPECT_RATIO =   0.625; // 0.75, ideally

  //
  // For resize of screen, at start of game.
  // It will not work dynamically, see visplanes.
  //
  public static final int BASE_WIDTH =     320;
  public static final int BASE_HEIGHT=     (int) (INV_ASPECT_RATIO*320); // 200

    
    int getScreenWidth();
    int getScreenHeight();
    int getScalingX();
    int getScalingY();
    
    /** Safest global scaling for fixed stuff like menus, titlepic etc */
    int getSafeScaling();
    
    /** Get floating point screen multiplier. Not recommended, as it causes
     *  visual glitches. Replace with safe scale, whenever possible */
    float getScreenMul();
    
    /** Future, should signal aware objects that they should
     * refresh their resolution-dependent state, structures, variables etc.
     * 
     * @return
     */
    boolean changed();
    
}

package v;

public class VideoScaleInfo
        implements IVideoScale {
    
   protected float scale;
   protected int width;
   protected int height;
   protected int bestScaleX;
   protected int bestScaleY;
   

   protected int bestSafeScale;
   
   /** Scale is intended as a multiple of the base resolution, 320 x 200.
    *  If changing the ratio is also desired, then keep in mind that
    *  the base width is always considered fixed, while the base height
    *  is not. 
    * 
    * @param scale
    */
   
   public VideoScaleInfo(float scale){
       this.scale=scale;
       width=(int) (BASE_WIDTH*scale);
       height=(int) (scale*BASE_WIDTH*INV_ASPECT_RATIO);
       bestScaleX= (int) Math.floor((float)width/(float)BASE_WIDTH);
       bestScaleY= (int) Math.floor((float)height/(float)BASE_HEIGHT);
       bestSafeScale= Math.min(bestScaleX, bestScaleY);
       
   }
   
   /** It's possible to specify other aspect ratios, too, keeping in mind
    *  that there are maximum width and height limits to take into account,
    *  and that scaling of graphics etc. will be rather problematic. Default
    *  ratio is 0.625, 0.75 will give a nice 4:3 ratio.
    *  
    *  TODO: pretty lame...
    *  
    * @param scale
    * @param ratio
    */
   
   public VideoScaleInfo(float scale, float ratio){
       this.scale=scale;
       width=(int) (BASE_WIDTH*scale);
       height=(int) (scale*BASE_WIDTH*ratio);
       bestScaleX= (int) Math.floor((float)width/(float)BASE_WIDTH);
       bestScaleY= (int) Math.floor((float)height/(float)BASE_HEIGHT);
       bestSafeScale= Math.min(bestScaleX, bestScaleY);
       
   }
      
    @Override
    public int getScreenWidth() {
        return width;
    }

    @Override
    public int getScreenHeight() {      
        return height;
    }

    @Override
    public int getScalingX() {
        return bestScaleX;
    }

    @Override
    public int getScalingY() {
        return bestScaleY;
    }

    @Override
    public int getSafeScaling() {
        return bestSafeScale;
    }

    @Override
    public boolean changed() {
        return false;
    }

    @Override
    public float getScreenMul() {        
        return scale;
    }

}

package doom64;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

import w.CacheableDoomObject;

public class MapThing implements CacheableDoomObject{

	public short x;
    public short y;    
    public short z;
    public short angle;
    public short type;
    public short flags;    
    public short id;
	
	public MapThing() {
		// TODO Auto-generated constructor stub
	}

	@Override
	public void unpack(ByteBuffer buf) throws IOException {
        buf.order(ByteOrder.LITTLE_ENDIAN);
        this.x = buf.getShort();
        this.y = buf.getShort();
        this.z = buf.getShort();
        this.angle = buf.getShort();
        this.type = buf.getShort();
        this.flags = buf.getShort();
        this.id = buf.getShort();		
	}

    public static int sizeOf() {
        return 14;
    }

	
}

package doom64;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

import w.CacheableDoomObject;

public class MapLinedef implements CacheableDoomObject{
	
	public short v1;
	public short v2;
	public int flags;
	public short special;
	public short tag;
	public short left;
	public short right;

	public MapLinedef() {
	}
	
    public void unpack(ByteBuffer buf)
            throws IOException {
    buf.order(ByteOrder.LITTLE_ENDIAN);
    this.v1 = buf.getShort();
    this.v2 = buf.getShort();
    this.flags = buf.getInt();
    this.special = buf.getShort();
    this.tag = buf.getShort();
    this.left= buf.getShort();
    this.right= buf.getShort();
    }
    
    public static int sizeOf() {
        return 16;
    }

    
    /* These are Doom 64-specific linedef flags (not all of them) */
    public static final int D64_ML_RENDERMIDDLE= 0x200;
    public static final int D64_ML_NOT_CLIPPED= 0x400;
    public static final int D64_ML_DONTPEGMIDDLE= 0x800;
    public static final int D64_ML_KILLTRIGGER= 0x1000;
    public static final int D64_ML_SWMASK1= 0x2000;
    public static final int D64_ML_SWMASK2= 0x4000;
    public static final int D64_ML_SWMASK3= 0x8000;
    
}

package doom64;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

import w.CacheableDoomObject;
import w.DoomBuffer;

public class MapLights implements CacheableDoomObject {	
	
	/** N.B.: read those as unsigned bytes */
	public short red,green,blue;
	public byte pad;
	public short tag;
	

	public MapLights() {
		// TODO Auto-generated constructor stub
	}

	public MapLights(short i) {
		this.red=this.green=this.blue= i;
	}

	@Override
	public void unpack(ByteBuffer buf) throws IOException {
		buf.order(ByteOrder.LITTLE_ENDIAN);
		this.red=(short) (0x00FF&buf.get());
		this.green=(short) (0x00FF&buf.get());
		this.blue=(short) (0x00FF&buf.get());
		this.pad=buf.get();
		this.tag=buf.getShort();
	}
	
	public final static int sizeOf(){
		return 6;
	}
	
	public short greyscaleLightLevel(){
		return (short) (0.2989 * red + 0.5870 * green + 0.1140 * blue); 
	}

}

package doom64;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

import w.CacheableDoomObject;

public class MapSidedef implements CacheableDoomObject{
	
    public short xoffset;

    public short yoffset;

    /* unsigned short hashes for DOOM64EX */
    public char toptexture;

    public char bottomtexture;

    public char midtexture;
    
    public short sector;    

	public MapSidedef() {
		// TODO Auto-generated constructor stub
	}

	@Override
	public void unpack(ByteBuffer buf) throws IOException {
        buf.order(ByteOrder.LITTLE_ENDIAN);
        this.xoffset= buf.getShort();
        this.yoffset= buf.getShort();
        this.toptexture= buf.getChar();
        this.bottomtexture= buf.getChar();
        this.midtexture= buf.getChar();
        this.sector = buf.getShort();
	}
	
    public static int sizeOf() {
        return 12;
    }
}

package doom64;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

import w.CacheableDoomObject;

public class MapVertex implements CacheableDoomObject{
	
    public int x;
    public int y;

	public MapVertex() {
	}

	@Override
	public void unpack(ByteBuffer buf) throws IOException {
		buf.order(ByteOrder.LITTLE_ENDIAN);		
		x=buf.getInt();
		y=buf.getInt();		
	}

	public static final int sizeOf() {
		return 8;
	}

}

package doom64;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

import w.CacheableDoomObject;

public class MapSector implements CacheableDoomObject{

	
	public short floorheight;
	public short ceilingheight;
	public char floorpic;
	public char ceilingpic;
	public short floorcolor;
	public short ceilingcolor;
	public short thingcolor;
	public short walltopcolor;
	public short wallbottomcolor;
	public short special;
	public short tag;
	public short flags;
	
	public MapSector() {
		// TODO Auto-generated constructor stub
	}

	@Override
	public void unpack(ByteBuffer buf) throws IOException {
        // ACHTUNG: the only situation where we'd
        // like to read memory-format sector_t's is from
        // savegames, and in vanilla savegames, not all info
        // is saved (or read) from disk.
		buf.order(ByteOrder.LITTLE_ENDIAN);
        this.floorheight = buf.getShort();
        this.ceilingheight = buf.getShort();
        this.floorpic = (char) buf.getShort();
        this.ceilingpic = (char) buf.getShort();
    	this.floorcolor= buf.getShort();
    	this.ceilingcolor= buf.getShort();
    	this.thingcolor= buf.getShort();
    	this.walltopcolor= buf.getShort();
    	this.wallbottomcolor= buf.getShort();
        
        this.special = buf.getShort();
        this.tag = buf.getShort();
        this.flags= buf.getShort();
    }
	
    public static int sizeOf() {
        return 24;
    }

	private static final ByteBuffer iobuffer=ByteBuffer.allocate(MapSector.sizeOf());

}

package savegame;
import static data.Limits.*;
import i.DoomStatusAware;
import i.IDoomSystem;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;

import data.info;
import doom.DoomStatus;
import doom.player_t;
import doom.think_t;
import doom.thinker_t;

import p.AbstractLevelLoader;
import p.Actions;
import p.ThinkerList;
import p.ceiling_t;
import p.floormove_t;
import p.glow_t;
import p.lightflash_t;
import p.mobj_t;
import p.plat_t;
import p.strobe_t;
import p.vldoor_t;
import rr.line_t;
import rr.sector_t;
import rr.side_t;
import utils.C2JUtils;

public class VanillaDSG implements IDoomSaveGame, DoomStatusAware {
    
    VanillaDSGHeader header;
    DoomStatus<?,?> DS;
    AbstractLevelLoader LL;
    Actions A;
    IDoomSystem I;
    
    public VanillaDSG(){
        
    }
    

    @Override
    public void setThinkerList(ThinkerList li) {
        // TODO Auto-generated method stub
        
    }

    @Override
    public IDoomSaveGameHeader getHeader() {
        return header;
    }

    @Override
    public void setHeader(IDoomSaveGameHeader header) {
        this.header=(VanillaDSGHeader) header;
        
    }

    private DataInputStream f;
    private DataOutputStream fo;
    private int maxsize;
    
    @Override
    public boolean doLoad(DataInputStream f)
            {
        try {
        this.f=f;
        maxsize=f.available();
        System.out.println("Max size "+maxsize);
        this.header=new VanillaDSGHeader();
        header.read(f);
        UnArchivePlayers();
        UnArchiveWorld();
        UnArchiveThinkers();
        UnArchiveSpecials();
        byte terminator=f.readByte();
        if (terminator != 0x1D) return false;
        else return true;
        } catch (Exception e){
            e.printStackTrace();
            System.err.printf("Error while loading savegame! Cause: %s",e.getMessage());
            return false; // Needed to shut up compiler.
        }
        
    }
    
    
 /**
  * P_UnArchivePlayers
 * @throws IOException 
  */
 protected void UnArchivePlayers () throws IOException
 {
     int     i;
     int     j;
     
     for (i=0 ; i<MAXPLAYERS ; i++)
     {
     // Multiplayer savegames are different!
     if (!DS.playeringame[i])
         continue;
     PADSAVEP(f,maxsize); // this will move us on the 52th byte, instead of 50th.
     DS.players[i].read(f);
     
     //memcpy (&players[i],save_p, sizeof(player_t));
     //save_p += sizeof(player_t);
     
     // will be set when unarc thinker
     DS.players[i].mo = null;   
     DS.players[i].message = null;
     DS.players[i].attacker = null;

     
     for (j=0 ; j<player_t.NUMPSPRITES ; j++)
     {
         if (C2JUtils.eval(DS.players[i].psprites[j].state))
         {
             // MAES HACK to accomoadate state_t type punning a-posteriori
             DS.players[i].psprites[j].state =
             info.states[DS.players[i].psprites[j].readstate];
         }
     }
     }
 }
    
 /**
  * P_UnArchivePlayers
 * @throws IOException 
  */
 protected void ArchivePlayers () throws IOException
 {
     for (int i=0 ; i<MAXPLAYERS ; i++)
     {
     // Multiplayer savegames are different!
     if (!DS.playeringame[i])
         continue;
     
     PADSAVEP(fo); // this will move us on the 52th byte, instead of 50th.

     // State will have to be serialized when saving.
     DS.players[i].write(fo);

     //System.out.printf("Player %d has mobj hashcode %d",(1+i),DS.players[i].mo.hashCode());
     }
 }

 //
//P_ArchiveWorld
//
protected void ArchiveWorld () throws IOException
{
  int         i;
  int         j;
  sector_t       sec;
  line_t     li;
  side_t     si;
  
  // do sectors (allocate 14 bytes per sector)
  ByteBuffer buffer=ByteBuffer.allocate(LL.numsectors*14);
  buffer.order(ByteOrder.LITTLE_ENDIAN);
  
  deAdaptSectors();
  for (i=0; i<LL.numsectors ; i++)
  {
      sec=LL.sectors[i];
      // MAES: sectors are actually carefully
      // marshalled, so we don't just read/write
      // their entire memory footprint to disk.
      sec.pack(buffer);
  }
  
  adaptSectors();
  fo.write(buffer.array(),0,buffer.position());
  
  // do lines 
  // Allocate for the worst-case scenario (6+20 per line)
  buffer=ByteBuffer.allocate(LL.numlines*(6+20));
  buffer.order(ByteOrder.LITTLE_ENDIAN);
  buffer.position(0); 
  
  //final side_t test1=new side_t(0x11111111,0x11111111,(short) 0x1111,(short)0x1111,(short)0x1111,null);
  //final side_t test2=new side_t(0x22222222,0x22222222,(short) 0x2222,(short)0x2222,(short)0x2222,null);
  
  for (i=0; i<LL.numlines ; i++)
  {
  li=LL.lines[i];
  li.pack(buffer);

  for (j=0 ; j<2 ; j++)
  {
      if (li.sidenum[j] == line_t.NO_INDEX)
      continue;
      si = LL.sides[li.sidenum[j]];
      si.pack(buffer);
      //if (j==0) test1.pack(buffer);
      //else test2.pack(buffer);
      
  }
  }
  
  int write=buffer.position();
  fo.write(buffer.array(),0,write);
}

 
//
//P_UnArchiveWorld
//
protected final void UnArchiveWorld () throws IOException
{
  int         i;
  int         j;
  sector_t       sec;
  line_t     li;
  side_t     si;
  // short      get;
  //get = (short *)save_p;

  //List<sector_t> sectors=new ArrayList<sector_t>();
  // do sectors
  for (i=0; i<LL.numsectors ; i++)
  {
      sec=LL.sectors[i];
      // MAES: sectors were actually carefully
      // unmarshalled, so we don't just read/write
      // their entire memory footprint to disk.
      sec.read(f);
      sec.specialdata = null;
      sec.soundtarget = null;
  }
  adaptSectors();
  // do lines
  for (i=0 ; i<LL.numlines ; i++)
  {
  li=LL.lines[i];
  // MAES: something similar occurs with lines, too.
  li.read(f);
  //System.out.println("Line "+i+": "+li);
  //System.out.print(i+ " {");
  for (j=0 ; j<2 ; j++)
  {
    //  System.out.print(li.sidenum[j]);
    //  if (j<2) System.out.print(",");
   //   System.out.printf("Skipped sidenum %d for line %d\n",j,i);
      if (li.sidenum[j] == line_t.NO_INDEX){
  //        System.out.printf("Skipped sidenum %d for line %d\n",j,i);
      continue;
      }
      // Similarly, sides also get a careful unmarshalling even
      // in vanilla. No "dumb" block reads here.
      si = LL.sides[li.sidenum[j]];
      si.read(f);

  }
  //System.out.printf("Position at end of WORLD: %d\n",f.getFilePointer());
  }
  
}

/** Convert loaded sectors from vanilla savegames into the internal,
 *  continuous index progression, by intercepting breaks corresponding to markers.
 */

protected void adaptSectors(){
	sector_t sec;
	switch(DS.getGameMode()){
	case registered:
	case shareware:
	for (int i=0;i<LL.numsectors;i++){
		sec=LL.sectors[i];
		// Between the F1_START and F1_END mark (in vanilla)
		if (sec.floorpic<=54){
			sec.floorpic-=1;
		} else {
		// Between the F2_START and F2_END mark (in vanilla)
			sec.floorpic-=3;
		}
		if (sec.ceilingpic<=54){
			sec.ceilingpic-=1;
		} else {
		// Between the F2_START and F2_END mark (in vanilla)
			sec.ceilingpic-=3;
		}
		
	}
	break;
	case commercial:
	case pack_plut:
	case pack_tnt:
		
		for (int i=0;i<LL.numsectors;i++){
			sec=LL.sectors[i];
			// Between the F1_START and F1_END mark (in vanilla)
			if (sec.floorpic<=54){
				sec.floorpic-=1;
			} else 
			if (sec.floorpic<=99){
			// Between the F2_START and F2_END mark (in vanilla)
				sec.floorpic-=3;
			} else {
				sec.floorpic-=5;
			}
			
			if (sec.ceilingpic<=54){
				sec.ceilingpic-=1;
			} else if (sec.ceilingpic<=99){
			// Between the F2_START and F2_END mark (in vanilla)
				sec.ceilingpic-=3;
			} else {
				sec.ceilingpic-=5;
			}
			
		}
	}
}

/** De-convert sectors from an absolute to a vanilla-like index
 *  progression, by adding proper skips
 */

protected void deAdaptSectors(){
    sector_t sec;
    switch(DS.getGameMode()){
    case registered:
    case shareware:
    for (int i=0;i<LL.numsectors;i++){
        sec=LL.sectors[i];
        // Between the F1_START and F1_END mark (in vanilla)
        if (sec.floorpic<54){
            sec.floorpic+=1;
        } else {
        // Between the F2_START and F2_END mark (in vanilla)
            sec.floorpic+=3;
        }
        if (sec.ceilingpic<54){
            sec.ceilingpic+=1;
        } else {
        // Between the F2_START and F2_END mark (in vanilla)
            sec.ceilingpic+=3;
        }
        
    }
    break;
    case commercial:
    case pack_plut:
    case pack_tnt:
        
        for (int i=0;i<LL.numsectors;i++){
            sec=LL.sectors[i];
            // Between the F1_START and F1_END mark (in vanilla)
            if (sec.floorpic<54){
                sec.floorpic+=1;
            } else 
            if (sec.floorpic<99){
            // Between the F2_START and F2_END mark (in vanilla)
                sec.floorpic+=3;
            } else {
                sec.floorpic+=5;
            }
            
            if (sec.ceilingpic<54){
                sec.ceilingpic+=1;
            } else if (sec.ceilingpic<99){
            // Between the F2_START and F2_END mark (in vanilla)
                sec.ceilingpic+=3;
            } else {
                sec.ceilingpic+=5;
            }
            
        }
    }
}


//
//Thinkers
//
protected enum thinkerclass_t 
{
 tc_end,
 tc_mobj;
}
 
List<mobj_t> TL=new ArrayList<mobj_t>();

//
//P_UnArchiveThinkers
//
protected void ArchiveThinkers () throws IOException
{
    thinker_t      th;
    mobj_t     mobj;

// save off the current thinkers
for (th = A.getThinkerCap().next ; th != A.getThinkerCap(); th=th.next)
{
if (th.function!=null && th.function==think_t.P_MobjThinker)
{
    // Indicate valid thinker
    fo.writeByte(thinkerclass_t.tc_mobj.ordinal());
    // Pad...
    PADSAVEP(fo);
    mobj=(mobj_t)th;
    mobj.write(fo);
   
    // MAES: state is explicit in state.id
   // save_p += sizeof(*mobj);
   // mobj->state = (state_t *)(mobj->state - states);
    
    // MAES: player is automatically generated at runtime and handled by the writer.
    //if (mobj->player)
    //mobj->player = (player_t *)((mobj->player-players) + 1);
    continue;
}
    
// I_Error ("P_ArchiveThinkers: Unknown thinker function");
}

// add a terminating marker
fo.writeByte(thinkerclass_t.tc_end.ordinal());

}

//
//P_UnArchiveThinkers
//
protected void UnArchiveThinkers () throws IOException
{
 thinkerclass_t        tclass; // was "byte", therefore unsigned
 thinker_t      currentthinker;
 thinker_t      next;
 mobj_t     mobj;
 int id=0;
 
 
 
 // remove all the current thinkers
 
 currentthinker = A.getThinkerCap().next;
 while (currentthinker!=null && currentthinker != A.getThinkerCap())
 {
 next = currentthinker.next;
 
 if (currentthinker.function ==  think_t.P_MobjThinker)
     A.RemoveMobj ((mobj_t)currentthinker);
 else{
     //currentthinker.next.prev=currentthinker.prev;
     //currentthinker.prev.next=currentthinker.next;
     currentthinker=null;
 }
     
 currentthinker = next;
 }
 
 A.InitThinkers ();
 
 // read in saved thinkers
 boolean end=false;
 while (!end)
 {
     int tmp=f.readUnsignedByte();
     tclass=thinkerclass_t.values()[tmp];
 switch (tclass)
 {
   case tc_end:
       // That's how we know when to stop.
       end=true;
     break;     // end of list
         
   case tc_mobj:
     PADSAVEP(f,maxsize);     
     mobj=new mobj_t(A);
     mobj.read(f);
     mobj.id=++id;
     TL.add(mobj);
     mobj.state = info.states[mobj.stateid];
     mobj.target = null;
     if (mobj.playerid!=0)
     {
     mobj.player = DS.players[mobj.playerid-1];
     mobj.player.mo = mobj;

     }
     LL.SetThingPosition (mobj);
     mobj.info = info.mobjinfo[mobj.type.ordinal()];
     mobj.floorz = mobj.subsector.sector.floorheight;
     mobj.ceilingz = mobj.subsector.sector.ceilingheight;
     mobj.function = think_t.P_MobjThinker;
     A.AddThinker (mobj);
     break;
         
   default:
     I.Error ("Unknown tclass %d in savegame",tclass);
 }
 
 }
 reconstructPointers();
 rewirePointers();
}

Hashtable<Integer,mobj_t> pointindex=new Hashtable<Integer,mobj_t> ();

/** Allows reconstructing infighting targets from stored pointers/indices.
 *  Works even with vanilla savegames as long as whatever it is that you
 *  store is unique. A good choice would be progressive indices or hash values.
 * 
 */

protected void reconstructPointers(){
    
    int player=0;
    
    for(mobj_t th: TL){

        if (th.player!=null){
        player=th.id;
        // Player found, so that's our first key.
        pointindex.put(th.player.p_mobj,th);
        }
    }
    
    if (player==0) {
        System.err.println("Player not found, cannot reconstruct pointers!");
        return;
    }
    
    int curr; // next or prev index
    
    // We start from the player's index, if found.
    // We subtract -1 so it matches that inside the thinkers list.
    for (int i=(player-1);i<TL.size()-1;i++){
        // Get "next" pointer.
        curr=TL.get(i).nextid;
        pointindex.put(curr, TL.get(i+1));
    }
    
    // We also search backwards, in case player wasn't first object
    // (can this even happen, in vanilla?)
    // -1 so it matches that of the TL list.
    for (int i=(player-1);i>0;i--){
        // Get "prev" pointer.
    	curr=TL.get(i).previd;
    	pointindex.put(curr,TL.get(i-1));
    }

}

/** Allows reconstructing infighting targets from stored pointers/indices from
 * the hashtable created by reconstructPointers.
 * 
 */

protected void rewirePointers(){
    
    for(mobj_t th: TL){
        if (th.p_target!=0){
            th.target=pointindex.get(th.p_target);
            th.tracer=pointindex.get(th.p_tracer);
           // System.out.printf("Object %s has target %s\n",th.type.toString(),th.target.type.toString());
        }
    }
}

protected enum specials_e
{
    tc_ceiling,
    tc_door,
    tc_floor,
    tc_plat,
    tc_flash,
    tc_strobe,
    tc_glow,
    tc_endspecials

} ;   

//
//P_UrchiveSpecials
//
protected void ArchiveSpecials () throws IOException
{
ceiling_t      ceiling;
vldoor_t       door;
floormove_t    floor;
plat_t     plat;
lightflash_t   flash;
strobe_t       strobe;
glow_t     glow;
int i;

// Most of these objects are quite hefty, but estimating 128 bytes tops
// for each should do (largest one is 56);
ByteBuffer buffer=ByteBuffer.allocate(128);
buffer.order(ByteOrder.LITTLE_ENDIAN);

// save off the current thinkers
for (thinker_t th = A.getThinkerCap().next ; th != A.getThinkerCap() ; th=th.next){
    
    // Write out any pending objects.
    if (buffer.position()>0){
        fo.write(buffer.array(),0,buffer.position());
        //System.out.println("Wrote out "+buffer.position()+" bytes");
            
        }
    
    // Back to the beginning.
    buffer.position(0);
    
    // So ceilings don't think?
    if (th.function== null) {
        // i maintains status between iterations
        for (i = 0; i < A.getMaxCeilings();i++)
        if ((th instanceof ceiling_t) && (A.getActiveCeilings()[i] == (ceiling_t)th))
            break;
        
        if (i<MAXCEILINGS)
        {
        fo.writeByte(specials_e.tc_ceiling.ordinal());
        PADSAVEP(fo);        
        // Set id for saving        
        ceiling=(ceiling_t)th;
        ceiling.sectorid=ceiling.sector.id;
        ceiling.pack(buffer);        
        }
        continue;
    }

    // Well, apparently some do.
    if (th.function== think_t.T_MoveCeiling) {
        
        fo.writeByte(specials_e.tc_ceiling.ordinal());
        PADSAVEP(fo);        
        ceiling=(ceiling_t)th;
        ceiling.sectorid=ceiling.sector.id;
        ceiling.pack(buffer);
        continue;
        }
   
    // Well, apparently some do.
    if (th.function== think_t.T_VerticalDoor) {

        fo.writeByte(specials_e.tc_door.ordinal());
        PADSAVEP(fo);
        door=(vldoor_t)th;
        door.sectorid=door.sector.id;
        door.pack(buffer);
        continue;
        }
    
    // Well, apparently some do.
    if (th.function== think_t.T_MoveFloor) {
        fo.writeByte(specials_e.tc_floor.ordinal());
        PADSAVEP(fo);
        floor=(floormove_t)th;
        floor.sectorid=floor.sector.id;
        floor.pack(buffer);
        continue;
        }
    
    // Well, apparently some do.
    if (th.function== think_t.T_PlatRaise) {
        fo.writeByte(specials_e.tc_plat.ordinal());
        PADSAVEP(fo);
        plat=(plat_t)th;
        plat.sectorid=plat.sector.id;
        plat.pack(buffer);
        continue;
        }

    // Well, apparently some do.
    if (th.function== think_t.T_LightFlash) {
        fo.writeByte(specials_e.tc_flash.ordinal());
        PADSAVEP(fo);
        flash=(lightflash_t)th;
        flash.sectorid=flash.sector.id;
        flash.pack(buffer);
        continue;
        }

    // Well, apparently some do.
    if (th.function== think_t.T_StrobeFlash) {
        fo.writeByte(specials_e.tc_strobe.ordinal());
        PADSAVEP(fo);
        strobe=(strobe_t)th;
        strobe.sectorid=strobe.sector.id;
        strobe.pack(buffer);
        continue;
        }
    
    // Well, apparently some do.
    if (th.function== think_t.T_Glow) {
        fo.writeByte(specials_e.tc_glow.ordinal());
        PADSAVEP(fo);
        glow=(glow_t)th;
        glow.sectorid=glow.sector.id;
        glow.pack(buffer);
        continue;
        }
    
}

if (buffer.position()>0){
    fo.write(buffer.array(),0,buffer.position());        
    }


// Finito!
fo.writeByte((byte) specials_e.tc_endspecials.ordinal());
}


//
//P_UnArchiveSpecials
//
protected void UnArchiveSpecials () throws IOException
{
    specials_e        tclass;
 ceiling_t      ceiling;
 vldoor_t       door;
 floormove_t    floor;
 plat_t     plat;
 lightflash_t   flash;
 strobe_t       strobe;
 glow_t     glow;
 
 //List<thinker_t> A=new ArrayList<thinker_t>();
 
 A.ClearPlatsBeforeLoading();
 A.ClearCeilingsBeforeLoading();
 
 // read in saved thinkers
 while (true)
 {
     int tmp=f.readUnsignedByte();
     //tmp&=0x00ff; // To "unsigned byte"
     tclass=specials_e.values()[tmp];
 switch (tclass)
 {
   case tc_endspecials:
     return; // end of list
         
   case tc_ceiling:
     PADSAVEP(f,maxsize);
     ceiling = new ceiling_t();
     ceiling.read(f);
     ceiling.sector = LL.sectors[ceiling.sectorid];
     ceiling.sector.specialdata = ceiling;

     if (ceiling.functionid!=0)
     ceiling.function = think_t.T_MoveCeiling;

     A.AddThinker (ceiling);
     A.AddActiveCeiling(ceiling);
     break;
             
   case tc_door:
     PADSAVEP(f,maxsize);
     door=new vldoor_t();
     door.read(f);
     door.sector = LL.sectors[door.sectorid];
     door.sector.specialdata = door;
     door.function = think_t.T_VerticalDoor;
     
     A.AddThinker (door);
     break;
             
   case tc_floor:
     PADSAVEP(f,maxsize);
     floor=new floormove_t();
     floor.read(f);
     floor.sector = LL.sectors[floor.sectorid];
     floor.sector.specialdata = floor;
     floor.function = think_t.T_MoveFloor;
     
     A.AddThinker (floor);
     break;
             
   case tc_plat:
     PADSAVEP(f,maxsize);
     plat=new plat_t();
     plat.read(f);
     plat.sector = LL.sectors[plat.sectorid];
     plat.sector.specialdata = plat;

     if (plat.functionid!=0)
     plat.function =  think_t.T_PlatRaise;
     
     A.AddThinker (plat);
     A.AddActivePlat(plat);
     break;
             
   case tc_flash:
     PADSAVEP(f,maxsize);
     flash=new lightflash_t(this.DS.RND);
     flash.read(f);
     
     flash.sector =LL.sectors[flash.sectorid];
     flash.function =  think_t.T_LightFlash;
     
     A.AddThinker (flash);
     break;
             
   case tc_strobe:
     PADSAVEP(f,maxsize);
     
     strobe = new strobe_t();
     strobe.read(f);
     strobe.sector = LL.sectors[strobe.sectorid];
     strobe.function =  think_t.T_StrobeFlash;
     
     A.AddThinker (strobe);
     break;
             
   case tc_glow:
     PADSAVEP(f,maxsize);
     glow = new glow_t();
     glow.read(f);
     glow.sector = LL.sectors[glow.sectorid];
     glow.function = think_t.T_Glow;
     
     A.AddThinker (glow);
     break;
             
   default:
     I.Error ("P_UnarchiveSpecials:Unknown tclass %d in savegame",tmp);
 }
 }
 
 }

    /**
     * Pads save_p to a 4-byte boundary
     * so that the load/save works on SGI&Gecko.
     * 
     * @param save_p
     */
    
    protected final int PADSAVEP(int save_p){
        return (save_p += (4 - ((int) save_p & 3)) & 3);
    }
    
    //protected final int PADSAVEP(ByteBuffer b, int save_p){
    //    ByteBuffer
    //    return (save_p += (4 - ((int) save_p & 3)) & 3);
    //}

    protected final long PADSAVEP(DataInputStream f, int maxsize) throws IOException{
        long save_p=maxsize-f.available();
        int padding =(4 - ((int) save_p & 3)) & 3;
       // System.out.printf("Current position %d Padding by %d bytes %d\n",save_p,padding,maxsize);        
        f.skip(padding);
        return padding;        
        }
    
    protected final long PADSAVEP(DataOutputStream f) throws IOException{
        long save_p=f.size();
        int padding =(4 - ((int) save_p & 3)) & 3;
       // System.out.printf("Current position %d Padding by %d bytes\n",save_p,padding);
        for (int i=0;i<padding;i++)
        	f.write(0);
        return padding;        
        }

    @Override
    public void updateStatus(DoomStatus<?,?> DS) {
        this.DS=DS;
        this.LL=DS.LL;
        this.A=DS.P;       
    	}


    @Override
    public boolean doSave(DataOutputStream f) {
            try {
             // The header must have been set, at this point.
                this.fo=f;
                //f.setLength(0); // Kill old info.
                header.write(f);
            
            
            //header.read(f);
            ArchivePlayers ();
            ArchiveWorld();
            ArchiveThinkers();  
            ArchiveSpecials();
            // TODO: the rest...
            f.write(0x1D);
            } catch (Exception e){
                e.printStackTrace();
                System.err.printf("Error while saving savegame! Cause: %s",e.getMessage());
                return false; // Needed to shut up compiler.
            }
        return true;
    }

}
package savegame;

import p.ThinkerList;

public interface ILoadSaveGame {
    void setThinkerList(ThinkerList li);
    void doSave(ThinkerList li);
    void doLoad(ThinkerList li);
}

package savegame;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import i.DoomStatusAware;
import p.ThinkerList;

public interface IDoomSaveGame extends DoomStatusAware{    
    void setThinkerList(ThinkerList li);
    boolean doLoad(DataInputStream f);
    IDoomSaveGameHeader getHeader();
    void setHeader(IDoomSaveGameHeader header);
    boolean doSave(DataOutputStream f);
}

package savegame;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;

import utils.C2JUtils;
import w.CacheableDoomObject;
import w.DoomBuffer;
import w.DoomIO;
import w.IReadableDoomObject;
import w.IWritableDoomObject;
import defines.skill_t;

import static data.Defines.VERSION;
import static data.Limits.MAXPLAYERS;
import static data.Limits.SAVESTRINGSIZE;
import static data.Limits.VERSIONSIZE;

/** The header of a vanilla savegame.
 *  
 *  It contains a fixed-length, null-terminated string of 24 bytes max, in any case.
 *  Then a 16-byte "version string", which normally reads "version 109".
 *  Then bytes that record:
 *  skill +1
 *  episode +1
 *  map +1
 *  players in game +4
 *  gametime +3 (as 24-bit big-endian)
 *  
 *  So the header has an total size of *drum roll* 50 bytes.
 *  
 * 
 * @author admin
 *
 */


public class  VanillaDSGHeader implements IDoomSaveGameHeader,IReadableDoomObject, IWritableDoomObject, CacheableDoomObject{
    
public String name; // max size SAVEGAMENAME
public String vcheck;
    // These are for DS
public skill_t gameskill;
public int gameepisode;
public int gamemap;
public boolean[] playeringame;
    /** what bullshit, stored as 24-bit integer?! */
 public int leveltime;
    // These help checking shit.
public boolean wrongversion;
   public boolean properend;
    
   public VanillaDSGHeader(){
       this.playeringame=new boolean[MAXPLAYERS];
   }
   
   
   @Override
   public void unpack(ByteBuffer buf)
           throws IOException {
       name=DoomBuffer.getNullTerminatedString(buf, SAVESTRINGSIZE);
       vcheck=DoomBuffer.getNullTerminatedString(buf, VERSIONSIZE);
       String vcheckb= ("version "+VERSION);
       // no more unpacking, and report it.
       if (wrongversion = !(vcheckb.equalsIgnoreCase(vcheck))) return;
       gameskill = skill_t.values()[buf.get()]; 
       gameepisode = buf.get();
       gamemap = buf.get();
       
              for (int i=0 ; i<MAXPLAYERS ; i++)
       playeringame[i] = buf.get()!=0; 

       // load a base level (this doesn't advance the pointer?) 
       //G_InitNew (gameskill, gameepisode, gamemap); 
    
       // get the times 
       int a = C2JUtils.toUnsignedByte(buf.get()); 
       int b = C2JUtils.toUnsignedByte(buf.get());
       int c =  C2JUtils.toUnsignedByte(buf.get());
       // Quite anomalous, leveltime is stored as a BIG ENDIAN, 24-bit unsigned integer :-S
       leveltime = (a<<16) | (b<<8) | c; 

       // Mark this position...
       buf.mark();
       buf.position(buf.limit()-1);
       if (buf.get() != 0x1d) properend=false; else
           properend=true;
       buf.reset();
           
       // We've loaded whatever consistutes "header" info, the rest must be unpacked by proper
       // methods in the game engine itself.
   }
   
   @Override
   public void write(DataOutputStream f)
           throws IOException {
       DoomIO.writeString(f,name,SAVESTRINGSIZE);
       DoomIO.writeString(f,vcheck,VERSIONSIZE);
       f.writeByte(gameskill.ordinal()); 
       f.writeByte(gameepisode);
       f.writeByte(gamemap);
       for (int i=0 ; i<MAXPLAYERS ; i++) 
       f.writeBoolean(playeringame[i]); 

       // load a base level (this doesn't advance the pointer?) 
       //G_InitNew (gameskill, gameepisode, gamemap); 
    
       // get the times 
       byte a = (byte) (0x0000FF&(leveltime>>16)); 
       byte b = (byte) (0x00FF&(leveltime>>8));
       byte c =  (byte) (0x00FF&(leveltime));
       // Quite anomalous, leveltime is stored as a BIG ENDIAN, 24-bit unsigned integer :-S
       f.writeByte(a);
       f.writeByte(b);
       f.writeByte(c);

       // The end. This is actually just the header, so we don't "end" here just yet.
       // f.writeByte(0x1d);
           
   } 
   
   @Override
   public void read(DataInputStream f)
           throws IOException {
       name= DoomIO.readNullTerminatedString(f,SAVESTRINGSIZE);
       vcheck=DoomIO.readNullTerminatedString(f,VERSIONSIZE);
       gameskill=skill_t.values()[f.readUnsignedByte()]; 
       gameepisode=f.readByte();
       gamemap=f.readByte();
       for (int i=0 ; i<MAXPLAYERS ; i++) 
           playeringame[i]=f.readBoolean(); 
    
       // get the times 
       int a = f.readUnsignedByte(); 
       int b = f.readUnsignedByte();
       int c = f.readUnsignedByte();
       // Quite anomalous, leveltime is stored as a BIG ENDIAN, 24-bit unsigned integer :-S
       leveltime = (a<<16) | (b<<8) | c; 
           
   } 
   
   ////////////////////////// NASTY GETTERS //////////////////////////////
    
    @Override
    public String getName() {
        return name;
    }
    
    @Override
    public void setName(String name) {
        this.name = name;
    }
    @Override
    public String getVersion() {
        return vcheck;
    }
    @Override
    public void setVersion(String vcheck) {
        this.vcheck = vcheck;
    }
    @Override
    public skill_t getGameskill() {
        return gameskill;
    }
    @Override
    public void setGameskill(skill_t gameskill) {
        this.gameskill = gameskill;
    }
    @Override
    public int getGameepisode() {
        return gameepisode;
    }
    @Override
    public void setGameepisode(int gameepisode) {
        this.gameepisode = gameepisode;
    }
    @Override
    public int getGamemap() {
        return gamemap;
    }
    @Override
    public void setGamemap(int gamemap) {
        this.gamemap = gamemap;
    }
    @Override
    public boolean[] getPlayeringame() {
        return playeringame;
    }
    @Override
    public void setPlayeringame(boolean[] playeringame) {
        this.playeringame = playeringame;
    }
    @Override
    public int getLeveltime() {
        return leveltime;
    }
    @Override
    public void setLeveltime(int leveltime) {
        this.leveltime = leveltime;
    }
    @Override
    public boolean isWrongversion() {
        return wrongversion;
    }
    @Override
    public void setWrongversion(boolean wrongversion) {
        this.wrongversion = wrongversion;
    }
    @Override
    public boolean isProperend() {
        return properend;
    }

}

package savegame;

import defines.skill_t;


/** A Save Game Header should be able to be loaded quickly and return 
 *  some basic info about it (name, version, game time, etc.) in an unified
 *  manner, no matter what actual format you use for saving.
 * 
 * @author admin
 *
 */

public interface IDoomSaveGameHeader {

    String getName();

    void setName(String name);

    skill_t getGameskill();

    void setGameskill(skill_t gameskill);
    
    String getVersion();

    void setVersion(String vcheck);

    int getGameepisode();
    
    void setGameepisode(int gameepisode);

    boolean isProperend();

    void setWrongversion(boolean wrongversion);

    boolean isWrongversion();

    void setLeveltime(int leveltime);

    int getLeveltime();

    void setPlayeringame(boolean[] playeringame);

    boolean[] getPlayeringame();

    void setGamemap(int gamemap);

    int getGamemap();

}

package timing;

import static data.Defines.TICRATE;

public class NanoTicker
        implements ITicker {

    /**
     * I_GetTime
     * returns time in 1/70th second tics
     */
   
    @Override
    public int GetTime ()
    {
        long    tp;
        //struct timezone   tzp;
        int         newtics;
        
        // Attention: System.nanoTime() might not be consistent across multicore CPUs.
        // To avoid the core getting back to the past,
        tp=System.nanoTime();
        if (basetime==0)
        basetime = tp;
        newtics = (int) (((tp-basetime)*TICRATE)/1000000000);// + tp.tv_usec*TICRATE/1000000;
        if (newtics<oldtics) {
            System.err.printf("Timer discrepancies detected : %d",(++discrepancies));
            return oldtics;
        }
        return (oldtics=newtics);
    }

    protected volatile long basetime=0;
    protected volatile int oldtics=0;
    protected volatile int discrepancies;
    
}

package timing;

public interface ITicker {
    public int GetTime();
    
}

package timing;

import static data.Defines.TICRATE;

public class MilliTicker
        implements ITicker {

    /**
     * I_GetTime
     * returns time in 1/70th second tics
     */
   
    @Override    public int GetTime ()
    {
        long    tp;
        //struct timezone   tzp;
        int         newtics;
        
        tp=System.currentTimeMillis();
        if (basetime==0)
        basetime = tp;
        newtics = (int) (((tp-basetime)*TICRATE)/1000);
        return newtics;
    }
    
    protected volatile long basetime=0;
    protected volatile int oldtics=0;
    protected volatile int discrepancies;
    
}

package timing;

public class FastTicker
        implements ITicker {

    /**
     * I_GetTime
     * returns time in 1/70th second tics
     */
   
    @Override
    public int GetTime ()
    {
        return fasttic++;
    }

    protected volatile int fasttic=0;
    
}

package s;

import static data.sounds.S_sfx;

import java.util.HashMap;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Semaphore;

import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.SourceDataLine;

import pooling.AudioChunkPool;
import data.sounds.sfxenum_t;
import doom.DoomStatus;

/**
 * A spiffy new sound system, based on the Classic sound driver.
 * It is entirely asynchronous (runs in its own thread) and even has its own timer.
 * This allows it to continue mixing even when the main loop is not responding 
 * (something which, arguably, could be achieved just with a timer calling
 * UpdateSound and SubmitSound). Uses message passing to deliver channel status
 * info, and mixed audio directly without using an intermediate buffer,
 * saving memory bandwidth.
 * 
 * PROS:
 * a) All those of ClassicSoundDriver plus:
 * b) Continues normal playback even under heavy CPU load, works smoother
 *    even on lower powered CPUs.
 * c) More efficient due to less copying of audio blocks.
 * c) Fewer audio glitches compared to ClassicSoundDriver.
 * 
 * CONS:
 * a) All those of ClassicSoundDriver plus regarding timing accuracy.
 * 
 * @author Maes
 */

public class SuperDoomSoundDriver extends AbstractSoundDriver
         {

    protected final Semaphore produce;

    protected final Semaphore consume;

    protected final Semaphore update_mixer;

    protected int chunk = 0;

    //protected FileOutputStream fos;
    //protected DataOutputStream dao;

    // The one and only line
    protected SourceDataLine line = null;

    protected HashMap<Integer, byte[]> cachedSounds =
        new HashMap<Integer, byte[]>();

    protected final Timer MIXTIMER;
        
    public SuperDoomSoundDriver(DoomStatus DS, int numChannels) {
    	super(DS,numChannels);
        channels = new boolean[numChannels];
        produce = new Semaphore(1);
        consume = new Semaphore(1);
        update_mixer = new Semaphore(1);
        produce.drainPermits();
        update_mixer.drainPermits();
        this.MIXSRV=new MixServer(numChannels);
        MIXTIMER= new Timer(true);
        // Sound tics every 1/35th of a second. Grossly
        // inaccurate under Windows though, will get rounded
        // down to the closest multiple of 15 or 16 ms.
        MIXTIMER.schedule(new SoundTimer(), 0,SOUND_PERIOD);        
    }




    /** These are still defined here to decouple them from the mixer's 
     *  ones, however they serve  more as placeholders/status indicators;
     */
    protected volatile boolean[] channels;

    protected volatile boolean mixed = false;

    /**
     * This function loops all active (internal) sound channels, retrieves a
     * given number of samples from the raw sound data, modifies it according to
     * the current (internal) channel parameters, mixes the per channel samples
     * into the global mixbuffer, clamping it to the allowed range, and sets up
     * everything for transferring the contents of the mixbuffer to the (two)
     * hardware channels (left and right, that is). This function currently
     * supports only 16bit.
     */

    public void UpdateSound() {
    	// This is pretty much a dummy.
    	// The mixing thread goes on by itself, guaranteeing that it will
    	// carry out at least currently enqueued mixing messages, regardless
    	// of how badly the engine lags.

    }

    /**
     * SFX API Note: this was called by S_Init. However, whatever they did in
     * the old DPMS based DOS version, this were simply dummies in the Linux
     * version. See soundserver initdata().
     */

    @Override
    public void SetChannels(int numChannels) {
        // Init internal lookups (raw data, mixing buffer, channels).
        // This function sets up internal lookups used during
        // the mixing process.

        int steptablemid = 128;

        // Okay, reset internal mixing channels to zero.
        for (int i = 0; i < this.numChannels; i++) {
            channels[i] = false;
        }
        
        generateStepTable(steptablemid);

        generateVolumeLUT();
    }
    
    protected  PlaybackServer SOUNDSRV;
    protected final MixServer MIXSRV;
    
    protected Thread MIXTHREAD;
    protected Thread SOUNDTHREAD;

    @Override
    public boolean InitSound() {

        // Secure and configure sound device first.
        System.err.println("I_InitSound: ");

        // We only need a single data line.
        // PCM, signed, 16-bit, stereo, 22025 KHz, 2048 bytes per "frame",
        // maximum of 44100/2048 "fps"
        AudioFormat format = new AudioFormat(SAMPLERATE, 16, 2, true, true);

        DataLine.Info info = new DataLine.Info(SourceDataLine.class, format);

        if (AudioSystem.isLineSupported(info))
            try {
                line = (SourceDataLine) AudioSystem.getSourceDataLine(format);
                line.open(format, AUDIOLINE_BUFFER);
            }	catch (Exception e) {
                e.printStackTrace();
                System.err.print("Could not play signed 16 data\n");
                return false;
            }

        if (line != null) {
            System.err.print(" configured audio device\n");
            line.start();
        } else {
        	 System.err.print(" could not configure audio device\n");
        	 return false;
        }

        SOUNDSRV = new PlaybackServer(line);
        SOUNDTHREAD = new Thread(SOUNDSRV);
        SOUNDTHREAD.setDaemon(true);
        SOUNDTHREAD.start();
        // Vroom!        
        MIXTHREAD= new Thread(MIXSRV);
        MIXTHREAD.setDaemon(true);
        MIXTHREAD.start();
        
        // Initialize external data (all sounds) at start, keep static.
        System.err.print("I_InitSound: ");

        super.initSound8();

        System.err.print(" pre-cached all sound data\n");

        // Finished initialization.
        System.err.print("I_InitSound: sound module ready\n");
        
        return true;

    }

    @Override
    protected int addsfx(int sfxid, int volume, int step, int seperation) {
        int i;
        int rc = -1;

        int oldest = DS.gametic;
        int oldestnum = 0;
        int slot;

        int rightvol;
        int leftvol;

        int broken=-1;
        
        // Chainsaw troubles.
        // Play these sound effects only one at a time.
        if (sfxid == sfxenum_t.sfx_sawup.ordinal()
                || sfxid == sfxenum_t.sfx_sawidl.ordinal()
                || sfxid == sfxenum_t.sfx_sawful.ordinal()
                || sfxid == sfxenum_t.sfx_sawhit.ordinal()
                || sfxid == sfxenum_t.sfx_stnmov.ordinal()
                || sfxid == sfxenum_t.sfx_pistol.ordinal()) {
            // Loop all channels, check.
            for (i = 0; i < numChannels; i++) {
                // Active, and using the same SFX?
                if (channels[i] && (channelids[i] == sfxid)) {
                    // Reset.
                	
                	MixMessage m=new MixMessage();
                	m.stop=true;
                	
                    // We are sure that iff,
                    // there will only be one.
                    broken=i;
                    break;
                }
            }
        }

        // Loop all channels to find oldest SFX.
        if (broken>=0) {
        	i=broken;
        	oldestnum=broken;
        }
        else
        for (i = 0; (i < numChannels) && channels[i]; i++) {
            if (channelstart[i] < oldest) {
                oldestnum = i;
            }
        }

        oldest = channelstart[oldestnum];
        
        // Tales from the cryptic.
        // If we found a channel, fine.
        // If not, we simply overwrite the first one, 0.
        // Probably only happens at startup.
        if (i == numChannels)
            slot = oldestnum;
        else
            slot = i;

        
        MixMessage m=new MixMessage();
        
        // Okay, in the less recent channel,
        // we will handle the new SFX.
        // Set pointer to raw data.
        channels[slot]=true;
        m.channel=slot;
        m.data=S_sfx[sfxid].data;

        // MAES: if you don't zero-out the channel pointer here, it gets ugly
        m.pointer= 0;

        // Set pointer to end of raw data.
        m.end = lengths[sfxid];

        // Reset current handle number, limited to 0..100.
        if (handlenums == 0) // was !handlenums, so it's actually 1...100?
            handlenums = 100;

        // Assign current handle number.
        // Preserved so sounds could be stopped (unused).
        // Maes: this should really be decreasing, otherwide handles
        // should start at 0 and go towards 100. Just saying.
        channelhandles[slot] = rc = handlenums--;

        // Set stepping???
        // Kinda getting the impression this is never used.
        // MAES: you're wrong amigo.
        m.step= step;
        // ???
        m.remainder = 0;
        // Should be gametic, I presume.
        channelstart[slot] = DS.gametic;

        // Separation, that is, orientation/stereo.
        // range is: 1 - 256
        seperation += 1;

        // Per left/right channel.
        // x^2 seperation,
        // adjust volume properly.
        leftvol = volume - ((volume * seperation * seperation) >> 16); // /(256*256);
        seperation = seperation - 257;
        rightvol = volume - ((volume * seperation * seperation) >> 16);

        // Sanity check, clamp volume.

        if (rightvol < 0 || rightvol > 127)
            DS.I.Error("rightvol out of bounds");

        if (leftvol < 0 || leftvol > 127)
            DS.I.Error("leftvol out of bounds");

        // Get the proper lookup table piece
        // for this volume level???
        m.leftvol_lookup = vol_lookup[leftvol];
        m.rightvol_lookup = vol_lookup[rightvol];

        // Preserve sound SFX id,
        // e.g. for avoiding duplicates of chainsaw.
        channelids[slot] = sfxid;

        if (D) System.err.println(channelStatus());
        if (D) System.err.printf(
                "Playing sfxid %d handle %d length %d vol %d on channel %d\n",
                sfxid, rc, S_sfx[sfxid].data.length, volume, slot);

        
        MIXSRV.submitMixMessage(m);
        
        // You tell me.
        return rc;
    }

    @Override
    public void ShutdownSound() {

        boolean done;

        // Unlock sound thread if it's waiting.
        produce.release();
        update_mixer.release();

        int i=0;
        do {
        	done=true;
            for (i=0; i < numChannels; i++) {
            	// If even one channel is playing, loop again.
            	done&=!channels[i];            	
            	}
            	//System.out.println(done+" "+this.channelStatus());
            	
            } while (!done);
        
        
        this.line.flush();
        
        
        SOUNDSRV.terminate = true;
        MIXSRV.terminate = true;
        produce.release();
        update_mixer.release();
        try {
            SOUNDTHREAD.join();
            MIXTHREAD.join();
        } catch (InterruptedException e) {
        	// Well, I don't care.
        }
        System.err.printf("3\n");
        line.close();
        System.err.printf("4\n");

    }

    protected class PlaybackServer
            implements Runnable {

        public boolean terminate = false;

        public PlaybackServer(SourceDataLine line) {
            this.auline = line;
        }

        private SourceDataLine auline;

        private ArrayBlockingQueue<AudioChunk> audiochunks =
            new ArrayBlockingQueue<AudioChunk>(BUFFER_CHUNKS * 2);

        public void addChunk(AudioChunk chunk) {
            audiochunks.offer(chunk);
        }

        public volatile int currstate = 0;

        public void run() {

            while (!terminate) {

                // while (timing[mixstate]<=mytime){

                // Try acquiring a produce permit before going on.

                try {
                    //System.err.print("Waiting for a permit...");
                    produce.acquire();
                    //System.err.print("...got it\n");
                } catch (InterruptedException e) {
                    // Well, ouch.
                    e.printStackTrace();
                }

                int chunks = 0;

                // System.err.printf("Audio queue has %d chunks\n",audiochunks.size());

                // Play back only at most a given number of chunks once you reach
                // this spot.
                
                int atMost=Math.min(ISoundDriver.BUFFER_CHUNKS,audiochunks.size());
                
                while (atMost-->0){

                    AudioChunk chunk = null;
                    try {
                        chunk = audiochunks.take();
                    } catch (InterruptedException e1) {
                        // Should not block
                    }
                    // Play back all chunks present in a buffer ASAP
                    auline.write(chunk.buffer, 0, MIXBUFFERSIZE);
                    chunks++;
                    // No matter what, give the chunk back!
                    chunk.free = true;
                    audiochunkpool.checkIn(chunk);
                }
                
                //System.err.println(">>>>>>>>>>>>>>>>> CHUNKS " +chunks);
                // Signal that we consumed a whole buffer and we are ready for
                // another one.
                
                consume.release();
            }
        }
    }
    
    /** A single channel does carry a lot of crap, figuratively speaking.
     *  Instead of making updates to ALL channel parameters, it makes more
     *  sense having a "mixing queue" with instructions that tell the 
     *  mixer routine to do so-and-so with a certain channel. The mixer
     *  will then "empty" the queue when it has completed a complete servicing
     *  of all messages and mapped them to its internal status.
     *
     */
    protected class MixMessage {
    	/** If this is set, the mixer considers that channel "muted" */
    	public boolean stop;
    	
    	/** This signals an update of a currently active channel. 
    	 * Therefore pointer, remainder and data should remain untouched. 
    	 * However volume and step of a particular channel can change.
    	 */
    	public boolean update; 
    	
		public int remainder;
		public int end;
		public int channel;
    	public byte[] data;    	
    	public int step;
    	public int stepremainder;
    	public int[] leftvol_lookup;
    	public int[] rightvol_lookup;

    	public int pointer;
    	
    }
    
    /** Mixing thread. Mixing and submission must still go on even if
     *  the engine lags behind due to excessive CPU load.
     * 
     * @author Maes
     *
     */
    protected class MixServer
    implements Runnable {

        private final ArrayBlockingQueue<MixMessage> mixmessages;
    	
        /**
         * MAES: we'll have to use this for actual pointing. channels[] holds just
         * the data.
         */
        protected int[] p_channels;

        /**
         * The second one is supposed to point at "the end", so I'll make it an int.
         */
        protected int[] channelsend;

    	private final byte[][] channels;
        /** The channel step amount... */
        protected final int[] channelstep;

        /** ... and a 0.16 bit remainder of last step. */
        protected final int[] channelstepremainder;
        
        protected final int[][] channelrightvol_lookup;
        protected final int[][] channelleftvol_lookup;
    	    	
    	private volatile boolean update=false;
    	
    	public MixServer(int numChannels){
    		// We can put only so many messages "on hold"
    		mixmessages=new ArrayBlockingQueue<MixMessage>(35*numChannels);
    		this.p_channels=new int[numChannels];
    		this.channels=new byte[numChannels][];
    		this.channelstepremainder=new int[numChannels];
    		this.channelsend=new int[numChannels];
    		this.channelstep=new int[numChannels];
    		this.channelleftvol_lookup=new int[numChannels][];
    		this.channelrightvol_lookup=new int[numChannels][];
    	}
    	
    	/** Adds a channel mixing message to the queue */
    	
    	public void submitMixMessage(MixMessage m){
    	    try{
    		this.mixmessages.add(m);
    	    } catch (IllegalStateException  e){
    	        // Queue full. Force clear (VERY rare).
    	        mixmessages.clear();
    	        mixmessages.add(m);
    	    }
    		}
    	
    	public boolean terminate=false;
    	
    	@Override
		public void run()  {

	        // Mix current sound data.
	        // Data, from raw sound, for right and left.
	        int sample = 0;
	        int dl;
	        int dr;

	        // Pointers in global mixbuffer, left, right, end.
	        // Maes: those were explicitly signed short pointers...

	        int leftout;
	        int rightout;
	        
	        // Step in mixbuffer, left and right, thus two.
	        final int step=4;

	        // Mixing channel index.
	        int chan;
	        
	        // Determine end, for left channel only
	        // (right channel is implicit).
	        // MAES: this implies that the buffer will only mix
	        // that many samples at a time, and that the size is just right.
	        // Thus, it must be flushed (p_mixbuffer=0) before reusing it.
	        final int leftend = SAMPLECOUNT * step;

	        // Mix the next chunk, regardless of what the rest of the game is doing. 
	        while (!terminate) {
	        	
		        // POINTERS to Left and right channel
		        // which are in global mixbuffer, alternating.

		        leftout = 0;
		        rightout = 2;

	        	// Wait on interrupt semaphore anyway before draining queue.
	        	// This allows continuing mixing even if the main game loop
	        	// is stalled. This will result in continuous sounds,
	        	// rather than choppy interruptions.

	        		try {
	        			//System.err.print("Waiting on semaphore...");
						update_mixer.acquire();
						//System.err.print("...broke free\n");
					} catch (InterruptedException e) {
						// Nothing to do. Suck it down.
					}
	        	
	        	
	        	
	        	// Get current number of element in queue.
	        	// At worse, there will be none.
	        	int messages=mixmessages.size();
	
	        	// Drain the queue, applying changes to currently
	        	// looping channels, if applicable. This may result in new channels,
	        	// older ones being stopped, or current ones being altered. Changes
	        	// will be applied with priority either way.
	        	if (messages>0) drainAndApply(messages);

	        	// This may have changed in the mean.
	        	mixed=activeChannels();
	        
	        if (mixed) {// Avoid mixing entirely if no active channel.
			
				// Get audio chunk NOW
				gunk= audiochunkpool.checkOut();
    	        // Ha ha you're ass is mine!
    	        gunk.free = false;
				mixbuffer=gunk.buffer;
			
	        while (leftout < leftend) {
	            // Reset left/right value.
	            dl = 0;
	            dr = 0;

	            // Love thy L2 chache - made this a loop.
	            // Now more channels could be set at compile time
	            // as well. Thus loop those channels.

	            for (chan = 0; chan < numChannels; chan++) {

	                // Check channel, if active.
	                // MAES: this means that we must point to raw data here.
	                if (channels[chan] != null) {
	                    int channel_pointer = p_channels[chan];

	                    // Get the raw data from the channel.
	                    // Maes: this is supposed to be an 8-bit unsigned value.
	                        sample = 0x00FF & channels[chan][channel_pointer];
	                        
	                    // Add left and right part for this channel (sound)
	                    // to the current data. Adjust volume accordingly.                        
	                    // Q: could this be optimized by converting samples to 16-bit
	                    // at load time, while also allowing for stereo samples?
	                    // A: Only for the stereo part. You would still look a lookup
	                    // for the CURRENT volume level.

	                    dl += channelleftvol_lookup[chan][sample];
	                    dr += channelrightvol_lookup[chan][sample];

	                    // This should increment the index inside a channel, but is
	                    // expressed in 16.16 fixed point arithmetic.
	                    channelstepremainder[chan] += channelstep[chan];

	                    // The actual channel pointer is increased here.
	                    // The above trickery allows playing back different pitches.
	                    // The shifting retains only the integer part.
	                    channel_pointer += channelstepremainder[chan] >> 16;

	                    // This limits it to the "decimal" part in order to
	                    // avoid undue accumulation.
	                    channelstepremainder[chan] &= 0xFFFF;

	                    // Check whether we are done. Also to avoid overflows.
	                    if (channel_pointer >= channelsend[chan]) {
	                        // Reset pointer for a channel.
	                           if (D)  System.err
	                                    .printf(
	                                        "Channel %d handle %d pointer %d thus done, stopping\n",
	                                        chan, channelhandles[chan],
	                                        channel_pointer);
	                        channels[chan] = null;
	                        
	                        // Communicate back to driver.
	                        SuperDoomSoundDriver.this.channels[chan]=false;
	                        channel_pointer = 0;
	                    }

	                    // Write pointer back, so we know where a certain channel
	                    // is the next time UpdateSounds is called.

	                    p_channels[chan] = channel_pointer;
	                }

	            } // for all channels.

	            // MAES: at this point, the actual values for a single sample
	            // (YIKES!) are in d1 and d2. We must use the leftout/rightout
	            // pointers to write them back into the mixbuffer.

	            // Clamp to range. Left hardware channel.
	            // Remnant of 8-bit mixing code? That must have raped ears
	            // and made them bleed.
	            // if (dl > 127) *leftout = 127;
	            // else if (dl < -128) *leftout = -128;
	            // else *leftout = dl;

	            if (dl > 0x7fff)
	                dl = 0x7fff;
	            else if (dl < -0x8000)
	                dl = -0x8000;

	            // Write left channel
	            mixbuffer[leftout] = (byte) ((dl & 0xFF00) >>> 8);
	            mixbuffer[leftout + 1] = (byte) (dl & 0x00FF);

	            // Same for right hardware channel.
	            if (dr > 0x7fff)
	                dr = 0x7fff;
	            else if (dr < -0x8000)
	                dr = -0x8000;

	            // Write right channel.
	            mixbuffer[rightout] = (byte) ((dr & 0xFF00) >>> 8);
	            mixbuffer[rightout + 1] = (byte) (dr & 0x00FF);

	            // Increment current pointers in mixbuffer.
	            leftout += step;
	            rightout += step;
	        } // End leftend/leftout while

	       // for (chan = 0; chan < numChannels; chan++) {
	       // 	if (channels[chan]!=null){
	       // 		System.err.printf("Channel %d pointer %d\n",chan,this.p_channels[chan]);
	       // 	}
	       // }

		   } // if-mixed
		   
	        // After an entire buffer has been mixed, we can apply any updates.
			// This includes silent updates.
	        submitSound();
			
	        
	        } // terminate loop
    	}	
    	
		private AudioChunk gunk;
		
    		private final void submitSound(){
    			// It's possible to stay entirely silent and give the audio
    	        // queue a chance to get drained. without sending any data.
    			// Saves BW and CPU cycles.
    	        if (mixed) {
    	            silence=0;


    	            // System.err.printf("Submitted sound chunk %d to buffer %d \n",chunk,mixstate);

    	            // Copy the currently mixed chunk into its position inside the
    	            // master buffer.
    	            // System.arraycopy(mixbuffer, 0, gunk.buffer, 0, MIXBUFFERSIZE);

    	            SOUNDSRV.addChunk(gunk);

    	            // System.err.println(chunk++);

    	            chunk++;
    	            // System.err.println(chunk);

    	            if (consume.tryAcquire())
    	                produce.release();

    	        } else {
    	            silence++;
    	            // MAES: attempt to fix lingering noise error
    	            if (silence >ISoundDriver.BUFFER_CHUNKS){
    	                line.flush();
    	                silence=0;
    	                }
    	        }
    		}
    	
    		/** Drains message queue and applies to individual channels. 
    		 *  More recently enqueued messages will trump older ones. This method
    		 *  only changes the STATUS of channels, and actual message submissions 
    		 *  can occur at most every sound frame. 
    		 *  
    		 * @param messages
    		 */
    		
	        private void drainAndApply(int messages ) {			
	        	MixMessage m;
	        	for (int i=0;i<messages;i++){
	        		// There should be no problems, in theory.
	        		m=this.mixmessages.remove();
	        		if (m.stop){
	        			stopChannel(m.channel);
	        			}
	        		else if (m.update){
	        			updateChannel(m);
	        		} else insertChannel(m);			
	        		}
	        	}
	        
	        private final void stopChannel(int channel){
	        	//System.err.printf("Stopping channel %d\n",channel);
	        	this.channels[channel]=null;
    			this.p_channels[channel]=0;
	        	}
	        
	        private final void updateChannel(MixMessage m){
	        	//System.err.printf("Updating channel %d\n",m.channel);
	        	this.channelleftvol_lookup[m.channel]=m.leftvol_lookup;
	        	this.channelrightvol_lookup[m.channel]=m.rightvol_lookup;
	        	this.channelstep[m.channel]=m.step;
	        	this.channelsend[m.channel]=m.end;
	        }
	        
	        private final void insertChannel(MixMessage m){
	        	int ch=m.channel;
	        	//System.err.printf("Inserting channel %d\n",ch);
    			this.channels[ch]=m.data;
    			this.p_channels[ch]=m.pointer;
    			this.channelsend[ch]=m.end;
    			this.channelstepremainder[ch]=m.remainder;
	        	this.channelleftvol_lookup[ch]=m.leftvol_lookup;
	        	this.channelrightvol_lookup[ch]=m.rightvol_lookup;
	        	this.channelstep[ch]=m.step;
	        }
			
			private final boolean activeChannels(){
		        for (int chan = 0; chan < numChannels; chan++) {
		            if (channels[chan] != null)
		                // SOME mixing has taken place.
		                return true;
		        		}
		        
		        return false;
	        }
			
			public final boolean channelIsPlaying(int num){
				return (channels[num]!=null);
			}
	        
		}

    
    @Override
    public boolean SoundIsPlaying(int handle) {

        int c = getChannelFromHandle(handle);
        return (c != -2 && channels[c]);

    }

    /**
     * Internal use.
     * 
     * @param handle
     * @return the channel that has the handle, or -2 if none has it.
     */
    protected int getChannelFromHandle(int handle) {
        // Which channel has it?
        for (int i = 0; i < numChannels; i++) {
            if (channelhandles[i] == handle)
                return i;
        }

        return BUSY_HANDLE;
    }

    @Override
    public void StopSound(int handle) {
        // Which channel has it?
        int hnd = getChannelFromHandle(handle);
        if (hnd >= 0) {
        	
            channels[hnd] = false;
            
            
            this.channelhandles[hnd] = IDLE_HANDLE;
            
			MixMessage m=new MixMessage();
			m.channel=hnd;
			m.stop=true;
			// We can only "ask" the mixer to stop at the next
			//chunk.
            MIXSRV.submitMixMessage(m);
        }
    }

    @Override
    public void SubmitSound() {

        // Also a dummy. The mixing thread is in a better position to
    	// judge when sound should be submitted.
    }
    
    private int silence=0; 

    @Override
    public void UpdateSoundParams(int handle, int vol, int sep, int pitch) {

        int chan = this.getChannelFromHandle(handle);
        // Per left/right channel.
        // x^2 seperation,
        // adjust volume properly.
        int leftvol = vol - ((vol * sep * sep) >> 16); // /(256*256);
        sep = sep - 257;
        int rightvol = vol - ((vol * sep * sep) >> 16);

        // Sanity check, clamp volume.

        if (rightvol < 0 || rightvol > 127)
            DS.I.Error("rightvol out of bounds");

        if (leftvol < 0 || leftvol > 127)
            DS.I.Error("leftvol out of bounds");

        MixMessage m=new MixMessage();
        
        // We are updating a currently active channel
        m.update=true;
        m.channel=chan;
        
        // Get the proper lookup table piece
        // for this volume level???
        
        m.leftvol_lookup = vol_lookup[leftvol];
        m.rightvol_lookup = vol_lookup[rightvol];

        // Well, if you can get pitch to change too...
        m.step = steptable[pitch];
        
        // Oddly enough, we could be picking a different channel here? :-S
        m.end = lengths[channelids[chan]];
        
        
        MIXSRV.submitMixMessage(m);
    }

    protected StringBuilder sb = new StringBuilder();

    public String channelStatus() {
        sb.setLength(0);
        for (int i = 0; i < numChannels; i++) {
            if (MIXSRV.channelIsPlaying(i))
                sb.append(i);
            else
                sb.append('-');
        }

        return sb.toString();

    }

    
    // Schedule this to release the sound thread at regular intervals
    // so that it doesn't outrun the audioline's buffer and game updates.
    
    protected class SoundTimer extends TimerTask {
        public void run() {
           update_mixer.release();         
        }
      }
    
    
    protected final AudioChunk SILENT_CHUNK = new AudioChunk();

    protected final AudioChunkPool audiochunkpool = new AudioChunkPool();
}
package s;

import data.sfxinfo_t;
import data.sounds.musicenum_t;
import data.sounds.sfxenum_t;
import p.mobj_t;

// Emacs style mode select   -*- C++ -*- 
//-----------------------------------------------------------------------------
//
// $Id: IDoomSound.java,v 1.5 2011/08/24 15:55:12 velktron Exp $
//
// Copyright (C) 1993-1996 by id Software, Inc.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// DESCRIPTION:
//	The not so system specific sound interface (s_sound.*)
// Anything high-level like e.g. handling of panning, sound origin,
// sound multiplicity etc. should be handled here, but not e.g. actual
// sound playback, sound threads, etc.
//  That's the job of ISound and IMusic (roughly equivelnt to i_sound.*, but
// with separate concerns for SFX and MUSIC.
//
//-----------------------------------------------------------------------------

public interface IDoomSound {

	class channel_t{

		    // sound information (if null, channel avail.)
		    sfxinfo_t	sfxinfo;

		    // origin of sound
		    ISoundOrigin	origin;

		    // handle of the sound being played
		    int		handle;
			}
	
	/** Convenience hack */
	public static final int NUMSFX=sfxenum_t.NUMSFX.ordinal();
	
	// Purpose?
	public static final char snd_prefixen[]
	                                      = { 'P', 'P', 'A', 'S', 'S', 'S', 'M', 'M', 'M', 'S', 'S', 'S' };

	public static final int S_MAX_VOLUME=127;

	// when to clip out sounds
	// Does not fit the large outdoor areas.
	public static final int S_CLIPPING_DIST	=	(1200*0x10000);

	// Distance tp origin when sounds should be maxed out.
	// This should relate to movement clipping resolution
	// (see BLOCKMAP handling).
	// Originally: (200*0x10000).
	public static final int S_CLOSE_DIST	=(160*0x10000);

	public static final int S_ATTENUATOR	=((S_CLIPPING_DIST-S_CLOSE_DIST)>>m.fixed_t.FRACBITS);

	// Adjustable by menu.
	//protected final int NORM_VOLUME    		snd_MaxVolume

	public static final int NORM_PITCH    = 		128;
	public final static int NORM_PRIORITY	=	64;
	public final static int NORM_SEP		=128;

	public final static int S_PITCH_PERTURB	=	1;
	public final static int S_STEREO_SWING	=	(96*0x10000);

	// percent attenuation from front to back
	public final static int S_IFRACVOL	=	30;

	public final static int NA	=		0;
	public final static int S_NUMCHANNELS=		2;

	/**
	 * Initializes sound stuff, including volume Sets channels, SFX and music
	 * volume, allocates channel buffer, sets S_sfx lookup.
	 */

	void Init(int sfxVolume, int musicVolume);

	/**
	 * Per level startup code. Kills playing sounds at start of level,
	 * determines music if any, changes music.
	 */
	public void Start();

	/**
	 * Start sound for thing at <origin> using <sound_id> from sounds.h
	 */
	public void StartSound(ISoundOrigin origin, int sound_id);

	/**
	 * Start sound for thing at <origin> using <sound_id> from sounds.h
	 * Convenience method using sfxenum_t instead. Delegated to int version.
	 * 
	 */
	public void StartSound(ISoundOrigin origin, sfxenum_t sound_id);
	
	/** Will start a sound at a given volume. */
	public void StartSoundAtVolume(ISoundOrigin origin, int sound_id, int volume);

	/** Stop sound for thing at <origin> */
	public void StopSound(ISoundOrigin origin);

	/**
	 * Start music using <music_id> from sounds.h, and set whether looping
	 * 
	 * @param musicnum
	 * @param looping
	 */
	public void ChangeMusic(int musicnum, boolean looping);
	
	public void ChangeMusic(musicenum_t musicnum, boolean looping);

	/** Stops the music fer sure. */
	public void StopMusic();

	/** Stop and resume music, during game PAUSE. */
	public void PauseSound();

	public void ResumeSound();

	/**
	 * Updates music & sounds
	 * 
	 * @param listener
	 */
	public void UpdateSounds(mobj_t listener);

	public void SetMusicVolume(int volume);

	public void SetSfxVolume(int volume);


	/** Start music using <music_id> from sounds.h */
	public void StartMusic(int music_id);
	
	/** Start music using <music_id> from sounds.h 
	 *  Convenience method using musicenum_t.
	 */
	public void StartMusic(musicenum_t music_id);
	
	//
	// Internals. 
	// 
	// MAES: these appear to be only of value for internal implementation,
	// and are never called externally. Thus, they might as well
	// not be part of the interface, even though it's convenient to reuse them.
	//
	
	/*
	int
	S_getChannel
	( mobj_t		origin,
	  sfxinfo_t	sfxinfo );


	int
	S_AdjustSoundParams
	( mobj_t	listener,
	  mobj_t	source,
	  int		vol,
	  int		sep,
	  int		pitch );

	void S_StopChannel(int cnum);
	*/

}

package s;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import javax.sound.midi.InvalidMidiDataException;
import javax.sound.midi.MidiDevice;
import javax.sound.midi.MidiEvent;
import javax.sound.midi.MidiMessage;
import javax.sound.midi.MidiSystem;
import javax.sound.midi.MidiUnavailableException;
import javax.sound.midi.Receiver;
import javax.sound.midi.Sequence;
import javax.sound.midi.ShortMessage;
import javax.sound.midi.SysexMessage;
import javax.sound.midi.Track;
import javax.sound.midi.Transmitter;

/** A music driver that bypasses Sequences and sends events from a MUS lump
 *  directly to a MIDI device.
 *
 *  Some songs (e.g. D_E1M8) vary individual channel volumes dynamically. This
 *  driver multiplies the dynamic volume by the music volume set in the menu.
 *  This does not work well with a {@link Sequence} because changes to events
 *  (e.g. channel volume change events) do not take effect while the sequencer
 *  is running.
 *  
 *  Disadvantages of this driver:
 *  <ul><li>Supports MUS lumps only (no MID, OGG etc.)</li>
 *      <li>Creates its own thread</li>
 *      <li>Pausing is not implemented yet</li></ul>
 *
 * @author finnw
 *
 */
public class FinnwMusicModule implements IMusic {

    public FinnwMusicModule() {
        this.lock = new ReentrantLock();
        this.channels = new ArrayList<Channel>(15);
        this.songs = new ArrayList<Song>(1);
        for (int midiChan = 0; midiChan < 16; ++ midiChan) {
            if (midiChan != 9) {
                channels.add(new Channel(midiChan));
            }
        }
        channels.add(new Channel(9));
    }

    @Override
    public void InitMusic() {
        try {
            receiver = getReceiver();
            EventGroup genMidiEG = new EventGroup(1f);
            genMidiEG.generalMidi(1);
            genMidiEG.sendTo(receiver);
            sleepUninterruptibly(100, TimeUnit.MILLISECONDS);
        } catch (MidiUnavailableException ex) {
            System.err.println(ex);
            receiver = null;
        }
        exec = Executors.newSingleThreadScheduledExecutor(new ThreadFactoryImpl());
    }

    /** Not yet implemented */
    @Override
    public void PauseSong(int handle) {
    }

    @Override
    public void PlaySong(int handle, boolean looping) {
        lock.lock();
        try {
            if (currentTransmitter != null) {
                currentTransmitter.stop();
            }
            currentTransmitter = null;
            if (0 <= handle && handle < songs.size()) {
                prepare(receiver);
                Song song = songs.get(handle);
                currentTransmitter =
                    new ScheduledTransmitter(song.getScoreBuffer(), looping);
                currentTransmitter.setReceiver(receiver);
            }
        } finally {
            lock.unlock();
        }
    }

    @Override
    public int RegisterSong(byte[] data) {
        return RegisterSong(ByteBuffer.wrap(data));
    }

    public int RegisterSong(ByteBuffer data) {
        Song song = new Song(data);
        lock.lock();
        try {
            int result = songs.indexOf(null);
            if (result >= 0) {
                songs.set(result, song);
            } else {
                result = songs.size();
                songs.add(song);
            }
            return result;
        } finally {
            lock.unlock();
        }
    }

    @Override
    public void ResumeSong(int handle) {
    }

    @Override
    public void SetMusicVolume(int volume) {
        float fVol = volume * (1/127f);
        fVol = Math.max(0f, Math.min(fVol, 1f));
        lock.lock();
        try {
            this.volume = fVol;
            if (currentTransmitter != null) {
                currentTransmitter.volumeChanged();
            }
        } finally {
            lock.unlock();
        }
    }

    @Override
    public void ShutdownMusic() {
        exec.shutdown();
    }

    @Override
    public void StopSong(int handle) {
        lock.lock();
        try {
            if (currentTransmitter != null) {
                currentTransmitter.stop();
                currentTransmitter = null;
            }
        } finally {
            lock.unlock();
        }
    }

    @Override
    public void UnRegisterSong(int handle) {
        lock.lock();
        try {
            if (0 <= handle && handle < songs.size()) {
                songs.set(handle, null);
            }
        } finally {
            lock.unlock();
        }
    }

    static boolean hasMusMagic(ByteBuffer magicBuf) {
        return magicBuf.get(0) == 'M' &&
               magicBuf.get(1) == 'U' &&
               magicBuf.get(2) == 'S' &&
               magicBuf.get(3) == 0x1a;
    }

    EventGroup nextEventGroup(ByteBuffer scoreBuffer, boolean looping)  {
        EventGroup result = new EventGroup(volume);
        boolean last;
        do {
            if (! scoreBuffer.hasRemaining()) {
                if (looping) {
                    scoreBuffer.flip();
                } else {
                    return result.emptyToNull();
                }
            }
            int descriptor = scoreBuffer.get() & 0xff;
            last = (descriptor & 0x80) != 0;
            int eventType = (descriptor >> 4) & 7;
            int chanIndex = descriptor & 15;
            Channel channel = channels.get(chanIndex);
            switch (eventType) {
            case 0:
                {
                    int note = scoreBuffer.get() & 0xff;
                    if ((note & 0x80) != 0) {
                        throw new IllegalArgumentException("Invalid note byte");
                    }
                    checkChannelExists("note off", channel).noteOff(note, result);
                }
                break;
            case 1:
                {
                    int note = scoreBuffer.get() & 0xff;
                    boolean hasVelocity = (note & 0x80) != 0;
                    if (hasVelocity) {
                        int velocity = scoreBuffer.get() & 0xff;
                        if ((velocity & 0x80) != 0) {
                            throw new IllegalArgumentException("Invalid velocity byte");
                        }
                        checkChannelExists("note on", channel).noteOn(note & 127, velocity, result);
                    } else {
                        checkChannelExists("note on", channel).noteOn(note, result);
                    }
                }
                break;
            case 2:
                {
                    int wheelVal = scoreBuffer.get() & 0xff;
                    checkChannelExists("pitch bend", channel).pitchBend(wheelVal, result);
                }
                break;
            case 3:
                {
                    int sysEvt = scoreBuffer.get() & 0xff;
                    switch (sysEvt) {
                    case 10:
                        checkChannelExists("all sounds off", channel).allSoundsOff(result);
                        break;
                    case 11:
                        checkChannelExists("all notes off", channel).allNotesOff(result);
                        break;
                    case 14:
                        checkChannelExists("reset all controllers", channel).resetAll(result);
                        break;
                    default:
                        String msg = String.format("Invalid system event (%d)", sysEvt);
                        throw new IllegalArgumentException(msg);
                    }
                }
                break;
            case 4:
                int cNum = scoreBuffer.get() & 0xff;
                if ((cNum & 0x80) != 0) {
                    throw new IllegalArgumentException("Invalid controller number ");
                }
                int cVal = scoreBuffer.get() & 0xff;
                if (cNum == 3 && 133 <= cVal && cVal <= 135) {
                    // workaround for some TNT.WAD tracks
                    cVal = 127;
                }
                if ((cVal & 0x80) != 0) {
                    String msg = String.format("Invalid controller value (%d; cNum=%d)", cVal, cNum);
                    throw new IllegalArgumentException(msg);
                }
                switch (cNum) {
                case 0:
                    checkChannelExists("patch change", channel).patchChange(cVal, result);
                    break;
                case 1:
                    // Don't forward this to the MIDI device.  Some devices
                    // react badly to banks that are undefined in GM Level 1
                    checkChannelExists("bank switch", channel);
                    break;
                case 2:
                    checkChannelExists("vibrato change", channel).vibratoChange(cVal, result);
                    break;
                case 3:
                    checkChannelExists("volume", channel).volume(cVal, result);
                    break;
                case 4:
                    checkChannelExists("pan", channel).pan(cVal, result);
                    break;
                case 5:
                    checkChannelExists("expression", channel).expression(cVal, result);
                    break;
                case 6:
                    checkChannelExists("reverb depth", channel).reverbDepth(cVal, result);
                    break;
                case 7:
                    checkChannelExists("chorus depth", channel).chorusDepth(cVal, result);
                    break;
                default:
                    throw new AssertionError("Controller number " + cNum + ": not yet implemented");
                }
                break;
            case 6:
                if (looping) {
                    scoreBuffer.flip();
                } else {
                    return result.emptyToNull();
                }
                break;
            default:
                String msg = String.format("Unknown event type: last=%5s eventType=%d chanIndex=%d%n", last, eventType, chanIndex);
                throw new IllegalArgumentException(msg);
            }
        } while (! last);
        int qTics = readTime(scoreBuffer);
        result.addDelay(qTics);
        return result;
    }

    static class EventGroup {
        EventGroup(float volScale) {
            this.messages = new ArrayList<MidiMessage>();
            this.volScale = volScale;
        }
        void addDelay(int tics) {
            delay += tics;
        }
        private static final int CHM_ALL_NOTES_OFF = 123;
        private static final int CHM_ALL_SOUND_OFF = 120;
        private static final int CTRL_CHORUS_DEPTH = 93;
        private static final int CTRL_EXPRESSION_POT = 11;
        private static final int CTRL_PAN = 10;
        private static final int RPM_PITCH_BEND_SENSITIVITY = 0;
        private static final int RPL_PITCH_BEND_SENSITIVITY = 0;
        private static final int CHM_RESET_ALL = 121;
        private static final int CTRL_REVERB_DEPTH = 91;
        private static final int CTRL_MODULATION_POT = 1;
        private static final int CTRL_VOLUME = 7;
        void allNotesOff(int midiChan) {
            addControlChange(midiChan, CHM_ALL_NOTES_OFF, 0);
        }
        void allSoundsOff(int midiChan) {
            addControlChange(midiChan, CHM_ALL_SOUND_OFF, 0);
        }
        long appendTo(Sequence sequence, int trackNum, long pos) {
            Track track = sequence.getTracks()[trackNum];
            for (MidiMessage msg: messages) {
                track.add(new MidiEvent(msg, pos));
            }
            return pos + delay * 3;
        }
        long appendTo(Track track, long pos, int scale) {
            for (MidiMessage msg: messages) {
                track.add(new MidiEvent(msg, pos));
            }
            return pos + delay * scale;
        }
        void chorusDepth(int midiChan, int depth) {
            addControlChange(midiChan, CTRL_CHORUS_DEPTH, depth);
        }
        void generalMidi(int mode) {
             addSysExMessage(0xf0, (byte)0x7e, (byte)0x7f, (byte)9, (byte)mode, (byte)0xf7);
        }
        EventGroup emptyToNull() {
            if (messages.isEmpty()) {
                return null;
            } else {
                return this;
            }
        }
        void expression(int midiChan, int expr) {
            addControlChange(midiChan, CTRL_EXPRESSION_POT, expr);
        }
        int getDelay() {
            return delay;
        }
        void noteOn(int midiChan, int note, int velocity) {
            addShortMessage(midiChan, ShortMessage.NOTE_ON, note, velocity);
        }
        void noteOff(int midiChan, int note) {
            addShortMessage(midiChan, ShortMessage.NOTE_OFF, note, 0);
        }
        void pan(int midiChan, int pan) {
            addControlChange(midiChan, CTRL_PAN, pan);
        }
        void patchChange(int midiChan, int patchId) {
            addShortMessage(midiChan, ShortMessage.PROGRAM_CHANGE, patchId, 0);
        }
        void pitchBend(int midiChan, int wheelVal) {
            int pb14 = wheelVal * 64;
            addShortMessage(midiChan, ShortMessage.PITCH_BEND, pb14 % 128, pb14 / 128);
        }
        void pitchBendSensitivity(int midiChan, int semitones) {
            addRegParamChange(midiChan, RPM_PITCH_BEND_SENSITIVITY, RPL_PITCH_BEND_SENSITIVITY, semitones);
        }
        void resetAllControllern(int midiChan) {
            addControlChange(midiChan, CHM_RESET_ALL, 0);
        }
        void reverbDepth(int midiChan, int depth) {
            addControlChange(midiChan, CTRL_REVERB_DEPTH, depth);
        }
        void sendTo(Receiver receiver) {
            for (MidiMessage msg: messages) {
                receiver.send(msg, -1);
            }
        }
        void vibratoChange(int midiChan, int depth) {
            addControlChange(midiChan, CTRL_MODULATION_POT, depth);
        }
        void volume(int midiChan, int vol) {
            vol = (int) Math.round(vol * volScale);
            addControlChange(midiChan, CTRL_VOLUME, vol);
        }
        private void addControlChange(int midiChan, int ctrlId, int ctrlVal) {
            addShortMessage(midiChan, ShortMessage.CONTROL_CHANGE, ctrlId, ctrlVal);
        }
        private void addRegParamChange(int midiChan, int paramMsb, int paramLsb, int valMsb) {
            addControlChange(midiChan, 101, paramMsb);
            addControlChange(midiChan, 100, paramLsb);
            addControlChange(midiChan, 6, valMsb);
        }
        private void addShortMessage(int midiChan, int cmd, int data1, int data2) {
            try {
                ShortMessage msg = new ShortMessage();
                msg.setMessage(cmd, midiChan, data1, data2);
                messages.add(msg);
            } catch (InvalidMidiDataException ex) {
                throw new RuntimeException(ex);
            }
        }
        private void addSysExMessage(int status, byte... data) {
            try {
                SysexMessage msg = new SysexMessage();
                msg.setMessage(status, data, data.length);
                messages.add(msg);
            } catch (InvalidMidiDataException ex) {
                throw new RuntimeException(ex);
            }
        }
        private int delay;
        private final List<MidiMessage> messages;
        private final float volScale;
    }

    /** A collection of kludges to pick a MIDI output device until cvars are implemented */
    static class MidiDeviceComparator implements Comparator<MidiDevice.Info> {
        @Override
        public int compare(MidiDevice.Info o1, MidiDevice.Info o2) {
            float score1 = score(o1), score2 = score(o2);
            if (score1 < score2) {
                return 1;
            } else if (score1 > score2) {
                return -1;
            } else {
                return 0;
            }
        }
        private float score(MidiDevice.Info info) {
            String lcName = info.getName().toLowerCase(Locale.ENGLISH);
            float result = 0f;
            if (lcName.contains("mapper")) {
                // "Midi Mapper" is ideal, because the user can select the default output device in the control panel
                result += 100;
            } else {
                if (lcName.contains("synth")) {
                    // A synthesizer is usually better than a sequencer or USB MIDI port
                    result += 50;
                    if (lcName.contains("java")) {
                        // "Java Sound Synthesizer" has a low sample rate; Prefer another software synth
                        result -= 20;
                    }
                    if (lcName.contains("microsoft")) {
                        // "Microsoft GS Wavetable Synth" is notoriously unpopular, but sometimes it's the only one
                        // with a decent sample rate.
                        result -= 7;
                    }
                }
            }
            return result;
        }
    }

    static class ThreadFactoryImpl implements ThreadFactory {
        @Override
        public Thread newThread(final Runnable r) {
            Thread thread =
                new Thread(r, String.format("FinnwMusicModule-%d", NEXT_ID.getAndIncrement()));
            thread.setPriority(Thread.MAX_PRIORITY - 1);
            return thread;
        }
        private static final AtomicInteger NEXT_ID =
            new AtomicInteger(1);
    }

    final Lock lock;

    static final long nanosPerTick = 1000000000 / 140;

    /** Channels in MUS order (0-14 = instruments, 15 = percussion) */
    final List<Channel> channels;

    ScheduledExecutorService exec;

    float volume;

    private static Receiver getReceiver() throws MidiUnavailableException {
        List<MidiDevice.Info> dInfos =
            new ArrayList<MidiDevice.Info>(Arrays.asList(MidiSystem.getMidiDeviceInfo()));
        for (Iterator<MidiDevice.Info> it = dInfos.iterator();
             it.hasNext();
             ) {
            MidiDevice.Info dInfo = it.next();
            MidiDevice dev = MidiSystem.getMidiDevice(dInfo);
            if (dev.getMaxReceivers() == 0) {
                // We cannot use input-only devices
                it.remove();
            }
        }
        if (dInfos.isEmpty()) return null;
        Collections.sort(dInfos, new MidiDeviceComparator());
        MidiDevice.Info dInfo = dInfos.get(0);
        MidiDevice dev = MidiSystem.getMidiDevice((MidiDevice.Info) dInfo);
        dev.open();
        return dev.getReceiver();
    }

    private void prepare(Receiver receiver) {
        EventGroup setupEG = new EventGroup(volume);
        for (Channel chan: channels) {
            chan.allSoundsOff(setupEG);
            chan.resetAll(setupEG);
            chan.pitchBendSensitivity(2, setupEG);
            chan.volume(127, setupEG);
        }
        setupEG.sendTo(receiver);
    }

    private static void sleepUninterruptibly(int timeout, TimeUnit timeUnit) {
        boolean interrupted = false;
        long now = System.nanoTime();
        final long expiry = now + timeUnit.toNanos(timeout);
        long remaining;
        while ((remaining = expiry - now) > 0L) {
            try {
                TimeUnit.NANOSECONDS.sleep(remaining);
            } catch (InterruptedException ex) {
                interrupted = true;
            } finally {
                now = System.nanoTime();
            }
        }
        if (interrupted) {
            Thread.currentThread().interrupt();
        }
    }
    private static Channel checkChannelExists(String type, Channel channel)
            throws IllegalArgumentException {
        if (channel == null) {
            String msg = String.format("Invalid channel for %s message", type);
            throw new IllegalArgumentException(msg);
        } else {
            return channel;
        }
    }

    private int readTime(ByteBuffer scoreBuffer) {
        int result = 0;
        boolean last;
        do {
            int digit = scoreBuffer.get() & 0xff;
            last = (digit & 0x80) == 0;
            result <<= 7;
            result |= digit & 127;
        } while (! last);
        return result;
    }

    private static class Channel {
        Channel(int midiChan) {
            this.midiChan = midiChan;
        }
        void allNotesOff(EventGroup eventGroup) {
            eventGroup.allNotesOff(midiChan);
        }
        void allSoundsOff(EventGroup eventGroup) {
            eventGroup.allSoundsOff(midiChan);
        }
        void chorusDepth(int depth, EventGroup eventGroup) {
            eventGroup.chorusDepth(midiChan, depth);
        }
        void expression(int expr, EventGroup eventGroup) {
            eventGroup.expression(midiChan, expr);
        }
        void noteOff(int note, EventGroup eventGroup) {
            eventGroup.noteOff(midiChan, note);
        }
        void noteOn(int note, EventGroup eventGroup) {
            eventGroup.noteOn(midiChan, note, lastVelocity);
        }
        void noteOn(int note, int velocity, EventGroup eventGroup) {
            lastVelocity = velocity;
            noteOn(note, eventGroup);
        }
        void pan(int pan, EventGroup eventGroup) {
            eventGroup.pan(midiChan, pan);
        }
        void patchChange(int patchId, EventGroup eventGroup) {
            eventGroup.patchChange(midiChan, patchId);
        }
        void pitchBend(int wheelVal, EventGroup eventGroup) {
            eventGroup.pitchBend(midiChan, wheelVal);
        }
        void pitchBendSensitivity(int semitones, EventGroup eventGroup) {
            eventGroup.pitchBendSensitivity(midiChan, semitones);
        }
        void resetAll(EventGroup eventGroup) {
            eventGroup.resetAllControllern(midiChan);
        }
        void reverbDepth(int depth, EventGroup eventGroup) {
            eventGroup.reverbDepth(midiChan, depth);
        }
        void vibratoChange(int depth, EventGroup eventGroup) {
            eventGroup.vibratoChange(midiChan, depth);
        }
        void volume(int vol, EventGroup eventGroup) {
            eventGroup.volume(midiChan, vol);
            lastVolume = vol;
        }
        void volumeChanged(EventGroup eventGroup) {
            eventGroup.volume(midiChan, lastVolume);
        }
        private int lastVelocity;
        private int lastVolume;
        private final int midiChan;
    }

    private class ScheduledTransmitter implements Transmitter {

        @Override
        public void close() {
            lock.lock();
            try {
                if (autoShutdown && exec != null) {
                    exec.shutdown();
                }
                autoShutdown = false;
                exec = null;
            } finally {
                lock.unlock();
            }
        }

        @Override
        public Receiver getReceiver() {
            return receiver;
        }

        @Override
        public void setReceiver(Receiver receiver) {
            EventGroup currentGroup = null;
            lock.lock();
            try {
                if (this.receiver != null) {
                    if (this.future.cancel(false)) {
                        currentGroup = triggerTask.eventGroup;
                    }
                } else {
                    nextGroupTime = System.nanoTime();
                }
                this.receiver = receiver;
                scheduleIfRequired(receiver, currentGroup);
            } finally {
                lock.unlock();
            }
        }

        ScheduledTransmitter(ByteBuffer scoreBuffer, boolean looping) {
            this.exec = FinnwMusicModule.this.exec;
            this.looping = looping;
            this.scoreBuffer = scoreBuffer;
        }

        void scheduleIfRequired(Receiver receiver,
                                EventGroup currentGroup) {
            assert (((ReentrantLock) lock).isHeldByCurrentThread());
            if (currentGroup == null) {
                try {
                    currentGroup = nextEventGroup(scoreBuffer, looping);
                    if (currentGroup != null) {
                        triggerTask = new TriggerTask(currentGroup, receiver);
                        long delay = Math.max(0, nextGroupTime - System.nanoTime());
                        future =
                            exec.schedule(triggerTask, delay, TimeUnit.NANOSECONDS);
                        nextGroupTime += currentGroup.getDelay() * nanosPerTick;
                    } else {
                        triggerTask = null;
                        future = null;
                    }
                } catch (RejectedExecutionException ex) {
                    // This is normal when shutting down
                } catch (Exception ex) {
                    System.err.println(ex);
                }
            }
        }

        void stop() {
            assert (((ReentrantLock) lock).isHeldByCurrentThread());
            if (future != null) {
                future.cancel(false);
                try {
                    future.get();
                } catch (InterruptedException ex) {
                } catch (ExecutionException ex) {
                } catch (CancellationException ex) {
                }
                future = null;
            }
            EventGroup cleanup = new EventGroup(0f);
            for (Channel chan: channels) {
                chan.allNotesOff(cleanup);
            }
            cleanup.sendTo(receiver);
        }

        void volumeChanged() {
            assert (((ReentrantLock) lock).isHeldByCurrentThread());
            EventGroup adjust = new EventGroup(volume);
            for (Channel chan: channels) {
                chan.volumeChanged(adjust);
            }
            adjust.sendTo(receiver);
        }
        TriggerTask triggerTask;

        private class TriggerTask implements Runnable {
            @Override
            public void run() {
                boolean shouldSend = false;
                lock.lock();
                try {
                    if (triggerTask == this) {
                        shouldSend = true;
                        scheduleIfRequired(receiver, null);
                    }
                } finally {
                    lock.unlock();
                }
                if (shouldSend) {
                    eventGroup.sendTo(receiver);
                }
            }
            TriggerTask(EventGroup eventGroup, Receiver receiver) {
                this.eventGroup = eventGroup;
                this.receiver = receiver;
            }

            final EventGroup eventGroup;
            final Receiver receiver;
        }

        private boolean autoShutdown;

        private ScheduledExecutorService exec;

        private ScheduledFuture<?> future;

        private final boolean looping;

        private long nextGroupTime;

        private Receiver receiver;

        private final ByteBuffer scoreBuffer;
    }

    /** Contains unfiltered MUS data */
    private class Song {
        Song(ByteBuffer data) {
            this.data = data.asReadOnlyBuffer();
            this.data.order(ByteOrder.LITTLE_ENDIAN);
            byte[] magic = new byte[4];
            this.data.get(magic);
            ByteBuffer magicBuf = ByteBuffer.wrap(magic);
            if (! hasMusMagic(magicBuf)) {
                throw new IllegalArgumentException("Expected magic string \"MUS\\x1a\" but found " + Arrays.toString(magic));
            }
            this.scoreLen = this.data.getShort() & 0xffff;
            this.scoreStart = this.data.getShort() & 0xffff;
        }

        /** Get only the score part of the data (skipping the header) */
        ByteBuffer getScoreBuffer() {
            ByteBuffer scoreBuffer = this.data.duplicate();
            scoreBuffer.position(scoreStart);
            scoreBuffer.limit(scoreStart + scoreLen);
            ByteBuffer slice = scoreBuffer.slice();
            return slice;
        }
        private final ByteBuffer data;
        private final int scoreLen;
        private final int scoreStart;
    }

    private ScheduledTransmitter currentTransmitter;

    private Receiver receiver;

    /** Songs indexed by handle */
    private final List<Song> songs;

}

package s;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;

import javax.sound.midi.InvalidMidiDataException;
import javax.sound.midi.MidiDevice;
import javax.sound.midi.MidiMessage;
import javax.sound.midi.MidiSystem;
import javax.sound.midi.MidiUnavailableException;
import javax.sound.midi.Receiver;
import javax.sound.midi.Sequencer;
import javax.sound.midi.ShortMessage;
import javax.sound.midi.Synthesizer;

/** A {@link Receiver} that scales channel volumes.
 * 
 * Works by recognising channel volume change events and scaling the new volume
 * by the global music volume setting before forwarding the event to the
 * synthesizer.
 *
 * @author finnw
 *
 */
public class VolumeScalingReceiver implements Receiver {

    /** Guess which is the "best" available synthesizer & create a
     *  VolumeScalingReceiver that forwards to it.
     *
     * @return a <code>VolumeScalingReceiver</code> connected to a semi-
     * intelligently-chosen synthesizer.
     *
     */
    public static VolumeScalingReceiver getInstance() {
        try {
            List<MidiDevice.Info> dInfos =
                new ArrayList<MidiDevice.Info>(Arrays.asList(MidiSystem.getMidiDeviceInfo()));
            for (Iterator<MidiDevice.Info> it = dInfos.iterator();
                 it.hasNext();
                 ) {
                MidiDevice.Info dInfo = it.next();
                MidiDevice dev = MidiSystem.getMidiDevice(dInfo);
                if (dev.getMaxReceivers() == 0) {
                    // We cannot use input-only devices
                    it.remove();
                }
            }
            if (dInfos.isEmpty()) return null;
            Collections.sort(dInfos, new MidiDeviceComparator());
            MidiDevice.Info dInfo = dInfos.get(0);
            MidiDevice dev = MidiSystem.getMidiDevice((MidiDevice.Info) dInfo);
            dev.open();
            return new VolumeScalingReceiver(dev.getReceiver());
        } catch (MidiUnavailableException ex) {
            return null;
        }
    }

    /** Create a VolumeScalingReceiver connected to a specific receiver. */
    public VolumeScalingReceiver(Receiver delegate) {
        this.channelVolume = new int[16];
        this.synthReceiver = delegate;
        Arrays.fill(this.channelVolume, 127);
    }

    @Override
    public void close() {
        synthReceiver.close();
    }

    /** Set the scaling factor to be applied to all channel volumes */
    public synchronized void setGlobalVolume(float globalVolume) {
        this.globalVolume = globalVolume;
        for (int chan = 0; chan < 16; ++ chan) {
            int volScaled = (int) Math.round(channelVolume[chan] * globalVolume);
            sendVolumeChange(chan, volScaled, -1);
        }
    }

    /** A collection of kludges to pick a synthesizer until cvars are implemented */
    static class MidiDeviceComparator implements Comparator<MidiDevice.Info> {
        @Override
        public int compare(MidiDevice.Info o1, MidiDevice.Info o2) {
            float score1 = score(o1), score2 = score(o2);
            if (score1 < score2) {
                return 1;
            } else if (score1 > score2) {
                return -1;
            } else {
                return 0;
            }
        }
        /** Guess how suitable a MidiDevice is for music output. */
        private float score(MidiDevice.Info info) {
            String lcName = info.getName().toLowerCase(Locale.ENGLISH);
            float result = 0f;
            try {
                MidiDevice dev = MidiSystem.getMidiDevice(info);
                dev.open();
                try {
                    if (dev instanceof Sequencer) {
                        // The sequencer cannot be the same device as the synthesizer - that would create an infinite loop.
                        return Float.NEGATIVE_INFINITY;
                    } else if (lcName.contains("mapper")) {
                        // "Midi Mapper" is ideal, because the user can select the default output device in the control panel
                        result += 100;
                    } else {
                        if (dev instanceof Synthesizer) {
                            // A synthesizer is usually better than a sequencer or USB MIDI port
                            result += 50;
                            if (lcName.contains("java")) {
                                // "Java Sound Synthesizer" often has a low sample rate or no default soundbank;  Prefer another software synth
                                if (((Synthesizer) dev).getDefaultSoundbank() != null) {
                                    result -= 10;
                                } else {
                                    // Probably won't be audible
                                    result -= 500;
                                }
                            }
                            if (lcName.contains("microsoft")) {
                                // "Microsoft GS Wavetable Synth" is notoriously unpopular, but sometimes it's the only one
                                // with a decent sample rate.
                                result -= 7;
                            }
                        }
                    }
                    return result;
                } finally {
                    dev.close();
                }
            } catch (MidiUnavailableException ex) {
                // Cannot use this one
                return Float.NEGATIVE_INFINITY;
            }
        }
    }

    /** Forward a message to the synthesizer.
     * 
     *  If <code>message</code> is a volume change message, the volume is
     *  first multiplied by the global volume.  Otherwise, the message is
     *  passed unmodified to the synthesizer.
     */
    @Override
    public synchronized void send(MidiMessage message, long timeStamp) {
        int chan = getVolumeChangeChannel(message);
        if (chan < 0) {
            synthReceiver.send(message, timeStamp);
        } else {
            int newVolUnscaled = message.getMessage()[2];
            channelVolume[chan] = newVolUnscaled;
            int newVolScaled = (int) Math.round(newVolUnscaled * globalVolume);
            sendVolumeChange(chan, newVolScaled, timeStamp);
        }
    }

    /** Send a volume update to a specific channel.
     *
     *  This is used for both local & global volume changes.
     */
    private void sendVolumeChange(int chan, int newVolScaled, long timeStamp) {
        newVolScaled = Math.max(0, Math.min(newVolScaled, 127));
        ShortMessage message = new ShortMessage();
        try {
            message.setMessage(0xb0 | (chan & 15), 7, newVolScaled);
            synthReceiver.send(message, timeStamp);
        } catch (InvalidMidiDataException ex) {
            System.err.println(ex);
        }
    }

    /** Determine if the given message is a channel volume change.
     *
     * @return Channel number for which volume is being changed, or -1 if not a
     * channel volume change command.
     */
    private int getVolumeChangeChannel(MidiMessage message) {
        if (message.getLength() >= 3) {
            byte[] mBytes = message.getMessage();
            if ((byte) 0xb0 <= mBytes[0] && mBytes[0] < (byte) 0xc0 &&
                mBytes[1] == 7) {
                return mBytes[0] & 15;
            }
        }
        return -1;
    }

    private final int[] channelVolume;

    private float globalVolume;

    private final Receiver synthReceiver;

}

package s;

//
//  MUSIC I/O
//

public interface IMusic {

	void InitMusic();
	void ShutdownMusic();
	// Volume.
	void SetMusicVolume(int volume);
	/** PAUSE game handling. */
	void PauseSong(int handle);
	void ResumeSong(int handle);
	
	/** Registers a song handle to song data. 
	 *  This should handle any conversions from MUS/MIDI/OPL/etc.
	 * 
	 * */
	int RegisterSong(byte[] data);
	
	
	/** Called by anything that wishes to start music.
	   plays a song, and when the song is done,
	  starts playing it again in an endless loop.
     Horrible thing to do, considering. */
	
	void
	PlaySong
	( int		handle,
	  boolean		looping );
	
	/** Stops a song over 3 seconds. */
	void StopSong(int handle);
	
	/** See above (register), then think backwards */
	void UnRegisterSong(int handle);
}

package s;

import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import javax.sound.midi.InvalidMidiDataException;
import javax.sound.midi.MetaMessage;
import javax.sound.midi.MidiEvent;
import javax.sound.midi.MidiMessage;
import javax.sound.midi.MidiSystem;
import javax.sound.midi.Sequence;
import javax.sound.midi.ShortMessage;
import javax.sound.midi.Track;

import m.Swap;

/**
 * A MUS lump reader that loads directly to a Sequence.
 * 
 * Unlike QMusToMid, does not keep the MIDI version in a temporary file.
 *
 * @author finnw
 *
 */
public class MusReader {

    /** Create a sequence from an InputStream.
     *  This is the counterpart of {@link MidiSystem#getSequence(InputStream)}
     *  for MUS format.
     *
     * @param is MUS data (this method does not try to auto-detect the format.)
     */
    public static Sequence getSequence(InputStream is)
    throws IOException, InvalidMidiDataException {
        DataInputStream dis = new DataInputStream(is);
        dis.skip(6);
        int rus = dis.readUnsignedShort();
        short scoreStart = Swap.SHORT((char) rus);
        dis.skip(scoreStart - 8);
        Sequence sequence = new Sequence(Sequence.SMPTE_30, 14, 1);
        Track track = sequence.getTracks()[0];
        int[] chanVelocity = new int[16];
        Arrays.fill(chanVelocity, 100);
        EventGroup eg;
        long tick = 0;
        while ((eg = nextEventGroup(dis, chanVelocity)) != null) {
            tick = eg.appendTo(track, tick);
        }
        MetaMessage endOfSequence = new MetaMessage();
        endOfSequence.setMessage(47, new byte[] {0}, 1);
        track.add(new MidiEvent(endOfSequence, tick));
        return sequence;
    }

    private static EventGroup
    nextEventGroup(InputStream is, int[] channelVelocity) throws IOException {
        EventGroup result = new EventGroup();
        boolean last;
        do {
            int b = is.read();
            if (b < 0) {
                return result.emptyToNull();
            }
            int descriptor = b & 0xff;
            last = (descriptor & 0x80) != 0;
            int eventType = (descriptor >> 4) & 7;
            int chanIndex = descriptor & 15;
            final int midiChan;
            if (chanIndex < 9) {
                midiChan = chanIndex;
            } else if (chanIndex < 15) {
                midiChan = chanIndex + 1;
            } else {
                midiChan = 9;
            }
            switch (eventType) {
            case 0:
                {
                    int note = is.read() & 0xff;
                    if ((note & 0x80) != 0) {
                        throw new IllegalArgumentException("Invalid note byte");
                    }
                    result.noteOff(midiChan, note);
                }
                break;
            case 1:
                {
                    int note = is.read() & 0xff;
                    boolean hasVelocity = (note & 0x80) != 0;
                    final int velocity;
                    if (hasVelocity) {
                        velocity = is.read() & 0xff;
                        if ((velocity & 0x80) != 0) {
                            throw new IllegalArgumentException("Invalid velocity byte");
                        }
                        channelVelocity[midiChan] = velocity;
                    } else {
                        velocity = channelVelocity[midiChan];
                    }
                    result.noteOn(midiChan, note & 0x7f, velocity);
                }
                break;
            case 2:
                {
                    int wheelVal = is.read() & 0xff;
                    result.pitchBend(midiChan, wheelVal);
                }
                break;
            case 3:
                {
                    int sysEvt = is.read() & 0xff;
                    switch (sysEvt) {
                    case 10:
                        result.allSoundsOff(midiChan);
                        break;
                    case 11:
                        result.allNotesOff(midiChan);
                        break;
                    case 14:
                        result.resetAllControllers(midiChan);
                        break;
                    default:
                        String msg = String.format("Invalid system event (%d)", sysEvt);
                        throw new IllegalArgumentException(msg);
                    }
                }
                break;
            case 4:
                int cNum = is.read() & 0xff;
                if ((cNum & 0x80) != 0) {
                    throw new IllegalArgumentException("Invalid controller number ");
                }
                int cVal = is.read() & 0xff;
                if (cNum == 3 && 133 <= cVal && cVal <= 135) {
                    // workaround for some TNT.WAD tracks
                    cVal = 127;
                }
                if ((cVal & 0x80) != 0) {
                    String msg = String.format("Invalid controller value (%d; cNum=%d)", cVal, cNum);
                    throw new IllegalArgumentException(msg);
                }
                switch (cNum) {
                case 0:
                    result.patchChange(midiChan, cVal);
                    break;
                case 1:
                    // Don't forward this to the MIDI device.  Some synths if
                    // in GM level 1 mode will react badly to banks that are
                    // undefined in GM Level 1
                    break;
                case 2:
                    result.vibratoChange(midiChan, cVal);
                    break;
                case 3:
                    result.volume(midiChan, cVal);
                    break;
                case 4:
                    result.pan(midiChan, cVal);
                    break;
                case 5:
                    result.expression(midiChan, cVal);
                    break;
                case 6:
                    result.reverbDepth(midiChan, cVal);
                    break;
                case 7:
                    result.chorusDepth(midiChan, cVal);
                    break;
                case 8:
                    result.sustain(midiChan, cVal);
                    break;
                default:
                    throw new AssertionError("Unknown controller number: " + cNum + "(value: " + cVal + ")");
                }
                break;
            case 6:
                return result.emptyToNull();
            default:
                String msg = String.format("Unknown event type: %d", eventType);
                throw new IllegalArgumentException(msg);
            }
        } while (! last);
        int qTics = readVLV(is);
        result.addDelay(qTics);
        return result;
    }

    private static int readVLV(InputStream is) throws IOException {
        int result = 0;
        boolean last;
        do {
            int digit = is.read() & 0xff;
            last = (digit & 0x80) == 0;
            result <<= 7;
            result |= digit & 127;
        } while (! last);
        return result;
    }

    private static class EventGroup {
        EventGroup() {
            this.messages = new ArrayList<MidiMessage>();
        }
        void addDelay(long ticks) {
            delay += ticks;
        }
        void allNotesOff(int midiChan) {
            addControlChange(midiChan, CHM_ALL_NOTES_OFF, 0);
        }
        void allSoundsOff(int midiChan) {
            addControlChange(midiChan, CHM_ALL_SOUND_OFF, 0);
        }
        long appendTo(Track track, long tick) {
            for (MidiMessage msg: messages) {
                track.add(new MidiEvent(msg, tick));
            }
            return tick + delay * 3;
        }
        void chorusDepth(int midiChan, int depth) {
            addControlChange(midiChan, CTRL_CHORUS_DEPTH, depth);
        }
        EventGroup emptyToNull() {
            if (messages.isEmpty()) {
                return null;
            } else {
                return this;
            }
        }
        void expression(int midiChan, int expr) {
            addControlChange(midiChan, CTRL_EXPRESSION_POT, expr);
        }
        void noteOn(int midiChan, int note, int velocity) {
            addShortMessage(midiChan, ShortMessage.NOTE_ON, note, velocity);
        }
        void noteOff(int midiChan, int note) {
            addShortMessage(midiChan, ShortMessage.NOTE_OFF, note, 0);
        }
        void pan(int midiChan, int pan) {
            addControlChange(midiChan, CTRL_PAN, pan);
        }
        void patchChange(int midiChan, int patchId) {
            addShortMessage(midiChan, ShortMessage.PROGRAM_CHANGE, patchId, 0);
        }
        void pitchBend(int midiChan, int wheelVal) {
            int pb14 = wheelVal * 64;
            addShortMessage(midiChan, ShortMessage.PITCH_BEND, pb14 % 128, pb14 / 128);
        }
        void resetAllControllers(int midiChan) {
            addControlChange(midiChan, CHM_RESET_ALL, 0);
        }
        void reverbDepth(int midiChan, int depth) {
            addControlChange(midiChan, CTRL_REVERB_DEPTH, depth);
        }
        void sustain(int midiChan, int on) {
            addControlChange(midiChan, CTRL_SUSTAIN, on);
        }
        void vibratoChange(int midiChan, int depth) {
            addControlChange(midiChan, CTRL_MODULATION_POT, depth);
        }
        void volume(int midiChan, int vol) {
            addControlChange(midiChan, CTRL_VOLUME, vol);
        }
        private void addControlChange(int midiChan, int ctrlId, int ctrlVal) {
            addShortMessage(midiChan, ShortMessage.CONTROL_CHANGE, ctrlId, ctrlVal);
        }
        private void addShortMessage(int midiChan, int cmd, int data1, int data2) {
            try {
                ShortMessage msg = new ShortMessage();
                msg.setMessage(cmd, midiChan, data1, data2);
                messages.add(msg);
            } catch (InvalidMidiDataException ex) {
                throw new RuntimeException(ex);
            }
        }

        private static final int CHM_ALL_NOTES_OFF = 123;
        private static final int CHM_ALL_SOUND_OFF = 120;
        private static final int CTRL_CHORUS_DEPTH = 93;
        private static final int CTRL_EXPRESSION_POT = 11;
        private static final int CTRL_PAN = 10;
        private static final int CTRL_SUSTAIN = 64;
        private static final int CHM_RESET_ALL = 121;
        private static final int CTRL_REVERB_DEPTH = 91;
        private static final int CTRL_MODULATION_POT = 1;
        private static final int CTRL_VOLUME = 7;

        private long delay;
        private final List<MidiMessage> messages;
    }

}

package s;

import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.SourceDataLine;

import p.mobj_t;
import data.sfxinfo_t;

public class channel_t 
{
	
	public channel_t(){
		sfxinfo=new sfxinfo_t();
	}
    
	/** Currently playing sound. If null, then it's free */
	DoomSound currentSound = null;
	
    sfxinfo_t	sfxinfo;

    // origin of sound (usually a mobj_t).
    mobj_t	origin;

    // handle of the sound being played
    int		handle;
    
    AudioFormat format;
    
	public int sfxVolume;
    
	SourceDataLine auline = null;
}

package s;

import static data.sounds.S_sfx;
import m.FixedFloat;
import data.sfxinfo_t;
import data.sounds;
import doom.DoomStatus;

/**
 * Functionality and fields that are common among the various "sound drivers"
 * should go here.
 * 
 * @author Maes
 */

public abstract class AbstractSoundDriver
        implements ISoundDriver {

    protected final static boolean D = false; // debug
    
    protected final DoomStatus<?,?> DS;

    /**
     * The global mixing buffer. Basically, samples from all active internal
     * channels are modifed and added, and stored in the buffer that is
     * submitted to the audio device. This is a 16-bit stereo signed PCM
     * mixbuffer. Memory order is LSB (?) and channel order is L-R-L-R...
     * 
     * Not all i
     * 
     */

    protected byte[] mixbuffer;// = new byte[MIXBUFFERSIZE];
    
    protected final int numChannels;

    /** The actual lengths of all sound effects. */
    protected final int[] lengths = new int[NUMSFX];

    /**
     * The sound in channel handles, determined on registration, might be used
     * to unregister/stop/modify, currently unused.
     */

    protected final int[] channelhandles;

    /**
     * SFX id of the playing sound effect. Used to catch duplicates (like
     * chainsaw).
     */
    protected final int[] channelids;

    /**
     * Pitch to stepping lookup, used in ClassicSoundDriver It's actually rigged
     * to have a -/+ 400% pitch variation!
     */
    protected final int[] steptable = new int[256];

    /** Volume lookups. 128 levels */
    protected final int[][] vol_lookup = new int[128][256];

    /**
     * Time/gametic that the channel started playing, used to determine oldest,
     * which automatically has lowest priority. In case number of active sounds
     * exceeds available channels.
     */
    protected final int[] channelstart;

    // protected final static DataLine.Info info = new DataLine.Info(Clip.class,
    // format);

    public AbstractSoundDriver(DoomStatus<?,?> DS, int numChannels) {
        this.DS = DS;
        this.numChannels = numChannels;
        channelids = new int[numChannels];
        channelhandles = new int[numChannels];
        channelstart = new int[numChannels];
    }

    /**
     * Generates volume lookup tables which also turn the unsigned samples into
     * signed samples.
     */

    protected final void generateVolumeLUT() {
        for (int i = 0; i < 128; i++)
            for (int j = 0; j < 256; j++)
                vol_lookup[i][j] = (i * (j - 128) * 256) / 127;
    }

    /**
     * This table provides step widths for pitch parameters. Values go from 16K
     * to 256K roughly, with the middle of the table being 64K, and presumably
     * representing unitary pitch. So the pitch variation can be quite extreme,
     * allowing -/+ 400% stepping :-S
     * 
     * @param steptablemid
     * @return
     */

    protected void generateStepTable(int steptablemid) {
        for (int i = -128; i < 128; i++) {
            steptable[steptablemid + i] =
                (int) (Math.pow(2.0, (i / 64.0)) * 65536.0);
            //System.out.printf("Pitch %d %d %f\n",i,steptable[steptablemid + i],FixedFloat.toFloat(steptable[steptablemid + i]));
        }
    }

    /** Read a Doom-format sound effect from disk, leaving it in 8-bit mono format but
     *  upsampling it to the target sample rate.
     *  
     * @param sfxname
     * @param len
     * @param index
     * @return
     */
    
    protected byte[] getsfx(String sfxname, int[] len, int index) {
        byte[] sfx;
        byte[] paddedsfx;
        int i;
        int size;
        int paddedsize;
        String name;
        int sfxlump;

        // Get the sound data from the WAD, allocate lump
        // in zone memory.
        name = String.format("ds%s", sfxname).toUpperCase();

        // Now, there is a severe problem with the
        // sound handling, in it is not (yet/anymore)
        // gamemode aware. That means, sounds from
        // DOOM II will be requested even with DOOM
        // shareware.
        // The sound list is wired into sounds.c,
        // which sets the external variable.
        // I do not do runtime patches to that
        // variable. Instead, we will use a
        // default sound for replacement.
        if (DS.W.CheckNumForName(name) == -1)
            sfxlump = DS.W.GetNumForName("dspistol");
        else
            sfxlump = DS.W.GetNumForName(name);

        DMXSound dmx= DS.W.CacheLumpNum(sfxlump, 0, DMXSound.class);
        
        // KRUDE
        if (dmx.speed==SAMPLERATE/2){
            // Plain linear interpolation.
            dmx.data=DSP.crudeResample(dmx.data,2);
            //DSP.filter(dmx.data,SAMPLERATE, SAMPLERATE/4);
            dmx.datasize=dmx.data.length;
            
        }
        
        sfx = dmx.data;

        // MAES: A-ha! So that's how they do it.
        // SOund effects are padded to the highest multiple integer of
        // the mixing buffer's size (with silence)

        paddedsize =
            ((dmx.datasize + (SAMPLECOUNT - 1)) / SAMPLECOUNT) * SAMPLECOUNT;

        // Allocate from zone memory.
        paddedsfx = new byte[paddedsize];

        // Now copy and pad. The first 8 bytes are header info, so we discard
        // them.
        System.arraycopy(sfx, 0, paddedsfx, 0, dmx.datasize);

        // Pad with silence (unsigned)
        for (i = dmx.datasize; i < paddedsize; i++)
            paddedsfx[i] = (byte) 127;

        // Remove the cached lump.
        DS.W.UnlockLumpNum(sfxlump);

        if (D) System.out.printf("SFX %d name %s size %d speed %d padded to %d\n", index, S_sfx[index].name, dmx.datasize,dmx.speed,paddedsize);
        // Preserve padded length.
        len[index] = paddedsize;

        // Return allocated padded data.
        // So the first 8 bytes are useless?
        return paddedsfx;
    }

    /**
     * Modified getsfx, which transforms samples into 16-bit, signed, stereo
     * beforehand, before being "fed" to the audio clips.
     * 
     * @param sfxname
     * @param index
     * @return
     */
    protected final byte[] getsfx16(String sfxname, int[] len, int index) {
        byte[] sfx;
        byte[] paddedsfx;
        int i;
        int size;
        int paddedsize;
        String name;
        int sfxlump;

        // Get the sound data from the WAD, allocate lump
        // in zone memory.
        name = String.format("ds%s", sfxname).toUpperCase();

        // Now, there is a severe problem with the
        // sound handling, in it is not (yet/anymore)
        // gamemode aware. That means, sounds from
        // DOOM II will be requested even with DOOM
        // shareware.
        // The sound list is wired into sounds.c,
        // which sets the external variable.
        // I do not do runtime patches to that
        // variable. Instead, we will use a
        // default sound for replacement.
        if (DS.W.CheckNumForName(name) == -1)
            sfxlump = DS.W.GetNumForName("dspistol");
        else
            sfxlump = DS.W.GetNumForName(name);

        size = DS.W.LumpLength(sfxlump);

        sfx = DS.W.CacheLumpNumAsRawBytes(sfxlump, 0);

        // Size blown up to accommodate two channels and 16 bits.
        // Sampling rate stays the same.

        paddedsize = (size - 8) * 2 * 2;
        // Allocate from zone memory.
        paddedsfx = new byte[paddedsize];

        // Skip first 8 bytes (header), blow up the data
        // to stereo, BIG ENDIAN, SIGNED, 16 bit. Don't expect any fancy DSP
        // here!

        int sample = 0;
        for (i = 8; i < size; i++) {
            // final short sam=(short) vol_lookup[127][0xFF&sfx[i]];
            final short sam = (short) ((0xFF & sfx[i] - 128) << 8);
            paddedsfx[sample++] = (byte) (0xFF & (sam >> 8));
            paddedsfx[sample++] = (byte) (0xFF & sam);
            paddedsfx[sample++] = (byte) (0xFF & (sam >> 8));
            paddedsfx[sample++] = (byte) (0xFF & sam);
        }

        // Remove the cached lump.
        DS.W.UnlockLumpNum(sfxlump);

        // Preserve padded length.
        len[index] = paddedsize;

        // Return allocated padded data.
        // So the first 8 bytes are useless?
        return paddedsfx;
    }

    /**
     * Starting a sound means adding it to the current list of active sounds in
     * the internal channels. As the SFX info struct contains e.g. a pointer to
     * the raw data it is ignored. As our sound handling does not handle
     * priority, it is ignored. Pitching (that is, increased speed of playback)
     * is set, but whether it's used or not depends on the final implementation
     * (e.g. classic mixer uses it, but AudioLine-based implementations are not
     * guaranteed.
     */

    @Override
    public int StartSound(int id, int vol, int sep, int pitch, int priority) {

        if (id < 1 || id > S_sfx.length - 1)
            return BUSY_HANDLE;

        // Find a free channel and get a timestamp/handle for the new sound.
        int handle = this.addsfx(id, vol, steptable[pitch], sep);

        return handle;
    }

    /**
     * This function adds a sound to the list of currently active sounds, which
     * is maintained as a given number (eight, usually) of internal channels.
     * Returns a handle.
     * 
     * @param sfxid
     * @param volume
     * @param step
     * @param seperation
     * @return
     */

    protected abstract int addsfx(int sfxid, int volume, int step,
            int seperation);

    protected short handlenums = 0;

    //
    // Retrieve the raw data lump index
    // for a given SFX name.
    //
    public final int GetSfxLumpNum(sfxinfo_t sfx) {
        String namebuf;
        namebuf = String.format("ds%s", sfx.name).toUpperCase();
        if (namebuf.equals("DSNONE"))
            return -1;

        int lump;
        try {
            lump = DS.W.GetNumForName(namebuf);
        } catch (Exception e) {
            e.printStackTrace();
            return -1;
        }

        return lump;
    }

    /**
     * Initialize 
     * 
     * @return
     */
    protected  final void initMixBuffer() {
        for (int i = 0; i < MIXBUFFERSIZE; i += 4) {
            mixbuffer[i] =
                (byte) (((int) (0x7FFF * Math.sin(1.5 * Math.PI * (double) i
                        / MIXBUFFERSIZE)) & 0xff00) >>> 8);
            mixbuffer[i + 1] =
                (byte) ((int) (0x7FFF * Math.sin(1.5 * Math.PI * (double) i
                        / MIXBUFFERSIZE)) & 0xff);
            mixbuffer[i + 2] =
                (byte) (((int) (0x7FFF * Math.sin(1.5 * Math.PI * (double) i
                        / MIXBUFFERSIZE)) & 0xff00) >>> 8);
            mixbuffer[i + 3] =
                (byte) ((int) (0x7FFF * Math.sin(1.5 * Math.PI * (double) i
                        / MIXBUFFERSIZE)) & 0xff);

        }
    }
    
    /**
     * Loads samples in 8-bit format, forcibly converts them to the common sampling rate.
     * Used by.
     */

    protected final void initSound8() {
        int i;

        // Initialize external data (all sounds) at start, keep static.

        for (i = 1; i < NUMSFX; i++) {
            // Alias? Example is the chaingun sound linked to pistol.
            if (sounds.S_sfx[i].link == null) {
                // Load data from WAD file.
                S_sfx[i].data = getsfx(S_sfx[i].name, lengths, i);
            } else {
                // Previously loaded already?
                S_sfx[i].data = S_sfx[i].link.data;
            }
        }
    }

    /**
     * This is only the common part of InitSound that caches sound data in
     * 16-bit, stereo format (used by Audiolines). INTO sfxenum_t.
     * 
     * Only used by the Clip and David "drivers".
     * 
     */

    protected final void initSound16() {
        int i;

        // Initialize external data (all sounds) at start, keep static.

        for (i = 1; i < NUMSFX; i++) {
            // Alias? Example is the chaingun sound linked to pistol.
            if (sounds.S_sfx[i].link == null) {
                // Load data from WAD file.
                S_sfx[i].data = getsfx16(S_sfx[i].name, lengths, i);
            } else {
                // Previously loaded already?
                S_sfx[i].data = S_sfx[i].link.data;
            }
        }
    }

}

package s;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Hashtable;

import w.CacheableDoomObject;

/** Blatantly ripping off Chocolate Doom */

public class SpeakerSound implements CacheableDoomObject{
    
    public short header;
    public short length;
    public byte[] data;
    
    public static int[] timer_values=new int[]{0,
        6818,        6628,        6449,        6279,
        6087,        5906,        5736,        5575,
        5423,        5279,        5120,        4971,
        4830,        4697,        4554,        4435,
        4307,        4186,        4058,        3950,
        3836,        3728,        3615,        3519,
        3418,        3323,        3224,        3131,
        3043,        2960,        2875,        2794,
        2711,        2633,        2560,        2485,
        2415,        2348,        2281,        2213,
        2153,        2089,        2032,        1975,
        1918,        1864,        1810,        1757,
        1709,        1659,        1612,        1565,
        1521,        1478,        1435,         1395,
        1355,        1316,        1280,        1242,
        1207,        1173,       1140,         1107,
        1075,        1045,        1015,        986,
        959,        931,        905,        879,
        854,        829,        806,        783,
        760,        739,        718,        697,
        677,        658,        640,        621,
        604,        586,        570,        553,
        538,        522,        507,        493,
        479,        465,        452};
    
    /* From analysis of fraggle's PC Speaker timings, it was found
     * that their natural logarithm had the following intercept 
     * (starting at x=1) and slope. Therefore, it's possible
     * to go beyong the original 95 hardcoded values.
     */
    public static final double INTERCEPT=8.827321453;
    public static final double SLOPE=-0.028890647;
    public static final int CIA_8543_FREQ=1193182;

    public static float[] f=new float[256];
    
    static {
        f[0]=0;
        
        for (int x=1;x<f.length;x++){
            
            //f[x] = CIA_8543_FREQ/timer_values[x];
            
            f[x] = (float) (CIA_8543_FREQ/Math.exp(INTERCEPT+SLOPE*(x-1)));
            
        }
    }

    /** Will return a very basic, 8-bit 11.025 KHz rendition of the sound 
     *  This ain't no CuBase or MatLab, so if you were expecting perfect
     *  sound and solid DSP, go elsewhere.
     * 
     */
    public byte[] toRawSample(){
        // Length is in 1/140th's of a second 
        byte[] chunk=new byte[this.length*11025/140];

        int counter=0;
        for (int i=0;i<this.length;i++){
            byte[] tmp=getPhoneme(this.data[i]);
            System.arraycopy(tmp, 0,chunk,counter,tmp.length);
            counter+=tmp.length;
        }
        return chunk;
    }
    
    private static Hashtable<Integer,byte[]> phonemes=new Hashtable<Integer,byte[]>();
    
    public static byte[] getPhoneme(int phoneme){
        
        if (!phonemes.containsKey(phoneme)){
            
            // Generate a square wave with a duration of 1/140th of a second
            int samples=11025/140;
            byte[] tmp=new byte[samples];

            float frequency=f[phoneme];
            for (int i=0;i<samples;i++){                
                tmp[i]=(byte) (127+127*Math.signum(Math.sin(frequency*Math.PI*2*(i/11025f))));
            }
            
            phonemes.put(phoneme, tmp);
        }
        
        return phonemes.get(phoneme);
        
    }
    
    @Override
    public void unpack(ByteBuffer buf)
            throws IOException {
        buf.order(ByteOrder.LITTLE_ENDIAN);
        header=buf.getShort();
        length=buf.getShort();
        data=new byte[length];
        buf.get(data);
        
    }
    
}

package s;

public class DummyMusic implements IMusic {

	@Override
	public void InitMusic() {
		// TODO Auto-generated method stub

	}

	@Override
	public void ShutdownMusic() {
		// TODO Auto-generated method stub

	}

	@Override
	public void SetMusicVolume(int volume) {
		// TODO Auto-generated method stub

	}

	@Override
	public void PauseSong(int handle) {
		// TODO Auto-generated method stub

	}

	@Override
	public void ResumeSong(int handle) {
		// TODO Auto-generated method stub

	}

	@Override
	public int RegisterSong(byte[] data) {
		// TODO Auto-generated method stub
		return 0;
	}

	@Override
	public void PlaySong(int handle, boolean looping) {
		// TODO Auto-generated method stub

	}

	@Override
	public void StopSong(int handle) {
		// TODO Auto-generated method stub

	}

	@Override
	public void UnRegisterSong(int handle) {
		// TODO Auto-generated method stub

	}

}

package s;

import p.mobj_t;
import data.sounds.musicenum_t;
import data.sounds.sfxenum_t;

/** Does nothing. Just allows me to code without
 *  commenting out ALL sound-related code. Hopefully
 *  it will be superseded by a real sound driver one day.
 * 
 * @author Velktron
 *
 */

public class DummySoundDriver implements IDoomSound{

	@Override
	public void Init(int sfxVolume, int musicVolume) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void Start() {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void StartSound(ISoundOrigin origin, int sound_id) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void StartSound(ISoundOrigin origin, sfxenum_t sound_id) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void StartSoundAtVolume(ISoundOrigin origin, int sound_id, int volume) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void StopSound(ISoundOrigin origin) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void ChangeMusic(int musicnum, boolean looping) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void StopMusic() {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void PauseSound() {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void ResumeSound() {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void UpdateSounds(mobj_t listener) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void SetMusicVolume(int volume) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void SetSfxVolume(int volume) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void StartMusic(int music_id) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void StartMusic(musicenum_t music_id) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void ChangeMusic(musicenum_t musicnum, boolean looping) {
		// TODO Auto-generated method stub
		
	}

}

package s;

import java.util.ArrayList;
import java.util.concurrent.Semaphore;

import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.FloatControl;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.SourceDataLine;
import javax.sound.sampled.FloatControl.Type;

import data.sounds;
import data.sounds.sfxenum_t;
import doom.DoomStatus;

/** David Martel's sound driver for Mocha Doom. Excellent work!
 * 
 *  However, it's based on Java Audiolines, and as such has a number
 *  of drawbacks:
 *  
 * a) Sounds are forcibly blown to be stereo, 16-bit otherwise it's 
 *    impossible to get panning controls.
 * b) Volume, master gain, panning, pitch etc. controls are NOT guaranteed
 *    to be granted across different OSes , and your mileage may vary. It's
 *    fairly OK under Windows and OS X, but Linux is very clunky. The only
 *    control that is -somewhat- guaranteed is the volume one.
 * c) Spawns as many threads as channels. Even if semaphore waiting it used,
 *    that can be taxing for slower systems.

 * 
 * @author David
 * @author Velktron
 *
 */

public class DavidSFXModule extends AbstractSoundDriver{
	
	ArrayList<DoomSound> cachedSounds=new ArrayList<DoomSound>();
	
	public final float[] linear2db;	
	
	private SoundWorker[] channels;
	private Thread[] soundThread;
	
	public DavidSFXModule(DoomStatus DS,int numChannels) {
		super(DS,numChannels);
		linear2db=computeLinear2DB();
		
		}
	
    private float[] computeLinear2DB() {
    	
    	// Maximum volume is 0 db, minimum is ... -96 db.
    	// We rig this so that half-scale actually gives quarter power,
    	// and so is -6 dB.
    	float[] tmp=new float[VOLUME_STEPS];
    	
    	for (int i=0;i<VOLUME_STEPS;i++){
    		float linear=(float)(10*Math.log10((float)i/(float)VOLUME_STEPS));
    		// Hack. The minimum allowed value as of now is -80 db.
    		if (linear<-36.0) linear=-36.0f;
    		tmp[i]= linear;
    		
    	}
    		
    		
    		
		return tmp;
	}

	@Override
	public boolean InitSound() {
        // Secure and configure sound device first.
        System.err.println("I_InitSound: ");

        // Initialize external data (all sounds) at start, keep static.

        initSound16();

        // Cache sounds internally so they can be "fed" to AudioLine threads later.
        // These can be more than the usual built-in sounds.
        
        
        for (int i=0;i<sounds.S_sfx.length;i++){
        	DoomSound tmp=new DoomSound(sounds.S_sfx[i],DoomSound.DEFAULT_SAMPLES_FORMAT);
        	cachedSounds.add(tmp);	
        	}
        
        System.err.print(" pre-cached all sound data\n");
        // Finished initialization.
        System.err.print("I_InitSound: sound module ready\n");
        
        return true;
		
	}

	@Override
	public void UpdateSound() {
		// In theory, we should update volume + panning for each active channel.
		// Ouch. Ouch Ouch.
		
	}

	@Override
	public void SubmitSound() {
		// Sound should be submitted to the sound threads, which they pretty much
		// do themselves.
		
	}

	@Override
	public void ShutdownSound() {
		 // Wait till all pending sounds are finished.
		  boolean done = false;
		  int i;
		  
		  while ( !done)
		  {
		    for( i=0 ; i<numChannels && !(channels[i].isPlaying()) ; i++);
		    if (i==numChannels)  done=true;
		  }
		  
		  for( i=0 ; i<numChannels; i++){
			channels[i].terminate=true;  
			channels[i].wait.release();
			try {
				this.soundThread[i].join();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		  }
		  // Done.
		  return;
		
	}

	@Override
	public void SetChannels(int numChannels) {

		channels= new SoundWorker[numChannels];
		soundThread= new Thread[numChannels];	
		
		// This is actually called from IDoomSound.
		for (int i = 0; i < numChannels; i++) {
			channels[i]=new SoundWorker(i);
			soundThread[i] = new Thread(channels[i]);
			soundThread[i].start();
		}
		
	}
	
	/** This one will only create datalines for common clip/audioline samples
	 *  directly.
	 * 
	 * @param c
	 * @param sfxid
	 */
	private final void  createDataLineForChannel(int c, int sfxid){
		
		// None? Make a new one.
		
		if (channels[c].auline == null) {
        	try {
        		DoomSound tmp=cachedSounds.get(sfxid);
        		// Sorry, Charlie. Gotta make a new one.
        		DataLine.Info info = new DataLine.Info(SourceDataLine.class, DoomSound.DEFAULT_SAMPLES_FORMAT);
				channels[c].auline = (SourceDataLine) AudioSystem.getLine(info);
				channels[c].auline.open(tmp.format);
			} catch (LineUnavailableException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
					boolean errors=false;
        			// Add individual volume control.
        			if (channels[c].auline.isControlSupported(Type.MASTER_GAIN))
        				channels[c].vc=(FloatControl) channels[c].auline
        				.getControl(Type.MASTER_GAIN);
        			else {
        			System.err.print("MASTER_GAIN, ");
        			errors=true;
        			if (channels[c].auline.isControlSupported(Type.VOLUME))
            				channels[c].vc=(FloatControl) channels[c].auline
            				.getControl(Type.VOLUME);
        			else 
        				System.err.print("VOLUME, ");
        			} 
        			

        			// Add individual pitch control.
        			if (channels[c].auline.isControlSupported(Type.SAMPLE_RATE)){
        				channels[c].pc=(FloatControl) channels[c].auline
        				.getControl(Type.SAMPLE_RATE);
        			} else {
        				errors=true;
        				System.err.print("SAMPLE_RATE, ");
        			} 
        			
        			// Add individual pan control
        			if (channels[c].auline.isControlSupported(Type.BALANCE)){
        				channels[c].bc=(FloatControl) channels[c].auline
        				.getControl(FloatControl.Type.BALANCE);
        			} else {
        				System.err.print("BALANCE, ");
        				errors=true;
        				if (channels[c].auline.isControlSupported(Type.PAN)){        					
        				channels[c].bc=(FloatControl) channels[c].auline
        				.getControl(FloatControl.Type.PAN);
        			} else {
        				System.err.print("PANNING ");
        				}
        			}

        			if (errors) System.err.printf("for channel %d NOT supported!\n",c);
        			
        			channels[c].auline.start();
        		}
	}

	/* UNUSED version, designed to work on any type of sample (in theory).
	   Requires a DoomSound container for separate format information.
	  
	 private final void  createDataLineForChannel(int c, DoomSound sound){
		if (channels[c].auline == null) {
        	AudioFormat format = sound.ais.getFormat();
        	DataLine.Info info = new DataLine.Info(SourceDataLine.class, format);
        	try {
				channels[c].auline = (SourceDataLine) AudioSystem.getLine(info);
				channels[c].auline.open(format);
			} catch (LineUnavailableException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
        			// Add individual volume control.
        			if (channels[c].auline.isControlSupported(Type.MASTER_GAIN))
        				channels[c].vc=(FloatControl) channels[c].auline
        				.getControl(Type.MASTER_GAIN);
        			else {
        			System.err.printf("MASTER_GAIN for channel %d NOT supported!\n",c);
        			if (channels[c].auline.isControlSupported(Type.VOLUME))
            				channels[c].vc=(FloatControl) channels[c].auline
            				.getControl(Type.VOLUME);
        			else 
        				System.err.printf("VOLUME for channel %d NOT supported!\n",c);
        			} 
        			

        			// Add individual pitch control.
        			if (channels[c].auline.isControlSupported(Type.SAMPLE_RATE)){
        				channels[c].pc=(FloatControl) channels[c].auline
        				.getControl(Type.SAMPLE_RATE);
        			} else {
        				System.err.printf("SAMPLE_RATE for channel %d NOT supported!\n",c);
        			} 
        			
        			// Add individual pan control (TODO: proper positioning).
        			if (channels[c].auline.isControlSupported(Type.BALANCE)){
        				channels[c].bc=(FloatControl) channels[c].auline
        				.getControl(FloatControl.Type.BALANCE);
        			} else {
        				System.err.printf("BALANCE for channel %d NOT supported!\n",c);
        				if (channels[c].auline.isControlSupported(Type.PAN)){        					
        				channels[c].bc=(FloatControl) channels[c].auline
        				.getControl(FloatControl.Type.PAN);
        			} else {
        				System.err.printf("PAN for channel %d NOT supported!\n",c);
        			}
        			}

        			channels[c].auline.start();
        		}
	}
	*/
	
	@Override
	protected int addsfx( int sfxid,int volume,int pitch, int seperation)
	{
		int		i;
		int		rc = -1;

		int		oldest = DS.gametic;
		int		oldestnum = 0;
		int		slot;

		int		rightvol;
		int		leftvol;

		// Chainsaw troubles.
		// Play these sound effects only one at a time.
		if ( sfxid == sfxenum_t.sfx_sawup.ordinal()
				|| sfxid == sfxenum_t.sfx_sawidl.ordinal()
				|| sfxid == sfxenum_t.sfx_sawful.ordinal()
				|| sfxid == sfxenum_t.sfx_sawhit.ordinal()
				|| sfxid == sfxenum_t.sfx_stnmov.ordinal()
				|| sfxid == sfxenum_t.sfx_pistol.ordinal()	 )
		{
			// Loop all channels, check.
			for (i=0 ; i<numChannels ; i++)
			{
				// Active, and using the same SFX?
				if ( (channels[i].isPlaying())
						&& (channelids[i] == sfxid) )
				{
					// Reset.
					channels[i].stopSound();
					// We are sure that iff,
					//  there will only be one.
					break;
				}
			}
		}

		// Loop all channels to find oldest SFX.
		for (i=0; (i<numChannels) && (channels[i]!=null); i++)
		{
			if (channelstart[i] < oldest)
			{
				oldestnum = i;
				oldest = channelstart[i];
			}
		}

		// Tales from the cryptic.
		// If we found a channel, fine.
		// If not, we simply overwrite the first one, 0.
		// Probably only happens at startup.
		if (i == numChannels)
			slot = oldestnum;
		else
			slot = i;

		// Okay, in the less recent channel,
		//  we will handle the new SFX.
		// Set pointer to raw data.
	      
        // Create a dataline for the "lucky" channel,
		// or reuse an existing one if it exists.
        createDataLineForChannel(slot,sfxid);

		// Reset current handle number, limited to 0..100.
		if (handlenums==0) // was !handlenums, so it's actually 1...100?
			handlenums = MAXHANDLES;

		// Assign current handle number.
		// Preserved so sounds could be stopped (unused).
		channelhandles[slot]= rc = handlenums--;
		channelstart[slot] = DS.gametic;

		// Separation, that is, orientation/stereo.
		//  range is: 1 - 256
		seperation += 1;

		// Per left/right channel.
		//  x^2 seperation,
		//  adjust volume properly.
		leftvol =
			volume - ((volume*seperation*seperation) >> 16); ///(256*256);
		seperation = seperation - 257;
		rightvol =
			volume - ((volume*seperation*seperation) >> 16);	


		// Sanity check, clamp volume.

		if (rightvol < 0 || rightvol > 127)
			DS.I.Error("rightvol out of bounds");

		if (leftvol < 0 || leftvol > 127)
			DS.I.Error("leftvol out of bounds"); 

		// Preserve sound SFX id,
		//  e.g. for avoiding duplicates of chainsaw.
		channelids[slot] = sfxid;

		channels[slot].setVolume(volume);
		channels[slot].setPanning(seperation+256);
		channels[slot].addSound(cachedSounds.get(sfxid).data, handlenums);
		channels[slot].setPitch(pitch);
		
		if (D) System.err.println(channelStatus());
        if (D) System.err.printf("Playing %d vol %d on channel %d\n",rc,volume,slot);
		// You tell me.
		return rc;
	}
	
	@Override
	public void StopSound(int handle) {
		// Which channel has it?
		int  hnd=getChannelFromHandle(handle);
		if (hnd>=0) 
			channels[hnd].stopSound();
	}

	@Override
	public boolean SoundIsPlaying(int handle) {
		
		return getChannelFromHandle(handle)!=BUSY_HANDLE;
		}

	
	@Override
	public void UpdateSoundParams(int handle, int vol, int sep, int pitch) {
		
		// This should be called on sounds that are ALREADY playing. We really need
		// to retrieve channels from their handles.
		
		//System.err.printf("Updating sound with handle %d vol %d sep %d pitch %d\n",handle,vol,sep,pitch);
		
		int i=getChannelFromHandle(handle);
		// None has it?
		if (i!=BUSY_HANDLE){
			//System.err.printf("Updating sound with handle %d in channel %d\n",handle,i);
			channels[i].setVolume(vol);
			channels[i].setPitch(pitch);
			channels[i].setPanning(sep);
			}
		
	}
	
	/** Internal use. 
	 * 
	 * @param handle
	 * @return the channel that has the handle, or -2 if none has it.
	 */
	private int getChannelFromHandle(int handle){
		// Which channel has it?
		for (int i=0;i<numChannels;i++){
			if (channelhandles[i]==handle) return i;
		}
		
		return BUSY_HANDLE;
	}
	
	/** A Thread for playing digital sound effects.
	 * 
	 *  Obviously you need as many as channels?
	 *   
	 *  In order not to end up in a hell of effects,
	 *  certain types of sounds must be limited to 1 per object.
	 *
	 */


	private class SoundWorker implements Runnable {
			public Semaphore wait; // Holds the worker still until there's a new sound
			FloatControl vc; // linear volume control
			FloatControl bc; // balance/panning control
			FloatControl pc; // pitch control
			byte[] currentSoundSync;
			byte[] currentSound;
			
			public SoundWorker(int id){
				this.id=id;
				this.handle=IDLE_HANDLE;
				wait=new Semaphore(1);
			}
			

			int id;
			/** Used to find out whether the same object is continuously making
			 *  sounds. E.g. the player, ceilings etc. In that case, they must be
			 *  interrupted.
			 */
			int handle;
			public boolean terminate;
			SourceDataLine auline;
			
			/** This is how you tell the thread to play a sound,
			 * I suppose.  */
			
			public void addSound(byte[] ds, int handle) {
				
				if (D) System.out.printf("Added handle %d to channel %d\n",handle,id);
				this.handle=handle;
				this.currentSound=ds;
				this.auline.stop();
				this.auline.start();
				this.wait.release();
				
			}

			/** Accepts volume in "Doom" format (0-127).
			 * 
			 * @param volume
			 */
			public void setVolume(int volume){
				if (vc!=null){
					if (vc.getType()==FloatControl.Type.MASTER_GAIN) {
						float vol = linear2db[volume];
						vc.setValue(vol);
						}
					else if (vc.getType()==FloatControl.Type.VOLUME){
						float vol = vc.getMinimum()+(vc.getMaximum()-vc.getMinimum())*(float)volume/127f;
						vc.setValue(vol);
					}
				}
				}
			
			public void setPanning(int sep){
				// Q: how does Doom's sep map to stereo panning?
				// A: Apparently it's 0-255 L-R.
				if (bc!=null){
				float pan= bc.getMinimum()+(bc.getMaximum()-bc.getMinimum())*(float)(sep)/ISoundDriver.PANNING_STEPS;
				//System.err.printf("Panning %d %f %f %f\n",sep,bc.getMinimum(),bc.getMaximum(),pan);
				bc.setValue(pan);
				}
			}
			
			/** Expects a steptable value between 16K and 256K, with
			 *  64K being the middle.
			 * 
			 * @param pitch
			 */
			public void setPitch(int pitch){
				if (pc!=null){
				float pan= (float) (pc.getValue()*((float)pitch/65536.0));
				pc.setValue(pan);
				}
			}
			
			public void run() {
				System.err.printf("Sound thread %d started\n",id);
				while (!terminate) {
					currentSoundSync = currentSound;
					if (currentSoundSync != null) {

						try {
							auline.write(currentSoundSync, 0, currentSoundSync.length);
						} catch (Exception e) { 
							e.printStackTrace();
							return;
						} finally {
							// The previous steps are actually VERY fast.
							// However this one waits until the data has been
							// consumed, Interruptions/signals won't reach  here,
							// so it's pointless trying to interrupt the actual filling.
							//long a=System.nanoTime();
							auline.drain();
							//long b=System.nanoTime();
							//System.out.printf("Channel %d completed in %f.\n",id,(float)(b-a)/1000000000f);
							}
						// Report that this channel is free.
						currentSound = null;
						// Remove its handle.
						
						//System.out.printf("Channel  %d with handle %d done. Marking as free\n",id,handle);
						if (handle>0)
						channelhandles[this.id]=IDLE_HANDLE;
						this.handle=IDLE_HANDLE;
					}

					// If we don't sleep at least a bit here, busy waiting becomes
					// way too taxing. Waiting on a semaphore (triggered by adding a new sound)
					// seems like a better method.

					try {
						wait.acquire();
					} catch (InterruptedException e) {
					} 
				}
			}

			public void stopSound() {
					auline.stop();
					auline.flush();
					//System.out.printf("Channel %d with handle %d interrupted. Marking as free\n",id,handle);
					channelhandles[this.id]=IDLE_HANDLE;
					this.handle=IDLE_HANDLE;
					currentSound = null;
					auline.start();
					}

			public boolean isPlaying() {
				//System.out.printf("Channel %d with handle %d queried\n",id,handle);
				return (this.handle!=IDLE_HANDLE||this.currentSound!=null);
			}

		}

		StringBuilder sb=new StringBuilder();
	
		public String channelStatus(){
			sb.setLength(0);
			for (int i=0;i<numChannels;i++){
				if (channels[i].isPlaying())
				sb.append(i);
				else sb.append('-');
			}
			
			return sb.toString();
			
			
		}
	
}


package s;

import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioFormat.Encoding;

import data.sfxinfo_t;

/** A class representing a sample in memory 
 *  Convenient for wrapping/mirroring it regardless of what it represents.
 * */
class DoomSound extends sfxinfo_t {

	/** This audio format is the one used by internal samples (16 bit, 11KHz, Stereo) 
     *  for Clips and AudioLines. Sure, it's not general enough... who cares though?
     */
	public final static AudioFormat DEFAULT_SAMPLES_FORMAT=new AudioFormat(Encoding.PCM_SIGNED, ISoundDriver.SAMPLERATE, 16, 2, 4, ISoundDriver.SAMPLERATE, true);
	
	public final static AudioFormat DEFAULT_DOOM_FORMAT=new AudioFormat(Encoding.PCM_UNSIGNED, ISoundDriver.SAMPLERATE, 8, 1, 1, ISoundDriver.SAMPLERATE, true);
	
	
	public AudioFormat format;
	
	public DoomSound(AudioFormat format) {
		this.format=format;
	}
	
	public DoomSound(){
		this.format=DEFAULT_DOOM_FORMAT;
	}
	
	public DoomSound(sfxinfo_t sfx,AudioFormat format){
		this(format);
		this.data=sfx.data;
		this.pitch=sfx.pitch;
		this.link=sfx.link;
		this.lumpnum=sfx.lumpnum;
		this.name=sfx.name;
		this.priority=sfx.priority;
		this.singularity=sfx.singularity;
		this.usefulness=sfx.usefulness;
		this.volume=sfx.volume;
		}
	
}

package s;

import data.sfxinfo_t;

public class DummySFX implements ISoundDriver {

	@Override
	public boolean InitSound() {
		// Dummy is super-reliable ;-)
		return true;
	}

	@Override
	public void UpdateSound() {
		// TODO Auto-generated method stub

	}

	@Override
	public void SubmitSound() {
		// TODO Auto-generated method stub

	}

	@Override
	public void ShutdownSound() {
		// TODO Auto-generated method stub

	}

	@Override
	public int GetSfxLumpNum(sfxinfo_t sfxinfo) {
		// TODO Auto-generated method stub
		return 0;
	}

	@Override
	public int StartSound(int id, int vol, int sep, int pitch, int priority) {
		// TODO Auto-generated method stub
		return 0;
	}

	@Override
	public void StopSound(int handle) {
		// TODO Auto-generated method stub

	}

	@Override
	public boolean SoundIsPlaying(int handle) {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public void UpdateSoundParams(int handle, int vol, int sep, int pitch) {
		// TODO Auto-generated method stub

	}

	@Override
	public void SetChannels(int numChannels) {
		// TODO Auto-generated method stub
		
	}

}

package s;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

import utils.C2JUtils;

public class DoomToWave {

	static int MEMORYCACHE = 0x8000;
	static class RIFFHEAD	{
	  byte[] riff = new byte[4];
	  int length;
	  byte[] wave = new byte[4];
	  
	  public void pack(ByteBuffer b){
		  b.put(riff);
		  b.putInt(length);
		  b.put(wave);
	  }
	  
	  public int size(){
		  return 12;
	  }
	  
	}
	
	RIFFHEAD headr = new RIFFHEAD();
	
	static class CHUNK 
	{ byte[] name = new byte[4];
	  int size;
	  
	  public void pack(ByteBuffer b){
		  b.put(name);
		  b.putInt(size);
	  }
	  
	  public int size(){
		  return 8;
	  }
	}
	
	CHUNK headc = new CHUNK();
	
	static class WAVEFMT
	{ byte[] fmt = new byte[4];      /* "fmt " */
	  int fmtsize;    /*0x10*/
	  int tag;        /*format tag. 1=PCM*/
	  int channel;    /*1*/
	  int smplrate;
	  int bytescnd;   /*average bytes per second*/
	  int align;      /*block alignment, in bytes*/
	  int nbits;      /*specific to PCM format*/
	  
	  public void pack(ByteBuffer b){
		  b.put(fmt);
		  b.putInt(fmtsize);
		  b.putChar((char) tag);
		  b.putChar((char) channel);
		  b.putInt(smplrate);
		  b.putInt(bytescnd);
		  b.putChar((char) align);
		  b.putChar((char) nbits);
	  	}	
	  
	  public int size(){
		  return 24;
	  }
	}
	
	WAVEFMT headf = new WAVEFMT();
	int SIZEOF_WAVEFMT = 24;

	static class WAVEDATA /*data*/
	{  byte[] data = new byte[4];    /* "data" */
	   int datasize;
	   
		  public void pack(ByteBuffer b){
			  b.put(data);
			  b.putInt(datasize);
		  	}	
	}
	WAVEDATA headw = new WAVEDATA();
	int SIZEOF_WAVEDATA = 8;

	public void SNDsaveSound(InputStream is, OutputStream os) throws IOException {
	  int type = DoomIO.freadint(is, 2);//  peek_i16_le (buffer);
	  int speed = DoomIO.freadint(is, 2);//peek_u16_le (buffer + 2);
	  int datasize = DoomIO.freadint(is, 4);//peek_i32_le (buffer + 4);
	  if (type!=3)
	    System.out.println("Sound: weird type "+type+". Extracting anyway.");
	  
	  int headsize = 2 + 2 + 4;
	  int size = is.available();
	  
	  int phys_size = size /*- headsize*/;
	  if (datasize > phys_size)
	  {
	    System.out.println("Sound %s: declared sample size %lu greater than lump size %lu ;"/*,
		lump_name (name), (unsigned long) datasize, (unsigned long) phys_size*/);
	    System.out.println("Sound %s: truncating to lump size."/*, lump_name (name)*/);
	    datasize = phys_size;
	  }
	  /* Sometimes the size of sound lump is greater
	     than the declared sound size. */

	  else if (datasize < phys_size)
	  {
	    if (/*fullSND == TRUE*/true)       /* Save entire lump */
	      datasize = phys_size;
	    else
	    {
	      /*Warning (
		"Sound %s: lump size %lu greater than declared sample size %lu ;",
		lump_name (name), (unsigned long) datasize, (unsigned long) phys_size);
	      Warning ("Sound %s: truncating to declared sample size.",
		  lump_name (name));*/
	    }
	  }
	  
	  DoomIO.writeEndian = DoomIO.Endian.BIG;

	  SNDsaveWave(is, os, speed, datasize);
	}
	
	public byte[] DMX2Wave(byte[] DMXSound) throws IOException {
		  ByteBuffer is=ByteBuffer.wrap(DMXSound);
		  is.order(ByteOrder.LITTLE_ENDIAN);
		  int type = 0x0000FFFF&is.getShort();//  peek_i16_le (buffer);
		  int speed = 0x0000FFFF&is.getShort();//peek_u16_le (buffer + 2);
		  int datasize = is.getInt();//peek_i32_le (buffer + 4);
		  if (type!=3)
		    System.out.println("Sound: weird type "+type+". Extracting anyway.");
		  
		  int headsize = 2 + 2 + 4;
		  int size = is.remaining();
		  
		  int phys_size = size /*- headsize*/;
		  if (datasize > phys_size)
		  {
		    System.out.println("Sound %s: declared sample size %lu greater than lump size %lu ;"/*,
			lump_name (name), (unsigned long) datasize, (unsigned long) phys_size*/);
		    System.out.println("Sound %s: truncating to lump size."/*, lump_name (name)*/);
		    datasize = phys_size;
		  }
		  /* Sometimes the size of sound lump is greater
		     than the declared sound size. */

		  else if (datasize < phys_size)
		  {
		    if (/*fullSND == TRUE*/true)       /* Save entire lump */
		      datasize = phys_size;
		    else
		    {
		      /*Warning (
			"Sound %s: lump size %lu greater than declared sample size %lu ;",
			lump_name (name), (unsigned long) datasize, (unsigned long) phys_size);
		      Warning ("Sound %s: truncating to declared sample size.",
			  lump_name (name));*/
		    }
		  }

		  return SNDsaveWave(is, speed, datasize);
		}
	
	protected byte[] SNDsaveWave(ByteBuffer is, int speed, int size) throws IOException
	{
	
		// Size with header and data etc.
		byte[] output=new byte[headr.size()+headf.size() + SIZEOF_WAVEDATA+2*size];
		ByteBuffer os=ByteBuffer.wrap(output);
		os.order(ByteOrder.LITTLE_ENDIAN);
		os.position(0);
	  headr.riff = ("RIFF").getBytes();
	  int siz = 4 + SIZEOF_WAVEFMT + SIZEOF_WAVEDATA+2*size;
	  headr.length = siz;
	  headr.wave = C2JUtils.toByteArray("WAVE");
	  
	  headr.pack(os);

	  headf.fmt = C2JUtils.toByteArray("fmt ");
	  headf.fmtsize = SIZEOF_WAVEFMT - 8;
	  headf.tag = 1;
	  headf.channel = 2; // Maes: HACK to force stereo lines.
	  headf.smplrate = speed;
	  headf.bytescnd = 2*speed; // Ditto.
	  headf.align = 1;
	  headf.nbits = 8;

	  headf.pack(os);

	  headw.data = C2JUtils.toByteArray("data");
	  headw.datasize = 2*size;
	  //byte[] wtf=DoomIO.toByteArray(headw.datasize, 4);
	  
	  
	  headw.pack(os);
	
	  byte tmp;
	  
	  for (int i=0;i<size;i++)
	  {
	  tmp=is.get();
	  os.put(tmp);
	  os.put(tmp);
	  }
	  
	  return os.array();
	}
	

	void SNDsaveWave(InputStream is, OutputStream os, int speed, int size) throws IOException
	{
	  int wsize,sz=0;
	  headr.riff = DoomIO.toByteArray("RIFF");
	  int siz = 4 + SIZEOF_WAVEFMT + SIZEOF_WAVEDATA+size;
	  headr.length = siz;
	  headr.wave = DoomIO.toByteArray("WAVE");
	  
	  DoomIO.fwrite2(headr.riff, os);
	  DoomIO.fwrite2(DoomIO.toByteArray(headr.length, 4), os);
	  DoomIO.fwrite2(headr.wave, os);

	  headf.fmt = DoomIO.toByteArray("fmt ");
	  headf.fmtsize = SIZEOF_WAVEFMT - 8;
	  headf.tag = 1;
	  headf.channel = 1; // Maes: HACK to force stereo lines.
	  headf.smplrate = speed;
	  headf.bytescnd = speed;
	  headf.align = 1;
	  headf.nbits = 8;

	  DoomIO.fwrite2(headf.fmt, os);
	  DoomIO.fwrite2(DoomIO.toByteArray(headf.fmtsize, 4), os);
	  DoomIO.fwrite2(DoomIO.toByteArray(headf.tag, 2), os);
	  DoomIO.fwrite2(DoomIO.toByteArray(headf.channel, 2), os);
	  DoomIO.fwrite2(DoomIO.toByteArray(headf.smplrate, 4), os);
	  DoomIO.fwrite2(DoomIO.toByteArray(headf.bytescnd, 4), os);
	  DoomIO.fwrite2(DoomIO.toByteArray(headf.align, 2), os);
	  DoomIO.fwrite2(DoomIO.toByteArray(headf.nbits, 2), os);

	  headw.data = DoomIO.toByteArray("data");
	  headw.datasize = size;
	  
	  DoomIO.fwrite2(headw.data, os);
	  DoomIO.fwrite2(DoomIO.toByteArray(headw.datasize, 4), os);
	  
	  ByteArrayOutputStream shit=( ByteArrayOutputStream)os;
	 
	  byte[] crap=shit.toByteArray();
	  
	  byte[] bytes = new byte[MEMORYCACHE];
	  for(wsize=0;wsize<size;wsize+=sz)
	  { sz= (size-wsize>MEMORYCACHE)? MEMORYCACHE:(size-wsize);
	  is.read(bytes, 0, sz);
	  os.write(bytes, 0, sz);
	    //if(fwrite((buffer+(wsize)),(size_t)sz,1,fp)!=1)
	    //  ProgError("%s: write error (%s)", fname (file), strerror (errno));
	  }
	}

}

package s;

public class DSP {

    /**
     * QDSS Windowed Sinc ReSampling subroutine in Basic
     * 
     * @param x
     *        new sample point location (relative to old indexes) (e.g. every
     *        other integer for 0.5x decimation)
     * @param indat
     *        = original data array
     * @param alim
     *        = size of data array
     * @param fmax
     *        = low pass filter cutoff frequency
     * @param fsr
     *        = sample rate
     * @param wnwdth
     *        = width of windowed Sinc used as the low pass filter rem resamp()
     *        returns a filtered new sample point
     */

    public float resamp(float x, float[] indat, int alim, float fmax,
            float fsr, int wnwdth) {
        int i, j;
        float r_w, r_g, r_a;
        int r_snc, r_y; // some local variables
        r_g = 2 * fmax / fsr; // Calc gain correction factor
        r_y = 0;
        for (i = -wnwdth / 2; i < wnwdth / 2; i++) { // For 1 window width
            j = (int) (x + i); // Calc input sample index
            // calculate von Hann Window. Scale and calculate Sinc
            r_w =
                (float) (0.5 - 0.5 * Math.cos(2 * Math.PI
                        * (0.5 + (j - x) / wnwdth)));
            r_a = (float) (2 * Math.PI * (j - x) * fmax / fsr);
            r_snc = 1;
            if (Math.abs(r_a) > 0)
                r_snc = (int) (Math.sin(r_a) / r_a);
            if ((j >= 0) && (j < alim)) {
                r_y = (int) (r_y + r_g * r_w * r_snc * indat[j]);
            }
        }
        return r_y; // return new filtered sample
    }

    /*
     * Ron Nicholson's QDSS ReSampler cookbook recipe QDSS = Quick, Dirty,
     * Simple and Short Version 0.1b - 2007-Aug-01 Copyright 2007 Ronald H.
     * Nicholson Jr. No warranties implied. Error checking, optimization, and
     * quality assessment of the "results" is left as an exercise for the
     * student. (consider this code Open Source under a BSD style license) IMHO.
     * YMMV. http://www.nicholson.com/rhn/dsp.html
     */

    /**
     * R. Nicholson's QDDS FIR filter generator cookbook recipe QDDS = Quick,
     * Dirty, Dumb and Short version 0.6b - 2006-Dec-14, 2007-Sep-30 No
     * warranties implied. Error checking, optimization, and quality assessment
     * of the "results" is left as an exercise for the student. (consider this
     * code Open Source under a BSD style license) Some example filter
     * parameters:
     * 
     * @param fsr
     *        = 44100 : rem set fsr = sample rate
     * @param fc
     *        = 0 : rem set fc = 0 for lowpass fc = center frequency for
     *        bandpass filter fc = fsr/2 for a highpass
     * @param bw
     *        = 3000 : rem bw = bandwidth, range 0 .. fsr/2 and bw >= fsr/n bw =
     *        3 db corner frequency for a lowpass bw = half the 3 db passband
     *        for a bandpass filter
     * @param nt
     *        = 128 : rem nt = number of taps + 1 (nt must be even) nt should be
     *        > fsr / bw transition band will be around 3.5 * fsr / nt depending
     *        on the window applied and ripple spec.
     * @param g
     *        = 1 : rem g = filter gain for bandpass g = 0.5 , half the gain for
     *        a lowpass filter
     * @return array of FIR taps
     */

    public static double[] wsfiltgen(int nt, double fc, double fsr, double bw, double g) {
        double[] fir = new double[nt];//
        // fir(0) = 0
        // fir(1) is the first tap
        // fir(nt/2) is the middle tap
        // fir(nt-1) is the last tap

        double a, ys, yg, yf, yw;
        for (int i = 1; i < nt; i++) {
            a = (i - nt / 2) * 2.0 * Math.PI * bw / fsr; // scale Sinc width
            ys = 1;
            if (Math.abs(a) > 0)
                ys = Math.sin(a) / a; // calculate Sinc function
            yg = g * (4.0 * bw / fsr); // correct window gain
            yw = 0.54 - 0.46 * Math.cos(i * 2.0 * Math.PI / nt); // Hamming
                                                                 // window
            yf = Math.cos((i - nt / 2) * 2.0 * Math.PI * fc / fsr); // spectral
                                                                    // shift to
                                                                    // fc
            fir[i] = yf * yw * yg * ys; // rem assign fir coeff.
        }
        return fir;
    }
    
    public static void main(String[] argv){
        double[] fir=wsfiltgen(128,11025/2.0,22050,22050*3.0/4,0.5);
        System.out.println(fir);
        
    }
    
    public static byte[] crudeResample(byte[] input,int factor){        
        
        if (input==null || input.length<1) return null;
        
        final int LEN=input.length;
        
        byte[] res=new byte[LEN*factor];
        int k=0;        
        float start,end;
        
        res[0]=input[0];
        
        for (int i=0;i<LEN;i++){
            
            if (i==0) 
                start=127;
            else
                start=0xFF&input[i];
            
            if (i<LEN-1)
                end=0xFF&input[i+1];
            else
                end=127;
            
            double slope=(end-start)/factor;
            
            res[k]=input[i];
            //res[k+factor]=input[i+1];
            
            for (int j=1;j<factor;j++){
                double ratio=j/(double)factor;
                double value=start+slope*ratio;
                byte bval=(byte)Math.round(value);
                res[k+j]=bval;
                }
            k+=factor;
        }
        
        return res;
        
    }
    
    public static void filter(byte[] input,int samplerate, int cutoff){        
       
        double[] tmp=new double[input.length];
        
        // Normalize
        for (int i=0;i<input.length;i++){
            tmp[i]=(0xFF&input[i])/255.0;
        }
        
        filter(tmp,samplerate,cutoff,tmp.length);
        
        // De-normalize
        for (int i=0;i<input.length;i++){
            input[i]=(byte) (0xFF&(int)(tmp[i]*255.0));
        }
        
    }
    

    /** Taken from here
     * http://baumdevblog.blogspot.gr/2010/11/butterworth-lowpass-filter-coefficients.html
     */
    
    private static void getLPCoefficientsButterworth2Pole(final int samplerate, final double cutoff, final double[] ax, final double[] by)
    {
        double PI      = 3.1415926535897932385;
        double sqrt2 = 1.4142135623730950488;

        double QcRaw  = (2 * PI * cutoff) / samplerate; // Find cutoff frequency in [0..PI]
        double QcWarp = Math.tan(QcRaw); // Warp cutoff frequency

        double gain = 1 / (1+sqrt2/QcWarp + 2/(QcWarp*QcWarp));
        by[2] = (1 - sqrt2/QcWarp + 2/(QcWarp*QcWarp)) * gain;
        by[1] = (2 - 2 * 2/(QcWarp*QcWarp)) * gain;
        by[0] = 1;
        ax[0] = 1 * gain;
        ax[1] = 2 * gain;
        ax[2] = 1 * gain;
    }



    public static void filter(double[] samples, int smp, double cutoff,int count)
    {
      // Essentially a 3-tap filter?
       double[] ax=new double[3];
       double[] by=new double[3];
       double[] xv=new double[3];
       double[] yv=new double[3];

       getLPCoefficientsButterworth2Pole(smp, cutoff, ax, by);

       for (int i=0;i<count;i++)
       {
           xv[2] = xv[1];
           xv[1] = xv[0];
           xv[0] = samples[i];
           
           yv[2] = yv[1]; 
           yv[1] = yv[0];
           yv[0] =   (ax[0] * xv[0] + ax[1] * xv[1] + ax[2] * xv[2]
                        - by[1] * yv[0]
                        - by[2] * yv[1]);

           samples[i] = yv[0];
       }
    }
    
}

package s;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

import w.CacheableDoomObject;

/** An object representation of Doom's sound format */

public class DMXSound implements CacheableDoomObject{

    /** ushort, all Doom samples are "type 3". No idea how  */    
    public int type;
    /** ushort, speed in Hz. */
    public int speed;    
    /** uint */
    public int datasize;

    public byte[] data;
    
    @Override
    public void unpack(ByteBuffer buf)
            throws IOException {
       buf.order(ByteOrder.LITTLE_ENDIAN);
       type=buf.getChar();
       speed=buf.getChar();
       datasize=buf.getInt();
       data=new byte[Math.min(buf.remaining(),datasize)];
       buf.get(data);
    }

}

package s;

import static data.sounds.S_sfx;

import java.util.Collection;
import java.util.HashMap;

import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.FloatControl;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.FloatControl.Type;

import w.DoomBuffer;
import data.sounds.sfxenum_t;
import doom.DoomStatus;

/** Experimental Clip based driver. It does work, but it has no
 *  tangible advantages over the Audioline or Classic one. If the
 *  Audioline can be used, there's no reason to fall back to this 
 *  one.
 * 
 * KNOWN ISSUES:
 * 
 * a) Same general restrictions as audiolines (in fact, Clips ARE Audioline 
 *    in disguise)
 * b) Multiple instances of the same sound require multiple clips, so
 *    even caching them is a half-baked solution, and if you have e.g. 40 imps
 *    sound in a room.... 
 *    
 *    
 *  Currently unused.
 * 
 * @author Velktron
 *
 */

public class ClipSFXModule extends AbstractSoundDriver{
	
	HashMap<Integer,Clip> cachedSounds = new HashMap<Integer,Clip>();

	
	// Either it's null (no clip is playing) or non-null (some clip is playing).
	Clip[] channels;

	
	public final float[] linear2db;
	
	public ClipSFXModule(DoomStatus DS, int numChannels) {
		super(DS,numChannels);
		linear2db=computeLinear2DB();		
		}
	
    private float[] computeLinear2DB() {
    	
    	// Maximum volume is 0 db, minimum is ... -96 db.
    	// We rig this so that half-scale actually gives quarter power,
    	// and so is -6 dB.
    	float[] tmp=new float[VOLUME_STEPS];
    	
    	for (int i=0;i<VOLUME_STEPS;i++){
    		float linear=(float)(20*Math.log10((float)i/(float)VOLUME_STEPS));
    		// Hack. The minimum allowed value as of now is -80 db.
    		if (linear<-36.0) linear=-36.0f;
    		tmp[i]= linear;
    		
    	}
    		
    		
    		
		return tmp;
	}



	@Override
	public boolean InitSound() {
        // Secure and configure sound device first.
        System.err.println("I_InitSound: ");

        // We don't actually do this here (will happen only when we
        // create the first audio clip).

        // Initialize external data (all sounds) at start, keep static.

        initSound16();

        System.err.print(" pre-cached all sound data\n");
        // Finished initialization.
        System.err.print("I_InitSound: sound module ready\n");
        return true;

    }


/** Modified getsfx. The individual length of each sfx is not of interest.
 * However, they must be transformed into 16-bit, signed, stereo samples
 * beforehand, before being "fed" to the audio clips.
 * 
 * @param sfxname
 * @param index
 * @return
 */
	 protected byte[] getsfx(String sfxname,int index) {
	        byte[] sfx;
	        byte[] paddedsfx;
	        int i;
	        int size;
	        int paddedsize;
	        String name;
	        int sfxlump;

	        // Get the sound data from the WAD, allocate lump
	        // in zone memory.
	        name = String.format("ds%s", sfxname).toUpperCase();

	        // Now, there is a severe problem with the
	        // sound handling, in it is not (yet/anymore)
	        // gamemode aware. That means, sounds from
	        // DOOM II will be requested even with DOOM
	        // shareware.
	        // The sound list is wired into sounds.c,
	        // which sets the external variable.
	        // I do not do runtime patches to that
	        // variable. Instead, we will use a
	        // default sound for replacement.
	        if (DS.W.CheckNumForName(name) == -1)
	            sfxlump = DS.W.GetNumForName("dspistol");
	        else
	            sfxlump = DS.W.GetNumForName(name);

	        size = DS.W.LumpLength(sfxlump);

	        sfx = DS.W.CacheLumpNumAsRawBytes(sfxlump, 0);

	        // Size blown up to accommodate two channels and 16 bits.
	        // Sampling rate stays the same.
	        
	        paddedsize = (size-8)*2*2;
	        // Allocate from zone memory.
	        paddedsfx = new byte[paddedsize];

	        // Skip first 8 bytes (header), blow up the data
	        // to stereo, BIG ENDIAN, SIGNED, 16 bit. Don't expect any fancy DSP here!

	        int sample=0;
	        for (i = 8; i < size; i++){
	        	// final short sam=(short) vol_lookup[127][0xFF&sfx[i]];
	        	final short sam=(short) ((0xFF&sfx[i]-128)<<8);
	            paddedsfx[sample++] = (byte) (0xFF&(sam>>8));
	            paddedsfx[sample++]=(byte) (0xFF&sam);
	            paddedsfx[sample++]=(byte) (0xFF&(sam>>8));
	            paddedsfx[sample++]=(byte) (0xFF&sam);
	        }
	        
	        // Remove the cached lump.
	        DS.W.UnlockLumpNum(sfxlump);

	        // Return allocated padded data.
	        // So the first 8 bytes are useless?
	        return paddedsfx;
	    }

	@Override
	public void UpdateSound() {
		// We do nothing here, since the mixing is delegated to the OS
		// Just hope that it's more efficient that our own...
		
	}

	@Override
	public void SubmitSound() {
		// Dummy. Nothing actual to do here.
		
	}

	@Override
	public void ShutdownSound() {
		 // Wait till all pending sounds are finished.
		  boolean done = false;
		  int i;
		  

		  // FIXME (below).
		  //fprintf( stderr, "I_ShutdownSound: NOT finishing pending sounds\n");
		  //fflush( stderr );
		  
		  while ( !done)
		  {
		    for( i=0 ; i<numChannels && ((channels[i]==null)||(!channels[i].isActive())) ; i++);
		    // FIXME. No proper channel output.
		    if (i==numChannels)  done=true;
		  }
		  
		  for( i=0 ; i<numChannels; i++){
			  if (channels[i]!=null)
			channels[i].close();			
		  	}
		  
		  // Free up resources taken up by cached clips.
		  Collection<Clip> clips=this.cachedSounds.values();
		  for (Clip c:clips){
			  c.close();
		  }
		  
		  // Done.
		  return;
		
	}

	@Override
	public void SetChannels(int numChannels) {
		channels= new Clip[numChannels];
	}
	
	private final void  getClipForChannel(int c, int sfxid){
		
		// Try to see if we already have such a clip.
		Clip clip=this.cachedSounds.get(sfxid);
		
		boolean exists=false;
		
		// Does it exist?
		if (clip!=null){
			
			// Well, it does, but we are not done yet.
			exists=true;
			// Is it NOT playing already?
			if (!clip.isActive()){
				// Assign it to the channel.
				channels[c]=clip;
				return;
			}
		}
		
		// Sorry, Charlie. Gotta make a new one.
		DataLine.Info info = new DataLine.Info(Clip.class, DoomSound.DEFAULT_SAMPLES_FORMAT);
		
		try {
			clip = (Clip) AudioSystem.getLine(info);
		} catch (LineUnavailableException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	    try {
			clip.open( DoomSound.DEFAULT_SAMPLES_FORMAT, S_sfx[sfxid].data, 0, S_sfx[sfxid].data.length);
		} catch (LineUnavailableException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		if (!exists)
		this.cachedSounds.put(sfxid,clip);
		
	    channels[c]=clip;
	    
		
	   // Control[] cs=clip.getControls();
	   // 
	   // for (Control cc:cs){
	   // 	System.out.println("Control "+cc.getType().toString());
       // 		}
	}
	
	//
	// This function adds a sound to the
	//  list of currently active sounds,
	//  which is maintained as a given number
	//  (eight, usually) of internal channels.
	// Returns a handle.
	//
	protected short	handlenums = 0;

	protected int addsfx 	( int sfxid,int		volume,int pitch,int		seperation)
	{
		int		i;
		int		rc = -1;

		int		oldest = DS.gametic;
		int		oldestnum = 0;
		int		slot;

		// Chainsaw troubles.
		// Play these sound effects only one at a time.
		if ( sfxid == sfxenum_t.sfx_sawup.ordinal()
				|| sfxid == sfxenum_t.sfx_sawidl.ordinal()
				|| sfxid == sfxenum_t.sfx_sawful.ordinal()
				|| sfxid == sfxenum_t.sfx_sawhit.ordinal()
				|| sfxid == sfxenum_t.sfx_stnmov.ordinal()
				|| sfxid == sfxenum_t.sfx_pistol.ordinal()	 )
		{
			// Loop all channels, check.
			for (i=0 ; i<numChannels ; i++)
			{
				// Active, and using the same SFX?
				if (channels[i]!=null && channels[i].isRunning()
						&& channelids[i] == sfxid)
				{
					// Reset.
					channels[i].stop();
					// We are sure that iff,
					//  there will only be one.
					break;
				}
			}
		}

		// Loop all channels to find oldest SFX.
		for (i=0; (i<numChannels) && (channels[i]!=null); i++)
		{
			if (channelstart[i] < oldest)
			{
				oldestnum = i;
				oldest = channelstart[i];
			}
		}

		// Tales from the cryptic.
		// If we found a channel, fine.
		// If not, we simply overwrite the first one, 0.
		// Probably only happens at startup.
		if (i == numChannels)
			slot = oldestnum;
		else
			slot = i;

		// Okay, in the less recent channel,
		//  we will handle the new SFX.
		
		// We need to decide whether we can reuse an existing clip
		// or create a new one. In any case, when this method return 
		// we should have a valid clip assigned to channel "slot".

        getClipForChannel(slot,sfxid);

        
		// Reset current handle number, limited to 0..100.
		if (handlenums==0) // was !handlenums, so it's actually 1...100?
			handlenums = MAXHANDLES;

		// Assign current handle number.
		// Preserved so sounds could be stopped (unused).
		channelhandles[slot]= rc = handlenums--;

		// Should be gametic, I presume.
		channelstart[slot] = DS.gametic;

		// Get the proper lookup table piece
		//  for this volume level???
		//channelleftvol_lookup[slot] = vol_lookup[leftvol];
		//channelrightvol_lookup[slot] = vol_lookup[rightvol];

		// Preserve sound SFX id,
		//  e.g. for avoiding duplicates of chainsaw.
		channelids[slot] = sfxid;

		setVolume(slot,volume);
		setPanning(slot,seperation);
		//channels[slot].addSound(sound, handlenums);
		//channels[slot].setPitch(pitch);
		
		if(D) System.err.println(channelStatus());
        if(D) System.err.printf("Playing %d vol %d on channel %d\n",rc,volume,slot);
		// Well...play it.
      
        // FIXME VERY BIG PROBLEM: stop() is blocking!!!! WTF ?!
        //channels[slot].stop();
        //long  a=System.nanoTime();
        channels[slot].setFramePosition(0);
		channels[slot].start();
		// b=System.nanoTime();
		//System.err.printf("Sound playback completed in %d\n",(b-a));
        
        // You tell me.
		return rc;
	}
	
	
	/** Accepts volume in "Doom" format (0-127).
	 * 
	 * @param volume
	 */
	public void setVolume(int chan,int volume){
		Clip c=channels[chan];
		
		if (c.isControlSupported(Type.MASTER_GAIN)){
			FloatControl vc=(FloatControl) c.getControl(Type.MASTER_GAIN);
				float vol = linear2db[volume];
				vc.setValue(vol);
				}
			else if (c.isControlSupported(Type.VOLUME)){
				FloatControl vc=(FloatControl) c.getControl(Type.VOLUME);
				float vol = vc.getMinimum()+(vc.getMaximum()-vc.getMinimum())*(float)volume/127f;
				vc.setValue(vol);
			}
		}
	
	public void setPanning(int chan,int sep){
		Clip c=channels[chan];
		
		if (c.isControlSupported(Type.PAN)){
			FloatControl bc=(FloatControl) c.getControl(Type.PAN);
			// Q: how does Doom's sep map to stereo panning?
			// A: Apparently it's 0-255 L-R.
			float pan= bc.getMinimum()+(bc.getMaximum()-bc.getMinimum())*(float)sep/ISoundDriver.PANNING_STEPS;
			bc.setValue(pan);
			}
		}
	
	@Override
	public void StopSound(int handle) {
		// Which channel has it?
		int  hnd=getChannelFromHandle(handle);
		if (hnd>=0) {
			channels[hnd].stop();
			channels[hnd]=null;
		}
	}

	@Override
	public boolean SoundIsPlaying(int handle) {
		
		return getChannelFromHandle(handle)!=BUSY_HANDLE;
		}

	
	@Override
	public void UpdateSoundParams(int handle, int vol, int sep, int pitch) {
		
		// This should be called on sounds that are ALREADY playing. We really need
		// to retrieve channels from their handles.
		
		//System.err.printf("Updating sound with handle %d vol %d sep %d pitch %d\n",handle,vol,sep,pitch);
		
		int i=getChannelFromHandle(handle);
		// None has it?
		if (i!=BUSY_HANDLE){
			//System.err.printf("Updating sound with handle %d in channel %d\n",handle,i);
			setVolume(i,vol);
			setPanning(i,sep);
			//channels[i].setPanning(sep);
			}
		
	}
	
	
	/** Internal use. 
	 * 
	 * @param handle
	 * @return the channel that has the handle, or -2 if none has it.
	 */
	private int getChannelFromHandle(int handle){
		// Which channel has it?
		for (int i=0;i<numChannels;i++){
			if (channelhandles[i]==handle) return i;
		}
		
		return BUSY_HANDLE;
	}

		StringBuilder sb=new StringBuilder();
	
		public String channelStatus(){
			sb.setLength(0);
			for (int i=0;i<numChannels;i++){
				if (channels[i]!=null && channels[i].isActive())
				sb.append(i);
				else sb.append('-');
			}
			
			return sb.toString();
			
			
		}
	
}


package s;

import static data.Tables.ANGLETOFINESHIFT;
import static data.Tables.BITS32;
import static data.Tables.finesine;
import static data.sounds.S_sfx;
import static m.fixed_t.FRACBITS;
import static m.fixed_t.FixedMul;
import p.mobj_t;
import data.Defines;
import data.musicinfo_t;
import data.sfxinfo_t;
import data.sounds;
import data.sounds.musicenum_t;
import data.sounds.sfxenum_t;
import doom.DoomStatus;

/** Some stuff that is not implementation dependant
 *  This includes channel management, sound priorities,
 *  positioning, distance attenuation etc. It's up to 
 *  lower-level "drivers" to actually implements those.
 *  This particular class needs not be a dummy itself, but
 *  the drivers it "talks" to might be. 
 *  
 * 
 * */


public class AbstractDoomAudio implements IDoomSound{

	protected final DoomStatus<?,?> DS;
	protected final IMusic IMUS;
	protected final ISoundDriver ISND;

	protected final int numChannels;

	protected final static boolean D=false;

	/** the set of channels available. These are "soft" descriptor
	   channels,  not to be confused with actual hardware audio 
	   lines, which are an entirely different concern.

	 */

	protected final channel_t[]	channels;


	// These are not used, but should be (menu).
	// Maximum volume of a sound effect.
	// Internal default is max out of 0-15.
	protected int 		snd_SfxVolume = 15;

	// Maximum volume of music. Useless so far.
	protected int 		snd_MusicVolume = 15; 

	// whether songs are mus_paused
	protected boolean mus_paused;

	// music currently being played
	protected musicinfo_t mus_playing;

	protected int nextcleanup;

	public AbstractDoomAudio(DoomStatus<?,?> DS, int numChannels){
		this.DS = DS;
		this.numChannels=numChannels;
		this.channels=new channel_t[numChannels];
		this.IMUS=DS.IMUS;
		this.ISND=DS.ISND;
	}



	/** Volume, pitch, separation  & priority packed for parameter passing */

	protected class vps_t{
		int volume;
		int pitch;
		int sep;
		int priority;
	}


	/**
	 * Initializes sound stuff, including volume
	 * Sets channels, SFX and music volume,
	 *  allocates channel buffer, sets S_sfx lookup.
	 */

	public void Init
	( int		sfxVolume,
			int		musicVolume )
	{  
		int		i;

		System.err.printf("S_Init: default sfx volume %d\n", sfxVolume);

		this.snd_SfxVolume=sfxVolume;
		this.snd_MusicVolume=musicVolume;
		// Whatever these did with DMX, these are rather dummies now.
		// MAES: any implementation-dependant channel setup should start here.
		ISND.SetChannels(numChannels);

		SetSfxVolume(sfxVolume);
		// No music with Linux - another dummy.
		// MAES: these must be initialized somewhere, perhaps not here?
		IMUS.SetMusicVolume(musicVolume);

		// Allocating the internal channels for mixing
		// (the maximum numer of sounds rendered
		// simultaneously) within zone memory.
		// MAES: already done that in the constructor.

		// Free all channels for use
		for (i=0 ; i<numChannels ; i++){
			channels[i]=new channel_t();
			//channels[i].sfxinfo = null;
		}

		// no sounds are playing, and they are not mus_paused
		mus_paused = false;

		// Note that sounds have not been cached (yet).
		for (i=1 ; i<S_sfx.length ; i++)
			S_sfx[i].lumpnum = S_sfx[i].usefulness = -1;
	}

	//
	// Per level startup code.
	// Kills playing sounds at start of level,
	//  determines music if any, changes music.
	//
	public void Start()
	{
		int cnum;
		int mnum;

		// kill all playing sounds at start of level
		//  (trust me - a good idea)
		for (cnum=0 ; cnum<numChannels ; cnum++)
			if (channels[cnum].sfxinfo!=null)
				StopChannel(cnum);

		// start new music for the level
		mus_paused = false;

		if (DS.isCommercial())
			mnum = musicenum_t.mus_runnin.ordinal() + DS.gamemap - 1;
		else
		{
			musicenum_t[] spmus=
			{
					// Song - Who? - Where?

					musicenum_t.mus_e3m4,	// American	e4m1
					musicenum_t.mus_e3m2,	// Romero	e4m2
					musicenum_t.mus_e3m3,	// Shawn	e4m3
					musicenum_t.mus_e1m5,	// American	e4m4
					musicenum_t.mus_e2m7,	// Tim 	e4m5
					musicenum_t.mus_e2m4,	// Romero	e4m6
					musicenum_t.mus_e2m6,	// J.Anderson	e4m7 CHIRON.WAD
					musicenum_t.mus_e2m5,	// Shawn	e4m8
					musicenum_t.mus_e1m9	// Tim		e4m9
			};

			if (DS.gameepisode < 4)
				mnum = musicenum_t.mus_e1m1.ordinal() + (DS.gameepisode-1)*9 + DS.gamemap-1;
			else
				mnum = spmus[DS.gamemap-1].ordinal();
		}	

		// HACK FOR COMMERCIAL
		//  if (commercial && mnum > mus_e3m9)	
		//      mnum -= mus_e3m9;

		ChangeMusic(mnum, true);

		nextcleanup = 15;
	}

	private vps_t vps=new vps_t();

	public void
	StartSoundAtVolume
	( ISoundOrigin		origin_p,
			int		sfx_id,
			int		volume )
	{

		boolean		rc;
		int		sep = 0; // This is set later.
		int		pitch;
		int		priority;
		sfxinfo_t	sfx;
		int		cnum;

		ISoundOrigin	origin = (ISoundOrigin) origin_p;


		// Debug.
		
		//if (origin!=null && origin.type!=null)
		// System.err.printf(
	  	//   "S_StartSoundAtVolume: playing sound %d (%s) from %s %d\n",
	  	 //  sfx_id, S_sfx[sfx_id].name , origin.type.toString(),origin.hashCode());
		 

		// check for bogus sound #
		if (sfx_id < 1 || sfx_id > NUMSFX){
			Exception e=new Exception();
			e.printStackTrace();
			DS.I.Error("Bad sfx #: %d", sfx_id);
		}

		sfx = S_sfx[sfx_id];

		// Initialize sound parameters
		if (sfx.link!=null)
		{
			pitch = sfx.pitch;
			priority = sfx.priority;
			volume += sfx.volume;

			if (volume < 1)
				return;

			if (volume > snd_SfxVolume)
				volume = snd_SfxVolume;
		}	
		else
		{
			pitch = NORM_PITCH;
			priority = NORM_PRIORITY;
		}


		// Check to see if it is audible,
		//  and if not, modify the params
		if ((origin!=null) && origin != DS.players[DS.consoleplayer].mo)
		{
			vps.volume=volume;
			vps.pitch=pitch;
			vps.sep=sep;
			rc = AdjustSoundParams(DS.players[DS.consoleplayer].mo,
					origin, vps);
			volume=vps.volume;
			pitch=vps.pitch;
			sep=vps.sep;


			if ( origin.getX() == DS.players[DS.consoleplayer].mo.x
					&& origin.getY() == DS.players[DS.consoleplayer].mo.y)
			{	
				sep 	= NORM_SEP;
			}

			if (!rc) {
				//System.err.printf("S_StartSoundAtVolume: Sound %d (%s) rejected because: inaudible\n",
			  	//   sfx_id, S_sfx[sfx_id].name );
				return;
			}
		}	
		else
		{
			sep = NORM_SEP;
		}

		// hacks to vary the sfx pitches
		if (sfx_id >= sfxenum_t.sfx_sawup.ordinal()
				&& sfx_id <= sfxenum_t.sfx_sawhit.ordinal())
		{	
			pitch += 8 - (DS.RND.M_Random()&15);

			if (pitch<0)
				pitch = 0;
			else if (pitch>255)
				pitch = 255;
		}
		else if (sfx_id != sfxenum_t.sfx_itemup.ordinal()
				&& sfx_id != sfxenum_t.sfx_tink.ordinal())
		{
			pitch += 16 - (DS.RND.M_Random()&31);

			if (pitch<0)
				pitch = 0;
			else if (pitch>255)
				pitch = 255;
		}

		// kill old sound
		StopSound(origin);

		// try to find a channel
		cnum = getChannel(origin, sfx);

		if (cnum<0)
			return;

		//
		// This is supposed to handle the loading/caching.
		// For some odd reason, the caching is done nearly
		//  each time the sound is needed?
		//

		// get lumpnum if necessary
		if (sfx.lumpnum < 0) // Now, it crosses into specific territory.
			sfx.lumpnum = ISND.GetSfxLumpNum(sfx);

		/*
	#ifndef SNDSRV
	  // cache data if necessary
	  if (!sfx->data)
	  {
	    fprintf( stderr,
		     "S_StartSoundAtVolume: 16bit and not pre-cached - wtf?\n");

	    // DOS remains, 8bit handling
	    //sfx->data = (void *) W_CacheLumpNum(sfx->lumpnum, PU_MUSIC);
	    // fprintf( stderr,
	    //	     "S_StartSoundAtVolume: loading %d (lump %d) : 0x%x\n",
	    //       sfx_id, sfx->lumpnum, (int)sfx->data );

	  }
	#endif */

		// increase the usefulness
		if (sfx.usefulness++ < 0)
			sfx.usefulness = 1;

		// Assigns the handle to one of the channels in the
		//  mix/output buffer. This is when things actually
		// become hard (pun intended).
		// TODO: which channel? How do we know how the actual hardware 
		// ones map with the "soft" ones?
		// Essentially we're begging to get an actual channel.		
		
		channels[cnum].handle = ISND.StartSound(sfx_id,
				/*sfx->data,*/
				volume,
				sep,
				pitch,
				priority);
		
		if (D) System.err.printf("Handle %d for channel %d for sound %s vol %d sep %d\n",channels[cnum].handle,
				cnum,sfx.name,volume,sep);
	}	


	public void
	StartSound
	( ISoundOrigin		origin,
			sfxenum_t		sfx_id ){
		//  MAES: necessary sanity check at this point.
		if (sfx_id!=null && sfx_id.ordinal()>0)
			StartSound(origin,sfx_id.ordinal());
	}

	public void
	StartSound
	( ISoundOrigin		origin,
			int		sfx_id )
	{
		/* #ifdef SAWDEBUG
	    // if (sfx_id == sfx_sawful)
	    // sfx_id = sfx_itemup;
	#endif */

		StartSoundAtVolume(origin, sfx_id, snd_SfxVolume);


		// UNUSED. We had problems, had we not?
		/* #ifdef SAWDEBUG
	{
	    int i;
	    int n;

	    static mobj_t*      last_saw_origins[10] = {1,1,1,1,1,1,1,1,1,1};
	    static int		first_saw=0;
	    static int		next_saw=0;

	    if (sfx_id == sfx_sawidl
		|| sfx_id == sfx_sawful
		|| sfx_id == sfx_sawhit)
	    {
		for (i=first_saw;i!=next_saw;i=(i+1)%10)
		    if (last_saw_origins[i] != origin)
			fprintf(stderr, "old origin 0x%lx != "
				"origin 0x%lx for sfx %d\n",
				last_saw_origins[i],
				origin,
				sfx_id);

		last_saw_origins[next_saw] = origin;
		next_saw = (next_saw + 1) % 10;
		if (next_saw == first_saw)
		    first_saw = (first_saw + 1) % 10;

		for (n=i=0; i<numChannels ; i++)
		{
		    if (channels[i].sfxinfo == &S_sfx[sfx_sawidl]
			|| channels[i].sfxinfo == &S_sfx[sfx_sawful]
			|| channels[i].sfxinfo == &S_sfx[sfx_sawhit]) n++;
		}

		if (n>1)
		{
		    for (i=0; i<numChannels ; i++)
		    {
			if (channels[i].sfxinfo == &S_sfx[sfx_sawidl]
			    || channels[i].sfxinfo == &S_sfx[sfx_sawful]
			    || channels[i].sfxinfo == &S_sfx[sfx_sawhit])
			{
			    fprintf(stderr,
				    "chn: sfxinfo=0x%lx, origin=0x%lx, "
				    "handle=%d\n",
				    channels[i].sfxinfo,
				    channels[i].origin,
				    channels[i].handle);
			}
		    }
		    fprintf(stderr, "\n");
		}
	    }
	}
	#endif*/

	}

	// This one is public.
	public void StopSound(ISoundOrigin origin)
	{

		int cnum;

		for (cnum=0 ; cnum<numChannels ; cnum++)
		{
			if (channels[cnum].sfxinfo!=null && channels[cnum].origin == origin)
			{
				// This one is not.
				StopChannel(cnum);
				break;
			}
		}
	}

	//
	// Stop and resume music, during game PAUSE.
	//
	public void PauseSound()
	{
		if (mus_playing!=null && !mus_paused)
		{
			IMUS.PauseSong(mus_playing.handle);
			mus_paused = true;
		}
	}

	public void ResumeSound()
	{
		if (mus_playing!=null && mus_paused)
		{
			IMUS.ResumeSong(mus_playing.handle);
			mus_paused = false;
		}
	}

	@Override
	public void UpdateSounds(mobj_t listener) {
		boolean		audible;
		int		cnum;
		//int		volume;
		//int		sep;
		//int		pitch;
		sfxinfo_t	sfx;
		channel_t	c;

		// Clean up unused data.
		// This is currently not done for 16bit (sounds cached static).
		// DOS 8bit remains. 
		/*if (gametic.nextcleanup)
		    {
			for (i=1 ; i<NUMSFX ; i++)
			{
			    if (S_sfx[i].usefulness < 1
				&& S_sfx[i].usefulness > -1)
			    {
				if (--S_sfx[i].usefulness == -1)
				{
				    Z_ChangeTag(S_sfx[i].data, PU_CACHE);
				    S_sfx[i].data = 0;
				}
			    }
			}
			nextcleanup = gametic + 15;
		    }*/

		for (cnum=0 ; cnum<numChannels ; cnum++)
		{		    
			c = channels[cnum];
			sfx = c.sfxinfo;

			//System.out.printf("Updating channel %d %s\n",cnum,c);
			if (c.sfxinfo!=null)
			{
				if (ISND.SoundIsPlaying(c.handle))
				{
					// initialize parameters
					vps.volume = snd_SfxVolume;
					vps.pitch = NORM_PITCH;
					vps.sep = NORM_SEP;

					sfx=c.sfxinfo;

					if (sfx.link!=null)
					{
						vps.pitch = sfx.pitch;
						vps.volume += sfx.volume;
						if (vps.volume < 1)
						{
							StopChannel(cnum);
							continue;
						}
						else if (vps.volume > snd_SfxVolume)
						{
							vps.volume = snd_SfxVolume;
						}
					}

					// check non-local sounds for distance clipping
					//  or modify their params
					if (c.origin!=null && (listener != c.origin))
					{
						audible = AdjustSoundParams(listener,
								c.origin,
								vps);

						if (!audible)
						{
							StopChannel(cnum);
						}
						else
							ISND.UpdateSoundParams(c.handle, vps.volume, vps.sep, vps.pitch);
					}
				}
				else
				{
					// if channel is allocated but sound has stopped,
					//  free it
					StopChannel(cnum);
				}
			}
		}
		// kill music if it is a single-play && finished
		// if (	mus_playing
		//      && !I_QrySongPlaying(mus_playing->handle)
		//      && !mus_paused )
		// S_StopMusic();
	}

	public void SetMusicVolume(int volume)
	{
		if (volume < 0 || volume > 127)
		{
			DS.I.Error("Attempt to set music volume at %d",
					volume);
		}    

		IMUS.SetMusicVolume(volume);
		snd_MusicVolume = volume;
	}

	public void SetSfxVolume(int volume)
	{

		if (volume < 0 || volume > 127)
			DS.I.Error("Attempt to set sfx volume at %d", volume);

		snd_SfxVolume = volume;

	}

	//
	// Starts some music with the music id found in sounds.h.
	//
	public void StartMusic(int m_id)
	{
		ChangeMusic(m_id, false);
	}

	//
	// Starts some music with the music id found in sounds.h.
	//
	public void StartMusic(musicenum_t m_id)
	{
		ChangeMusic(m_id.ordinal(), false);
	}
	
	public void ChangeMusic(musicenum_t musicnum,
			boolean			looping )
	{
		ChangeMusic(musicnum.ordinal(), false);
	}


	public void
	ChangeMusic
	( int			musicnum,
			boolean			looping )
	{
		musicinfo_t	music = null;
		String		namebuf;

		if ( (musicnum <= musicenum_t.mus_None.ordinal())
				|| (musicnum >= musicenum_t.NUMMUSIC.ordinal()) )
		{

			DS.I.Error("Bad music number %d", musicnum);
		}
		else
			music = sounds.S_music[musicnum];

		if (mus_playing == music)
			return;

		// shutdown old music
		StopMusic();

		// get lumpnum if neccessary
		if (music.lumpnum==0)
		{
			namebuf=String.format("d_%s", music.name);
			music.lumpnum = DS.W.GetNumForName(namebuf);
		}

		// load & register it
		music.data = DS.W.CacheLumpNumAsRawBytes(music.lumpnum, Defines.PU_MUSIC);
		music.handle = IMUS.RegisterSong(music.data);

		// play it
		IMUS.PlaySong(music.handle, looping);
		SetMusicVolume(this.snd_MusicVolume);

		mus_playing = music;
	}

	public void StopMusic()
	{
		if (mus_playing!=null)
		{
			if (mus_paused)
				IMUS.ResumeSong(mus_playing.handle);

			IMUS.StopSong(mus_playing.handle);
			IMUS.UnRegisterSong(mus_playing.handle);
			//Z_ChangeTag(mus_playing->data, PU_CACHE);

			mus_playing.data = null;
			mus_playing = null;
		}
	}


	/** This is S_StopChannel. There's another StopChannel
	 *  with a similar contract in ISound. Don't confuse the two.
	 *  
	 * 
	 *  
	 * @param cnum
	 */

	protected void StopChannel(int cnum)
	{

		int		i;
		channel_t	c = channels[cnum];

		// Is it playing?
		if (c.sfxinfo!=null)
		{
			// stop the sound playing
			if (ISND.SoundIsPlaying(c.handle))
			{
				/*#ifdef SAWDEBUG
		    if (c.sfxinfo == &S_sfx[sfx_sawful])
			fprintf(stderr, "stopped\n");
	#endif*/
				ISND.StopSound(c.handle);
			}

			// check to see
			//  if other channels are playing the sound
			for (i=0 ; i<numChannels ; i++)
			{
				if (cnum != i
						&& c.sfxinfo == channels[i].sfxinfo)
				{
					break;
				}
			}

			// degrade usefulness of sound data
			c.sfxinfo.usefulness--;

			c.sfxinfo = null;
		}
	}

	//
	// Changes volume, stereo-separation, and pitch variables
	//  from the norm of a sound effect to be played.
	// If the sound is not audible, returns a 0.
	// Otherwise, modifies parameters and returns 1.
	//
	protected boolean 
	AdjustSoundParams
	( mobj_t	listener,
			ISoundOrigin	source,
			vps_t vps)
	{
		int	approx_dist;
		int	adx;
		int	ady;
		long	angle;

		// calculate the distance to sound origin
		//  and clip it if necessary
		adx = Math.abs(listener.x - source.getX());
		ady = Math.abs(listener.y - source.getY());

		// From _GG1_ p.428. Appox. eucledian distance fast.
		approx_dist = adx + ady - ((adx < ady ? adx : ady)>>1);

		if (DS.gamemap != 8
				&& approx_dist > S_CLIPPING_DIST)
		{
			return false;
		}

		// angle of source to listener
		angle = rr.RendererState.PointToAngle(listener.x,
				listener.y,
				source.getX(),
				source.getY());

		if (angle > listener.angle)
			angle = angle - listener.angle;
		else
			angle = angle + (0xffffffffL - listener.angle&BITS32);

		angle&=BITS32;
		angle >>= ANGLETOFINESHIFT;

		// stereo separation
		vps.sep = 128 - (FixedMul(S_STEREO_SWING,finesine[(int) angle])>>FRACBITS);

		// volume calculation
		if (approx_dist < S_CLOSE_DIST)
		{
			vps.volume = snd_SfxVolume;
		}
		else if (DS.gamemap == 8)
		{
			if (approx_dist > S_CLIPPING_DIST)
				approx_dist = S_CLIPPING_DIST;

			vps.volume = 15+ ((snd_SfxVolume-15)
					*((S_CLIPPING_DIST - approx_dist)>>FRACBITS))
					/ S_ATTENUATOR;
		}
		else
		{
			// distance effect
			vps.volume = (snd_SfxVolume
					* ((S_CLIPPING_DIST - approx_dist)>>FRACBITS))
					/ S_ATTENUATOR;
			// Let's do some maths here: S_CLIPPING_DIST-approx_dist
			// can be at most 0x04100000. shifting left means 0x0410,
			// or 1040 in decimal. 
			// The unmultiplied max volume is 15, attenuator is 1040.
			// So snd_SfxVolume should be 0-127.
			
		}
		
		// MAES: pitch calculation for doppler effects. Nothing to write
		// home about.
		
		/*
		
		// calculate the relative speed between source and sound origin.
		//  and clip it if necessary
		adx = Math.abs(listener.momx - source.momx);
		ady = Math.abs(listener.momy - source.momy);
			
		// From _GG1_ p.428. Appox. eucledian distance fast.
		// Here used for "approximate speed"
		approx_dist = adx + ady - ((adx < ady ? adx : ady)>>1);
		
		// The idea is that for low speeds, no doppler effect occurs.
		// For higher ones however, a shift occurs. We don't want this
		// to be annoying, so we'll only apply it for large speed differences
		// Then again, Doomguy can sprint like Carl Lewis...
			
		if (approx_dist>0x100000){
		
		// Quickly decide sign of pitch based on speed vectors
			
			// angle of source (speed) to listener (speed)
			angle = rr.RendererState.PointToAngle(listener.momx,
					listener.momy,
					source.momx,
					source.momy);
			
			if ((0<=angle && angle<=Tables.ANG90)||
				(180<=angle && angle<=Tables.ANG270))
		vps.pitch+=(approx_dist>>16);
			else
		vps.pitch-=(approx_dist>>16);
		}

		if (vps.pitch<0) vps.pitch=0;
		if (vps.pitch>255) vps.pitch=255;
		*/
		
		return (vps.volume > 0);
	}




	//
	// S_getChannel :
	//   If none available, return -1.  Otherwise channel #.
	//
	protected int 	getChannel( ISoundOrigin origin,sfxinfo_t	sfxinfo )
	{
		// channel number to use
		int		cnum;

		channel_t	c;

		// Find an open channel
		// If it's null, OK, use that.
		// If it's an origin-specific sound and has the same origin, override.
		for (cnum=0 ; cnum<numChannels ; cnum++)
		{
			if (channels[cnum].sfxinfo==null)
				break;
			else if (origin!=null &&  channels[cnum].origin ==  origin)
			{
				StopChannel(cnum);
				break;
			}
		}

		// None available
		if (cnum == numChannels)
		{
			// Look for lower priority
			for (cnum=0 ; cnum<numChannels ; cnum++)
				if (channels[cnum].sfxinfo.priority >= sfxinfo.priority) break;

			if (cnum == numChannels)
			{
				// FUCK!  No lower priority.  Sorry, Charlie.
				return -1;
			}
			else
			{
				// Otherwise, kick out lower priority.
				StopChannel(cnum);
			}
		}

		c = channels[cnum];

		// channel is decided to be cnum.
		c.sfxinfo = sfxinfo;
		c.origin = origin;

		return cnum;
	}	

	/** Nice one. A sound should have a maximum duration in tics,
	 * and we can give it a handle proportional to the future tics
	 * it should play until. Ofc, this means the minimum timeframe
	 * for cutting a sound off is just 1 tic.
	 * 
	 * @param handle
	 * @return
	 */

	/*
	public boolean SoundIsPlaying(int handle)
	{
	    // Ouch.
	    return (DS.gametic < handle);
	} */



}

package s;

import java.io.ByteArrayInputStream;

import javax.sound.midi.InvalidMidiDataException;
import javax.sound.midi.MidiDevice;
import javax.sound.midi.MidiSystem;
import javax.sound.midi.MidiUnavailableException;
import javax.sound.midi.Receiver;
import javax.sound.midi.Sequence;
import javax.sound.midi.Sequencer;
import javax.sound.midi.ShortMessage;
import javax.sound.midi.SysexMessage;
import javax.sound.midi.Transmitter;

/** Concern separated from David Martel's MIDI & MUS player
 *  for Mocha Doom. Greatly improved upon by finnw, perfecting volume changes
 *  and MIDI device detection.
 *  
 * @author David Martel
 * @author velktron
 * @author finnw
 *
 */

public class DavidMusicModule implements IMusic {
	
	public static final int CHANGE_VOLUME=7;
	public static final int CHANGE_VOLUME_FINE=9;
	
	Sequencer sequencer;
	VolumeScalingReceiver receiver;
	Transmitter transmitter;
	boolean songloaded;
	
	public DavidMusicModule(){

	}

	@Override
	public void InitMusic() {
		try {
			
			 int x=-1;
			MidiDevice.Info[] info = MidiSystem.getMidiDeviceInfo();   
		     for (int i = 0; i < info.length; i++)  {
		    	 MidiDevice mdev=MidiSystem.getMidiDevice(info[i]);
		    	 if (mdev instanceof Sequencer) x=i;
		        //  System.out.println(info[i].getName()+"\t\t\t"+ mdev.isOpen()+"\t"+mdev.hashCode());
		          
		     }
		
		     //System.out.printf("x %d y %d \n",x,y);
		     //--This sets the Sequencer and Synthesizer  
		     //--The indices x and y correspond to the correct entries for the  
		     //--default Sequencer and Synthesizer, as determined above  	       
		      
		    if (x!=-1)
		    	sequencer = (Sequencer) MidiSystem.getMidiDevice(info[x]);
		    else
		    	sequencer = (Sequencer) MidiSystem.getSequencer(false);
			sequencer.open();
			
		    receiver = VolumeScalingReceiver.getInstance();
		    // Configure General MIDI level 1
		    sendSysexMessage(receiver, (byte)0xf0, (byte)0x7e, (byte)0x7f, (byte)9, (byte)1, (byte)0xf7);
		    transmitter = sequencer.getTransmitter();
		    transmitter.setReceiver(receiver);
		} catch (MidiUnavailableException e) {
			e.printStackTrace();
		}
	}

    private static void sendControlChange(Receiver receiver, int midiChan, int ctrlId, int value) {
        ShortMessage msg = new ShortMessage();
        try {
            msg.setMessage(ShortMessage.CONTROL_CHANGE, midiChan, ctrlId, value);
        } catch (InvalidMidiDataException ex) {
            throw new RuntimeException(ex);
        }
        receiver.send(msg, -1);
    }

	private static void sendSysexMessage(Receiver receiver, byte... message) {
	    SysexMessage msg = new SysexMessage();
	    try {
            msg.setMessage(message, message.length);
        } catch (InvalidMidiDataException ex) {
            throw new RuntimeException(ex);
        }
        receiver.send(msg, -1);
    }

    @Override
	public void ShutdownMusic() {
		sequencer.stop();
		sequencer.close();
	}

	@Override
	public void SetMusicVolume(int volume) {
		
		System.out.println("Midi volume set to "+volume);
		receiver.setGlobalVolume(volume / 127f);

	}

	@Override
	public void PauseSong(int handle) {
		if (songloaded)
		sequencer.stop();
		}

	@Override
	public void ResumeSong(int handle) {		
		if (songloaded){
			System.out.println("Resuming song");
		sequencer.start();
		}

	}

	@Override
	public int RegisterSong(byte[] data) {
		try {
            Sequence sequence;
	        ByteArrayInputStream bis;
	        try {
	            // If data is a midi file, load it directly
	            bis = new ByteArrayInputStream(data);
	            sequence = MidiSystem.getSequence(bis);
	        } catch (InvalidMidiDataException ex) {
	        	// Well, it wasn't. Dude.
                bis = new ByteArrayInputStream(data);
	            sequence = MusReader.getSequence(bis);
	        }
            sequencer.stop(); // stops current music if any
            sequencer.setSequence(sequence); // Create a sequencer for the sequence
            songloaded=true;
	    } catch (Exception e) {
	    	e.printStackTrace();
	    	return -1;
	    } 
		// In good old C style, we return 0 upon success?
		return 0;
	}

	@Override
	public void PlaySong(int handle, boolean looping) {
		if (songloaded){
	        for (int midiChan = 0; midiChan < 16; ++ midiChan) {
	            setPitchBendSensitivity(receiver, midiChan, 2);
	        }
            if (looping)
            	sequencer.setLoopCount(Sequencer.LOOP_CONTINUOUSLY);
            else
            	sequencer.setLoopCount(0);
            sequencer.start(); // Start playing
		}
	}

	private void setPitchBendSensitivity(Receiver receiver, int midiChan, int semitones) {
	    sendRegParamChange(receiver, midiChan, 0, 0, 2);
    }

    private void sendRegParamChange(Receiver receiver, int midiChan, int paramMsb, int paramLsb, int valMsb) {
        sendControlChange(receiver, midiChan, 101, paramMsb);
        sendControlChange(receiver, midiChan, 100, paramLsb);
        sendControlChange(receiver, midiChan, 6, valMsb);
    }

    @Override
	public void StopSong(int handle) {
		sequencer.stop();

	}

	@Override
	public void UnRegisterSong(int handle) {
		// In theory, we should ask the sequencer to "forget" about the song.
		// However since we can register another without unregistering the first,
		// this is practically a dummy.
		
		songloaded=false;

	}

}

package s;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Writer;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class DoomIO {
	InputStream is;
	OutputStream os;
	
	public DoomIO(InputStream is, OutputStream os) {
		this.is = is;
		this.os = os;
	}
	
	public static int toUnsigned(byte signed) {
		int unsigned = (signed & 0xff);
		unsigned = (signed >= 0 ? signed : 256 + signed);
		unsigned = (256 + signed) % 256;
		return unsigned;
	}

	public static int fread(byte[] bytes, int size, int count, InputStream file) throws IOException {
		int retour = 0;
		do {
			if (file.read(bytes, retour*size, size) < size)
				return retour;
			retour++;
		}
		while (--count > 0);
		return retour;
	}

	public static int freadint(InputStream file) throws IOException {
		/*byte[] bytes = new byte[2];
		if (fread(bytes, 2, 1, file) < 1)
			return -1;
		int retour = toUnsigned(bytes[1])*256 + toUnsigned(bytes[0]);
		return retour;*/
		return freadint(file, 2);
	}

	public static int freadint(InputStream file, int nbBytes) throws IOException {
		byte[] bytes = new byte[nbBytes];
		if (fread(bytes, nbBytes, 1, file) < 1)
			return -1;
		long retour = 0;
		for (int i = 0; i < nbBytes; i++) {
			retour += toUnsigned(bytes[i])*(long)Math.pow(256, i);
		}
		//toUnsigned(bytes[1])*256 + toUnsigned(bytes[0]);
		
		if (retour > (long)Math.pow(256, nbBytes)/2)
			retour -= (long)Math.pow(256, nbBytes);
		
		return (int)retour;
	}

	public static int fwrite2(byte[] ptr, int offset, int size, Object file) throws IOException
	{
		fwrite(ptr, offset, size, 1, file);
		return 0;
	}

	public static int fwrite2(byte[] ptr, int size, Object file) throws IOException
	{
		return fwrite2(ptr, 0, size, file);
	}

	public static int fwrite2(byte[] ptr, Object file) throws IOException
	{
		return fwrite2(ptr, 0, ptr.length, file);
	}

	public static void fwrite(String bytes, int size, int count, Object file) throws IOException {
		fwrite(toByteArray(bytes), size, count, file);
	}

	public static void fwrite(byte[] bytes, int size, int count, Object file) throws IOException {
		fwrite(bytes, 0, size, count, file);
	}

	public static void fwrite(byte[] bytes, int offset, int size, int count, Object file) throws IOException {
		if (file instanceof OutputStream) {
			/*byte[] b = bytes;
			if (bytes.length < size) {
				b = new byte[size];
				copyBytes(from, to, offset)
			}*/
			
			((OutputStream)file).write(bytes, offset, Math.min(bytes.length, size));
			for (int i = bytes.length; i < size; i++)
				((OutputStream)file).write((byte)0);  // padding effect if size is bigger than byte array
		}
		if (file instanceof Writer) {
			char[] ch = new char[bytes.length];
			for (int i = 0; i < bytes.length; i++) {
				ch[i] = (char)toUnsigned(bytes[i]);
			}
			
			((Writer)file).write(ch, offset, size);
		}
	}
	
	public static byte[] toByteArray(String str) {
			byte[] retour = new byte[str.length()];
			for (int i = 0; i < str.length(); i++) {
				retour[i] = (byte)(str.charAt(i) & 0xFF);
			}
			return retour;
		}

		public static byte[] toByteArray(int str) {
			return toByteArray(str, 2);
		}

		 public static enum Endian { BIG, LITTLE }
		 public static Endian writeEndian = Endian.LITTLE;

		 static int byteIdx(int i, int nbBytes) {
			return (writeEndian == Endian.BIG ? i : nbBytes-1-i);
		}
		
		 public static void copyBytes(byte[] from, byte[] to, int offset) {
			 for (byte b: from) {
				 to[offset++] = b;
			 }
		 }
		 
		 public static byte[] toByteArray(Long str, int nbBytes) {
			 return toByteArray(str.intValue(), nbBytes);
		 }

		 public static byte[] toByteArray(Short str, int nbBytes) {
			 return toByteArray(str.intValue(), nbBytes);
		 }

		 public static byte[] toByteArray(int[] str, int nbBytes) {
			 byte[] bytes = new byte[str.length*nbBytes];
			 for (int i = 0; i < str.length; i++) {
				 copyBytes(toByteArray(str[i], nbBytes), bytes, i*nbBytes);
			 }
			 return bytes;
		 }

		 /*
		 public static byte[] toByteArray(boolean[] bools, int nbBytes) {
			 byte[] bytes = new byte[bools.length*nbBytes];
			 for (int i = 0; i < bools.length; i++) {
				 copyBytes(toByteArray(bools[i], nbBytes), bytes, i*nbBytes);
			 }
			 return bytes;
		 } */

		 /*
		 public static byte[] toByteArray(Boolean bool, int nbBytes) {
			 int val = (bool?1:0);
			 return toByteArray(val, nbBytes);
		 }*/

		 public static byte[] toByteArray(Integer str, int nbBytes) {
			 Long val = str.longValue();
			 if (val < 0)
				 val = (long)Math.pow(256, nbBytes) + val;
			 
			 byte[] bytes = new byte[nbBytes];
			long tmp = val;
			for (int i = 0; i <nbBytes-1; i++) {
				bytes[byteIdx(i, nbBytes)] = (byte)(tmp%256);
				tmp = tmp / 256;
			}
			
			bytes[byteIdx(nbBytes-1, nbBytes)] = (byte)(tmp);
			return bytes;
		}

		 private static Field getField(Class clazz, String fieldName) throws NoSuchFieldException {     
			 try {       
				 return clazz.getDeclaredField(fieldName);     
			 } catch (NoSuchFieldException e) {
				 Class superClass = clazz.getSuperclass();
				 if (superClass == null) {
					 throw e;    
				 } else {  
					 return getField(superClass, fieldName);     
				 }    
			 }  
		 } 

		 public static void linkBA(Object obj, Object fieldName, Object stream, int size) {
		    	if (stream instanceof OutputStream) {
		    		try {
		    			Object val = null;
		    			if (fieldName instanceof String) {
		    				val = getField(obj.getClass(), (String)fieldName).get(obj);
			    			if (val instanceof Enum) {
			    				val = ((Enum)val).ordinal();
			    			}
		    			}
		    			if (fieldName instanceof Integer) {
		    				val = fieldName;
		    			}

		    			Method method = DoomIO.class.getMethod("toByteArray", val.getClass(), int.class);
		    			byte[] bytes = (byte[])method.invoke(null, val, size);
		    			((OutputStream)stream).write(bytes);

		    		} catch (Exception e) {
		    			// TODO Auto-generated catch block
		    			e.printStackTrace();
		    		}
		    	}
		    	
		    	if (stream instanceof InputStream) {
		    		try {
		    			if (fieldName instanceof String) {
		    				Field field = obj.getClass().getField((String)fieldName);
		    				assigner(obj, field, (InputStream)stream, size);
		    			}
		    			if (fieldName instanceof Integer) {
		    				((InputStream)stream).read(new byte[size]);
		    			}

					} catch (Exception e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
		    	}

		    	
//		    	public static int freadint(InputStream file, int nbBytes) throws IOException {

		    }
		 public static void assigner(Object obj, Field field, InputStream is, int size) throws IOException, IllegalArgumentException, IllegalAccessException {

				Class c = field.getType();
				if (c.isArray()) {
					Object a = field.get(obj);
					int len = Array.getLength(a);
					for (int i = 0; i < len; i++) {
						int val = DoomIO.freadint((InputStream)is, size);
						Object o = Array.get(a, i);
						Array.set(a, i, assignValue(val, o, o.getClass()));
					}
					return;
				}
				
				int val = DoomIO.freadint((InputStream)is, size);
				Object v = assignValue(val, field.get(obj), field.getType());
				field.set(obj, v);

				/*Object[] enums = c.getEnumConstants();
				if (enums != null) {
					int val = DoomIO.freadint((InputStream)is, size);
					field.set(obj, enums[val]);
				}
				else {
					int val = DoomIO.freadint((InputStream)is, size);
					field.set(obj, val);
				}*/
		 }
		 
		 public static Object assignValue(int val, Object objToReplace, Class classe) {
			 if (classe.isAssignableFrom(Boolean.class) || classe.isAssignableFrom(boolean.class)) {
				 return (val == 0 ? false : true);
			 }
			 
				Object[] enums = classe.getEnumConstants();
				if (enums != null) {
					//int val = DoomIO.freadint((InputStream)is, size);
					return enums[val];
					//field.set(obj, enums[val]);
				}
				else {
					//int val = DoomIO.freadint((InputStream)is, size);
					//field.set(obj, val);
				}
			 
			 return val;
		 }
		 
		 public static String baToString(byte[] bytes) {
			 String str = "";
			 for (int i = 0; i < bytes.length && bytes[i] != 0; i++)
				 str += (char)bytes[i];
			 return str;
		 }
		 
		   public static int indexOfArray(Object[] a, Object o) {
			   for (int i = 0; i < a.length/* Array.getLength(a)*/; i++) {
				   if (/*Array.get(a, i)*/a[i] == o)
					   return i;
			   }
			   return -1;
		   }

}

package s;

public final class degenmobj_t
        implements ISoundOrigin {

    private final int x, y, z;

    public degenmobj_t(int x, int y, int z) {
        this.x = x;
        this.y = y;
        this.z = z;
    }

    public degenmobj_t(int x, int y) {
        this.x = x;
        this.y = y;
        this.z = 0;
    }

    @Override
    public final int getX() {
        return x;
    }

    @Override
    public final int getY() {
        return y;
    }

    @Override
    public final int getZ() {
        return z;
    }

}

package s;

/** Use this instead of that degemobj_t crud */

public interface ISoundOrigin {
	public int getX();
	public int getY();
	public int getZ();
	}

package s;

public class AudioChunk{
    public AudioChunk() {
        buffer=new byte[s.ISoundDriver.MIXBUFFERSIZE];
        setStuff(0,0);
        this.free=true;
    }
    
    public void setStuff(int chunk, int time){
        this.chunk = chunk;
        this.time = time;
    }
    
    public int chunk;
    public int time;
    public byte[] buffer;
    public boolean free;
    

}
package s;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Writer;

public class QMusToMid {

	public static final int NOTMUSFILE  =    1;       /* Not a MUS file */
	public static final int COMUSFILE   =    2;       /* Can't open MUS file */
	public static final int COTMPFILE   =    3;       /* Can't open TMP file */
	public static final int CWMIDFILE   =    4;       /* Can't write MID file */
	public static final int MUSFILECOR  =    5;       /* MUS file corrupted */
	public static final int TOOMCHAN    =    6;       /* Too many channels */
	public static final int MEMALLOC    =    7;       /* Memory allocation error */

	/* some (old) compilers mistake the "MUS\x1A" construct (interpreting
	   it as "MUSx1A")      */

	public static final String MUSMAGIC =    "MUS\032";                    /* this seems to work */
	public static final String MIDIMAGIC =   "MThd\000\000\000\006\000\001";
	public static final String TRACKMAGIC1 =  "\000\377\003\035";
	public static final String TRACKMAGIC2 = "\000\377\057\000";
	public static final String TRACKMAGIC3 = "\000\377\002\026";
	public static final String TRACKMAGIC4 = "\000\377\131\002\000\000";
	public static final String TRACKMAGIC5 = "\000\377\121\003\011\243\032";
	public static final String TRACKMAGIC6 = "\000\377\057\000";

	public static final int EOF = -1;
	public static class Ptr<a> {
		a val;
		
		public Ptr(a val) {
			this.val = val;
		}
		
		public a get() {
			return val;
		}
		
		public void set(a newval) {
			val = newval;
		}
	}

	public static class MUSheader
	{
		byte[]        ID = new byte[4];            /* identifier "MUS" 0x1A */
		int        ScoreLength;
		int        ScoreStart;
		int        channels;         /* count of primary channels */
		int        SecChannels;      /* count of secondary channels (?) */
		int        InstrCnt;
		int        dummy;
		/* variable-length part starts here */
		int[]        instruments;
	}

	public static class Track
	{
		long  current;
		byte           vel;
		long           DeltaTime;
		byte  LastEvent;
		byte[]           data;            /* Primary data */
	}


	long TRACKBUFFERSIZE = 65536L ;  /* 64 Ko */


	void TWriteByte( int MIDItrack, byte byte_, Track track[] )
	{
	  long pos ;

	  pos = track[MIDItrack].current ;
	  if( pos < TRACKBUFFERSIZE )
	    track[MIDItrack].data[(int)pos] = byte_ ;
	  else
	    {
	      System.out.println("ERROR : Track buffer full.\n"+
	             "Increase the track buffer size (option -size).\n" ) ;
	      System.exit(1);
	    }
	  track[MIDItrack].current++ ;
	}


	void TWriteVarLen( int tracknum, long value, 
	                  Track[] track )
	{
	  long buffer ;

	  buffer = value & 0x7f ;
	  while( (value >>= 7) != 0 )
	    {
	      buffer <<= 8 ;
	      buffer |= 0x80 ;
	      buffer += (value & 0x7f) ;
	    }
	  while( true )
	    {
	      TWriteByte( tracknum, (byte)buffer, track ) ;
	      if( (buffer & 0x80) != 0 )
	        buffer >>= 8 ;
	      else
	        break;
	    }
	}

	int ReadMUSheader( MUSheader MUSh, InputStream file )
	{
		try {
	  if( DoomIO.fread( MUSh.ID, 4, 1, file ) != 1 ) return COMUSFILE ;
	  
	  /*if( strncmp( MUSh->ID, MUSMAGIC, 4 ) ) 
	    return NOTMUSFILE ;*/
	  if( (MUSh.ScoreLength = DoomIO.freadint(file)) == -1 ) return COMUSFILE ;
	  if( (MUSh.ScoreStart = DoomIO.freadint(file)) == -1 ) return COMUSFILE ;
	  if( (MUSh.channels = DoomIO.freadint(file)) == -1 ) return COMUSFILE ;
	  if( (MUSh.SecChannels = DoomIO.freadint(file)) == -1 ) return COMUSFILE ;
	  if( (MUSh.InstrCnt = DoomIO.freadint(file)) == -1 ) return COMUSFILE ;
	  if( (MUSh.dummy = DoomIO.freadint(file)) == -1 ) return COMUSFILE ;

	  MUSh.instruments = new int[MUSh.InstrCnt];
	  for (int i = 0; i < MUSh.InstrCnt; i++) {
		  if ((MUSh.instruments[i] = DoomIO.freadint(file)) == -1) {
		      return COMUSFILE ;
		  }
	  }

	  return 0 ;
		}
		catch (Exception e) {
			e.printStackTrace();
			return COMUSFILE;
		}
	}

	int WriteMIDheader( int ntrks, int division, Object file )
	{
		try {
			//_D_: those two lines for testing purposes only
			//fisTest.close();
			//fisTest = new FileInputStream("C:\\Users\\David\\Desktop\\qmus2mid\\test.mid");

			  DoomIO.fwrite( MIDIMAGIC , 10, 1, file ) ;
			  DoomIO.fwrite2( DoomIO.toByteArray(ntrks), 2, file) ;
			  DoomIO.fwrite2( DoomIO.toByteArray(division), 2, file ) ;
		} catch (IOException e) {
			e.printStackTrace();
		}

		return 0 ;
	}

	byte last(int e) {
		return (byte)(e & 0x80);
	}
	byte event_type(int e) {
		return (byte)((e & 0x7F) >> 4);
	}
	byte channel(int e) {
		return (byte)(e & 0x0F);
	}
	
	void TWriteString( char tracknum, String string, int length,
	                   Track[] track )
	{
	  int i ;

	  for( i = 0 ; i < length ; i++ )
	    TWriteByte( tracknum, (byte)string.charAt(i), track ) ;
	}


	void WriteTrack( int tracknum, Object file, Track[] track )
	{
	  long size ;
	  int quot, rem ;

	  try {
	  /* Do we risk overflow here ? */
	  size = track[tracknum].current+4 ;
	  DoomIO.fwrite( "MTrk", 4, 1, file );
	  if( tracknum == 0) size += 33 ;

	  DoomIO.fwrite2( DoomIO.toByteArray((int)size, 4), 4, file ) ;
	  if( tracknum == 0)
		  DoomIO.fwrite( TRACKMAGIC1 + "Quick MUS->MID ! by S.Bacquet", 33, 1, file ) ;

	  quot = (int) (track[tracknum].current / 4096) ;
	  rem = (int) (track[tracknum].current - quot*4096) ;
	  
	  DoomIO.fwrite(track[tracknum].data, (int)track[tracknum].current, 1, file ) ;
	  DoomIO.fwrite( TRACKMAGIC2, 4, 1, file ) ;
	  }
	  catch (Exception e) {
		  e.printStackTrace();
	  }
	}

	void WriteFirstTrack( Object file )
	{
		try {
			  byte[] size = DoomIO.toByteArray(43, 4);
			  
			  DoomIO.fwrite( "MTrk", 4, 1, file ) ;
			  DoomIO.fwrite2( size, 4, file ) ;
			  DoomIO.fwrite( TRACKMAGIC3 , 4, 1, file ) ;
			  DoomIO.fwrite( "QMUS2MID (C) S.Bacquet", 22, 1, file ) ;
			  DoomIO.fwrite( TRACKMAGIC4, 6, 1, file ) ;
			  DoomIO.fwrite( TRACKMAGIC5, 7, 1, file ) ;
			  DoomIO.fwrite( TRACKMAGIC6, 4, 1, file ) ;

		}
		catch (Exception e) {
			e.printStackTrace();
		}
	}

	long ReadTime( InputStream file ) throws IOException
	{
	  long time = 0 ;
	  int byte_ ;

	  do
	    {
	      byte_ = getc( file ) ;
	      if( byte_ != EOF ) time = (time << 7) + (byte_ & 0x7F) ;
	    } while( (byte_ != EOF) && ((byte_ & 0x80) != 0) ) ;

	  return time ;
	}

	byte FirstChannelAvailable( byte[] MUS2MIDchannel )
	{
	  int i ;
	  byte old15 = MUS2MIDchannel[15], max = -1 ;

	  MUS2MIDchannel[15] = -1 ;
	  for( i = 0 ; i < 16 ; i++ )
	    if( MUS2MIDchannel[i] > max ) max = MUS2MIDchannel[i] ;
	  MUS2MIDchannel[15] = old15 ;

	  return (max == 8 ? 10 : (byte)(max+1)) ;
	}


	int getc(InputStream is) throws IOException {
		return is.read();
	}
	
	int qmus2mid( InputStream mus, Object mid, boolean nodisplay, 
	             int division, int BufferSize, boolean nocomp ) throws IOException
	{
	  Track[] track = new Track[16] ;
	  for (int i = 0; i < track.length; i++)
		  track[i] = new Track(); 
	  
	  int TrackCnt = 0 ;
	  byte et, MUSchannel, MIDIchannel, MIDItrack, NewEvent ;
	  int i, event, data, r ;
	  MUSheader MUSh = new MUSheader() ;
	  long DeltaTime, TotalTime = 0, time, min, n = 0 ;
	  byte[] MUS2MIDcontrol = new byte[] {
	    0,                          /* Program change - not a MIDI control change */
	    0x00,                       /* Bank select */
	    0x01,                       /* Modulation pot */
	    0x07,                       /* Volume */
	    0x0A,                       /* Pan pot */
	    0x0B,                       /* Expression pot */
	    0x5B,                       /* Reverb depth */
	    0x5D,                       /* Chorus depth */
	    0x40,                       /* Sustain pedal */
	    0x43,                       /* Soft pedal */
	    0x78,                       /* All sounds off */
	    0x7B,                       /* All notes off */
	    0x7E,                       /* Mono */
	    0x7F,                       /* Poly */
	    0x79                        /* Reset all controllers */
	  };
	  byte[] MIDIchan2track = new byte[16];
	  byte[] MUS2MIDchannel = new byte[16] ;
	  char ouch = 0, sec ;

	  DoomIO.writeEndian = DoomIO.Endian.LITTLE;
	  
	  r = ReadMUSheader( MUSh, mus ) ;
	  if( r != 0)
	    {
	      return r ;
	    }
	 /* if( fseek( file_mus, MUSh.ScoreStart, SEEK_SET ) )
	    {
	      Close() ;
	      return MUSFILECOR ;
	    }*/
	  if( !nodisplay )
	    System.out.println( mus+" ("+mus.available()+"  bytes) contains "+MUSh.channels+" melodic channel"+ (MUSh.channels >= 2 ? "s" : "")+"\n");

	  if( MUSh.channels > 15 )      /* <=> MUSchannels+drums > 16 */
	    {
	      return TOOMCHAN ;
	    }

	  for( i = 0 ; i < 16 ; i++ )
	    {
	      MUS2MIDchannel[i] = -1 ;
	      track[i].current = 0 ;
	      track[i].vel = 64 ;
	      track[i].DeltaTime = 0 ;
	      track[i].LastEvent = 0 ;
	      track[i].data = null ;
	    }
	  if( BufferSize != 0)
	    {
	      TRACKBUFFERSIZE = ((long) BufferSize) << 10 ;
	      if( !nodisplay )
	      System.out.println( "Track buffer size set to "+BufferSize+" KB.\n") ;
	    }
	  
	  if( !nodisplay )
	    {
	  System.out.println( "Converting..." ) ;
	    }
	  event = getc( mus ) ;
	  et = event_type( event ) ;
	  MUSchannel = channel( event ) ;
	  while( (et != 6) && mus.available() > 0 && (event != EOF) )
	    {
	      if( MUS2MIDchannel[MUSchannel] == -1 )
	        {
	          MIDIchannel = MUS2MIDchannel[MUSchannel ] = 
	            (MUSchannel == 15 ? 9 : FirstChannelAvailable( MUS2MIDchannel)) ;
	          MIDItrack   = MIDIchan2track[MIDIchannel] = (byte)TrackCnt++ ;
	          if( (track[MIDItrack].data = new byte[(int)TRACKBUFFERSIZE]) == null )
	            {
	              return MEMALLOC ;
	            }
	        }
	      else
	        {
	          MIDIchannel = MUS2MIDchannel[MUSchannel] ;
	          MIDItrack   = MIDIchan2track [MIDIchannel] ;
	        }
	      TWriteVarLen( MIDItrack, track[MIDItrack].DeltaTime, track ) ;
	      track[MIDItrack].DeltaTime = 0 ;
	      switch( et )
	        {
	        case 0 :                /* release note */
	          NewEvent = (byte)(0x90 | MIDIchannel) ;
	          if( (NewEvent != track[MIDItrack].LastEvent) || (nocomp) )
	            {
	              TWriteByte( MIDItrack, NewEvent, track ) ;
	              track[MIDItrack].LastEvent = NewEvent ;
	            }
	          else
	            n++ ;
	          data = getc( mus ) ;
	          TWriteByte( MIDItrack, (byte)data, track ) ;
	          TWriteByte( MIDItrack, (byte)0, track ) ;
	          break ;
	        case 1 :
	          NewEvent = (byte)(0x90 | MIDIchannel) ;
	          if( (NewEvent != track[MIDItrack].LastEvent) || (nocomp) )
	            {
	              TWriteByte( MIDItrack, NewEvent, track ) ;
	              track[MIDItrack].LastEvent = NewEvent ;
	            }
	          else
	            n++ ;
	          data = getc( mus ) ;
	          TWriteByte( MIDItrack, (byte)(data & 0x7F), track ) ;
	          if( (data & 0x80) != 0 )
	            track[MIDItrack].vel = (byte)getc( mus ) ;
	          TWriteByte( MIDItrack, (byte)track[MIDItrack].vel, track ) ;
	          break ;
	        case 2 :
	          NewEvent = (byte)(0xE0 | MIDIchannel) ;
	          if( (NewEvent != track[MIDItrack].LastEvent) || (nocomp) )
	            {
	              TWriteByte( MIDItrack, NewEvent, track ) ;
	              track[MIDItrack].LastEvent = NewEvent ;
	            }
	          else
	            n++ ;
	          data = getc( mus ) ;
	          TWriteByte( MIDItrack, (byte)((data & 1) << 6), track ) ;
	          TWriteByte( MIDItrack, (byte)(data >> 1), track ) ;
	          break ;
	        case 3 :
	          NewEvent = (byte)(0xB0 | MIDIchannel) ;
	          if( (NewEvent != track[MIDItrack].LastEvent) || (nocomp) )
	            {
	              TWriteByte( MIDItrack, NewEvent, track ) ;
	              track[MIDItrack].LastEvent = NewEvent ;
	            }
	          else
	            n++ ;
	          data = getc( mus ) ;
	          TWriteByte( MIDItrack, MUS2MIDcontrol[data], track ) ;
	          if( data == 12 )
	            TWriteByte( MIDItrack, (byte)(MUSh.channels+1), track ) ;
	          else
	            TWriteByte( MIDItrack, (byte)0, track ) ;
	          break ;
	        case 4 :
	          data = getc( mus ) ;
	          if( data != 0 )
	            {
	              NewEvent = (byte)(0xB0 | MIDIchannel) ;
	              if( (NewEvent != track[MIDItrack].LastEvent) || (nocomp) )
	                {
	                  TWriteByte( MIDItrack, NewEvent, track ) ;
	                  track[MIDItrack].LastEvent = NewEvent ;
	                }
	              else
	                n++ ;
	              TWriteByte( MIDItrack, MUS2MIDcontrol[data], track ) ;
	            }
	          else
	            {
	              NewEvent = (byte)(0xC0 | MIDIchannel) ;
	              if( (NewEvent != track[MIDItrack].LastEvent) || (nocomp) )
	                {
	                  TWriteByte( MIDItrack, NewEvent, track ) ;
	                  track[MIDItrack].LastEvent = NewEvent ;
	                }
	              else
	                n++ ;
	            }
	          data = getc( mus ) ;
	          TWriteByte( MIDItrack, (byte)data, track ) ;
	          break ;
	        case 5 :
	        case 7 :
	          return MUSFILECOR ;
	        default : break ;
	        }
	      if( last( event ) != 0 )
		{
	          DeltaTime = ReadTime( mus ) ;
	          TotalTime += DeltaTime ;
		  for( i = 0 ; i < (int) TrackCnt ; i++ )
		    track[i].DeltaTime += DeltaTime ;
	        }
	      event = getc( mus ) ;
	      if( event != EOF )
	                  {
	          et = event_type( event ) ;
	          MUSchannel = channel( event ) ;
	        }
	      else
	        ouch = 1 ;
	    }
	  if( !nodisplay ) System.out.println( "done !\n" ) ;
	  if( ouch != 0 )
	    System.out.println( "WARNING : There are bytes missing at the end of "+mus+".\n          "+
	           "The end of the MIDI file might not fit the original one.\n") ;
	  if( division == 0 )
	    division = 89 ;
	  else
	    if( !nodisplay ) System.out.println( "Ticks per quarter note set to "+division+".\n") ;
	  if( !nodisplay )
	    {
	      if( division != 89 )
	        {
	          time = TotalTime / 140 ;
	          min = time / 60 ;
	          sec = (char) (time - min*60) ;
	          //System.out.println( "Playing time of the MUS file : %u'%.2u''.\n", min, sec ) ;
	        }
	      time = (TotalTime * 89) / (140 * division) ;
	      min = time / 60 ;
	      sec = (char) (time - min*60) ;
	      if( division != 89 )
	    	  System.out.println( "                    MID file" ) ;
	      else
	    	  System.out.println( "Playing time: "+min+"min "+sec+"sec") ;
	    }
	  if( !nodisplay )
	    {
	      System.out.println("Writing..." ) ;
	    }
	  WriteMIDheader( TrackCnt+1, division, mid ) ;
	  WriteFirstTrack( mid ) ;
	  for( i = 0 ; i < (int) TrackCnt ; i++ )
	    WriteTrack( i, mid, track ) ;
	  if( !nodisplay )
	    System.out.println( "done !\n" ) ;
	  if( !nodisplay && (!nocomp) )
	    System.out.println( "Compression : %u%%.\n"/*,
	           (100 * n) / (n+ (long) ftell( mid ))*/ ) ;
	  
	  return 0 ;
	}


	int convert( String mus, String mid, boolean nodisplay, int div,
            int size, boolean nocomp, Ptr<Integer> ow ) throws IOException
{
		InputStream is = new BufferedInputStream(new FileInputStream(new File(mid)));
		OutputStream os = new BufferedOutputStream(new FileOutputStream(new File(mid)));
		
	  int error;
	  //struct stat file_data ;
	  char[] buffer = new char[30] ;


	  /* we don't need _all_ that checking, do we ? */
	  /* Answer : it's more user-friendly */
	/*#ifdef MSDOG

	  if( access( mus, 0 ) )
	    {
	      System.out.println( "ERROR : %s does not exist.\n", mus ) ;
	      return 1 ;
	    }

	  if( !access( mid, 0 ) )
	    {
	      if( !*ow )
	        {
	          System.out.println( "Can't overwrite %s.\n", mid ) ;
	          return 2 ;
	        }
	      if( *ow == 1 )
	        {
	          System.out.println( "%s exists : overwrite (Y=Yes,N=No,A=yes for All,Q=Quit)"
	                 " ? [Y]\b\b", mid ) ;
	          fflush( stdout ) ;
	          do
	            n = toupper( getxkey() ) ;
	          while( (n != 'Y') && (n != 'N') && (n != K_Return) && (n != 'A')
	                && (n != 'Q')) ;
	          switch( n )
	            {
	            case 'N' :
	              System.out.println( "N\n%s NOT converted.\n", mus ) ;
	              return 3 ;
	            case 'A' :
	              System.out.println( "A" ) ;
	              *ow = 2 ;
	              break ;
	            case 'Q' :
	              System.out.println( "Q\nQMUS2MID aborted.\n" ) ;
	              exit( 0 ) ;
	              break ;
	            default : break ;
	            }
	          System.out.println( "\n" ) ;
	        }
	    }
	#else*/
	  /*if ( ow.get() == 0 ) {
	    file = fopen(mid, "r");
	    if ( file ) {
	      fclose(file);
	      System.out.println( "qmus2mid: file %s exists, not removed.\n", mid ) ;
	      return 2 ;
	    }
	  }*/
	/*#endif*/

	  return convert(is, os, nodisplay, div, size, nocomp, ow);
}
	
	int convert( InputStream mus, Object mid, boolean nodisplay, int div,
            int size, boolean nocomp, Ptr<Integer> ow ) throws IOException
{
	  int error = qmus2mid( mus, mid, nodisplay, div, size, nocomp ) ;

	  if( error != 0 )
	    {
		  System.out.println( "ERROR : " ) ;
	      switch( error )
	        {
	        case NOTMUSFILE :
	        	System.out.println( "%s is not a MUS file.\n"/*, mus*/ ) ; break ;
	        case COMUSFILE :
	        	System.out.println( "Can't open %s for read.\n"/*, mus*/ ) ; break ;
	        case COTMPFILE :
	        	System.out.println( "Can't open temp file.\n" ) ; break  ;
	        case CWMIDFILE :
	        	System.out.println( "Can't write %s (?).\n"/*, mid */) ; break ;
	        case MUSFILECOR :
	        	System.out.println( "%s is corrupted.\n"/*, mus*/) ; break ;
	        case TOOMCHAN :
	        	System.out.println( "%s contains more than 16 channels.\n"/*, mus*/ ) ; break ;
	        case MEMALLOC :
	        	System.out.println( "Not enough memory.\n" ) ; break ;
	        default : break ;
	        }
	      return 4 ;
	    }

	  if( !nodisplay )
	    {
	      System.out.println( mus+" converted successfully.\n") ;
	      /*if( (file = fopen( mid, "rb" )) != NULL )
	        {
	          //stat( mid, &file_data ) ;
	          fclose( file ) ;
	          sSystem.out.println( buffer, " : %lu bytes", (long) file_data.st_size ) ;
	        }*/
	      
	      /*System.out.println( "%s (%scompressed) written%s.\n", mid, nocomp ? "NOT " : "",
	             file ? buffer : ""  ) ;*/
	    }

	  return 0 ;
	}


//	int CheckParm( char[] check, int argc, char *argv[] )
//	{
//	  int i;
//
//	  for ( i = 1 ; i<argc ; i++ )
//	/*#ifdef MSDOG
//	    if( !stricmp( check, argv[i] ) )
//	#else*/
//	    if( !strcmp( check, argv[i] ) )
//	/*#endif*/
//	      return i ;
//
//	  return 0;
//	}


	void PrintHeader( )
	{
//	  System.out.println( "===============================================================================\n"
//	         "              Quick MUS->MID v2.0 ! (C) 1995,96 Sebastien Bacquet\n"
//	         "                        E-mail : bacquet@iie.cnam.fr\n"
//	         "===============================================================================\n" ) ;
	}


	void PrintSyntax( )
	{
//	  PrintHeader() ;
//	  System.out.println( 
//	#ifdef MSDOG
//	         "\nSyntax : QMUS2MID musfile1[.mus] {musfile2[.mus] ... | "
//	         "midifile.mid} [options]\n"
//	         "   Wildcards are accepted.\n"
//	         "   Options are :\n"
//	         "     -query    : Query before processing\n"
//	         "     -ow       : OK, overwrite (without query)\n"
//	#else
//	         "\nSyntax : QMUS2MID musfile midifile [options]\n"
//	         "   Options are :\n"
//	#endif
//	         "     -noow     : Don't overwrite !\n"
//	         "     -nodisp   : Display nothing ! (except errors)\n"
//	         "     -nocomp   : Don't compress !\n"
//	         "     -size ### : Set the track buffer size to ### (in KB). "
//	         "Default = 64 KB\n"
//	         "     -t ###    : Ticks per quarter note. Default = 89\n" 
//	         ) ;
	}


	int main( int argc, char[] argv )
	{
	  int div = 0, ow = 1, nocomp = 0, size = 0, n ;
	  boolean nodisplay = false;
	/*#ifdef MSDOG
	  int FileCount, query = 0, i, line = 0 ;
	  char mus[MAXPATH], mid[MAXPATH], drive[MAXDRIVE], middrive[MAXDRIVE],
	  dir[MAXDIR], middir[MAXDIR], musname[MAXFILE], midname[MAXFILE],
	  ext[MAXEXT] ;
	  struct stat s ;
	#else*/
	  String  mus, mid;
	/*#endif*/


	/*#ifndef MSDOG
	  if ( !LittleEndian() ) {
	    System.out.println("\nSorry, this program presently only works on "
		   "little-endian machines... \n\n");
	    exit( EXIT_FAILURE ) ;
	  }
	#endif*/

	/*#ifdef MSDOG
	  if( (argc == 1) || (argv[1][0] == '-') )
	#else
	    if( argc < 3 )
	#endif
	      {
	        PrintSyntax() ;
	        exit( EXIT_FAILURE ) ;
	      }*/

	/*#ifdef MSDOG
	  if( (strrchr( argv[1], '*' ) != NULL) || (strrchr( argv[1], '?' ) != NULL) )
	    {
	      PrintHeader() ;
	      System.out.println( "Sorry, there is nothing matching %s...\n", argv[1] ) ;
	      exit( EXIT_FAILURE ) ;
	    }
	  strncpy( mus, argv[1], MAXPATH ) ;
	  strupr( mus ) ;
	  if( !(fnsplit( mus, drive, dir, musname, NULL ) & FILENAME) )
	    {
	      PrintSyntax() ;
	      exit( EXIT_FAILURE ) ;
	    }
	#else*/
	  //strncpy( mus, argv[1], FILENAME_MAX ) ;
	  //strncpy( mid, argv[2], FILENAME_MAX ) ;
	/*#endif*/

	/*#ifdef MSDOG
	  if( CheckParm( "-query", argc, argv ) )
	    query = 1 ;
	#endif*/

	/*  if( CheckParm( "-nodisp", argc, argv ) )
	    nodisplay = 1 ;
	  */
	  if( !nodisplay )
	    PrintHeader() ;
	  
	  /*if( (n = CheckParm( "-size", argc, argv )) != 0 )
	    size = atoi( argv[n+1] ) ;*/
	/*#ifdef MSDOG
	  if( CheckParm( "-ow", argc, argv ) )
	    ow += 1 ;
	#endif
	  if( CheckParm( "-noow", argc, argv ) )
	    ow -= 1 ;
	  if( (n = CheckParm( "-t", argc, argv )) != 0 )
	    div = atoi( argv[n+1] ) ;
	  if( CheckParm( "-nocomp", argc, argv ) )
	    nocomp = 1 ;*/

	/*#ifdef MSDOG
	  for( FileCount = 1 ; (FileCount < argc) && (argv[FileCount][0] != '-') ;
	      FileCount++ ) ;
	  FileCount-- ;
	  midname[0] = middrive[0] = middir[0] = 0 ;
	  if( FileCount == 2 )
	    {
	      if( fnsplit( argv[FileCount], middrive, middir, midname, ext )
	         & FILENAME )
	        {
	          if( stricmp( ext, ".MID" ) )
	            midname[0] = middrive[0] = middir[0] = 0 ;
	          else
	            {
	              strcpy( mid, argv[FileCount--] ) ;
	              strupr( mid ) ;
	            }
	        }
	      else
	        FileCount-- ;
	    }
	  if( FileCount > 2 )
	    {
	      if( fnsplit( argv[FileCount], middrive, middir, NULL, NULL ) & FILENAME )
	        midname[0] = middrive[0] = middir[0] = 0 ;
	      else
	        FileCount-- ;
	    }
	  for( i = 0 ; i < FileCount ; i++ )
	    {
	      strupr( argv[i+1] ) ;
	      n = fnsplit( argv[i+1], drive, dir, musname, ext ) ;
	      if( !(n & EXTENSION) || !stricmp( ext, ".MUS" ) )
	        {
	          stat( argv[i+1], &s ) ;
	          if( !S_ISDIR( s.st_mode ) )
	            {
	              fnmerge( mus, drive, dir, musname, ".MUS" ) ;
	              if( line && !nodisplay )
	                System.out.println( "\n" ) ;
	              if( query )
	                {
	                  System.out.println( "Convert %s ? (Y=Yes,N=No,A=yes for All,Q=Quit)"
	                         " [Y]\b\b", mus ) ;
	                  fflush( stdout ) ;
	                  do
	                    n = toupper( getxkey() ) ;
	                  while( (n != 'Y') && (n != 'N') && (n != K_Return) 
	                        && (n != 'A') && (n != 'Q')) ;
	                  switch( n )
	                    {
	                    case 'N' :
	                      System.out.println( "N\n%s NOT converted.\n", mus ) ;
	                      line = 1 ;
	                      continue ;
	                      break ;
	                    case 'Q' :
	                      System.out.println( "Q\nQMUS2MID aborted.\n" ) ;
	                      exit( 0 ) ;
	                      break ;
	                    case 'A' :
	                      query = 0 ;
	                      System.out.println( "A\n" ) ;
	                      break ;
	                    default :
	                      System.out.println( "\n" ) ;
	                      break ;
	                    }
	                }
	              if( !midname[0] )
	                {
	                  fnmerge( mid, middrive, middir, musname, ".MID" ) ;
	                  strupr( mid ) ;
	                }
	              convert( mus, mid, nodisplay, div, size, nocomp, &ow ) ;
	              line = 1 ;
	            }
	        }
	    }
	  if( !line && !nodisplay && !query )
	    System.out.println( "Sorry, there is no MUS file matching...\n" ) ;
	  
	#else*/
	      //convert( mus, mid, nodisplay, div, size, nocomp, ow ) ;
/*	#endif*/

	  return 0;
	}


}

package s;

import static data.sounds.S_sfx;

import java.util.HashMap;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Semaphore;

import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.SourceDataLine;

import pooling.AudioChunkPool;
import data.sounds.sfxenum_t;
import doom.DoomStatus;

/**
 * A close recreation of the classic linux doom sound mixer.
 * 
 * PROS:
 * a) Very faithful to pitch and stereo effects, and original
 *    volume ramping.
 * b) Uses only one audioline and one playback thread
 * 
 * CONS:
 * a) May sound a bit off if production/consumption rates don't match
 * b) Sounds awful when mixing too many sounds together, just like the original.
 * 
 * @author Maes
 */

public class ClassicDoomSoundDriver extends AbstractSoundDriver
         {

    protected final Semaphore produce;

    protected final Semaphore consume;

    protected int chunk = 0;

    // protected FileOutputStream fos;
    // protected DataOutputStream dao;

    // The one and only line
    protected SourceDataLine line = null;

    protected HashMap<Integer, byte[]> cachedSounds =
        new HashMap<Integer, byte[]>();

    public ClassicDoomSoundDriver(DoomStatus<?,?> DS, int numChannels) {
    	super(DS,numChannels);
        channelleftvol_lookup = new int[numChannels][];
        channelrightvol_lookup = new int[numChannels][];
        channelstep = new int[numChannels];
        channelstepremainder = new int[numChannels];
        channels = new byte[numChannels][];
        p_channels = new int[numChannels];
        channelsend = new int[numChannels];
        produce = new Semaphore(1);
        consume = new Semaphore(1);
        produce.drainPermits();
        mixbuffer= new byte[MIXBUFFERSIZE];
    }

    /** The channel step amount... */
    protected final int[] channelstep;

    /** ... and a 0.16 bit remainder of last step. */
    protected final int[] channelstepremainder;

    /**
     * The channel data pointers, start and end. These were referred to as
     * "channels" in two different source files: s_sound.c and i_sound.c. In
     * s_sound.c they are actually channel_t (they are only informational). In
     * i_sound.c they are actual data channels.
     */
    protected byte[][] channels;

    /**
     * MAES: we'll have to use this for actual pointing. channels[] holds just
     * the data.
     */
    protected int[] p_channels;

    /**
     * The second one is supposed to point at "the end", so I'll make it an int.
     */
    protected int[] channelsend;

    /** Hardware left and right channel volume lookup. */
    protected final int[][] channelleftvol_lookup, channelrightvol_lookup;

    protected volatile boolean mixed = false;

    /**
     * This function loops all active (internal) sound channels, retrieves a
     * given number of samples from the raw sound data, modifies it according to
     * the current (internal) channel parameters, mixes the per channel samples
     * into the global mixbuffer, clamping it to the allowed range, and sets up
     * everything for transferring the contents of the mixbuffer to the (two)
     * hardware channels (left and right, that is). This function currently
     * supports only 16bit.
     */

    public void UpdateSound() {

        mixed = false;

        // Mix current sound data.
        // Data, from raw sound, for right and left.
        int sample = 0;
        int dl;
        int dr;

        // Pointers in global mixbuffer, left, right, end.
        // Maes: those were explicitly signed short pointers...

        int leftout;
        int rightout;
        int leftend;
        // Step in mixbuffer, left and right, thus two.
        int step;

        // Mixing channel index.
        int chan;

        // POINTERS to Left and right channel
        // which are in global mixbuffer, alternating.
        leftout = 0;
        rightout = 2;
        step = 4;

        // Determine end, for left channel only
        // (right channel is implicit).
        // MAES: this implies that the buffer will only mix
        // that many samples at a time, and that the size is just right.
        // Thus, it must be flushed (p_mixbuffer=0) before reusing it.
        leftend = SAMPLECOUNT * step;

        for (chan = 0; chan < numChannels; chan++) {
            if (channels[chan] != null)
                // SOME mixing has taken place.
                mixed = true;
        }

        // Mix sounds into the mixing buffer.
        // Loop over step*SAMPLECOUNT,
        // that is SAMPLECOUNT values for two channels.

        while (leftout < leftend) {
            // Reset left/right value.
            dl = 0;
            dr = 0;

            // Love thy L2 chache - made this a loop.
            // Now more channels could be set at compile time
            // as well. Thus loop those channels.

            for (chan = 0; chan < numChannels; chan++) {

                // if (D) System.err.printf("Checking channel %d\n",chan);
                // Check channel, if active.
                // MAES: this means that we must point to raw data here.
                if (channels[chan] != null) {
                    int channel_pointer = p_channels[chan];

                    // Get the raw data from the channel.
                    // Maes: this is supposed to be an 8-bit unsigned value.
                        sample = 0x00FF & channels[chan][channel_pointer];
                        
                    // Add left and right part for this channel (sound)
                    // to the current data. Adjust volume accordingly.                        
                    // Q: could this be optimized by converting samples to 16-bit
                    // at load time, while also allowing for stereo samples?
                    // A: Only for the stereo part. You would still look a lookup
                    // for the CURRENT volume level.

                    dl += channelleftvol_lookup[chan][sample];
                    dr += channelrightvol_lookup[chan][sample];

                    // This should increment the index inside a channel, but is
                    // expressed in 16.16 fixed point arithmetic.
                    channelstepremainder[chan] += channelstep[chan];

                    // The actual channel pointer is increased here.
                    // The above trickery allows playing back different pitches.
                    // The shifting retains only the integer part.
                    channel_pointer += channelstepremainder[chan] >> 16;

                    // This limits it to the "decimal" part in order to
                    // avoid undue accumulation.
                    channelstepremainder[chan] &= 0xFFFF;

                    // Check whether we are done. Also to avoid overflows.
                    if (channel_pointer >= channelsend[chan]) {
                        // Reset pointer for a channel.
                        if (D)
                            System.err
                                    .printf(
                                        "Channel %d handle %d pointer %d thus done, stopping\n",
                                        chan, this.channelhandles[chan],
                                        channel_pointer);
                        channels[chan] = null;
                        channel_pointer = 0;
                    }

                    // Write pointer back, so we know where a certain channel
                    // is the next time UpdateSounds is called.

                    p_channels[chan] = channel_pointer;
                }

            } // for all channels.

            // MAES: at this point, the actual values for a single sample
            // (YIKES!) are in d1 and d2. We must use the leftout/rightout
            // pointers to write them back into the mixbuffer.

            // Clamp to range. Left hardware channel.
            // Remnant of 8-bit mixing code? That must have raped ears
            // and made them bleed.
            // if (dl > 127) *leftout = 127;
            // else if (dl < -128) *leftout = -128;
            // else *leftout = dl;

            if (dl > 0x7fff)
                dl = 0x7fff;
            else if (dl < -0x8000)
                dl = -0x8000;

            // Write left channel
            mixbuffer[leftout] = (byte) ((dl & 0xFF00) >>> 8);
            mixbuffer[leftout + 1] = (byte) (dl & 0x00FF);

            // Same for right hardware channel.
            if (dr > 0x7fff)
                dr = 0x7fff;
            else if (dr < -0x8000)
                dr = -0x8000;

            // Write right channel.
            mixbuffer[rightout] = (byte) ((dr & 0xFF00) >>> 8);
            mixbuffer[rightout + 1] = (byte) (dr & 0x00FF);

            // Increment current pointers in mixbuffer.
            leftout += 4;
            rightout += 4;
        } // End leftend/leftout while

        // Q: how do we know whether the mixbuffer isn't entirely used
        // and instead it has residual garbage samples in it?
        // A: DOOM kind of padded samples in memory, so presumably
        // they all played silence.
        // Q: what's the purpose of channelremainder etc?
        // A: pitch variations were done with fractional pointers 16.16
        // style.

    }

    /**
     * SFX API Note: this was called by S_Init. However, whatever they did in
     * the old DPMS based DOS version, this were simply dummies in the Linux
     * version. See soundserver initdata().
     */

    @Override
    public void SetChannels(int numChannels) {
        // Init internal lookups (raw data, mixing buffer, channels).
        // This function sets up internal lookups used during
        // the mixing process.

        int steptablemid = 128;

        // Okay, reset internal mixing channels to zero.
        for (int i = 0; i < this.numChannels; i++) {
            channels[i] = null;
        }
        
        generateStepTable(steptablemid);

        generateVolumeLUT();
    }
    
    protected MixServer SOUNDSRV;

    protected Thread SOUNDTHREAD;

    @Override
    public boolean InitSound() {

        // Secure and configure sound device first.
        System.out.println("I_InitSound: ");

        // We only need a single data line.
        // PCM, signed, 16-bit, stereo, 22025 KHz, 2048 bytes per "frame",
        // maximum of 44100/2048 "fps"
        AudioFormat format = new AudioFormat(SAMPLERATE, 16, 2, true, true);

        DataLine.Info info = new DataLine.Info(SourceDataLine.class, format);

        if (AudioSystem.isLineSupported(info))
            try {
                line = (SourceDataLine) AudioSystem.getSourceDataLine(format);
                line.open(format,AUDIOLINE_BUFFER);
            } catch (Exception e) {
                e.printStackTrace();
                System.err.print("Could not play signed 16 data\n");
                return false;
            }

        if (line != null) {
            System.err.print(" configured audio device\n");
            line.start();
        } else {
        	 System.err.print(" could not configure audio device\n");
        	 return false;
        }

        // This was here only for debugging purposes
        /*
         * try { fos=new FileOutputStream("test.raw"); dao=new
         * DataOutputStream(fos); } catch (FileNotFoundException e) {
         * Auto-generated catch block e.printStackTrace(); }
         */

        SOUNDSRV = new MixServer(line);
        SOUNDTHREAD = new Thread(SOUNDSRV);
        SOUNDTHREAD.start();

        // Initialize external data (all sounds) at start, keep static.
        System.err.print("I_InitSound: ");

        super.initSound8();

        System.err.print(" pre-cached all sound data\n");

        // Now initialize mixbuffer with zero.
        initMixBuffer();

        // Finished initialization.
        System.err.print("I_InitSound: sound module ready\n");

        return true;
    }


    @Override
    protected int addsfx(int sfxid, int volume, int step, int seperation) {
        int i;
        int rc = -1;

        int oldest = DS.gametic;
        int oldestnum = 0;
        int slot;

        int rightvol;
        int leftvol;

        int broken=-1;
        
        // Chainsaw troubles.
        // Play these sound effects only one at a time.
        if ((sfxid >= sfxenum_t.sfx_sawup.ordinal()
                && sfxid <= sfxenum_t.sfx_sawhit.ordinal())
                || sfxid == sfxenum_t.sfx_stnmov.ordinal()
                || sfxid == sfxenum_t.sfx_pistol.ordinal()) {
            // Loop all channels, check.
            for (i = 0; i < numChannels; i++) {
                // Active, and using the same SFX?
                if ((channels[i] != null) && (channelids[i] == sfxid)) {
                    // Reset.
                    this.p_channels[i] = 0;
                    this.channels[i]=null;
                    // We are sure that iff,
                    // there will only be one.
                    broken=i;
                    break;
                }
            }
        }

        // Loop all channels to find oldest SFX.
        if (broken>=0) {
        	i=broken;
        	oldestnum=broken;
        }
        else
        for (i = 0; (i < numChannels) && (channels[i] != null); i++) {
            if (channelstart[i] < oldest) {
                oldestnum = i;
            }
        }

        oldest = channelstart[oldestnum];
        
        // Tales from the cryptic.
        // If we found a channel, fine.
        // If not, we simply overwrite the first one, 0.
        // Probably only happens at startup.
        if (i == numChannels)
            slot = oldestnum;
        else
            slot = i;

        // Okay, in the less recent channel,
        // we will handle the new SFX.
        // Set pointer to raw data.
        channels[slot] = S_sfx[sfxid].data;

        // MAES: if you don't zero-out the channel pointer here, it gets ugly
        p_channels[slot] = 0;

        // Set pointer to end of raw data.
        channelsend[slot] = lengths[sfxid];

        // Reset current handle number, limited to 0..100.
        if (handlenums == 0) // was !handlenums, so it's actually 1...100?
            handlenums = 100;

        // Assign current handle number.
        // Preserved so sounds could be stopped (unused).
        // Maes: this should really be decreasing, otherwide handles
        // should start at 0 and go towards 100. Just saying.
        channelhandles[slot] = rc = handlenums--;

        // Set stepping???
        // Kinda getting the impression this is never used.
        // MAES: you're wrong amigo.
        channelstep[slot] = step;
        // ???
        channelstepremainder[slot] = 0;
        // Should be gametic, I presume.
        channelstart[slot] = DS.gametic;

        // Separation, that is, orientation/stereo.
        // range is: 1 - 256
        seperation += 1;

        // Per left/right channel.
        // x^2 seperation,
        // adjust volume properly.
        leftvol = volume - ((volume * seperation * seperation) >> 16); // /(256*256);
        seperation = seperation - 257;
        rightvol = volume - ((volume * seperation * seperation) >> 16);

        // Sanity check, clamp volume.
        // Maes: better to clamp than to crash, no?

        if (rightvol < 0) rightvol=0;
        if (rightvol >127) rightvol=127;
        if (leftvol < 0) leftvol=0;
        if (leftvol >127) leftvol=127;

        // Get the proper lookup table piece
        // for this volume level???
        channelleftvol_lookup[slot] = vol_lookup[leftvol];
        channelrightvol_lookup[slot] = vol_lookup[rightvol];

        // Preserve sound SFX id,
        // e.g. for avoiding duplicates of chainsaw.
        channelids[slot] = sfxid;

        if (D) System.err.println(channelStatus());
        if (D) System.err.printf(
                "Playing sfxid %d handle %d length %d vol %d on channel %d\n",
                sfxid, rc, S_sfx[sfxid].data.length, volume, slot);

        // You tell me.
        return rc;
    }

    @Override
    public void ShutdownSound() {

        boolean done = false;

        // Unlock sound thread if it's waiting.
        produce.release();

        int i;
        while (!done) {
            for (i = 0; i < numChannels && (channels[i] == null); i++) {

            }

            // System.err.printf("%d channels died off\n",i);

            UpdateSound();
            SubmitSound();
            if (i == numChannels)
                done = true;
        }

        this.line.drain();
        SOUNDSRV.terminate = true;
        produce.release();

        try {
            SOUNDTHREAD.join();
        } catch (InterruptedException e) {
        	// Well, I don't care.
        }
        line.close();

    }

    protected class MixServer
            implements Runnable {

        public boolean terminate = false;

        public MixServer(SourceDataLine line) {
            this.auline = line;
        }

        private SourceDataLine auline;

        private ArrayBlockingQueue<AudioChunk> audiochunks =
            new ArrayBlockingQueue<AudioChunk>(BUFFER_CHUNKS * 2);

        public void addChunk(AudioChunk chunk) {
            audiochunks.offer(chunk);
        }

        public volatile int currstate = 0;

        public void run() {

            while (!terminate) {

                // while (timing[mixstate]<=mytime){

                // Try acquiring a produce permit before going on.

                try {
                    // System.err.println("Waiting for a permit...");
                    produce.acquire();
                    // System.err.println("Got a permit");
                } catch (InterruptedException e) {
                    // Well, ouch.
                    e.printStackTrace();
                }

                int chunks = 0;

                // System.err.printf("Audio queue has %d chunks\n",audiochunks.size());

                // Play back only at most a given number of chunks once you reach
                // this spot
                
                int atMost=Math.min(ISoundDriver.BUFFER_CHUNKS,audiochunks.size());
                
                while (atMost-->0){

                    AudioChunk chunk = null;
                    try {
                        chunk = audiochunks.take();
                    } catch (InterruptedException e1) {
                        // Should not block
                    }
                    // Play back all chunks present in a buffer ASAP
                    auline.write(chunk.buffer, 0, MIXBUFFERSIZE);
                    chunks++;
                    // No matter what, give the chunk back!
                    chunk.free = true;
                    audiochunkpool.checkIn(chunk);
                }

                // Signal that we consumed a whole buffer and we are ready for
                // another one.
                
                consume.release();
            }
        }
    }

    @Override
    public boolean SoundIsPlaying(int handle) {

        int c = getChannelFromHandle(handle);
        return (c != -2 && channels[c] == null);

    }

    /**
     * Internal use.
     * 
     * @param handle
     * @return the channel that has the handle, or -2 if none has it.
     */
    protected int getChannelFromHandle(int handle) {
        // Which channel has it?
        for (int i = 0; i < numChannels; i++) {
            if (channelhandles[i] == handle)
                return i;
        }

        return BUSY_HANDLE;
    }

    @Override
    public void StopSound(int handle) {
        // Which channel has it?
        int hnd = getChannelFromHandle(handle);
        if (hnd >= 0) {
            channels[hnd] = null;
            p_channels[hnd] = 0;
            this.channelhandles[hnd] = IDLE_HANDLE;
        }
    }

    @Override
    public void SubmitSound() {

        // It's possible for us to stay silent and give the audio
        // queue a chance to get drained.
        if (mixed) {
            silence=0;
            AudioChunk gunk = audiochunkpool.checkOut();
            // Ha ha you're ass is mine!
            gunk.free = false;

            // System.err.printf("Submitted sound chunk %d to buffer %d \n",chunk,mixstate);

            // Copy the currently mixed chunk into its position inside the
            // master buffer.
            System.arraycopy(mixbuffer, 0, gunk.buffer, 0, MIXBUFFERSIZE);

            this.SOUNDSRV.addChunk(gunk);

            // System.err.println(chunk++);

            chunk++;
            // System.err.println(chunk);

            if (consume.tryAcquire())
                produce.release();

        } else {
            silence++;
            // MAES: attempt to fix lingering noise error
            if (silence >ISoundDriver.BUFFER_CHUNKS*5){
                line.flush();
                silence=0;
                }
            // System.err.println("SILENT_CHUNK");
            // this.SOUNDSRV.addChunk(SILENT_CHUNK);
        }
        // line.write(mixbuffer, 0, mixbuffer.length);

    }
    
    private int silence=0; 

    @Override
    public void UpdateSoundParams(int handle, int vol, int sep, int pitch) {

        int chan = this.getChannelFromHandle(handle);
        // Per left/right channel.
        // x^2 seperation,
        // adjust volume properly.
        int leftvol = vol - ((vol * sep * sep) >> 16); // /(256*256);
        sep = sep - 257;
        int rightvol = vol - ((vol * sep * sep) >> 16);

        // Sanity check, clamp volume.

        if (rightvol < 0 || rightvol > 127)
            DS.I.Error("rightvol out of bounds");

        if (leftvol < 0 || leftvol > 127)
            DS.I.Error("leftvol out of bounds");

        // Get the proper lookup table piece
        // for this volume level???
        channelleftvol_lookup[chan] = vol_lookup[leftvol];
        channelrightvol_lookup[chan] = vol_lookup[rightvol];

        // Well, if you can get pitch to change too...
        this.channelstep[chan] = steptable[pitch];
        channelsend[chan] = this.lengths[this.channelids[chan]];

    }

    protected StringBuilder sb = new StringBuilder();

    public String channelStatus() {
        sb.setLength(0);
        for (int i = 0; i < numChannels; i++) {
            if (channels[i] != null)
                sb.append(i);
            else
                sb.append('-');
        }

        return sb.toString();

    }

    protected final AudioChunk SILENT_CHUNK = new AudioChunk();

    protected final AudioChunkPool audiochunkpool = new AudioChunkPool();
}

package s;

import data.sfxinfo_t;
import data.sounds.sfxenum_t;
import static data.Defines.TICRATE;

//Emacs style mode select   -*- C++ -*- 
//-----------------------------------------------------------------------------
//
// $Id: ISoundDriver.java,v 1.1 2012/11/08 17:12:42 velktron Exp $
//
// Copyright (C) 1993-1996 by id Software, Inc.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
//
// DESCRIPTION:
// System interface, sound. Anything implementation-specific should
// implement this.
//
//-----------------------------------------------------------------------------

public interface ISoundDriver {

	
	public static final int VOLUME_STEPS = 128;
	public static final int PANNING_STEPS = 256;
	public static final int IDLE_HANDLE = -1;
	public static final int BUSY_HANDLE = -2;
	// Needed for calling the actual sound output
	// We mix 1024 samples each time, but we only call UpdateSound()
	// 1 time out of three.
	
	public static final int NUM_CHANNELS	=	8;
	// It is 2 for 16bit, and 2 for two channels.
	public static final int BUFMUL            =      4;

	public static final int SAMPLERATE	=	22050;	// Hz
	
	// Update all 30 millisecs, approx. 30fps synchronized.
	// Linux resolution is allegedly 10 millisecs,
	//  scale is microseconds.
	public static final int SOUND_INTERVAL =    500;
	
	/** Yes, it's possible to select a different sound frame rate */
	
	public static final int SND_FRAME_RATE =21;
	// Was 512, but if you mix that many samples per tic you will
	// eventually outrun the buffer :-/ I fail to see the mathematical
	// justification behind this, unless they simply wanted the buffer to 
	// be a nice round number in size.
	public static final int SAMPLECOUNT		=SAMPLERATE/SND_FRAME_RATE;
	public static final int MIXBUFFERSIZE		=(SAMPLECOUNT*BUFMUL);
	public static final int SAMPLESIZE	=	16 ;  	// 16bit
	public static final int NUMSFX	=	sfxenum_t.NUMSFX.ordinal() ; 
	public static final int MAXHANDLES = 100;
	/** How many audio chunks/frames to mix before submitting them to
	 *  the output.
	 */
	public static final int BUFFER_CHUNKS=5;
	
	/** Ths audio buffer size of the audioline itself. 
	 *  Increasing this is the only effective way to combat output stuttering on
	 *  slower machines.
	 */
	public static final int AUDIOLINE_BUFFER=2*BUFFER_CHUNKS*MIXBUFFERSIZE;
	public static final int SOUND_PERIOD = 1000/SND_FRAME_RATE; // in ms
	
	/** Init at program start. Return false if device invalid,
	 *  so that caller can decide best course of action. 
	 *  The suggested one is to swap the sound "driver" for a dummy.
	 *  
	 * @return
	 */
	boolean InitSound();

	// ... update sound buffer and audio device at runtime...
	void UpdateSound();
	void SubmitSound();

	// ... shut down and relase at program termination.
	void ShutdownSound();


	//
	//  SFX I/O
	//

	// Initialize channels?
	void SetChannels(int numChannels);

	// Get raw data lump index for sound descriptor.
	int GetSfxLumpNum (sfxinfo_t sfxinfo );


	// Starts a sound in a particular sound channel.
	int StartSound
	( int		id,
	  int		vol,
	  int		sep,
	  int		pitch,
	  int		priority );


	// Stops a sound channel.
	void StopSound(int handle);

	/** Called by S_*() functions to see if a channel is still playing.
	    Returns false if no longer playing, true if playing. This is 
	    a relatively "high level" function, so its accuracy relies on
	    what the "system specific" sound code reports back */
	boolean SoundIsPlaying(int handle);

	/* Updates the volume, separation,
	   and pitch of a sound channel. */
	void UpdateSoundParams
	( int		handle,
	  int		vol,
	  int		sep,
	  int		pitch );
	
}

package s;

import w.DoomBuffer;
import doom.DoomStatus;

/** A variation of the Classic Sound Driver, decoding the DP-lumps
 *  instead of the DS. A better way would be to build-in an 
 *  automatic "WAV to SPEAKER" conversion, but that can wait...
 * 
 * @author Maes
 *
 */

public class SpeakerDoomSoundDriver extends ClassicDoomSoundDriver {

    public SpeakerDoomSoundDriver(DoomStatus DS, int numChannels) {
        super(DS, numChannels);
        // TODO Auto-generated constructor stub
    }
	
    /** Rigged so it gets SPEAKER sounds instead of regular ones */
    
    @Override
    protected byte[] getsfx
    ( String         sfxname,
            int[]          len, int index )
    {
        byte[]      sfx;
        byte[]      paddedsfx;
        int                 i;
        int                 size;
        int                 paddedsize;
        String                name;
        int                 sfxlump;

        // Get the sound data from the WAD, allocate lump
        //  in zone memory.
        name=String.format("dp%s", sfxname).toUpperCase();

        // Now, there is a severe problem with the
        //  sound handling, in it is not (yet/anymore)
        //  gamemode aware. That means, sounds from
        //  DOOM II will be requested even with DOOM
        //  shareware.
        // The sound list is wired into sounds.c,
        //  which sets the external variable.
        // I do not do runtime patches to that
        //  variable. Instead, we will use a
        //  default sound for replacement.
        if ( DS.W.CheckNumForName(name) == -1 )
            sfxlump = DS.W.GetNumForName("dppistol");
        else
            sfxlump = DS.W.GetNumForName(name);

        // We must first load and convert it to raw samples.
        
        SpeakerSound SP=(SpeakerSound) DS.W.CacheLumpNum(sfxlump, 0,SpeakerSound.class);
        
        sfx = SP.toRawSample();
        
        size = sfx.length;

        // MAES: A-ha! So that's how they do it.
        // SOund effects are padded to the highest multiple integer of 
        // the mixing buffer's size (with silence)

        paddedsize = ((size-8 + (SAMPLECOUNT-1)) / SAMPLECOUNT) * SAMPLECOUNT;

        // Allocate from zone memory.
        paddedsfx = new byte[paddedsize];

        // Now copy and pad. The first 8 bytes are header info, so we discard them.
        System.arraycopy(sfx,8, paddedsfx, 0,size-8 );
        
        for (i=size-8 ; i<paddedsize ; i++)
            paddedsfx[i] = (byte) 127;

        
        
        // Hmm....silence?
        for (i=size-8 ; i<paddedsize ; i++)
            paddedsfx[i] = (byte) 127;

        // Remove the cached lump.
        DS.W.UnlockLumpNum(sfxlump);

        if (D) System.out.printf("SFX %d size %d padded to %d\n",index,size,paddedsize);
        // Preserve padded length.
        len[index] = paddedsize;

        // Return allocated padded data.
        // So the first 8 bytes are useless?
        return paddedsfx;
    }

}

package p;

import doom.think_t;
import rr.SectorAction;
import rr.line_t;
import rr.sector_t;

public class slidedoor_t extends SectorAction {
	    sdt_e	type;
	    line_t	line;
	    int		frame;
	    int		whichDoorIndex;
	    int		timer;
	    sector_t	frontsector;
	    sector_t	backsector;
	    sd_e	 status;
	    
	    public slidedoor_t(){
	    	type=sdt_e.sdt_closeOnly;
	    	status=sd_e.sd_closing;
	    	function=think_t.T_SlidingDoor;
	    }

	}
package p;

import java.io.IOException;

import i.DoomStatusAware;
import rr.subsector_t;
import defines.skill_t;

public interface ILevelLoader extends DoomStatusAware {

	// Lump order in a map WAD: each map needs a couple of lumps
    // to provide a complete scene geometry description.

    public static final int ML_LABEL = 0;

    /** A separator, name, ExMx or MAPxx */
    public static final int ML_THINGS = 1;

    /** Monsters, items.. */
    public static final int ML_LINEDEFS = 2;

    /** LineDefs, from editing */
    public static final int ML_SIDEDEFS = 3;

    /** SideDefs, from editing */
    public static final int ML_VERTEXES = 4;

    /** Vertices, edited and BSP splits generated */
    public static final int ML_SEGS = 5;

    /** LineSegs, from LineDefs split by BSP */
    public static final int ML_SSECTORS = 6;

    /** SubSectors, list of LineSegs */
    public static final int ML_NODES = 7;

    /** BSP nodes */
    public static final int ML_SECTORS = 8;

    /** Sectors, from editing */
    public static final int ML_REJECT = 9;

    /** LUT, sector-sector visibility */
    public static final int ML_BLOCKMAP = 10;
    
    // Maintain single and multi player starting spots.
    public static final int MAX_DEATHMATCH_STARTS  = 10;

    /** Expected lump names for verification */
    public static final String[] LABELS={"MAPNAME","THINGS","LINEDEFS","SIDEDEFS",
                                        "VERTEXES","SEGS","SSECTORS","NODES",
                                        "SECTORS","REJECT","BLOCKMAP"};

    /** P_SetupLevel 
     * 
     * @param episode
     * @param map
     * @param playermask
     * @param skill
     * @throws IOException 
     */
	void SetupLevel(int episode, int map, int playermask, skill_t skill) throws IOException;

	/**
	 * P_SetThingPosition Links a thing into both a block and a subsector based
	 * on it's x y. Sets thing.subsector properly
	 *
	 * 
	 * @param thing
	 */
	void SetThingPosition(mobj_t thing);

	  /**
	   * R_PointInSubsector
	   * 
	   * MAES: it makes more sense to have this here.
	   * 
	   * @param x fixed
	   * @param y fixed
	   * 
	   */
	
	subsector_t PointInSubsector(int x, int y);
        
	
}

package p;

public enum floor_e {
    // lower floor to highest surrounding floor
    lowerFloor,
    
    // lower floor to lowest surrounding floor
    lowerFloorToLowest,
    
    // lower floor to highest surrounding floor VERY FAST
    turboLower,
    
    // raise floor to lowest surrounding CEILING
    raiseFloor,
    
    // raise floor to next highest surrounding floor
    raiseFloorToNearest,

    // raise floor to shortest height texture around it
    raiseToTexture,
    
    // lower floor to lowest surrounding floor
    //  and change floorpic
    lowerAndChange,
  
    raiseFloor24,
    raiseFloor24AndChange,
    raiseFloorCrush,

     // raise to next highest floor, turbo-speed
    raiseFloorTurbo,       
    donutRaise,
    raiseFloor512
    
}

package p;

import java.io.DataInputStream;
import java.io.IOException;
import java.nio.ByteBuffer;

import w.DoomIO;
import w.IPackableDoomObject;
import w.IReadableDoomObject;
import data.state_t;

public class pspdef_t implements IReadableDoomObject,IPackableDoomObject{

    public pspdef_t(){
        state=new state_t();
    }

    /** a NULL state means not active */
    public state_t	state;	
    public int		tics;
    /** fixed_t */
    public int	sx, sy;
    // When read from disk.
    public int readstate;
    
    @Override
    public void read(DataInputStream f) throws IOException {
        //state=data.info.states[f.readLEInt()];
        readstate=DoomIO.readLEInt(f);
        tics=DoomIO.readLEInt(f);
        sx=DoomIO.readLEInt(f);
        sy=DoomIO.readLEInt(f);
    }
    
    @Override
    public void pack(ByteBuffer f) throws IOException {
        if (state==null) f.putInt(0);
        else f.putInt(state.id);
        f.putInt(tics);
        f.putInt(sx);
        f.putInt(sy);
    }

}

package p;

import static data.Defines.BLOCKMAPPADDING;
import static data.Defines.MAPBLOCKSHIFT;
import static data.Defines.MAPBLOCKUNITS;
import static data.Defines.NF_SUBSECTOR;
import static data.Defines.PU_LEVEL;
import static m.fixed_t.FRACBITS;
import static p.mobj_t.MF_NOBLOCKMAP;
import static p.mobj_t.MF_NOSECTOR;
import static utils.C2JUtils.flags;
import m.BBox;
import i.IDoomSystem;
import rr.Renderer;
import rr.TextureManager;
import rr.line_t;
import rr.node_t;
import rr.sector_t;
import rr.seg_t;
import rr.side_t;
import rr.subsector_t;
import rr.vertex_t;
import s.IDoomSound;
import utils.C2JUtils;
import v.DoomVideoRenderer;
import w.IWadLoader;
import data.Limits;
import data.mapthing_t;
import doom.DoomStatus;

/**
 * The idea is to lump common externally readable properties that need DIRECT
 * ACCESS (aka not behindsetters/getters) here, as well as some common shared
 * internal structures/status objects. If you need access to stuff like the
 * blockmap/reject table etc. then you should ask for this class. If you only
 * need access to some methods like e.g. SetupLevel, you can simply use the
 * ILevelLoader interface.
 * 
 * @author velktron
 */

public abstract class AbstractLevelLoader
        implements ILevelLoader {

    // ///////////////// Status objects ///////////////////

    IDoomSystem I;

    IWadLoader W;

    DoomStatus<?,?> DM;

    DoomVideoRenderer<?,?> V;

    Renderer<?,?> R;

    TextureManager<?> TM;

    Actions P;

    IDoomSound S;

    //
    // MAP related Lookup tables.
    // Store VERTEXES, LINEDEFS, SIDEDEFS, etc.
    //
    public int numvertexes;

    public vertex_t[] vertexes;

    public int numsegs;

    public seg_t[] segs;

    public int numsectors;

    public sector_t[] sectors;

    public int numsubsectors;

    public subsector_t[] subsectors;

    public int numnodes;

    public node_t[] nodes;

    public int numlines;

    public line_t[] lines;

    public int numsides;

    public side_t[] sides;

    // BLOCKMAP
    // Created from axis aligned bounding box
    // of the map, a rectangular array of
    // blocks of size ...
    // Used to speed up collision detection
    // by spatial subdivision in 2D.
    //
    /** Blockmap size. */
    protected int bmapwidth, bmapheight; // size in mapblocks

    /**
     * killough 3/1/98: remove blockmap limit internally. Maes 29/9/2011: Header
     * stripped during loading and pointers pre-modified, so there's no double
     * arraying.
     */
    public int[] blockmap; // was short -- killough

    /**
     * Maes 29/9/2011: Used only during loading. No more dichotomy with
     * blockmap.
     */
    protected int[] blockmaplump; // was short -- killough

    /** (fixed_t) origin of block map */
    public int bmaporgx, bmaporgy;

    /** for thing chains */
    public mobj_t[] blocklinks;

    /**
     * REJECT For fast sight rejection. Speeds up enemy AI by skipping detailed
     * LineOf Sight calculation. Without special effect, this could be used as a
     * PVS lookup as well.
     */

    protected byte[] rejectmatrix;

    // Maintain single and multi player starting spots.

    // 1/11/98 killough: Remove limit on deathmatch starts
    protected mapthing_t[] deathmatchstarts; // killough

    protected int num_deathmatchstarts; // killough

    // mapthing_t* deathmatch_p;
    protected int deathmatch_p;

    protected mapthing_t[] playerstarts = new mapthing_t[Limits.MAXPLAYERS];

    public AbstractLevelLoader(DoomStatus<?,?> DC) {
        this.updateStatus(DC);
    }

    @Override
    public void updateStatus(DoomStatus<?,?> DC) {
        this.W = DC.W;
        this.DM = DC;
        this.P = DC.P;
        this.R = DC.R;
        this.I = DC.I;
        this.S = DC.S;
        this.TM = DC.TM;

    }

    /**
     * P_SetThingPosition Links a thing into both a block and a subsector based
     * on it's x y. Sets thing.subsector properly
     */

    @Override
    public void SetThingPosition(mobj_t thing) {
        final subsector_t ss;
        final sector_t sec;
        int blockx;
        int blocky;
        final mobj_t link;

        // link into subsector
        ss = PointInSubsector(thing.x, thing.y);
        thing.subsector = ss;

        if (!flags(thing.flags, MF_NOSECTOR)) {
            // invisible things don't go into the sector links
            sec = ss.sector;

            thing.sprev = null;
            thing.snext = sec.thinglist;

            if (sec.thinglist != null)
                sec.thinglist.sprev = thing;

            sec.thinglist = thing;
        }

        // link into blockmap
        if (!flags(thing.flags, MF_NOBLOCKMAP)) {
            // inert things don't need to be in blockmap
            blockx = getSafeBlockX(thing.x - bmaporgx);
            blocky = getSafeBlockY(thing.y - bmaporgy);
            
            // Valid block?
            if (blockx >= 0 && blockx < bmapwidth && blocky >= 0
                    && blocky < bmapheight) {

                // Get said block.
                link = blocklinks[blocky * bmapwidth + blockx];
                thing.bprev = null; // Thing is put at head of block...
                thing.bnext = link;
                if (link != null) // block links back at thing...
                    // This will work
                    link.bprev = thing;

                // "thing" is now effectively the new head
                // Iterators only follow "bnext", not "bprev".
                // If link was null, then thing is the first entry.
                blocklinks[blocky * bmapwidth + blockx] = thing;
            } else {
                // thing is off the map
                thing.bnext = thing.bprev = null;
            }
        }

    }

    @Override
    public subsector_t PointInSubsector(int x, int y) {
        node_t node;
        int side;
        int nodenum;

        // single subsector is a special case
        if (numnodes == 0)
            return subsectors[0];
        
        nodenum = numnodes - 1;
        
        while ((nodenum & NF_SUBSECTOR) == 0) {
            node = nodes[nodenum];
            side = node.PointOnSide(x, y);
            nodenum = node.children[side];
        }

        return subsectors[nodenum & ~NF_SUBSECTOR];
    }

    //
    // jff 10/6/98
    // New code added to speed up calculation of internal blockmap
    // Algorithm is order of nlines*(ncols+nrows) not nlines*ncols*nrows
    //

    protected static final int blkshift = 7; /*
                                              * places to shift rel position for
                                              * cell num
                                              */

    protected static final int blkmask = ((1 << blkshift) - 1);/*
                                                                * mask for rel
                                                                * position
                                                                * within cell
                                                                */

    protected static final int blkmargin = 0; /* size guardband around map used */

    // jff 10/8/98 use guardband>0
    // jff 10/12/98 0 ok with + 1 in rows,cols

    protected class linelist_t // type used to list lines in each block
    {
        public int num;

        public linelist_t next;
    }

    /**
     * Subroutine to add a line number to a block list It simply returns if the
     * line is already in the block
     * 
     * @param lists
     * @param count
     * @param done
     * @param blockno
     * @param lineno
     */
    private final void AddBlockLine(linelist_t[] lists, int[] count,
            boolean[] done, int blockno, int lineno) {
        long a=System.nanoTime();
        linelist_t l;

        if (done[blockno])
            return;

        l = new linelist_t();
        l.num = lineno;
        l.next = lists[blockno];
        lists[blockno] = l;
        count[blockno]++;
        done[blockno] = true;
        long b=System.nanoTime();
        
        total+=(b-a);
    }

    long total=0;
    
    /**
     * Actually construct the blockmap lump from the level data This finds the
     * intersection of each linedef with the column and row lines at the left
     * and bottom of each blockmap cell. It then adds the line to all block
     * lists touching the intersection. MAES 30/9/2011: Converted to Java. It's
     * important that LINEDEFS and VERTEXES are already read-in and defined, so
     * it is necessary to change map lump ordering for this to work.
     */

    protected final void CreateBlockMap() {
        int xorg, yorg; // blockmap origin (lower left)
        int nrows, ncols; // blockmap dimensions
        linelist_t[] blocklists = null; // array of pointers to lists of lines
        int[] blockcount = null; // array of counters of line lists
        boolean[] blockdone = null; // array keeping track of blocks/line
        int NBlocks; // number of cells = nrows*ncols
        int linetotal = 0; // total length of all blocklists
        int map_minx = Integer.MAX_VALUE; // init for map limits search
        int map_miny = Integer.MAX_VALUE;
        int map_maxx = Integer.MIN_VALUE;
        int map_maxy = Integer.MIN_VALUE;

        long a = System.nanoTime();

        // scan for map limits, which the blockmap must enclose

        for (int i = 0; i < numvertexes; i++) {
            int t;

            if ((t = vertexes[i].x) < map_minx)
                map_minx = t;
            else if (t > map_maxx)
                map_maxx = t;
            if ((t = vertexes[i].y) < map_miny)
                map_miny = t;
            else if (t > map_maxy)
                map_maxy = t;
        }
        map_minx >>= FRACBITS; // work in map coords, not fixed_t
        map_maxx >>= FRACBITS;
        map_miny >>= FRACBITS;
        map_maxy >>= FRACBITS;

        // set up blockmap area to enclose level plus margin

        xorg = map_minx - blkmargin;
        yorg = map_miny - blkmargin;
        ncols = (map_maxx + blkmargin - xorg + 1 + blkmask) >> blkshift; // jff
                                                                         // 10/12/98
        nrows = (map_maxy + blkmargin - yorg + 1 + blkmask) >> blkshift; // +1
                                                                         // needed
                                                                         // for
        NBlocks = ncols * nrows; // map exactly 1 cell

        // create the array of pointers on NBlocks to blocklists
        // also create an array of linelist counts on NBlocks
        // finally make an array in which we can mark blocks done per line

        // CPhipps - calloc's
        blocklists = new linelist_t[NBlocks];
        blockcount = new int[NBlocks];
        blockdone = new boolean[NBlocks];

        // initialize each blocklist, and enter the trailing -1 in all
        // blocklists
        // note the linked list of lines grows backwards

        for (int i = 0; i < NBlocks; i++) {
            blocklists[i] = new linelist_t();
            blocklists[i].num = -1;
            blocklists[i].next = null;
            blockcount[i]++;
        }

        // For each linedef in the wad, determine all blockmap blocks it
        // touches,
        // and add the linedef number to the blocklists for those blocks

        for (int i = 0; i < numlines; i++) {
            int x1 = lines[i].v1x >> FRACBITS; // lines[i] map coords
            int y1 = lines[i].v1y >> FRACBITS;
            int x2 = lines[i].v2x >> FRACBITS;
            int y2 = lines[i].v2y >> FRACBITS;
            int dx = x2 - x1;
            int dy = y2 - y1;
            boolean vert = dx == 0; // lines[i] slopetype
            boolean horiz = dy == 0;
            boolean spos = (dx ^ dy) > 0;
            boolean sneg = (dx ^ dy) < 0;
            int bx, by; // block cell coords
            int minx = x1 > x2 ? x2 : x1; // extremal lines[i] coords
            int maxx = x1 > x2 ? x1 : x2;
            int miny = y1 > y2 ? y2 : y1;
            int maxy = y1 > y2 ? y1 : y2;

            // no blocks done for this linedef yet

            C2JUtils.memset(blockdone, false, NBlocks);

            // The line always belongs to the blocks containing its endpoints

            bx = (x1 - xorg) >> blkshift;
            by = (y1 - yorg) >> blkshift;
            AddBlockLine(blocklists, blockcount, blockdone, by * ncols + bx, i);
            bx = (x2 - xorg) >> blkshift;
            by = (y2 - yorg) >> blkshift;
            AddBlockLine(blocklists, blockcount, blockdone, by * ncols + bx, i);

            // For each column, see where the line along its left edge, which
            // it contains, intersects the Linedef i. Add i to each
            // corresponding
            // blocklist.

            if (!vert) // don't interesect vertical lines with columns
            {
                for (int j = 0; j < ncols; j++) {
                    // intersection of Linedef with x=xorg+(j<<blkshift)
                    // (y-y1)*dx = dy*(x-x1)
                    // y = dy*(x-x1)+y1*dx;

                    int x = xorg + (j << blkshift); // (x,y) is intersection
                    int y = (dy * (x - x1)) / dx + y1;
                    int yb = (y - yorg) >> blkshift; // block row number
                    int yp = (y - yorg) & blkmask; // y position within block

                    if (yb < 0 || yb > nrows - 1) // outside blockmap, continue
                        continue;

                    if (x < minx || x > maxx) // line doesn't touch column
                        continue;

                    // The cell that contains the intersection point is always
                    // added

                    AddBlockLine(blocklists, blockcount, blockdone, ncols * yb
                            + j, i);

                    // if the intersection is at a corner it depends on the
                    // slope
                    // (and whether the line extends past the intersection)
                    // which
                    // blocks are hit

                    if (yp == 0) // intersection at a corner
                    {
                        if (sneg) // \ - blocks x,y-, x-,y
                        {
                            if (yb > 0 && miny < y)
                                AddBlockLine(blocklists, blockcount, blockdone,
                                    ncols * (yb - 1) + j, i);
                            if (j > 0 && minx < x)
                                AddBlockLine(blocklists, blockcount, blockdone,
                                    ncols * yb + j - 1, i);
                        } else if (spos) // / - block x-,y-
                        {
                            if (yb > 0 && j > 0 && minx < x)
                                AddBlockLine(blocklists, blockcount, blockdone,
                                    ncols * (yb - 1) + j - 1, i);
                        } else if (horiz) // - - block x-,y
                        {
                            if (j > 0 && minx < x)
                                AddBlockLine(blocklists, blockcount, blockdone,
                                    ncols * yb + j - 1, i);
                        }
                    } else if (j > 0 && minx < x) // else not at corner: x-,y
                        AddBlockLine(blocklists, blockcount, blockdone, ncols
                                * yb + j - 1, i);
                }
            }

            // For each row, see where the line along its bottom edge, which
            // it contains, intersects the Linedef i. Add i to all the
            // corresponding
            // blocklists.

            if (!horiz) {
                for (int j = 0; j < nrows; j++) {
                    // intersection of Linedef with y=yorg+(j<<blkshift)
                    // (x,y) on Linedef i satisfies: (y-y1)*dx = dy*(x-x1)
                    // x = dx*(y-y1)/dy+x1;

                    int y = yorg + (j << blkshift); // (x,y) is intersection
                    int x = (dx * (y - y1)) / dy + x1;
                    int xb = (x - xorg) >> blkshift; // block column number
                    int xp = (x - xorg) & blkmask; // x position within block

                    if (xb < 0 || xb > ncols - 1) // outside blockmap, continue
                        continue;

                    if (y < miny || y > maxy) // line doesn't touch row
                        continue;

                    // The cell that contains the intersection point is always
                    // added

                    AddBlockLine(blocklists, blockcount, blockdone, ncols * j
                            + xb, i);

                    // if the intersection is at a corner it depends on the
                    // slope
                    // (and whether the line extends past the intersection)
                    // which
                    // blocks are hit

                    if (xp == 0) // intersection at a corner
                    {
                        if (sneg) // \ - blocks x,y-, x-,y
                        {
                            if (j > 0 && miny < y)
                                AddBlockLine(blocklists, blockcount, blockdone,
                                    ncols * (j - 1) + xb, i);
                            if (xb > 0 && minx < x)
                                AddBlockLine(blocklists, blockcount, blockdone,
                                    ncols * j + xb - 1, i);
                        } else if (vert) // | - block x,y-
                        {
                            if (j > 0 && miny < y)
                                AddBlockLine(blocklists, blockcount, blockdone,
                                    ncols * (j - 1) + xb, i);
                        } else if (spos) // / - block x-,y-
                        {
                            if (xb > 0 && j > 0 && miny < y)
                                AddBlockLine(blocklists, blockcount, blockdone,
                                    ncols * (j - 1) + xb - 1, i);
                        }
                    } else if (j > 0 && miny < y) // else not on a corner: x,y-
                        AddBlockLine(blocklists, blockcount, blockdone, ncols
                                * (j - 1) + xb, i);
                }
            }
        }

        // Add initial 0 to all blocklists
        // count the total number of lines (and 0's and -1's)

        C2JUtils.memset(blockdone, false, NBlocks);

        linetotal = 0;

        for (int i = 0; i < NBlocks; i++) {
            AddBlockLine(blocklists, blockcount, blockdone, i, 0);
            linetotal += blockcount[i];
        }

        // Create the blockmap lump

        // blockmaplump = malloc_IfSameLevel(blockmaplump, 4 + NBlocks +
        // linetotal);
        blockmaplump = new int[(int) (4 + NBlocks + linetotal)];
        // blockmap header

        blockmaplump[0] = bmaporgx = xorg << FRACBITS;
        blockmaplump[1] = bmaporgy = yorg << FRACBITS;
        blockmaplump[2] = bmapwidth = ncols;
        blockmaplump[3] = bmapheight = nrows;

        // offsets to lists and block lists

        for (int i = 0; i < NBlocks; i++) {
            linelist_t bl = blocklists[i];
            int offs =
                blockmaplump[4 + i] = // set offset to block's list
                    (i != 0 ? blockmaplump[4 + i - 1] : 4 + NBlocks)
                            + (i != 0 ? blockcount[i - 1] : 0);

            // add the lines in each block's list to the blockmaplump
            // delete each list node as we go

            while (bl != null) {
                linelist_t tmp = bl.next;
                blockmaplump[offs++] = bl.num;
                bl = tmp;
            }
        }

        long b = System.nanoTime();

        System.err.printf("Blockmap generated in %f sec\n", (b - a) / 1e9);
        System.err.printf("Time spend in AddBlockLine : %f sec\n", total / 1e9);
    }

    // jff 10/6/98
    // End new code added to speed up calculation of internal blockmap

    //
    // P_VerifyBlockMap
    //
    // haleyjd 03/04/10: do verification on validity of blockmap.
    //
    protected boolean VerifyBlockMap(int count) {
        int x, y;
        int p_maxoffs = count;

        for (y = 0; y < bmapheight; y++) {
            for (x = 0; x < bmapwidth; x++) {
                int offset;
                int p_list;
                int tmplist;
                int blockoffset;

                offset = y * bmapwidth + x;
                blockoffset = offset + 4; // That's where the shit starts.

                // check that block offset is in bounds
                if (blockoffset >= p_maxoffs) {
                    System.err
                            .printf("P_VerifyBlockMap: block offset overflow\n");
                    return false;
                }

                offset = blockmaplump[blockoffset];

                // check that list offset is in bounds
                if (offset < 4 || offset >= count) {
                    System.err
                            .printf("P_VerifyBlockMap: list offset overflow\n");
                    return false;
                }

                p_list = offset;

                // scan forward for a -1 terminator before maxoffs
                for (tmplist = p_list;; tmplist++) {
                    // we have overflowed the lump?
                    if (tmplist >= p_maxoffs) {
                        System.err.printf("P_VerifyBlockMap: open blocklist\n");
                        return false;
                    }
                    if (blockmaplump[tmplist] == -1) // found -1
                        break;
                }

                // scan the list for out-of-range linedef indicies in list
                for (tmplist = p_list; blockmaplump[tmplist] != -1; tmplist++) {
                    if (blockmaplump[tmplist] < 0
                            || blockmaplump[tmplist] >= numlines) {
                        System.err
                                .printf("P_VerifyBlockMap: index >= numlines\n");
                        return false;
                    }
                }
            }
        }

        return true;
    }

    // cph - convenient sub-function
    protected void AddLineToSector(line_t li, sector_t sector) {
        int[] bbox = sector.blockbox;

        sector.lines[sector.linecount++] = li;
        BBox.AddToBox(bbox, li.v1.x, li.v1.y);
        BBox.AddToBox(bbox, li.v2.x, li.v2.y);
    }

    /**
     * Compute density of reject table. Aids choosing LUT optimizations.
     * 
     * @return
     */

    protected float rejectDensity() {
        // float[] rowdensity=new float[numsectors];
        float tabledensity;
        int tcount = 0;

        for (int i = 0; i < numsectors; i++) {
            // int colcount=0;
            for (int j = 0; j < numsectors; j++) {
                // Determine subsector entries in REJECT table.
                int pnum = i * numsectors + j;
                int bytenum = pnum >> 3;
                int bitnum = 1 << (pnum & 7);

                // Check in REJECT table.
                if (!flags(rejectmatrix[bytenum], bitnum)) {
                    tcount++;
                    // colcount++;
                }
            }
            // rowdensity[i]=((float)colcount/numsectors);
        }

        tabledensity = (float) tcount / (numsectors * numsectors);
        return tabledensity;

    }

    protected static int[] POKE_REJECT = new int[] { 1, 2, 4, 8, 16, 32, 64,
            128 };

    /**
     * Updates Reject table dynamically based on what expensive LOS checks say.
     * It does decrease the "reject density" the longer the level runs, however
     * its by no means perfect, and results in many sleeping monsters. When
     * called, visibility between sectors x and y will be set to "false" for the
     * rest of the level, aka they will be rejected based on subsequent sight
     * checks.
     * 
     * @param x
     * @param y
     */

    protected void pokeIntoReject(int x, int y) {
        // Locate bit pointer e.g. for a 4x4 table, x=2 and y=3 give
        // 3*4+2=14
        final int pnum = y * numsectors + x;

        // Which byte?
        // 14= 1110 >>3 = 0001 so
        // Byte 0 Byte 1
        // xxxxxxxx xxxxxxxx
        // ^
        // 0.....bits......16
        // We are writing inside the second Byte 1
        final int bytenum = pnum >> 3;

        // OK, so how we pinpoint that one bit?
        // 1110 & 0111 = 0110 = 6 so it's the sixth bit
        // of the second byte
        final int bitnum = pnum & 7;

        // This sets only that one bit, and the reject lookup will be faster
        // next time.
        rejectmatrix[bytenum] |= POKE_REJECT[bitnum];

        System.out.println(rejectDensity());

    }

    protected void retrieveFromReject(int x, int y, boolean value) {
        // Locate bit pointer e.g. for a 4x4 table, x=2 and y=3 give
        // 3*4+2=14
        final int pnum = y * numsectors + x;

        // Which byte?
        // 14= 1110 >>3 = 0001 so
        // Byte 0 Byte 1
        // xxxxxxxx xxxxxxxx
        // ^
        // 0.....bits......16
        // We are writing inside the second Byte 1
        final int bytenum = pnum >> 3;

        // OK, so how we pinpoint that one bit?
        // 1110 & 0111 = 0110 = 6 so it's the sixth bit
        // of the second byte
        final int bitnum = pnum & 7;

        // This sets only that one bit, and the reject lookup will be faster
        // next time.
        rejectmatrix[bytenum] |= POKE_REJECT[bitnum];

        System.out.println(rejectDensity());

    }

    // Keeps track of lines that belong to a sector, to exclude e.g.
    // orphaned ones from the blockmap.
    protected boolean[] used_lines;

    // MAES: extensions to support 512x512 blockmaps.
    // They represent the maximum negative number which represents
    // a positive offset, otherwise they are left at -257, which
    // never triggers a check.
    // If a blockmap index is ever LE than either, then
    // its actual value is to be interpreted as 0x01FF&x.
    // Full 512x512 blockmaps get this value set to -1.
    // A 511x511 blockmap would still have a valid negative number
    // e.g. -1..510, so they would be set to -2
    
    public int blockmapxneg=-257;
    public int blockmapyneg=-257;

    /**
     * Returns an int[] array with orgx, orgy, and number of blocks. Order is:
     * orgx,orgy,bckx,bcky
     * 
     * @return
     */

    protected final int[] getMapBoundingBox(boolean playable) {

        int minx = Integer.MAX_VALUE;
        int miny = Integer.MAX_VALUE;
        int maxx = Integer.MIN_VALUE;
        int maxy = Integer.MIN_VALUE;

        // Scan linedefs to detect extremes

        for (int i = 0; i < this.lines.length; i++) {

            if (playable || used_lines[i]) {
                if (lines[i].v1x > maxx)
                    maxx = lines[i].v1x;
                if (lines[i].v1x < minx)
                    minx = lines[i].v1x;
                if (lines[i].v1y > maxy)
                    maxy = lines[i].v1y;
                if (lines[i].v1y < miny)
                    miny = lines[i].v1y;
                if (lines[i].v2x > maxx)
                    maxx = lines[i].v2x;
                if (lines[i].v2x < minx)
                    minx = lines[i].v2x;
                if (lines[i].v2y > maxy)
                    maxy = lines[i].v2y;
                if (lines[i].v2y < miny)
                    miny = lines[i].v2y;
            }
        }

        System.err.printf("Map bounding %d %d %d %d\n", minx >> FRACBITS,
            miny >> FRACBITS, maxx >> FRACBITS, maxy >> FRACBITS);

        // Blow up bounding to the closest 128-sized block, adding 8 units as
        // padding.
        // This seems to be the "official" formula.
        int orgx = -BLOCKMAPPADDING + MAPBLOCKUNITS * (minx / MAPBLOCKUNITS);
        int orgy = -BLOCKMAPPADDING + MAPBLOCKUNITS * (miny / MAPBLOCKUNITS);
        int bckx = ((BLOCKMAPPADDING + maxx) - orgx);
        int bcky = ((BLOCKMAPPADDING + maxy) - orgy);

        System.err.printf("%d %d %d %d\n", orgx >> FRACBITS, orgy >> FRACBITS,
            1 + (bckx >> MAPBLOCKSHIFT), 1 + (bcky >> MAPBLOCKSHIFT));

        return new int[] { orgx, orgy, bckx, bcky };
    }

    protected void LoadReject(int lumpnum) {
        byte[] tmpreject = new byte[0];

        // _D_: uncommented the rejectmatrix variable, this permitted changing
        // level to work
        try {
            tmpreject = W.CacheLumpNumAsRawBytes(lumpnum, PU_LEVEL);
        } catch (Exception e) {
            // Any exception at this point means missing REJECT lump. Fuck that,
            // and move on.
            // If everything goes OK, tmpreject will contain the REJECT lump's
            // data
            // BUT, alas, we're not done yet.
        }

        // Sanity check on matrix.
        // E.g. a 5-sector map will result in ceil(25/8)=4 bytes.
        // If the reject table is broken/corrupt, too bad. It will all be
        // zeroes.
        // Much better than overflowing.
        // TODO: build-in a REJECT-matrix rebuilder?
        rejectmatrix =
            new byte[(int) (Math
                    .ceil((this.numsectors * this.numsectors) / 8.0))];
        System.arraycopy(tmpreject, 0, rejectmatrix, 0,
            Math.min(tmpreject.length, rejectmatrix.length));

        // Do warn on atypical reject map lengths, but use either default
        // all-zeroes one,
        // or whatever you happened to read anyway.
        if (tmpreject.length < rejectmatrix.length)
            System.err.printf("BROKEN REJECT MAP! Length %d expected %d\n",
                tmpreject.length, rejectmatrix.length);

        // Maes: purely academic. Most maps are well above 0.68
        // System.out.printf("Reject table density: %f",rejectDensity());
    }
    
    /** Gets the proper blockmap block for a given X 16.16 Coordinate, sanitized
     *  for 512-wide blockmaps. 
     * 
     * @param blockx
     * @return
     */

    public final int getSafeBlockX(int blockx){
        blockx>>=MAPBLOCKSHIFT;
        return (blockx<=this.blockmapxneg)?blockx&0x1FF:blockx;
    }
    
    public final int getSafeBlockX(long blockx){
        blockx>>=MAPBLOCKSHIFT;
        return (int) ((blockx<=this.blockmapxneg)?blockx&0x1FF:blockx);
    }
    
    /** Gets the proper blockmap block for a given Y 16.16 Coordinate, sanitized
     *  for 512-wide blockmaps. 
     * 
     * @param blocky
     * @return     */

    
    public final int getSafeBlockY(int blocky){
        blocky>>=MAPBLOCKSHIFT;
        return (blocky<=this.blockmapyneg)?blocky&0x1FF:blocky;
    }
    
    public final int getSafeBlockY(long blocky){
        blocky>>=MAPBLOCKSHIFT;
        return (int) ((blocky<=this.blockmapyneg)?blocky&0x1FF:blocky);
    }

    /// Sector tag stuff, lifted off Boom
    
  /** Hash the sector tags across the sectors and linedefs.
   *  Call in SpawnSpecials.
   */
    
    public void InitTagLists()
    {
      int i;

      for (i=numsectors; --i>=0; )        // Initially make all slots empty.
        sectors[i].firsttag = -1;
      for (i=numsectors; --i>=0; )        // Proceed from last to first sector
        {                                 // so that lower sectors appear first
          int j = sectors[i].tag % numsectors; // Hash func
          sectors[i].nexttag = sectors[j].firsttag;   // Prepend sector to chain
          sectors[j].firsttag = i;
        }

      // killough 4/17/98: same thing, only for linedefs

      for (i=numlines; --i>=0; )        // Initially make all slots empty.
        lines[i].firsttag = -1;
      for (i=numlines; --i>=0; )        // Proceed from last to first linedef
        {                               // so that lower linedefs appear first
          int j = lines[i].tag % numlines; // Hash func
          lines[i].nexttag = lines[j].firsttag;   // Prepend linedef to chain
          lines[j].firsttag = i;
        }
    }
    
}

package p;

//
// P_PLATS
//

public enum plat_e {
        up,
        down,
        waiting,
        in_stasis
        } 

package p;

public class slideframe_t {
	int[] frontFrames = new int[4];
	int[] backFrames = new int[4];

}
package p;

import static m.fixed_t.FixedDiv;
import static m.fixed_t.FixedMul;
import static utils.C2JUtils.eval;

public class MapUtils {


   /**
   *  AproxDistance
   * Gives an estimation of distance (not exact)
   *
   * @param dx fixed_t
   * @param dy fixed_t
   * @return fixed_t
   */
  //
  public static int
  AproxDistance
  ( int   dx,
  int   dy )
  {
   dx = Math.abs(dx);
   dy = Math.abs(dy);
   if (dx < dy)
   return dx+dy-(dx>>1);
   return dx+dy-(dy>>1);
  }
  
  /**
   * P_InterceptVector
   * Returns the fractional intercept point
   * along the first divline.
   * This is only called by the addthings
   * and addlines traversers.
   * 
   * @return int to be treated as fixed_t
   */

  public static int 
  InterceptVector
  ( divline_t    v2,
  divline_t    v1 )
  {
   int frac, num,den; // fixed_t
   
   den = FixedMul (v1.dy>>8,v2.dx) - FixedMul(v1.dx>>8,v2.dy);

   if (den == 0)
   return 0;
   //  I_Error ("P_InterceptVector: parallel");
   
   num =
   FixedMul ( (v1.x - v2.x)>>8 ,v1.dy )
   +FixedMul ( (v2.y - v1.y)>>8, v1.dx );

   frac = FixedDiv (num , den);

   return frac;
  /*
   #else   // UNUSED, float debug.
   float   frac;
   float   num;
   float   den;
   float   v1x;
   float   v1y;
   float   v1dx;
   float   v1dy;
   float   v2x;
   float   v2y;
   float   v2dx;
   float   v2dy;

   v1x = (float)v1.x/FRACUNIT;
   v1y = (float)v1.y/FRACUNIT;
   v1dx = (float)v1.dx/FRACUNIT;
   v1dy = (float)v1.dy/FRACUNIT;
   v2x = (float)v2.x/FRACUNIT;
   v2y = (float)v2.y/FRACUNIT;
   v2dx = (float)v2.dx/FRACUNIT;
   v2dy = (float)v2.dy/FRACUNIT;
   
   den = v1dy*v2dx - v1dx*v2dy;

   if (den == 0)
   return 0;   // parallel
   
   num = (v1x - v2x)*v1dy + (v2y - v1y)*v1dx;
   frac = num / den;

   return frac*FRACUNIT;
  #endif */
  }
  

  /* cph - this is killough's 4/19/98 version of P_InterceptVector and
   *  P_InterceptVector2 (which were interchangeable). We still use this
   *  in compatibility mode. */
  private static final int P_InterceptVector2(final divline_t v2, final divline_t v1)
  {
    int  den;
    return eval(den = FixedMul(v1.dy>>8, v2.dx) - FixedMul(v1.dx>>8, v2.dy)) ?
      FixedDiv(FixedMul((v1.x - v2.x)>>8, v1.dy) +
               FixedMul((v2.y - v1.y)>>8, v1.dx), den) : 0;
  }

  
  /** Used by CrossSubSector
   * 
   * @param v2
   * @param v1
   * @return
   */
  public static final int P_InterceptVector(final divline_t v2, final divline_t v1)
  {
    if (false/*compatibility_level < prboom_4_compatibility*/)
      return P_InterceptVector2(v2, v1);
    else {
      /* cph - This was introduced at prboom_4_compatibility - no precision/overflow problems */
      long den = (long)v1.dy * v2.dx - (long)v1.dx * v2.dy;
      den >>= 16;
      if (!eval(den))
        return 0;
      return (int)(((long)(v1.x - v2.x) * v1.dy - (long)(v1.y - v2.y) * v1.dx) / den);
    }
  }
  
  /**
   * P_InterceptVector2 Returns the fractional intercept point along the
   * first divline. This is only called by the addthings and addlines
   * traversers.
   * 
   * @param v2
   * @param v1
   * @returnP_InterceptVector2
   */

  public static final int InterceptVector2(divline_t v2, divline_t v1) {
      int frac; // fixed_t
      int num; // fixed_t
      int den; // fixed_t

      den = FixedMul(v1.dy >> 8, v2.dx) - FixedMul(v1.dx >> 8, v2.dy);

      if (den == 0)
          return 0;
      // I_Error ("P_InterceptVector: parallel");

      num =
          FixedMul((v1.x - v2.x) >> 8, v1.dy)
                  + FixedMul((v2.y - v1.y) >> 8, v1.dx);
      frac = FixedDiv(num, den);

      return frac;
  }
  
}

package p;

import static m.fixed_t.MAPFRACUNIT;
import static data.Defines.TIC_MUL;

public final class ChaseDirections {

    public static final int DI_EAST = 0;

    public static final int DI_NORTHEAST = 1;

    public static final int DI_NORTH = 2;

    public static final int DI_NORTHWEST = 3;

    public static final int DI_WEST = 4;

    public static final int DI_SOUTHWEST = 5;

    public static final int DI_SOUTH = 6;

    public static final int DI_SOUTHEAST = 7;

    public static final int DI_NODIR = 8;

    public static final int NUMDIR = 9;
    
    //
    // P_NewChaseDir related LUT.
    //
    public final static int opposite[] =
        { DI_WEST, DI_SOUTHWEST, DI_SOUTH, DI_SOUTHEAST, DI_EAST, DI_NORTHEAST,
                DI_NORTH, DI_NORTHWEST, DI_NODIR };

    public final static int diags[] =
        { DI_NORTHWEST, DI_NORTHEAST, DI_SOUTHWEST, DI_SOUTHEAST };

    public final static int[] xspeed =
        { MAPFRACUNIT, 47000/TIC_MUL, 0, -47000/TIC_MUL, -MAPFRACUNIT, -47000/TIC_MUL, 0, 47000/TIC_MUL }; // all
                                                                     // fixed

    public final static int[] yspeed =
        { 0, 47000/TIC_MUL, MAPFRACUNIT, 47000/TIC_MUL, 0, -47000/TIC_MUL, -MAPFRACUNIT, -47000/TIC_MUL }; // all
                                                                     // fixed
    
}

package p;

public interface ISightChecker {

    public void setZStartTopBOttom(int zstart, int top, int bottom);
    public void setSTrace(mobj_t t1, mobj_t t2);
    public boolean CrossBSPNode(int bspnum);
    
}

package p;

import defines.*;
import static p.ChaseDirections.*;
import static p.DoorDefines.*;
import static data.Defines.BASETHRESHOLD;
import static data.Defines.FLOATSPEED;
import static data.Defines.ITEMQUESIZE;
import static data.Defines.MAPBLOCKSHIFT;
import static data.Defines.MAPBLOCKSIZE;
import static data.Defines.MAPBTOFRAC;
import static data.Defines.MELEERANGE;
import static data.Defines.MISSILERANGE;
import static rr.line_t.*;
import static data.Defines.MTF_AMBUSH;
import static data.Defines.NUMCARDS;
import static data.Defines.ONCEILINGZ;
import static data.Defines.ONFLOORZ;
import static data.Defines.PST_DEAD;
import static data.Defines.PST_LIVE;
import static data.Defines.PST_REBORN;
import static data.Defines.PT_ADDLINES;
import static data.Defines.PT_ADDTHINGS;
import static data.Defines.PT_EARLYOUT;
import static data.Defines.USERANGE;
import static data.Defines.VIEWHEIGHT;
import static data.Defines.pw_invulnerability;
import static data.Limits.CEILSPEED;
import static data.Limits.MAXCEILINGS;
import static data.Limits.MAXINT;
import static data.Limits.MAXMOVE;
import static data.Limits.MAXPLAYERS;
import static data.Limits.MAXRADIUS;
import static data.Limits.MAXSPECIALCROSS;
import static data.Limits.NUMMOBJTYPES;
import static data.Tables.*;
import static data.info.mobjinfo;
import static data.info.states;
import static doom.englsh.PD_BLUEK;
import static doom.englsh.PD_BLUEO;
import static doom.englsh.PD_REDK;
import static doom.englsh.PD_REDO;
import static doom.englsh.PD_YELLOWK;
import static doom.englsh.PD_YELLOWO;
import static m.BBox.BOXBOTTOM;
import static m.BBox.BOXLEFT;
import static m.BBox.BOXRIGHT;
import static m.BBox.BOXTOP;
import static m.fixed_t.FRACBITS;
import static m.fixed_t.FRACUNIT;
import static m.fixed_t.MAPFRACUNIT;
import static m.fixed_t.FixedDiv;
import static m.fixed_t.FixedMul;
import static p.ChaseDirections.DI_EAST;
import static p.ChaseDirections.DI_NODIR;
import static p.ChaseDirections.DI_NORTH;
import static p.ChaseDirections.DI_SOUTH;
import static p.ChaseDirections.DI_SOUTHEAST;
import static p.ChaseDirections.DI_WEST;
import static p.ChaseDirections.diags;
import static p.ChaseDirections.opposite;
import static p.MapUtils.AproxDistance;
import static utils.C2JUtils.*;

import static p.mobj_t.*;
import rr.line_t;
import rr.sector_t;
import rr.side_t;
import rr.subsector_t;
import utils.C2JUtils;
import data.Tables;
import data.mapthing_t;
import data.mobjinfo_t;
import data.mobjtype_t;
import data.state_t;
import data.sounds.sfxenum_t;
import doom.DoomStatus;
import doom.player_t;
import doom.think_t;
import doom.thinker_t;
import doom.weapontype_t;

/** Action functions need to be aware of:
    *  Random number generator (RND)
    *  Broader gameplay context (DoomGame)
    *  Some door actions
    *  Playfield checks.
    *  etc. etc. so they COULD be made into a separate file
    * 
    * @author admin
    *
    */

public class Actions extends UnifiedGameMap {
      
      //
      // CEILINGS
      //

      private ceiling_t[]  activeceilings=new ceiling_t[MAXCEILINGS];

      /** This needs to be called before loading, otherwise
       *  crushers won't be able to be restarted. 
       */
      public void ClearCeilingsBeforeLoading(){

    	  this.activeceilings=new ceiling_t[MAXCEILINGS];
          
    	  /*
    	  for (int i = 0; i < MAXPLATS; i++) {
                  this.activeceilings[i] = null;
              } */
      }
      
      
      /**
       * T_MoveCeiling
       */

      protected void MoveCeiling (ceiling_t ceiling)
      {
          result_e    res;
          
          switch(ceiling.direction)
          {
            case 0:
          // IN STASIS
          break;
            case 1:
          // UP
          res = MovePlane(ceiling.sector,
                    ceiling.speed,
                    ceiling.topheight,
                    false,1,ceiling.direction);
          
          if (!eval(DM.leveltime&7))
          {
              switch(ceiling.type)
              {
                case silentCrushAndRaise:
              break;
                default:
              S.StartSound(ceiling.sector.soundorg,sfxenum_t.sfx_stnmov);
                       
              // ?
              break;
              }
          }
          
          if (res == result_e.pastdest)
          {
              switch(ceiling.type)
              {
                case raiseToHighest:
              RemoveActiveCeiling(ceiling);
              break;
              
                case silentCrushAndRaise:
                    S.StartSound(ceiling.sector.soundorg, sfxenum_t.sfx_pstop);
                case fastCrushAndRaise:
                case crushAndRaise:
              ceiling.direction = -1;
              break;
              
                default:
              break;
              }
              
          }
          break;
          
            case -1:
          // DOWN
          res = MovePlane(ceiling.sector,
                    ceiling.speed,
                    ceiling.bottomheight,
                    ceiling.crush,1,ceiling.direction);
          
          if (!eval(DM.leveltime&7))
          {
              switch(ceiling.type)
              {
                case silentCrushAndRaise: break;
                default:
                	S.StartSound(ceiling.sector.soundorg,  sfxenum_t.sfx_stnmov);
                     
              }
          }
          
          if (res == result_e.pastdest)
          {
              switch(ceiling.type)
              {
                case silentCrushAndRaise:
                	S.StartSound(ceiling.sector.soundorg, sfxenum_t.sfx_pstop);
                case crushAndRaise:
              ceiling.speed = CEILSPEED;
                case fastCrushAndRaise:
              ceiling.direction = 1;
              break;

                case lowerAndCrush:
                case lowerToFloor:
              RemoveActiveCeiling(ceiling);
              break;

                default:
              break;
              }
          }
          else // ( res != result_e.pastdest )
          {
              if (res == result_e.crushed)
              {
              switch(ceiling.type)
              {
                case silentCrushAndRaise:
                case crushAndRaise:
                case lowerAndCrush:
                  ceiling.speed = CEILSPEED / 8;
                  break;

                default:
                  break;
              }
              }
          }
          break;
          }
      }


      

      //
      // Add an active ceiling
      //
      public void AddActiveCeiling(ceiling_t c)
      {
          int     i;
          
          for (i = 0; i < getMaxCeilings();i++)
          {
          if (getActiveCeilings()[i] == null)
          {
              getActiveCeilings()[i] = c;
              return;
          }
          }
          // Needs rezising
          setActiveceilings(C2JUtils.resize(c, getActiveCeilings(), 2*getActiveCeilings().length));
      }



      //
      // Remove a ceiling's thinker
      //
      protected void RemoveActiveCeiling(ceiling_t c)
      {
          int     i;
          
          for (i = 0;i < getMaxCeilings();i++)
          {
          if (getActiveCeilings()[i] == c)
          {
              getActiveCeilings()[i].sector.specialdata = null;
              RemoveThinker (getActiveCeilings()[i]);
              getActiveCeilings()[i] = null;
              break;
          }
          }
      }



      //
      // Restart a ceiling that's in-stasis
      //
      protected void ActivateInStasisCeiling(line_t line)
      {
          int     i;
          
          for (i = 0;i < getMaxCeilings();i++)
          {
          if (getActiveCeilings()[i]!=null
              && (getActiveCeilings()[i].tag == line.tag)
              && (getActiveCeilings()[i].direction == 0))
          {
              getActiveCeilings()[i].direction = getActiveCeilings()[i].olddirection;
              getActiveCeilings()[i].function
                = think_t.T_MoveCeiling;
              FUNS.doWireThinker( getActiveCeilings()[i]);
          }
          }
      }
      
      

      /**
       * MOVE A FLOOR TO IT'S DESTINATION (UP OR DOWN)
       *
       */
      protected void MoveFloor(floormove_t floor)
      {
          result_e    res;
          
          res = MovePlane(floor.sector,
                    floor.speed,
                    floor.floordestheight,
                    floor.crush,0,floor.direction);
          
          if (!eval(DM.leveltime&7))
              
        	  S.StartSound(floor.sector.soundorg,  sfxenum_t.sfx_stnmov);
          
          if (res == result_e.pastdest)
          {
          floor.sector.specialdata = null;

          if (floor.direction == 1)
          {
              switch(floor.type)
              {
                case donutRaise:
              floor.sector.special = (short) floor.newspecial;
              floor.sector.floorpic = floor.texture;
                default:
              break;
              }
          }
          else if (floor.direction == -1)
          {
              switch(floor.type) //TODO: check if a null floor.type is valid or a bug 
              // MAES: actually, type should always be set to something.
              // In C, this means "zero" or "null". In Java, we must make sure
              // it's actually set to something all the time.
              {
                case lowerAndChange:
              floor.sector.special = (short) floor.newspecial;
              floor.sector.floorpic = floor.texture;
                default:
              break;
              }
          }
          RemoveThinker(floor);

          S.StartSound(floor.sector.soundorg, sfxenum_t.sfx_pstop);
          }

      }

      //
      // EV.DoCeiling
      // Move a ceiling up/down and all around!
      //
      protected boolean DoCeiling( line_t   line, ceiling_e type )
      {
          int     secnum=-1;
          boolean     rtn=false;
          sector_t   sec;
          ceiling_t  ceiling;
                   
          //  Reactivate in-stasis ceilings...for certain types.
          switch(type)
          {
            case fastCrushAndRaise:
            case silentCrushAndRaise:
            case crushAndRaise:
          ActivateInStasisCeiling(line);
            default:
          break;
          }
          
          while ((secnum = FindSectorFromLineTag(line,secnum)) >= 0)
          {
          sec = LL.sectors[secnum];
          if (sec.specialdata!=null)
              continue;
          
          // new door thinker
          rtn = true;
          ceiling = new ceiling_t();
          sec.specialdata = ceiling;
          ceiling.function = think_t.T_MoveCeiling;
          AddThinker (ceiling);
          ceiling.sector = sec;
          ceiling.crush = false;
          
          switch(type)
          {
            case fastCrushAndRaise:
              ceiling.crush = true;
              ceiling.topheight = sec.ceilingheight;
              ceiling.bottomheight = sec.floorheight + (8*FRACUNIT);
              ceiling.direction = -1;
              ceiling.speed = CEILSPEED * 2;
              break;

            case silentCrushAndRaise:
            case crushAndRaise:
              ceiling.crush = true;
              ceiling.topheight = sec.ceilingheight;
            case lowerAndCrush:
            case lowerToFloor:
              ceiling.bottomheight = sec.floorheight;
              if (type != ceiling_e.lowerToFloor)
              ceiling.bottomheight += 8*FRACUNIT;
              ceiling.direction = -1;
              ceiling.speed = CEILSPEED;
              break;

            case raiseToHighest:
              ceiling.topheight = sec.FindHighestCeilingSurrounding();
              ceiling.direction = 1;
              ceiling.speed = CEILSPEED;
              break;
          }
              
          ceiling.tag = sec.tag;
          ceiling.type = type;
          AddActiveCeiling(ceiling);
          }
          return rtn;
      }

      
      /**
       * Special Stuff that can not be categorized
       * 
       * (I'm sure it has something to do with John Romero's obsession
       *  with fucking stuff and making them his bitches).
       *
       *@param line
       * 
       */
      protected boolean DoDonut(line_t  line)
      {
          sector_t       s1;
          sector_t       s2;
          sector_t       s3;
          int         secnum;
          boolean         rtn;
          int         i;
          floormove_t    floor;
          
          secnum = -1;
          rtn = false;
          while ((secnum = FindSectorFromLineTag(line,secnum)) >= 0)
          {
          s1 = LL.sectors[secnum];
              
          // ALREADY MOVING?  IF SO, KEEP GOING...
          if (s1.specialdata!=null)
              continue;
                  
          rtn = true;
          s2 = s1.lines[0].getNextSector(s1);
          for (i = 0;i < s2.linecount;i++)
          {
              if ((!eval(s2.lines[i].flags & ML_TWOSIDED)) ||
              (s2.lines[i].backsector == s1))
              continue;
              s3 = s2.lines[i].backsector;
              
              //  Spawn rising slime
              floor = new floormove_t();
              s2.specialdata = floor;
              floor.function = think_t.T_MoveFloor;
              AddThinker (floor);
              floor.type = floor_e.donutRaise;
              floor.crush = false;
              floor.direction = 1;
              floor.sector = s2;
              floor.speed = FLOORSPEED / 2;
              floor.texture = s3.floorpic;
              floor.newspecial = 0;
              floor.floordestheight = s3.floorheight;
              
              //  Spawn lowering donut-hole
              floor = new floormove_t();
              s1.specialdata = floor;
              floor.function = think_t.T_MoveFloor;
              AddThinker (floor);
              floor.type = floor_e.lowerFloor;
              floor.crush = false;
              floor.direction = -1;
              floor.sector = s1;
              floor.speed = FLOORSPEED / 2;
              floor.floordestheight = s3.floorheight;
              break;
          }
          }
          return rtn;
      }
      
      //
      // HANDLE FLOOR TYPES
      //
      protected boolean DoFloor( line_t   line,floor_e   floortype )
      {
          int         secnum=-1;
          boolean         rtn=false;
          int         i;
          sector_t       sec;
          floormove_t    floor;

          while ((secnum = FindSectorFromLineTag(line,secnum)) >= 0)
          {
          sec = LL.sectors[secnum];
              
          // ALREADY MOVING?  IF SO, KEEP GOING...
          if (sec.specialdata!=null)
              continue;
          
          // new floor thinker
          rtn = true;
          floor = new floormove_t();
          sec.specialdata = floor;
          floor.function = think_t.T_MoveFloor;
          AddThinker (floor);
          floor.type = floortype;
          floor.crush = false;

          switch(floortype)
          {
            case lowerFloor:
              floor.direction = -1;
              floor.sector = sec;
              floor.speed = FLOORSPEED;
              floor.floordestheight = 
              sec.FindHighestFloorSurrounding();
              break;

            case lowerFloorToLowest:
              floor.direction = -1;
              floor.sector = sec;
              floor.speed = FLOORSPEED;
              floor.floordestheight = 
              sec.FindLowestFloorSurrounding();
              break;

            case turboLower:
              floor.direction = -1;
              floor.sector = sec;
              floor.speed = FLOORSPEED * 4;
              floor.floordestheight = 
              sec.FindHighestFloorSurrounding();
              if (floor.floordestheight != sec.floorheight)
              floor.floordestheight += 8*FRACUNIT;
              break;

            case raiseFloorCrush:
              floor.crush = true;
            case raiseFloor:
              floor.direction = 1;
              floor.sector = sec;
              floor.speed = FLOORSPEED;
              floor.floordestheight = 
              sec.FindLowestCeilingSurrounding();
              if (floor.floordestheight > sec.ceilingheight)
              floor.floordestheight = sec.ceilingheight;
              floor.floordestheight -= (8*FRACUNIT)*
              eval(floortype == floor_e.raiseFloorCrush);
              break;

            case raiseFloorTurbo:
              floor.direction = 1;
              floor.sector = sec;
              floor.speed = FLOORSPEED*4;
              floor.floordestheight = 
              sec.FindNextHighestFloor(sec.floorheight);
              break;

            case raiseFloorToNearest:
              floor.direction = 1;
              floor.sector = sec;
              floor.speed = FLOORSPEED;
              floor.floordestheight = 
              sec.FindNextHighestFloor(sec.floorheight);
              break;

            case raiseFloor24:
              floor.direction = 1;
              floor.sector = sec;
              floor.speed = FLOORSPEED;
              floor.floordestheight = floor.sector.floorheight +
              24 * FRACUNIT;
              break;
            case raiseFloor512:
              floor.direction = 1;
              floor.sector = sec;
              floor.speed = FLOORSPEED;
              floor.floordestheight = floor.sector.floorheight +
              512 * FRACUNIT;
              break;

            case raiseFloor24AndChange:
              floor.direction = 1;
              floor.sector = sec;
              floor.speed = FLOORSPEED;
              floor.floordestheight = floor.sector.floorheight +
              24 * FRACUNIT;
              sec.floorpic = line.frontsector.floorpic;
              sec.special = line.frontsector.special;
              break;

            case raiseToTexture:
            {
                int   minsize = MAXINT;
                side_t   side;
                      
                floor.direction = 1;
                floor.sector = sec;
                floor.speed = FLOORSPEED;
                for (i = 0; i < sec.linecount; i++)
                {
                if (twoSided (secnum, i) )
                {
                    side = getSide(secnum,i,0);
                    if (side.bottomtexture >= 0)
                    if (TM.getTextureheight(side.bottomtexture) < 
                        minsize)
                        minsize = 
                        TM.getTextureheight(side.bottomtexture);
                    side = getSide(secnum,i,1);
                    if (side.bottomtexture >= 0)
                    if (TM.getTextureheight(side.bottomtexture) < 
                        minsize)
                        minsize = 
                        TM.getTextureheight(side.bottomtexture);
                }
                }
                floor.floordestheight =
                floor.sector.floorheight + minsize;
            }
            break;
            
            case lowerAndChange:
              floor.direction = -1;
              floor.sector = sec;
              floor.speed = FLOORSPEED;
              floor.floordestheight = 
              sec.FindLowestFloorSurrounding();
              floor.texture = sec.floorpic;

              for (i = 0; i < sec.linecount; i++)
              {
              if ( twoSided(secnum, i) )
              {
                  if (getSide(secnum,i,0).sector.id == secnum)
                  {
                  sec = getSector(secnum,i,1);

                  if (sec.floorheight == floor.floordestheight)
                  {
                      floor.texture = sec.floorpic;
                      floor.newspecial = sec.special;
                      break;
                  }
                  }
                  else
                  {
                  sec = getSector(secnum,i,0);

                  if (sec.floorheight == floor.floordestheight)
                  {
                      floor.texture = sec.floorpic;
                      floor.newspecial = sec.special;
                      break;
                  }
                  }
              }
              }
            default:
              break;
          }
          }
          return rtn;
      }

      //
      // EV_CeilingCrushStop
      // Stop a ceiling from crushing!
      //
      int CeilingCrushStop(line_t  line)
      {
          int     i;
          int     rtn;
          
          rtn = 0;
          for (i = 0;i < getMaxCeilings();i++)
          {
          if (getActiveCeilings()[i]!=null
              && (getActiveCeilings()[i].tag == line.tag)
              && (getActiveCeilings()[i].direction != 0))
          {
              getActiveCeilings()[i].olddirection = getActiveCeilings()[i].direction;
              // MAES: don't set it to NOP here, otherwise its thinker will be
              // removed and it won't be possible to restart it.
              getActiveCeilings()[i].function = null;
              getActiveCeilings()[i].direction = 0;       // in-stasis
              rtn = 1;
          }
          }
          

          return rtn;
      }


      /**
       * BUILD A STAIRCASE!
       */
      
      protected boolean BuildStairs(line_t line, stair_e type )
      {
          int         secnum;
          int         height;
          int         i;
          int         newsecnum;
          int         texture;
          boolean         ok;
          boolean         rtn;
          
          sector_t       sec;
          sector_t       tsec;

          floormove_t    floor;
          
          int     stairsize = 0; 
          int     speed=0; // shut up compiler

          secnum = -1;
          rtn = false;
          while ((secnum = FindSectorFromLineTag(line,secnum)) >= 0)
          {
          sec = LL.sectors[secnum];
              
          // ALREADY MOVING?  IF SO, KEEP GOING...
          if (sec.specialdata!=null)
              continue;
          
          // new floor thinker
          rtn = true;
          floor = new floormove_t ();
          sec.specialdata = floor;
          floor.function = think_t.T_MoveFloor;
          AddThinker (floor);
          floor.direction = 1;
          floor.sector = sec;
          switch(type)
          {
            case build8:
              speed = FLOORSPEED/4;
              stairsize = 8*FRACUNIT;
              break;
            case turbo16:
              speed = FLOORSPEED*4;
              stairsize = 16*FRACUNIT;
              break;
          }
          floor.speed = speed;
          height = sec.floorheight + stairsize;
          floor.floordestheight = height;
              
          texture = sec.floorpic;
          
          // Find next sector to raise
          // 1.   Find 2-sided line with same sector side[0]
          // 2.   Other side is the next sector to raise
          do
          {
              ok = false;
              for (i = 0;i < sec.linecount;i++)
              {
              if ( !eval((sec.lines[i]).flags & ML_TWOSIDED) )
                  continue;
                          
              tsec = (sec.lines[i]).frontsector;
              newsecnum = tsec.id;
              
              if (secnum != newsecnum)
                  continue;

              tsec = (sec.lines[i]).backsector;
              newsecnum = tsec.id;

              if (tsec.floorpic != texture)
                  continue;
                          
              height += stairsize;

              if (tsec.specialdata!=null)
                  continue;
                          
              sec = tsec;
              secnum = newsecnum;
              floor = new floormove_t();
              sec.specialdata = floor;
              floor.function = think_t.T_MoveFloor;
              AddThinker (floor);
              floor.direction = 1;
              floor.sector = sec;
              floor.speed = speed;
              floor.floordestheight = height;
              ok = true;
              break;
              }
          } while(ok);
          }
          return rtn;
      }
      



      //
      // VERTICAL DOORS
      //

      /**
       * T_VerticalDoor
       */
      protected void VerticalDoor (vldoor_t door)
      {
          result_e   res;
         
          switch(door.direction)
          {
            case 0:
         // WAITING
         if (!eval(--door.topcountdown))
         {
             switch(door.type)
             {
               case blazeRaise:
             door.direction = -1; // time to go back down
             S.StartSound(door.sector.soundorg,sfxenum_t.sfx_bdcls);
             break;
             
               case normal:
             door.direction = -1; // time to go back down
             S.StartSound(door.sector.soundorg, sfxenum_t.sfx_dorcls);
             break;
             
               case close30ThenOpen:
             door.direction = 1;
             S.StartSound(door.sector.soundorg, sfxenum_t.sfx_doropn);
             break;
             
               default:
             break;
             }
         }
         break;
         
            case 2:
         //  INITIAL WAIT
         if (!eval(--door.topcountdown))
         {
             switch(door.type)
             {
               case raiseIn5Mins:
             door.direction = 1;
             door.type = vldoor_e.normal;
             S.StartSound(door.sector.soundorg, sfxenum_t.sfx_doropn);
             break;
             
               default:
             break;
             }
         }
         break;
         
            case -1:
         // DOWN
         res = MovePlane(door.sector,
                   door.speed,
                   door.sector.floorheight,
                   false,1,door.direction);
         if (res == result_e.pastdest)
         {
             switch(door.type)
             {
               case blazeRaise:
               case blazeClose:
             door.sector.specialdata = null;
             RemoveThinker (door);  // unlink and free
             S.StartSound(door.sector.soundorg, sfxenum_t.sfx_bdcls);
             break;
             
               case normal:
               case close:
             door.sector.specialdata = null;
             RemoveThinker (door);  // unlink and free
             break;
             
               case close30ThenOpen:
             door.direction = 0;
             door.topcountdown = 35*30;
             break;
             
               default:
             break;
             }
         }
         else if (res == result_e.crushed)
         {
             switch(door.type)
             {
               case blazeClose:
               case close:       // DO NOT GO BACK UP!
             break;
             
               default:
             door.direction = 1;
               S.StartSound(door.sector.soundorg, sfxenum_t.sfx_doropn);
             break;
             }
         }
         break;
         
            case 1:
         // UP
         res = MovePlane(door.sector,
                   door.speed,
                   door.topheight,
                   false,1,door.direction);
         
         if (res == result_e.pastdest)
         {
             switch(door.type)
             {
               case blazeRaise:
               case normal:
             door.direction = 0; // wait at top
             door.topcountdown = door.topwait;
             break;
             
               case close30ThenOpen:
               case blazeOpen:
               case open:
             door.sector.specialdata = null;
             RemoveThinker (door);  // unlink and free
             break;
             
               default:
             break;
             }
         }
         break;
          }
      }


      /**
       * EV_DoLockedDoor
       * Move a locked door up/down
       */

      protected boolean DoLockedDoor
      ( line_t   line,
        vldoor_e type,
        mobj_t   thing )
      {
          player_t   p;
         
          p = thing.player;
         
          if (p==null)
         return false;
             
          switch(line.special)
          {
            case 99: // Blue Lock
            case 133:
/*         if ( p==null )
             return false; */
         if (!p.cards[card_t.it_bluecard.ordinal()] && !p.cards[card_t.it_blueskull.ordinal()])
         {
             p.message = PD_BLUEO;
             S.StartSound(null,sfxenum_t.sfx_oof);
             return false;
         }
         break;
         
            case 134: // Red Lock
            case 135:
 /*        if ( p==null )
             return false; */
         if (!p.cards[card_t.it_redcard.ordinal()] && !p.cards[card_t.it_redskull.ordinal()])
         {
             p.message = PD_REDO;
             S.StartSound(null,sfxenum_t.sfx_oof);
             return false;
         }
         break;
         
            case 136:    // Yellow Lock
            case 137:
 /*        if ( p==null )
             return false; */
         if (!p.cards[card_t.it_yellowcard.ordinal()] &&
             !p.cards[card_t.it_yellowskull.ordinal()])
         {
             p.message = PD_YELLOWO;
             S.StartSound(null,sfxenum_t.sfx_oof);
             return false;
         }
         break;  
          }

          return DoDoor(line,type);
      }


      protected boolean DoDoor(line_t line,vldoor_e type)
      {
          int        secnum;
          boolean rtn=false;
          sector_t   sec;
          vldoor_t   door;
         
          secnum = -1;
          
          while ((secnum = FindSectorFromLineTag(line,secnum)) >= 0)
          {
         sec = LL.sectors[secnum];
         if (sec.specialdata!=null)
             continue;
             
         
         // new door thinker
         rtn = true;
         door = new vldoor_t();
         sec.specialdata = door;
         door.function = think_t.T_VerticalDoor;
         AddThinker (door);
         door.sector = sec;
         door.type = type;
         door.topwait = VDOORWAIT;
         door.speed = VDOORSPEED;
             
         switch(type)
         {
           case blazeClose:
             door.topheight = sec.FindLowestCeilingSurrounding();
             door.topheight -= 4*FRACUNIT;
             door.direction = -1;
             door.speed = VDOORSPEED * 4;
             S.StartSound(door.sector.soundorg, sfxenum_t.sfx_bdcls);
             break;
             
           case close:
             door.topheight = sec.FindLowestCeilingSurrounding();
             door.topheight -= 4*FRACUNIT;
             door.direction = -1;
             S.StartSound(door.sector.soundorg, sfxenum_t.sfx_dorcls);
             break;
             
           case close30ThenOpen:
             door.topheight = sec.ceilingheight;
             door.direction = -1;
             S.StartSound(door.sector.soundorg, sfxenum_t.sfx_dorcls);
             break;
             
           case blazeRaise:
           case blazeOpen:
             door.direction = 1;
             door.topheight = sec.FindLowestCeilingSurrounding();
             door.topheight -= 4*FRACUNIT;
             door.speed = VDOORSPEED * 4;
             if (door.topheight != sec.ceilingheight)
            	 S.StartSound(door.sector.soundorg, sfxenum_t.sfx_bdopn);
             break;
             
           case normal:
           case open:
             door.direction = 1;
             door.topheight = sec.FindLowestCeilingSurrounding();
             door.topheight -= 4*FRACUNIT;
             if (door.topheight != sec.ceilingheight)
            	 S.StartSound(door.sector.soundorg, sfxenum_t.sfx_doropn);
             break;
             
           default:
             break;
         }
             
          }
          return rtn;
      }


      /**
       * EV_VerticalDoor : open a door manually, no tag value
       */
      
      protected void VerticalDoor(line_t line, mobj_t thing )
      {
          player_t   player;
          //int      secnum;
          sector_t   sec;
          vldoor_t   door;
          int        side;
         
          side = 0;  // only front sides can be used

          // Check for locks
          player = thing.player;
             
          switch(line.special)
          {
            case 26: // Blue Lock
            case 32:
         if ( player ==null)
             return;
         
         if (!player.cards[card_t.it_bluecard.ordinal()] && !player.cards[card_t.it_blueskull.ordinal()])
         {
             player.message = PD_BLUEK;
             S.StartSound(null,sfxenum_t.sfx_oof);
             return;
         }
         break;
         
            case 27: // Yellow Lock
            case 34:
         if ( player ==null)
             return;
         
         if (!player.cards[card_t.it_yellowcard.ordinal()] &&
             !player.cards[card_t.it_yellowskull.ordinal()])
         {
             player.message = PD_YELLOWK;
             S.StartSound(null,sfxenum_t.sfx_oof);
             return;
         }
         break;
         
            case 28: // Red Lock
            case 33:
         if ( player ==null)
             return;
         
         if (!player.cards[card_t.it_redcard.ordinal()] && !player.cards[card_t.it_redskull.ordinal()])
         {
             player.message = PD_REDK;
             S.StartSound(null,sfxenum_t.sfx_oof);
             return;
         }
         break;
          }
         
          // if the sector has an active thinker, use it
          sec = LL.sides[ line.sidenum[side^1]].sector;
         // secnum = sec.id;

          if (sec.specialdata!=null)
          {
        
        	  if (sec.specialdata instanceof plat_t)
        // [MAES]: demo sync for e1nm0646: emulates active plat_t interpreted
        // as door. TODO: add our own overflow handling class.
         door = ((plat_t)sec.specialdata).asVlDoor(LL.sectors);
        	  else
         door = (vldoor_t) sec.specialdata;
         switch(line.special)
         {
           case  1: // ONLY FOR "RAISE" DOORS, NOT "OPEN"s
           case  26:
           case  27:
           case  28:
           case  117:
             if (door.direction == -1)
             door.direction = 1; // go back up
             else
             {
             if (thing.player==null)
                 return;     // JDC: bad guys never close doors
             
             door.direction = -1;    // start going down immediately
             }
             return;
         }
          }
         
          // for proper sound
          switch(line.special)
          {
            case 117:    // BLAZING DOOR RAISE
            case 118:    // BLAZING DOOR OPEN
            	S.StartSound(sec.soundorg,sfxenum_t.sfx_bdopn);
         break;
         
            case 1:  // NORMAL DOOR SOUND
            case 31:
            	S.StartSound(sec.soundorg,sfxenum_t.sfx_doropn);
         break;
         
            default: // LOCKED DOOR SOUND
            	S.StartSound(sec.soundorg,sfxenum_t.sfx_doropn);
         break;
          }
         
          
          // new door thinker
          door = new vldoor_t();
          sec.specialdata = door;
          door.function = think_t.T_VerticalDoor;
          AddThinker (door);
          door.sector = sec;
          door.direction = 1;
          door.speed = VDOORSPEED;
          door.topwait = VDOORWAIT;

          switch(line.special)
          {
            case 1:
            case 26:
            case 27:
            case 28:
         door.type = vldoor_e.normal;
         break;
         
            case 31:
            case 32:
            case 33:
            case 34:
         door.type = vldoor_e.open;
         line.special = 0;
         break;
         
            case 117:    // blazing door raise
         door.type = vldoor_e.blazeRaise;
         door.speed = VDOORSPEED*4;
         break;
            case 118:    // blazing door open
         door.type = vldoor_e.blazeOpen;
         line.special = 0;
         door.speed = VDOORSPEED*4;
         break;
          }
          
          // find the top and bottom of the movement range
          door.topheight = sec.FindLowestCeilingSurrounding();
          door.topheight -= 4*FRACUNIT;
      }


     

  //
  // P_BulletSlope
  // Sets a slope so a near miss is at aproximately
  // the height of the intended target
  //
  int     bulletslope;


  void P_BulletSlope (mobj_t mo)
  {
      long an;

      // see which target is to be aimed at
      // FIXME: angle can already be negative here.
      // Not a problem if it's just moving about (accumulation will work)
      // but it needs to be sanitized before being used in any function.
      an = mo.angle;
      //_D_: &BITS32 will be used later in this function, by fine(co)sine()
      bulletslope = AimLineAttack (mo, an/*&BITS32*/, 16*64*FRACUNIT);

      if (!eval(linetarget))
      {
          an += 1<<26;
          bulletslope = AimLineAttack (mo, an/*&BITS32*/, 16*64*FRACUNIT);
          if (!eval(linetarget))
          {
              an -= 2<<26;
              bulletslope = AimLineAttack (mo, an/*&BITS32*/, 16*64*FRACUNIT);
          }
          
          // Give it one more try, with freelook
          if (mo.player.lookdir!=0 && !eval(linetarget)) 
          {
              an += 2<<26;
              an&=BITS32;
              bulletslope = (mo.player.lookdir<<FRACBITS)/173;
          }
      }
  }


  //
  // P_GunShot
  //
  void
  P_GunShot
  ( mobj_t   mo,
    boolean   accurate )
  {
      long angle;
      int     damage;
      
      damage = 5*(RND.P_Random ()%3+1);
      angle = mo.angle;

      if (!accurate)
      angle += (RND.P_Random()-RND.P_Random())<<18;

      LineAttack (mo, angle, MISSILERANGE, bulletslope, damage);
  }

    boolean Move (mobj_t actor)
    {
        // fixed_t
        int tryx;
        int tryy;
        
        line_t ld;
        
        // warning: 'catch', 'throw', and 'try'
        // are all C++ reserved words
        boolean try_ok;
        boolean good;
            
        if (actor.movedir == DI_NODIR)
        return false;
            
        if (actor.movedir >= 8)
        I.Error ("Weird actor.movedir!");
            
        tryx = actor.x + actor.info.speed*xspeed[actor.movedir];
        tryy = actor.y + actor.info.speed*yspeed[actor.movedir];

        try_ok = TryMove (actor, tryx, tryy);

        if (!try_ok)
        {
        // open any specials
        if (eval(actor.flags & MF_FLOAT) && floatok)
        {
            // must adjust height
            if (actor.z < tmfloorz)
            actor.z += FLOATSPEED;
            else
            actor.z -= FLOATSPEED;

            actor.flags |= MF_INFLOAT;
            return true;
        }
            
        if (numspechit==0)
            return false;
                
        actor.movedir = DI_NODIR;
        good = false;
        while ((numspechit--)>0)
        {
            ld = spechit[numspechit];
            // if the special is not a door
            // that can be opened,
            // return false
            if (UseSpecialLine (actor, ld,false))
            good = true;
        }
        return good;
        }
        else
        {
        actor.flags &= ~MF_INFLOAT;
        }
        
        
        if (! eval(actor.flags & MF_FLOAT) )   
        actor.z = actor.floorz;
        return true; 
    }

    //dirtype
    private int d1,d2;
    
    void NewChaseDir (mobj_t actor)
    {
        // fixed_t
        int deltax,deltay;        
        
        int     tdir;
        int   olddir;
        // dirtypes
        int   turnaround;

        if (actor.target==null)
        I.Error ("P_NewChaseDir: called with no target");
            
        olddir = actor.movedir;
        turnaround=opposite[olddir];

        deltax = actor.target.x - actor.x;
        deltay = actor.target.y - actor.y;

        if (deltax>10*FRACUNIT)
        d1= DI_EAST;
        else if (deltax<-10*FRACUNIT)
        d1= DI_WEST;
        else
        d1=DI_NODIR;

        if (deltay<-10*FRACUNIT)
        d2= DI_SOUTH;
        else if (deltay>10*FRACUNIT)
        d2= DI_NORTH;
        else
        d2=DI_NODIR;

        // try direct route
        if (d1 != DI_NODIR
        && d2 != DI_NODIR)
        {
        actor.movedir = diags[(eval(deltay<0)<<1)+eval(deltax>0)];
        if (actor.movedir != turnaround && TryWalk(actor))
            return;
        }

        // try other directions
        if (RND.P_Random() > 200
        ||  Math.abs(deltay)>Math.abs(deltax))
        {
        tdir=d1;
        d1=d2;
        d2=tdir;
        }

        if (d1==turnaround)
        d1=DI_NODIR;
        if (d2==turnaround)
        d2=DI_NODIR;
        
        if (d1!=DI_NODIR)
        {
        actor.movedir = d1;
        if (TryWalk(actor))
        {
            // either moved forward or attacked
            return;
        }
        }

        if (d2!=DI_NODIR)
        {
        actor.movedir =d2;

        if (TryWalk(actor))
            return;
        }

        // there is no direct path to the player,
        // so pick another direction.
        if (olddir!=DI_NODIR)
        {
        actor.movedir =olddir;

        if (TryWalk(actor))
            return;
        }

        // randomly determine direction of search
        if (eval(RND.P_Random()&1))   
        {
        for ( tdir=DI_EAST;
              tdir<=DI_SOUTHEAST;
              tdir++ )
        {
            if (tdir!=turnaround)
            {
            actor.movedir =tdir;
            
            if ( TryWalk(actor) )
                return;
            }
        }
        }
        else
        {
        for ( tdir=DI_SOUTHEAST;
              tdir != (DI_EAST-1);
              tdir-- )
        {
            if (tdir!=turnaround)
            {
            actor.movedir =tdir;
            
            if ( TryWalk(actor) )
                return;
            }
        }
        }

        if (turnaround !=  DI_NODIR)
        {
        actor.movedir =turnaround;
        if ( TryWalk(actor) )
            return;
        }

        actor.movedir = DI_NODIR;  // can not move
    }
    

    /**
     * TryWalk
     * Attempts to move actor on
     * in its current (ob.moveangle) direction.
     * If blocked by either a wall or an actor
     * returns FALSE
     * If move is either clear or blocked only by a door,
     * returns TRUE and sets...
     * If a door is in the way,
     * an OpenDoor call is made to start it opening.
     */
    private boolean TryWalk (mobj_t actor)
    {   
        if (!Move (actor))
        {
        return false;
        }

        actor.movecount = RND.P_Random()&15;
        return true;
    }

/**
 * P_NightmareRespawn
 */
void
NightmareRespawn (mobj_t mobj)
{
int     x,y, z; // fixed 
subsector_t    ss; 
mobj_t     mo;
mapthing_t     mthing;
  
x = mobj.spawnpoint.x << FRACBITS; 
y = mobj.spawnpoint.y << FRACBITS; 

// somthing is occupying it's position?
if (!CheckPosition (mobj, x, y) ) 
return; // no respwan

// spawn a teleport fog at old spot
// because of removal of the body?
mo = SpawnMobj (mobj.x,
        mobj.y,
        mobj.subsector.sector.floorheight , mobjtype_t.MT_TFOG); 
// initiate teleport sound
S.StartSound(mo, sfxenum_t.sfx_telept);

// spawn a teleport fog at the new spot
ss = LL.PointInSubsector (x,y); 

mo = SpawnMobj (x, y, ss.sector.floorheight , mobjtype_t.MT_TFOG); 

S.StartSound(mo, sfxenum_t.sfx_telept);

// spawn the new monster
mthing = mobj.spawnpoint;

// spawn it
if (eval(mobj.info.flags & MF_SPAWNCEILING))
z = ONCEILINGZ;
else
z = ONFLOORZ;

// inherit attributes from deceased one
mo = SpawnMobj (x,y,z, mobj.type);
mo.spawnpoint = mobj.spawnpoint;  
mo.angle = ANG45 * (mthing.angle/45);

if (eval(mthing.options & MTF_AMBUSH))
mo.flags |= MF_AMBUSH;

mo.reactiontime = 18;

// remove the old monster,
RemoveMobj (mobj);
}

/** P_SpawnMobj
 * 
 * @param x fixed
 * @param y fixed
 * @param z fixed
 * @param type
 * @return
 */

public mobj_t
SpawnMobj
( int   x,
        int   y,
        int   z,
mobjtype_t    type )
{
mobj_t mobj;
state_t    st;
mobjinfo_t info;

// MAES: I tried this approach but it's not really worth it.
// Testing with NUTS.WAD yielded pretty much identical results,
// and that was without using locking. Just let the GC do its
// job.
// mobj = mobjpool.checkOut();
mobj=new mobj_t(this);
info = mobjinfo[type.ordinal()];

mobj.type = type;
mobj.info = info;
mobj.x = x;
mobj.y = y;
mobj.radius = info.radius;
mobj.height = info.height;
mobj.flags = info.flags;
mobj.health = info.spawnhealth;

if (DM.gameskill != skill_t.sk_nightmare)
mobj.reactiontime = info.reactiontime;

mobj.lastlook = RND.P_Random () % MAXPLAYERS;
// do not set the state with P_SetMobjState,
// because action routines can not be called yet
st = states[info.spawnstate.ordinal()];

mobj.state = st;
mobj.tics = st.tics;
mobj.sprite = st.sprite;
mobj.frame = st.frame;

// set subsector and/or block links
LL.SetThingPosition (mobj);

mobj.floorz = mobj.subsector.sector.floorheight;
mobj.ceilingz = mobj.subsector.sector.ceilingheight;

if (z == ONFLOORZ)
mobj.z = mobj.floorz;
else if (z == ONCEILINGZ)
mobj.z = mobj.ceilingz - mobj.info.height;
else 
mobj.z = z;

mobj.function=think_t.P_MobjThinker;
AddThinker (mobj);

return mobj;
}

/**
 * P_RespawnSpecials
 */

void RespawnSpecials ()
{
int     x, y,z; // fixed

subsector_t    ss; 
mobj_t     mo;
mapthing_t     mthing;

int         i;

// only respawn items in deathmatch (deathmatch!=2)
if (!DM.altdeath)
return; // 

// nothing left to respawn?
if (iquehead == iquetail)
return;     

// wait at least 30 seconds
if (DM.leveltime - itemrespawntime[iquetail] < 30*35)
return;         

mthing = itemrespawnque[iquetail];

x = mthing.x << FRACBITS; 
y = mthing.y << FRACBITS; 

// spawn a teleport fog at the new spot
ss = LL.PointInSubsector (x,y); 
mo = SpawnMobj (x, y, ss.sector.floorheight , mobjtype_t.MT_IFOG); 
S.StartSound(mo, sfxenum_t.sfx_itmbk);

// find which type to spawn
for (i=0 ; i< mobjtype_t.NUMMOBJTYPES.ordinal() ; i++)
{
if (mthing.type == mobjinfo[i].doomednum)
  break;
}

// spawn it
if (eval(mobjinfo[i].flags &MF_SPAWNCEILING))
z = ONCEILINGZ;
else
z = ONFLOORZ;

mo = SpawnMobj (x,y,z, mobjtype_t.values()[i]);
mo.spawnpoint = mthing;   
mo.angle = ANG45 * (mthing.angle/45);

// pull it from the que
iquetail = (iquetail+1)&(ITEMQUESIZE-1);
}

/**
 * P_SpawnPlayer
 * Called when a player is spawned on the level.
 * Most of the player structure stays unchanged
 *  between levels.
 */
public void SpawnPlayer (mapthing_t mthing)
{
player_t       p;
int     x;
int     y;
int     z;

mobj_t     mobj;

int         i;

// not playing?
if (!DM.playeringame[mthing.type-1])
return;                 
  
p = DM.players[mthing.type-1];

if (p.playerstate == PST_REBORN)
	p.PlayerReborn();
//DM.PlayerReborn (mthing.type-1);

x       = mthing.x << FRACBITS;
y       = mthing.y << FRACBITS;
z       = ONFLOORZ;
mobj    = SpawnMobj (x,y,z, mobjtype_t.MT_PLAYER);

// set color translations for player sprites
if (mthing.type > 1)       
mobj.flags |= (mthing.type-1)<<MF_TRANSSHIFT;
  
mobj.angle = ANG45 * (mthing.angle/45);
mobj.player = p;
mobj.health = p.health[0];

p.mo = mobj;
p.playerstate = PST_LIVE;  
p.refire = 0;
p.message = null;
p.damagecount = 0;
p.bonuscount = 0;
p.extralight = 0;
p.fixedcolormap = 0;
p.viewheight = VIEWHEIGHT;

// setup gun psprite
p.SetupPsprites ();

// give all cards in death match mode
if (DM.deathmatch)
for (i=0 ; i<NUMCARDS ; i++)
  p.cards[i] = true;
      
if (mthing.type-1 == DM.consoleplayer)
{
// wake up the status bar
ST.Start ();
// wake up the heads up text
HU.Start ();        
}
}

/**
 * P_SpawnMapThing
 * The fields of the mapthing should
 * already be in host byte order.
 */

mobj_t SpawnMapThing (mapthing_t mthing)
{
int         i;
int         bit;
mobj_t     mobj;
int     x;
int     y;
int     z;

// count deathmatch start positions
if (mthing.type == 11)
{
if (DM.deathmatch_p < 10/*DM.deathmatchstarts[10]*/)
{
 // memcpy (deathmatch_p, mthing, sizeof(*mthing));
    DM.deathmatchstarts[DM.deathmatch_p]=new mapthing_t(mthing);
  DM.deathmatch_p++;
}
return null;
}

if (mthing.type <= 0)
{
    // Ripped from Chocolate Doom :-p
	// Thing type 0 is actually "player -1 start".  
    // For some reason, Vanilla Doom accepts/ignores this.
	// MAES: no kidding.

    return null;
}


// check for players specially
if (mthing.type <= 4 && mthing.type > 0)  // killough 2/26/98 -- fix crashes
{
// save spots for respawning in network games
DM.playerstarts[mthing.type-1]=new mapthing_t(mthing);
if (!DM.deathmatch)
  SpawnPlayer (mthing);

return null;
}

// check for apropriate skill level
if (!DM.netgame && eval(mthing.options & 16) )
return null;
  
if (DM.gameskill == skill_t.sk_baby)
bit = 1;
else if (DM.gameskill == skill_t.sk_nightmare)
bit = 4;
else
bit = 1<<(DM.gameskill.ordinal()-1);

if (!eval(mthing.options & bit) )
return null;

// find which type to spawn
for (i=0 ; i< NUMMOBJTYPES ; i++)
if (mthing.type == mobjinfo[i].doomednum)
  break;

// phares 5/16/98:
// Do not abort because of an unknown thing. Ignore it, but post a
// warning message for the player.

if (i==NUMMOBJTYPES) {
System.err.printf ("P_SpawnMapThing: Unknown type %d at (%d, %d)",
   mthing.type,
   mthing.x, mthing.y);
   return null;
}
  
// don't spawn keycards and players in deathmatch
if (DM.deathmatch && eval(mobjinfo[i].flags & MF_NOTDMATCH))
return null;
  
// don't spawn any monsters if -nomonsters
if (DM.nomonsters
&& ( i == mobjtype_t.MT_SKULL.ordinal()
   || eval(mobjinfo[i].flags & MF_COUNTKILL)) )
{
return null;
}

// spawn it
x = mthing.x << FRACBITS;
y = mthing.y << FRACBITS;

if (eval(mobjinfo[i].flags & MF_SPAWNCEILING))
z = ONCEILINGZ;
else
z = ONFLOORZ;

mobj = SpawnMobj (x,y,z, mobjtype_t.values()[i]);
mobj.spawnpoint.copyFrom(mthing);

if (mobj.tics > 0)
mobj.tics = 1 + (RND.P_Random () % mobj.tics);
if (eval(mobj.flags & MF_COUNTKILL))
DM.totalkills++;
if (eval(mobj.flags & MF_COUNTITEM))
DM.totalitems++;
  
mobj.angle = ANG45 * (mthing.angle/45);
if (eval(mthing.options & MTF_AMBUSH))
mobj.flags |= MF_AMBUSH;

return mobj;

}

/** P_SpawnBlood
 * 
 * @param x fixed
 * @param y fixed
 * @param z fixed
 * @param damage
 */

void
SpawnBlood
( int   x,
        int   y,
        int   z,
int       damage )
{
mobj_t th;

z += ((RND.P_Random()-RND.P_Random())<<10);
th = SpawnMobj (x,y,z, mobjtype_t.MT_BLOOD);
th.momz = FRACUNIT*2;
th.tics -= RND.P_Random()&3;

if (th.tics < 1)
th.tics = 1;
  
if (damage <= 12 && damage >= 9)
    th.SetMobjState (statenum_t.S_BLOOD2);
else if (damage < 9)
    th.SetMobjState (statenum_t.S_BLOOD3);
}

/** P_SpawnPuff
 * 
 * @param x fixed
 * @param y fixed
 * @param z fixed
 * 
 */

void
SpawnPuff
( int   x,
int   y,
int   z )
{
mobj_t th;

z += ((RND.P_Random()-RND.P_Random())<<10);

th = SpawnMobj (x,y,z, mobjtype_t.MT_PUFF);
th.momz = FRACUNIT;
th.tics -= RND.P_Random()&3;

if (th.tics < 1)
th.tics = 1;

// don't make punches spark on the wall
if (attackrange == MELEERANGE)
    th.SetMobjState (statenum_t.S_PUFF3);
}

/**
 * P_SpawnMissile
 */

protected mobj_t
SpawnMissile
( mobj_t   source,
mobj_t   dest,
mobjtype_t    type )
{
mobj_t th;
long an; // angle_t
int     dist;

th = SpawnMobj (source.x,
        source.y,
        source.z + 4*8*FRACUNIT, type);

if (th.info.seesound!=null)
	S.StartSound(th, th.info.seesound);

th.target = source;    // where it came from
an = R.PointToAngle2 (source.x, source.y, dest.x, dest.y)&BITS32;  

// fuzzy player
if (eval(dest.flags & MF_SHADOW))
an += (RND.P_Random()-RND.P_Random())<<20; 

th.angle = an&BITS32;
//an >>= ANGLETOFINESHIFT;
th.momx = FixedMul (th.info.speed, finecosine(an));
th.momy = FixedMul (th.info.speed, finesine(an));

dist = AproxDistance (dest.x - source.x, dest.y - source.y);
dist = dist / th.info.speed;

if (dist < 1)
dist = 1;

th.momz = (dest.z - source.z) / dist;
CheckMissileSpawn (th);

return th;
}


/**
* P_SpawnPlayerMissile
* Tries to aim at a nearby monster
*/

    public void SpawnPlayerMissile(mobj_t source, mobjtype_t type) {
        mobj_t th;
        long an; // angle_t
        int x, y, z, slope; // think_t

        // see which target is to be aimed at
        an = source.angle;
        slope = AimLineAttack(source, an, 16 * 64 * FRACUNIT);

        if (linetarget == null) {
            an += 1 << 26;
            an &= BITS32;
            slope = AimLineAttack(source, an, 16 * 64 * FRACUNIT);

            if (linetarget == null) {
                an -= 2 << 26;
                an &= BITS32;
                slope = AimLineAttack(source, an, 16 * 64 * FRACUNIT);
            }

            if (linetarget == null) {
                an = source.angle & BITS32;
                // angle should be "sane"..right?
                // Just this line allows freelook.
                slope = ((source.player.lookdir)<<FRACBITS)/173;
            }
        }

        x = source.x;
        y = source.y;
        z = source.z + 4 * 8 * FRACUNIT+slope;

        th = SpawnMobj(x, y, z, type);

        if (th.info.seesound != null)
            S.StartSound(th, th.info.seesound);

        th.target = source;
        th.angle = an;
        th.momx = FixedMul(th.info.speed, finecosine(an));
        th.momy = FixedMul(th.info.speed, finesine(an));
        th.momz = FixedMul(th.info.speed, slope);

        CheckMissileSpawn(th);
    }

    //
    // P_DamageMobj
    // Damages both enemies and players
    // "inflictor" is the thing that caused the damage
    //  creature or missile, can be NULL (slime, etc)
    // "source" is the thing to target after taking damage
    //  creature or NULL
    // Source and inflictor are the same for melee attacks.
    // Source can be NULL for slime, barrel explosions
    // and other environmental stuff.
    //
    public void
    DamageMobj
    ( mobj_t   target,
      mobj_t   inflictor,
      mobj_t   source,
      int       damage )
    {
        long    ang; // unsigned
        int     saved;
        player_t   player;
        int thrust; // fixed_t
        int     temp;
        
        if ( !eval(target.flags& MF_SHOOTABLE))
        return; // shouldn't happen...
            
        if (target.health <= 0)
        return;

        if ( eval(target.flags & MF_SKULLFLY ))
        {
        target.momx = target.momy = target.momz = 0;
        }
        
        player = target.player;
        if ((player!=null) && DM.gameskill == skill_t.sk_baby)
        damage >>= 1;   // take half damage in trainer mode
            

        // Some close combat weapons should not
        // inflict thrust and push the victim out of reach,
        // thus kick away unless using the chainsaw.
        if ((inflictor !=null)
        && !eval(target.flags& MF_NOCLIP)
        && (source==null
            || source.player==null
            || source.player.readyweapon != weapontype_t.wp_chainsaw))
        {
        ang = R.PointToAngle2 ( inflictor.x,
                    inflictor.y,
                    target.x,
                    target.y)&BITS32;
            
        thrust = damage*(MAPFRACUNIT>>3)*100/target.info.mass;

        // make fall forwards sometimes
        if ( (damage < 40)
             && (damage > target.health)
             && (target.z - inflictor.z > 64*FRACUNIT)
             && eval(RND.P_Random()&1) )
        {
            ang += ANG180;
            thrust *= 4;
        }
            
        //ang >>= ANGLETOFINESHIFT;
        target.momx += FixedMul (thrust, finecosine(ang));
        target.momy += FixedMul (thrust, finesine(ang));
        }
        
        // player specific
        if (player!=null)
        {
        // end of game hell hack
        if (target.subsector.sector.special == 11
            && damage >= target.health)
        {
            damage = target.health - 1;
        }
        

        // Below certain threshold,
        // ignore damage in GOD mode, or with INVUL power.
        if ( damage < 1000
             && ( eval(player.cheats&player_t.CF_GODMODE))
              || player.powers[pw_invulnerability]!=0 ) 
        {
            return;
        }
        
        if (player.armortype!=0)
        {
            if (player.armortype == 1)
            saved = damage/3;
            else
            saved = damage/2;
            
            if (player.armorpoints[0] <= saved)
            {
            // armor is used up
            saved = player.armorpoints[0];
            player.armortype = 0;
            }
            player.armorpoints[0] -= saved;
            damage -= saved;
        }
        player.health[0] -= damage;   // mirror mobj health here for Dave
        if (player.health[0] < 0)
            player.health[0] = 0;
        
        player.attacker = source;
        player.damagecount += damage;  // add damage after armor / invuln

        if (player.damagecount > 100)
            player.damagecount = 100;  // teleport stomp does 10k points...
        
        temp = damage < 100 ? damage : 100;

        if (player == DM.players[DM.consoleplayer]) 
            I.Tactile (40,10,40+temp*2);
        }
        
        // do the damage    
        target.health -= damage;   
        if (target.health <= 0)
        {
        KillMobj (source, target);
        return;
        }

        if ( (RND.P_Random () < target.info.painchance)
         && !eval(target.flags&MF_SKULLFLY) )
        {
        target.flags |= MF_JUSTHIT;    // fight back!
        
        target.SetMobjState (target.info.painstate);
        }
                
        target.reactiontime = 0;       // we're awake now...   

        if ( ((target.threshold==0) || (target.type == mobjtype_t.MT_VILE))
         && (source!=null) && (source != target)
         && (source.type != mobjtype_t.MT_VILE))
        {
        // if not intent on another player,
        // chase after this one
        target.target = source;        
        target.threshold = BASETHRESHOLD;
        if (target.state == states[target.info.spawnstate.ordinal()]
            && target.info.seestate != statenum_t.S_NULL)
            target.SetMobjState (target.info.seestate);
        }
                
    }
    
    //
    // KillMobj
    //
    public void
    KillMobj
    ( mobj_t   source,
      mobj_t   target )
    {
        mobjtype_t  item;
        mobj_t mo;
        
        // Maes: this seems necessary in order for barrel damage
        // to propagate inflictors.
        target.target=source;
        
        target.flags &= ~(MF_SHOOTABLE|MF_FLOAT|MF_SKULLFLY);

        if (target.type != mobjtype_t.MT_SKULL)
        target.flags &= ~MF_NOGRAVITY;

        target.flags |= MF_CORPSE|MF_DROPOFF;
        target.height >>= 2;

        if (source!=null && source.player!=null)
        {
        // count for intermission
        if ((target.flags & MF_COUNTKILL)!=0)
            source.player.killcount++;    

        if (target.player!=null) //; <-- _D_: that semicolon caused a bug!
           source.player.frags[target.player.identify()]++;
           // It's probably intended to increment the frags of source player vs target player. Lookup? 
        }
        else if (!DM.netgame && ((target.flags & MF_COUNTKILL)!=0) )
        {
        // count all monster deaths,
        // even those caused by other monsters
        DM.players[0].killcount++;
        }
        
        if (target.player!=null)
        {
        // count environment kills against you
        if (source==null)    
            // TODO: some way to indentify which one of the 
            // four possiblelayers is the current player
            
            target.player.frags[target.player.identify()]++;
                
        target.flags &= ~MF_SOLID;
        target.player.playerstate = PST_DEAD;
        target.player.DropWeapon (); // in PSPR

        if (target.player == DM.players[DM.consoleplayer]
            && DM.automapactive)
        {
            // don't die in auto map,
            // switch view prior to dying
            AM.Stop ();
        }
        
        }

        if (target.health < -target.info.spawnhealth 
        && target.info.xdeathstate!=statenum_t.S_NULL)
        {
            target.SetMobjState(target.info.xdeathstate);
        }
        else
            target.SetMobjState (target.info.deathstate);
        target.tics -= RND.P_Random()&3;

        if (target.tics < 1)
        target.tics = 1;
            
        //  I_StartSound (&actor.r, actor.info.deathsound);

        // Drop stuff.
        // This determines the kind of object spawned
        // during the death frame of a thing.
        switch (target.type)
        {
          case MT_WOLFSS:
          case MT_POSSESSED:
        item = mobjtype_t.MT_CLIP;
        break;
        
          case MT_SHOTGUY:
        item = mobjtype_t.MT_SHOTGUN;
        break;
        
          case MT_CHAINGUY:
        item = mobjtype_t.MT_CHAINGUN;
        break;
        
          default:
        return;
        }

        mo = SpawnMobj (target.x,target.y,ONFLOORZ, item);
        mo.flags |= MF_DROPPED;    // special versions of items
    }
    
  //
 // TELEPORTATION
 //
 int
 Teleport
 ( line_t    line,
   int       side,
   mobj_t    thing )
 {
     int     i;
     int     tag;
     mobj_t  m;     
     mobj_t  fog;
     int an;
     thinker_t   thinker;
     sector_t    sector;
     int oldx, oldy, oldz; // fixed_t

     // don't teleport missiles
     if ((thing.flags & MF_MISSILE)!=0)
     return 0;       

     // Don't teleport if hit back of line,
     //  so you can get out of teleporter.
     if (side == 1)      
     return 0;   

     
     tag = line.tag;
     for (i = 0; i < LL.numsectors; i++)
     {
     if (LL.sectors[ i ].tag == tag )
     {
         thinker = thinkercap.next;
         for (thinker = thinkercap.next;
          thinker != thinkercap;
          thinker = thinker.next)
         {
         // not a mobj
         if (thinker.function != think_t.P_MobjThinker)
             continue;   

         m = (mobj_t)thinker;
         
         // not a teleportman
         if (m.type != mobjtype_t.MT_TELEPORTMAN )
             continue;       

         sector = m.subsector.sector;
         // wrong sector
         if (sector.id != i )
             continue;   

         oldx = thing.x;
         oldy = thing.y;
         oldz = thing.z;
                 
         if (!TeleportMove (thing, m.x, m.y))
             return 0;
         
         thing.z = thing.floorz;  //fixme: not needed?
         if (thing.player!=null) {
             thing.player.viewz = thing.z+thing.player.viewheight;
             thing.player.lookdir = 0; // Reset lookdir
         }
                 
         // spawn teleport fog at source and destination
         fog = SpawnMobj (oldx, oldy, oldz, mobjtype_t.MT_TFOG);
         S.StartSound( fog, sfxenum_t.sfx_telept);
         an = Tables.toBAMIndex(m.angle);
         fog = SpawnMobj (m.x+20*finecosine[an], m.y+20*finesine[an]
                    , thing.z, mobjtype_t.MT_TFOG);

         // emit sound, where?
         S.StartSound (fog, sfxenum_t.sfx_telept);
         
         // don't move for a bit
         if (thing.player!=null)
             thing.reactiontime = 18;   

         thing.angle = m.angle;
         thing.momx = thing.momy = thing.momz = 0;
         return 1;
         }   
     }
     }
     return 0;
 }
 
 //
//EVENTS
//Events are operations triggered by using, crossing,
//or shooting special lines, or by timed thinkers.
//

/**
* P_CrossSpecialLine - TRIGGER
* Called every time a thing origin is about
*  to cross a line with a non 0 special.
*/

void
CrossSpecialLine
( line_t       line,
int       side,
mobj_t  thing )
{
  //line_t line;
  boolean     ok;

  //line = LL.lines[linenum];
  
  //  Triggers that other things can activate
  if (thing.player==null)
  {
  // Things that should NOT trigger specials...
  switch(thing.type)
  {
    case MT_ROCKET:
    case MT_PLASMA:
    case MT_BFG:
    case MT_TROOPSHOT:
    case MT_HEADSHOT:
    case MT_BRUISERSHOT:
      return;
     // break;
      
    default: break;
  }
      
  ok = false;
  switch(line.special)
  {
    case 39:  // TELEPORT TRIGGER
    case 97:  // TELEPORT RETRIGGER
    case 125: // TELEPORT MONSTERONLY TRIGGER
    case 126: // TELEPORT MONSTERONLY RETRIGGER
    case 4:   // RAISE DOOR
    case 10:  // PLAT DOWN-WAIT-UP-STAY TRIGGER
    case 88:  // PLAT DOWN-WAIT-UP-STAY RETRIGGER
      ok = true;
      break;
  }
  if (!ok)
      return;
  }

  
  // Note: could use some const's here.
  switch (line.special)
  {
  // TRIGGERS.
  // All from here to RETRIGGERS.
    case 2:
  // Open Door
  DoDoor(line,vldoor_e.open);
  line.special = 0;
  break;

    case 3:
  // Close Door
  DoDoor(line,vldoor_e.close);
  line.special = 0;
  break;

    case 4:
  // Raise Door
  DoDoor(line,vldoor_e.normal);
  line.special = 0;
  break;
  
    case 5:
  // Raise Floor
  DoFloor(line,floor_e.raiseFloor);
  line.special = 0;
  break;
  
    case 6:
  // Fast Ceiling Crush & Raise
  DoCeiling(line,ceiling_e.fastCrushAndRaise);
  line.special = 0;
  break;
  
    case 8:
  // Build Stairs
  BuildStairs(line,stair_e.build8);
  line.special = 0;
  break;
  
    case 10:
  // PlatDownWaitUp
  PEV.DoPlat(line,plattype_e.downWaitUpStay,0);
  line.special = 0;
  break;
  
    case 12:
  // Light Turn On - brightest near
  LEV.LightTurnOn(line,0);
  line.special = 0;
  break;
  
    case 13:
  // Light Turn On 255
  LEV.LightTurnOn(line,255);
  line.special = 0;
  break;
  
    case 16:
  // Close Door 30
  DoDoor(line,vldoor_e.close30ThenOpen);
  line.special = 0;
  break;
  
    case 17:
  // Start Light Strobing
  LEV.StartLightStrobing(line);
  line.special = 0;
  break;
  
    case 19:
  // Lower Floor
  DoFloor(line,floor_e.lowerFloor);
  line.special = 0;
  break;
  
    case 22:
  // Raise floor to nearest height and change texture
  PEV.DoPlat(line,plattype_e.raiseToNearestAndChange,0);
  line.special = 0;
  break;
  
    case 25:
  // Ceiling Crush and Raise
  DoCeiling(line,ceiling_e.crushAndRaise);
  line.special = 0;
  break;
  
    case 30:
  // Raise floor to shortest texture height
  //  on either side of lines.
  DoFloor(line,floor_e.raiseToTexture);
  line.special = 0;
  break;
  
    case 35:
  // Lights Very Dark
  LEV.LightTurnOn(line,35);
  line.special = 0;
  break;
  
    case 36:
  // Lower Floor (TURBO)
  DoFloor(line,floor_e.turboLower);
  line.special = 0;
  break;
  
    case 37:
  // LowerAndChange
  DoFloor(line,floor_e.lowerAndChange);
  line.special = 0;
  break;
  
    case 38:
  // Lower Floor To Lowest
  DoFloor( line, floor_e.lowerFloorToLowest );
  line.special = 0;
  break;
  
    case 39:
  // TELEPORT!
  Teleport( line, side, thing );
  line.special = 0;
  break;

    case 40:
  // RaiseCeilingLowerFloor
  DoCeiling( line, ceiling_e.raiseToHighest );
  DoFloor( line, floor_e.lowerFloorToLowest );
  line.special = 0;
  break;
  
    case 44:
  // Ceiling Crush
  DoCeiling( line, ceiling_e.lowerAndCrush );
  line.special = 0;
  break;
  
    case 52:
  // EXIT!
  DM.ExitLevel ();
  break;
  
    case 53:
  // Perpetual Platform Raise
  PEV.DoPlat(line,plattype_e.perpetualRaise,0);
  line.special = 0;
  break;
  
    case 54:
  // Platform Stop
  PEV.StopPlat(line);
  line.special = 0;
  break;

    case 56:
  // Raise Floor Crush
  DoFloor(line,floor_e.raiseFloorCrush);
  line.special = 0;
  break;

    case 57:
  // Ceiling Crush Stop
  CeilingCrushStop(line);
  line.special = 0;
  break;
  
    case 58:
  // Raise Floor 24
  DoFloor(line,floor_e.raiseFloor24);
  line.special = 0;
  break;

    case 59:
  // Raise Floor 24 And Change
  DoFloor(line,floor_e.raiseFloor24AndChange);
  line.special = 0;
  break;
  
    case 104:
  // Turn lights off in sector(tag)
  LEV.TurnTagLightsOff(line);
  line.special = 0;
  break;
  
    case 108:
  // Blazing Door Raise (faster than TURBO!)
  DoDoor (line,vldoor_e.blazeRaise);
  line.special = 0;
  break;
  
    case 109:
  // Blazing Door Open (faster than TURBO!)
        DoDoor (line,vldoor_e.blazeOpen);
  line.special = 0;
  break;
  
    case 100:
  // Build Stairs Turbo 16
  BuildStairs(line,stair_e.turbo16);
  line.special = 0;
  break;
  
    case 110:
  // Blazing Door Close (faster than TURBO!)
  DoDoor (line,vldoor_e.blazeClose);
  line.special = 0;
  break;

    case 119:
  // Raise floor to nearest surr. floor
  DoFloor(line,floor_e.raiseFloorToNearest);
  line.special = 0;
  break;
  
    case 121:
  // Blazing PlatDownWaitUpStay
  PEV.DoPlat(line,plattype_e.blazeDWUS,0);
  line.special = 0;
  break;
  
    case 124:
  // Secret EXIT
  DM.SecretExitLevel ();
  break;
      
    case 125:
  // TELEPORT MonsterONLY
  if (thing.player==null)
  {
      Teleport( line, side, thing );
      line.special = 0;
  }
  break;
  
    case 130:
  // Raise Floor Turbo
  DoFloor(line,floor_e.raiseFloorTurbo);
  line.special = 0;
  break;
  
    case 141:
  // Silent Ceiling Crush & Raise
  DoCeiling(line,ceiling_e.silentCrushAndRaise);
  line.special = 0;
  break;
  
  // RETRIGGERS.  All from here till end.
    case 72:
  // Ceiling Crush
  DoCeiling( line, ceiling_e.lowerAndCrush );
  break;

    case 73:
  // Ceiling Crush and Raise
  DoCeiling(line,ceiling_e.crushAndRaise);
  break;

    case 74:
  // Ceiling Crush Stop
  CeilingCrushStop(line);
  break;
  
    case 75:
  // Close Door
  DoDoor(line,vldoor_e.close);
  break;
  
    case 76:
  // Close Door 30
  DoDoor(line,vldoor_e.close30ThenOpen);
  break;
  
    case 77:
  // Fast Ceiling Crush & Raise
  DoCeiling(line,ceiling_e.fastCrushAndRaise);
  break;
  
    case 79:
  // Lights Very Dark
  LEV.LightTurnOn(line,35);
  break;
  
    case 80:
  // Light Turn On - brightest near
  LEV.LightTurnOn(line,0);
  break;
  
    case 81:
  // Light Turn On 255
  LEV.LightTurnOn(line,255);
  break;
  
    case 82:
  // Lower Floor To Lowest
  DoFloor( line, floor_e.lowerFloorToLowest );
  break;
  
    case 83:
  // Lower Floor
  DoFloor(line,floor_e.lowerFloor);
  break;

    case 84:
  // LowerAndChange
  DoFloor(line,floor_e.lowerAndChange);
  break;

    case 86:
  // Open Door
  DoDoor(line,vldoor_e.open);
  break;
  
    case 87:
  // Perpetual Platform Raise
  PEV.DoPlat(line,plattype_e.perpetualRaise,0);
  break;
  
    case 88:
  // PlatDownWaitUp
  PEV.DoPlat(line,plattype_e.downWaitUpStay,0);
  break;
  
    case 89:
  // Platform Stop
  PEV.StopPlat(line);
  break;
  
    case 90:
  // Raise Door
  DoDoor(line,vldoor_e.normal);
  break;
  
    case 91:
  // Raise Floor
  DoFloor(line,floor_e.raiseFloor);
  break;
  
    case 92:
  // Raise Floor 24
  DoFloor(line,floor_e.raiseFloor24);
  break;
  
    case 93:
  // Raise Floor 24 And Change
  DoFloor(line,floor_e.raiseFloor24AndChange);
  break;
  
    case 94:
  // Raise Floor Crush
  DoFloor(line,floor_e.raiseFloorCrush);
  break;
  
    case 95:
  // Raise floor to nearest height
  // and change texture.
  PEV.DoPlat(line,plattype_e.raiseToNearestAndChange,0);
  break;
  
    case 96:
  // Raise floor to shortest texture height
  // on either side of lines.
  DoFloor(line,floor_e.raiseToTexture);
  break;
  
    case 97:
  // TELEPORT!
  Teleport( line, side, thing );
  break;
  
    case 98:
  // Lower Floor (TURBO)
  DoFloor(line,floor_e.turboLower);
  break;

    case 105:
  // Blazing Door Raise (faster than TURBO!)
  DoDoor (line,vldoor_e.blazeRaise);
  break;
  
    case 106:
  // Blazing Door Open (faster than TURBO!)
  DoDoor (line,vldoor_e.blazeOpen);
  break;

    case 107:
  // Blazing Door Close (faster than TURBO!)
  DoDoor (line,vldoor_e.blazeClose);
  break;

    case 120:
  // Blazing PlatDownWaitUpStay.
  PEV.DoPlat(line,plattype_e.blazeDWUS,0);
  break;
  
    case 126:
  // TELEPORT MonsterONLY.
  if (thing.player==null)
      Teleport( line, side, thing );
  break;
  
    case 128:
  // Raise To Nearest Floor
  DoFloor(line,floor_e.raiseFloorToNearest);
  break;
  
    case 129:
  // Raise Floor Turbo
  DoFloor(line,floor_e.raiseFloorTurbo);
  break;
  }
}

///////////////// MOVEMENT'S ACTIONS ////////////////////////
    
    /** fixed_t */
    int[]       tmbbox=new int[4];
    mobj_t      tmthing;
    long     tmflags;
    /** fixed_t */
    int     tmx,    tmy;

    /** If "floatok" true, move would be ok
        if within "tmfloorz - tmceilingz". */
    public boolean     floatok;

    /** fixed_t */
    public int     tmfloorz, tmceilingz,   tmdropoffz;

    // keep track of the line that lowers the ceiling,
    // so missiles don't explode against sky hack walls
    public line_t      ceilingline;



    public line_t[]        spechit=new line_t[MAXSPECIALCROSS];
    public int     numspechit;
    
    
    protected final void ResizeSpechits() {
        spechit=C2JUtils.resize(spechit[0],spechit,spechit.length*2);
        }
    
////////////////// PTR Traverse Interception Functions ///////////////////////    
    public class PTR_AimTraverse implements PTR_InterceptFunc{ 
        public boolean
        invoke (intercept_t in)
        {
            line_t    li;
            mobj_t     th;
            int     slope;
            int     thingtopslope;
            int     thingbottomslope;
            int     dist;
                
            if (in.isaline)
            {
            li = (line_t) in.d();
            
            if ( !eval(li.flags & ML_TWOSIDED) )
                return false;       // stop
            
            // Crosses a two sided line.
            // A two sided line will restrict
            // the possible target ranges.
            LineOpening (li);
            
            if (openbottom >= opentop)
                return false;       // stop
            
            dist = FixedMul (attackrange, in.frac);

            if (li.frontsector.floorheight != li.backsector.floorheight)
            {
                slope = FixedDiv (openbottom - shootz , dist);
                if (slope > bottomslope)
                bottomslope = slope;
            }
                
            if (li.frontsector.ceilingheight != li.backsector.ceilingheight)
            {
                slope = FixedDiv (opentop - shootz , dist);
                if (slope < topslope)
                topslope = slope;
            }
                
            if (topslope <= bottomslope)
                return false;       // stop
                    
            return true;            // shot continues
            }
            
            // shoot a thing
            th = (mobj_t) in.d();
            if (th == shootthing)
            return true;            // can't shoot self
            
            if (!eval(th.flags&MF_SHOOTABLE))
            return true;            // corpse or something

            // check angles to see if the thing can be aimed at
            dist = FixedMul (attackrange, in.frac);
            thingtopslope = FixedDiv (th.z+th.height - shootz , dist);

            if (thingtopslope < bottomslope)
            return true;            // shot over the thing

            thingbottomslope = FixedDiv (th.z - shootz, dist);

            if (thingbottomslope > topslope)
            return true;            // shot under the thing
            
            // this thing can be hit!
            if (thingtopslope > topslope)
            thingtopslope = topslope;
            
            if (thingbottomslope < bottomslope)
            thingbottomslope = bottomslope;

            aimslope = (thingtopslope+thingbottomslope)/2;
            linetarget = th;

            return false;           // don't go any farther
        	}
        }
    
    /**
     * PTR_ShootTraverse
     *
     * 9/5/2011: Accepted _D_'s fix
     */
    
    public class PTR_ShootTraverse implements PTR_InterceptFunc {
    public boolean invoke(intercept_t in){
    int     x,y,z,frac; // fixed_t
    line_t    li;
    mobj_t     th;

    int     slope,dist,thingtopslope,thingbottomslope; // fixed_t

    if (in.isaline)
    {
        li = (line_t) in.d();

        if (li.special!=0)
            ShootSpecialLine (shootthing, li);

        if ( !eval(li.flags& ML_TWOSIDED) ) 
            return gotoHitLine(in, li);

        // crosses a two sided line
        LineOpening (li);

        dist = FixedMul (attackrange, in.frac);

        if (li.frontsector.floorheight != li.backsector.floorheight)
        {
            slope = FixedDiv (openbottom - shootz , dist);
            if (slope > aimslope)
                return gotoHitLine(in, li);
        }

        if (li.frontsector.ceilingheight != li.backsector.ceilingheight)
        {
            slope = FixedDiv (opentop - shootz , dist);
            if (slope < aimslope)
                return gotoHitLine(in, li);
        }

        // shot continues
        return true;            

    }

    // shoot a thing
    th = (mobj_t) in.d();
    if (th == shootthing)
        return true;        // can't shoot self

    if (!eval(th.flags&MF_SHOOTABLE))
        return true;        // corpse or something

    // check angles to see if the thing can be aimed at
    dist = FixedMul (attackrange, in.frac);
    thingtopslope = FixedDiv (th.z+th.height - shootz , dist);

    if (thingtopslope < aimslope)
        return true;        // shot over the thing

    thingbottomslope = FixedDiv (th.z - shootz, dist);

    if (thingbottomslope > aimslope)
        return true;        // shot under the thing


    // hit thing
    // position a bit closer
    frac = in.frac - FixedDiv (10*FRACUNIT,attackrange);

    x = trace.x + FixedMul (trace.dx, frac);
    y = trace.y + FixedMul (trace.dy, frac);
    z = shootz + FixedMul (aimslope, FixedMul(frac, attackrange));

    // Spawn bullet puffs or blod spots,
    // depending on target type.
    if (eval(((mobj_t)in.d()).flags & MF_NOBLOOD))
        SpawnPuff (x,y,z);
    else
        SpawnBlood (x,y,z, la_damage);

    if (la_damage!=0)
        DamageMobj (th, shootthing, shootthing, la_damage);

    // don't go any farther
    return false;
    
    
}

//_D_: NOTE: this function was added, because replacing a goto by a boolean flag caused a bug if shooting a single sided line
protected boolean gotoHitLine(intercept_t in, line_t li) {
    int x, y, z, frac;

    // position a bit closer
    frac = in.frac - FixedDiv (4*FRACUNIT,attackrange);
    x = trace.x + FixedMul (trace.dx, frac);
    y = trace.y + FixedMul (trace.dy, frac);
    z = shootz + FixedMul (aimslope, FixedMul(frac, attackrange));

    if (li.frontsector.ceilingpic == TM.getSkyFlatNum())
    {
        // don't shoot the sky!
        if (z > li.frontsector.ceilingheight)
            return false;

        // it's a sky hack wall
        if  (li.backsector!=null && li.backsector.ceilingpic == TM.getSkyFlatNum())
            return false;       
    }

    // Spawn bullet puffs.
    SpawnPuff (x,y,z);

    // don't go any farther
    return false;   
	}
    }
    
    //
    // PTR_SlideTraverse
    //   
    
    public class PTR_SlideTraverse implements PTR_InterceptFunc{ 
    public boolean
    invoke (intercept_t in)
    {
        line_t li;
        
        if (!in.isaline)
        I.Error ("PTR_SlideTraverse: not a line?");
            
        li = (line_t) in.d();
        
        if ( ! eval(li.flags &ML_TWOSIDED) )
        {
        if (li.PointOnLineSide (slidemo.x, slidemo.y))
        {
            // don't hit the back side
            return true;        
        }
        return isblocking(in,li);
        }

        // set openrange, opentop, openbottom
        LineOpening (li);
        
        if ((openrange < slidemo.height)|| // doesn't fit
            (opentop - slidemo.z < slidemo.height)|| // mobj is too high
            (openbottom - slidemo.z > 24*FRACUNIT )) // too big a step up
        {
        if (in.frac < bestslidefrac)
        {
        secondslidefrac = bestslidefrac;
        secondslideline = bestslideline;
        bestslidefrac = in.frac;
        bestslideline = li;
        }
        
        return false;   // stop
    }
        
        else
        // this line doesn't block movement
        return true;        
        

    }
    

    private final boolean isblocking(intercept_t in, line_t li){
    // the line does block movement,
    // see if it is closer than best so far
 
    if (in.frac < bestslidefrac)
    {
    secondslidefrac = bestslidefrac;
    secondslideline = bestslideline;
    bestslidefrac = in.frac;
    bestslideline = li;
    }
    
    return false;   // stop
    }
    }
    
    //
    // USE LINES
    //


    public class PTR_UseTraverse implements PTR_InterceptFunc{
    	

    public boolean invoke (intercept_t in)
    {
        boolean     side;
        // FIXME: some sanity check here?
        line_t line=(line_t) in.d();
        
        if (line.special==0)
        {
        LineOpening (line);
        if (openrange <= 0)
        {
        	 S.StartSound(A.usething, sfxenum_t.sfx_noway);
            
            // can't use through a wall
            return false;   
        }
        // not a special line, but keep checking
        return true ;       
        }
        
        side = false;
        if (line.PointOnLineSide (A.usething.x, A.usething.y))
        side = true;
        
        //  return false;       // don't use back side
        
        A.UseSpecialLine (A.usething, line, side);

        // can't use for than one special line in a row
        return false;
    	}
    }
  
    //
    //P_BlockThingsIterator
    //
    final boolean
    BlockThingsIterator
    ( int           x,
    int           y,
    PIT_MobjFunction func)
    {
     mobj_t     mobj;
     
     if ( x<0
      || y<0
      || x>=LL.bmapwidth
      || y>=LL.bmapheight)
     {
     return true;
     }
     

     for (mobj = LL.blocklinks[y*LL.bmapwidth+x] ; mobj!=null ;
      mobj = (mobj_t) mobj.bnext)
     {
     if (!func.invoke(mobj ) )
         return false;
     }
     return true;
    }

    
    //
    // TELEPORT MOVE
    // 

    //
    // P_TeleportMove
    //
    public boolean
    TeleportMove
    ( mobj_t    thing,
      int   x, //fixed
      int   y )
    {
        int         xl;
        int         xh;
        int         yl;
        int         yh;
        int         bx;
        int         by;
        
        subsector_t newsubsec;
        
        // kill anything occupying the position
        tmthing = thing;
        tmflags = thing.flags;
        
        tmx = x;
        tmy = y;
        
        tmbbox[BOXTOP] = y + tmthing.radius;
        tmbbox[BOXBOTTOM] = y - tmthing.radius;
        tmbbox[BOXRIGHT] = x + tmthing.radius;
        tmbbox[BOXLEFT] = x - tmthing.radius;

        newsubsec = LL.PointInSubsector (x,y);
        ceilingline = null;
        
        // The base floor/ceiling is from the subsector
        // that contains the point.
        // Any contacted lines the step closer together
        // will adjust them.
        tmfloorz = tmdropoffz = newsubsec.sector.floorheight;
        tmceilingz = newsubsec.sector.ceilingheight;
                
        R.increaseValidCount(1); // This is r_main's ?
        numspechit = 0;
        
        // stomp on any things contacted
        xl = LL.getSafeBlockX(tmbbox[BOXLEFT] - LL.bmaporgx - MAXRADIUS);
        xh = LL.getSafeBlockX(tmbbox[BOXRIGHT] - LL.bmaporgx + MAXRADIUS);
        yl = LL.getSafeBlockY(tmbbox[BOXBOTTOM] - LL.bmaporgy - MAXRADIUS);
        yh = LL.getSafeBlockY(tmbbox[BOXTOP] - LL.bmaporgy + MAXRADIUS);

        for (bx=xl ; bx<=xh ; bx++)
        for (by=yl ; by<=yh ; by++)
            if (!BlockThingsIterator(bx,by,StompThing))
            return false;
        
        // the move is ok,
        // so link the thing into its new position
        UnsetThingPosition (thing);

        thing.floorz = tmfloorz;
        thing.ceilingz = tmceilingz;    
        thing.x = x;
        thing.y = y;

        LL.SetThingPosition (thing);
        
        return true;
    }

    //
    // MOVEMENT CLIPPING
    //

    /**
     * P_CheckPosition
     *  This is purely informative, nothing is modified
     *  (except things picked up).
     *  
     *  in:
     *   a mobj_t (can be valid or invalid)
     *   a position to be checked
     *    (doesn't need to be related to the mobj_t.x,y)
     * 
     *  during:
     *   special things are touched if MF_PICKUP
     *   early out on solid lines?
     * 
     *  out:
     *   newsubsec
     *   floorz
     *   ceilingz
     *   tmdropoffz
     *    the lowest point contacted
     *    (monsters won't move to a dropoff)
     *   speciallines[]
     *   numspeciallines
     * @param thing
     * @param x fixed_t
     * @param y fixed_t
    */
    public boolean
    CheckPosition
    ( mobj_t    thing,
      int   x,
      int   y )
    {
        int         xl;
        int         xh;
        int         yl;
        int         yh;
        int         bx;
        int         by;
        subsector_t newsubsec;

        tmthing = thing;
        tmflags = thing.flags;
        
        tmx = x;
        tmy = y;
        
        tmbbox[BOXTOP] = y + tmthing.radius;
        tmbbox[BOXBOTTOM] = y - tmthing.radius;
        tmbbox[BOXRIGHT] = x + tmthing.radius;
        tmbbox[BOXLEFT] = x - tmthing.radius;

        newsubsec = LL.PointInSubsector (x,y);
        ceilingline = null;
        
        // The base floor / ceiling is from the subsector
        // that contains the point.
        // Any contacted lines the step closer together
        // will adjust them.
        tmfloorz = tmdropoffz = newsubsec.sector.floorheight;
        tmceilingz = newsubsec.sector.ceilingheight;
                
        R.increaseValidCount(1);
        numspechit = 0;

        if ( eval(tmflags &MF_NOCLIP ))
        return true;
        
        // Check things first, possibly picking things up.
        // The bounding box is extended by MAXRADIUS
        // because mobj_ts are grouped into mapblocks
        // based on their origin point, and can overlap
        // into adjacent blocks by up to MAXRADIUS units.
        xl = LL.getSafeBlockX(tmbbox[BOXLEFT] - LL.bmaporgx - MAXRADIUS);
        xh = LL.getSafeBlockX(tmbbox[BOXRIGHT] - LL.bmaporgx + MAXRADIUS);
        yl = LL.getSafeBlockY(tmbbox[BOXBOTTOM] - LL.bmaporgy - MAXRADIUS);
        yh = LL.getSafeBlockY(tmbbox[BOXTOP] - LL.bmaporgy + MAXRADIUS);

        for (bx=xl ; bx<=xh ; bx++)
        for (by=yl ; by<=yh ; by++)
            if (!BlockThingsIterator(bx,by,CheckThing))
            return false;
        
        // check lines
        xl = LL.getSafeBlockX(tmbbox[BOXLEFT] - LL.bmaporgx);
        xh = LL.getSafeBlockX(tmbbox[BOXRIGHT] - LL.bmaporgx);
        yl = LL.getSafeBlockY(tmbbox[BOXBOTTOM] - LL.bmaporgy);
        yh = LL.getSafeBlockY(tmbbox[BOXTOP] - LL.bmaporgy);

        // Maes's quick and dirty blockmap extension hack
        // E.g. for an extension of 511 blocks, max negative is -1.
        // A full 512x512 blockmap doesn't have negative indexes.
        
        if (xl<=LL.blockmapxneg) xl=0x1FF&xl;         // Broke width boundary
        if (xh<=LL.blockmapxneg) xh=0x1FF&xh;    // Broke width boundary
        if (yl<=LL.blockmapyneg) yl=0x1FF&yl;        // Broke height boundary
        if (yh<=LL.blockmapyneg) yh=0x1FF&yh;   // Broke height boundary     
       
        for (bx=xl ; bx<=xh ; bx++)
        for (by=yl ; by<=yh ; by++)
            if (!BlockLinesIterator (bx,by,CheckLine))
            return false;

        return true;
    }


    /**
    // P_TryMove
    // Attempt to move to a new position,
    // crossing special lines unless MF_TELEPORT is set.
     * @param x fixed_t
     * @param y fixed_t
     * 
    */
    boolean
    TryMove
    ( mobj_t    thing,
      int   x,
      int   y )
    {
        int oldx, oldy; // fixed_t    
        boolean     side, oldside; // both were int
        line_t  ld;

        floatok = false;
        if (!CheckPosition (thing, x, y))
        return false;       // solid wall or thing
        
        if ( !eval(thing.flags& MF_NOCLIP) )
        {
        if (tmceilingz - tmfloorz < thing.height)
            return false;   // doesn't fit

        floatok = true;
        
        if ( !eval(thing.flags&MF_TELEPORT) 
             &&tmceilingz - thing.z < thing.height)
            return false;   // mobj must lower itself to fit

        if ( !eval(thing.flags&MF_TELEPORT)
             && tmfloorz - thing.z > 24*FRACUNIT )
            return false;   // too big a step up

        if ( !eval(thing.flags&(MF_DROPOFF|MF_FLOAT))
             && tmfloorz - tmdropoffz > 24*FRACUNIT )
            return false;   // don't stand over a dropoff
        }
        
        // the move is ok,
        // so link the thing into its new position
        UnsetThingPosition (thing);

        oldx = thing.x;
        oldy = thing.y;
        thing.floorz = tmfloorz;
        thing.ceilingz = tmceilingz;    
        thing.x = x;
        thing.y = y;

        LL.SetThingPosition (thing);
        
        // if any special lines were hit, do the effect
        if (! eval(thing.flags&(MF_TELEPORT|MF_NOCLIP)) )
        {
        while (numspechit-->0)
        {
            // see if the line was crossed
            ld = spechit[numspechit];
            side = ld.PointOnLineSide (thing.x, thing.y );
            oldside = ld.PointOnLineSide (oldx, oldy );
            if (side != oldside)
            {
            if (ld.special!=0)
                CrossSpecialLine (ld, oldside?1:0, thing);
            }
        }
        }

        return true;
    }


    //
    // P_ThingHeightClip
    // Takes a valid thing and adjusts the thing.floorz,
    // thing.ceilingz, and possibly thing.z.
    // This is called for all nearby monsters
    // whenever a sector changes height.
    // If the thing doesn't fit,
    // the z will be set to the lowest value
    // and false will be returned.
    //
    boolean ThingHeightClip (mobj_t thing)
    {
        boolean     onfloor;
        
        onfloor = (thing.z == thing.floorz);
        
        CheckPosition (thing, thing.x, thing.y);  
        // what about stranding a monster partially off an edge?
        
        thing.floorz = tmfloorz;
        thing.ceilingz = tmceilingz;
        
        if (onfloor)
        {
        // walking monsters rise and fall with the floor
        thing.z = thing.floorz;
        }
        else
        {
        // don't adjust a floating monster unless forced to
        if (thing.z+thing.height > thing.ceilingz)
            thing.z = thing.ceilingz - thing.height;
        }
        
        if (thing.ceilingz - thing.floorz < thing.height)
        return false;
            
        return true;
    }



    //
    // SLIDE MOVE
    // Allows the player to slide along any angled walls.
    //
    int     bestslidefrac; // fixed
    int     secondslidefrac;

    line_t     bestslideline;
    line_t     secondslideline;

    mobj_t     slidemo;

    int     tmxmove; //fixed_t
    int     tmymove;



    //
    // P_HitSlideLine
    // Adjusts the xmove / ymove
    // so that the next move will slide along the wall.
    //
    private void HitSlideLine (line_t ld)
    {
        boolean         side;

        // all angles
        long     lineangle, moveangle,deltaangle;
        
        // fixed_t
        int     movelen,  newlen;
        
        
        if (ld.slopetype == slopetype_t.ST_HORIZONTAL)
        {
        tmymove = 0;
        return;
        }
        
        if (ld.slopetype == slopetype_t.ST_VERTICAL)
        {
        tmxmove = 0;
        return;
        }
        
        side = ld.PointOnLineSide (slidemo.x, slidemo.y);
        
        lineangle = R.PointToAngle2 (0,0, ld.dx, ld.dy);

        if (side == true)
        lineangle += ANG180;

        moveangle = R.PointToAngle2 (0,0, tmxmove, tmymove);
        deltaangle = (moveangle-lineangle)&BITS32;

        if (deltaangle > ANG180)
        deltaangle += ANG180;
        //  system.Error ("SlideLine: ang>ANG180");

        //lineangle >>>= ANGLETOFINESHIFT;
        //deltaangle >>>= ANGLETOFINESHIFT;
        
        movelen = AproxDistance (tmxmove, tmymove);
        newlen = FixedMul (movelen, finecosine(deltaangle));

        tmxmove = FixedMul (newlen, finecosine(lineangle)); 
        tmymove = FixedMul (newlen, finesine(lineangle));   
    }
    
    protected final static int FUDGE=0x800;///(FRACUNIT/MAPFRACUNIT);
    
    //
    // P_SlideMove
    // The momx / momy move is bad, so try to slide
    // along a wall.
    // Find the first line hit, move flush to it,
    // and slide along it
    //
    // This is a kludgy mess.
    //
    void SlideMove (mobj_t mo)
    {
        // fixed_t
        int     leadx,leady,trailx,traily,newx,newy;
        int         hitcount;
            
        slidemo = mo;
        hitcount = 0;
        
      do {
        if (++hitcount == 3) {
            // goto stairstep
            stairstep(mo);
            return;
        }     // don't loop forever

        
        // trace along the three leading corners
        if (mo.momx > 0)
        {
        leadx = mo.x + mo.radius;
        trailx = mo.x - mo.radius;
        }
        else
        {
        leadx = mo.x - mo.radius;
        trailx = mo.x + mo.radius;
        }
        
        if (mo.momy > 0)
        {
        leady = mo.y + mo.radius;
        traily = mo.y - mo.radius;
        }
        else
        {
        leady = mo.y - mo.radius;
        traily = mo.y + mo.radius;
        }
            
        bestslidefrac = FRACUNIT+1;
        
        PathTraverse ( leadx, leady, leadx+mo.momx, leady+mo.momy,
                 PT_ADDLINES, SlideTraverse );
        PathTraverse ( trailx, leady, trailx+mo.momx, leady+mo.momy,
                 PT_ADDLINES, SlideTraverse );
        PathTraverse ( leadx, traily, leadx+mo.momx, traily+mo.momy,
                 PT_ADDLINES, SlideTraverse );
        
        // move up to the wall
        if (bestslidefrac == FRACUNIT+1)
        {
            // the move most have hit the middle, so stairstep
            stairstep(mo);
            return;
          }     // don't loop forever

        // fudge a bit to make sure it doesn't hit
        bestslidefrac -= FUDGE; 
        if (bestslidefrac > 0)
        {
        newx = FixedMul (mo.momx, bestslidefrac);
        newy = FixedMul (mo.momy, bestslidefrac);
        
        if (!TryMove (mo, mo.x+newx, mo.y+newy))
        {
            // goto stairstep
            stairstep(mo);
            return;
        }     // don't loop forever
        }
        
        // Now continue along the wall.
        // First calculate remainder.
        bestslidefrac = FRACUNIT-(bestslidefrac+FUDGE);
        
        if (bestslidefrac > FRACUNIT)
        bestslidefrac = FRACUNIT;
        
        if (bestslidefrac <= 0)
        return;
        
        tmxmove = FixedMul (mo.momx, bestslidefrac);
        tmymove = FixedMul (mo.momy, bestslidefrac);

        HitSlideLine (bestslideline); // clip the moves

        mo.momx = tmxmove;
        mo.momy = tmymove;
        
          }
      // goto retry
        while (!TryMove (mo, mo.x+tmxmove, mo.y+tmymove));
    }

    /** Fugly "goto stairstep" simulation
     * 
     * @param mo
     */
    
    private final void stairstep(mobj_t mo){
        if (!TryMove (mo, mo.x, mo.y + mo.momy))
            TryMove (mo, mo.x + mo.momx, mo.y);
    }
    
    //
    // P_XYMovement  
    //
    protected final static int STOPSPEED =      0x1000;
    protected final static int FRICTION =       0xe800;

    public void XYMovement (mobj_t mo) 
    {
    //System.out.println("XYMovement");
    int     ptryx, ptryy; // pointers to fixed_t ???
    player_t   player;
    int  xmove, ymove; // fixed_t
          
    if ((mo.momx==0) && (mo.momy==0))
    {
    if ((mo.flags & MF_SKULLFLY)!=0)
    {
      // the skull slammed into something
      mo.flags &= ~MF_SKULLFLY;
      mo.momx = mo.momy = mo.momz = 0;

      mo.SetMobjState (mo.info.spawnstate);
    }
    return;
    }

    player = mo.player;
      
    if (mo.momx > MAXMOVE)
    mo.momx = MAXMOVE;
    else if (mo.momx < -MAXMOVE)
    mo.momx = -MAXMOVE;

    if (mo.momy > MAXMOVE)
    mo.momy = MAXMOVE;
    else if (mo.momy < -MAXMOVE)
    mo.momy = -MAXMOVE;
      
    xmove = mo.momx;
    ymove = mo.momy;

    do
    {
    if (xmove > MAXMOVE/2 || ymove > MAXMOVE/2)
    {
      ptryx = mo.x + xmove/2;
      ptryy = mo.y + ymove/2;
      xmove >>= 1;
      ymove >>= 1;
    }
    else
    {
      ptryx = mo.x + xmove;
      ptryy = mo.y + ymove;
      xmove = ymove = 0;
    }
      
    if (!TryMove (mo, ptryx, ptryy))
    {
      // blocked move
      if (mo.player!=null)
      {   // try to slide along it
      SlideMove (mo);
      }
      else if (eval(mo.flags & MF_MISSILE))
      {
      // explode a missile
      if (ceilingline!=null &&
          ceilingline.backsector!=null &&
          ceilingline.backsector.ceilingpic == TM.getSkyFlatNum())
      {
          // Hack to prevent missiles exploding
          // against the sky.
          // Does not handle sky floors.
          RemoveMobj (mo);
          return;
      }
      ExplodeMissile (mo);
      }
      else
      mo.momx = mo.momy = 0;
    }
    } while ((xmove | ymove)!=0);

    // slow down
    if (player!=null && eval(player.cheats & player_t.CF_NOMOMENTUM))
    {
    // debug option for no sliding at all
    mo.momx = mo.momy = 0;
    return;
    }

    if (eval(mo.flags & (MF_MISSILE | MF_SKULLFLY)) )
    return;     // no friction for missiles ever
      
    if (mo.z > mo.floorz)
    return;     // no friction when airborne

    if (eval(mo.flags & MF_CORPSE))
    {
    // do not stop sliding
    //  if halfway off a step with some momentum
    if (mo.momx > FRACUNIT/4
      || mo.momx < -FRACUNIT/4
      || mo.momy > FRACUNIT/4
      || mo.momy < -FRACUNIT/4)
    {
      if (mo.floorz != mo.subsector.sector.floorheight)
      return;
    }
    }

    if (mo.momx > -STOPSPEED
    && mo.momx < STOPSPEED
    && mo.momy > -STOPSPEED
    && mo.momy < STOPSPEED
    && (player==null
      || (player.cmd.forwardmove== 0
      && player.cmd.sidemove == 0 ) ) )
    {
    // if in a walking frame, stop moving
    // TODO: we need a way to get state indexed inside of states[], to sim pointer arithmetic.
   // FIX: added an "id" field.
    if ( player!=null&&(int)(player.mo.state.id- statenum_t.S_PLAY_RUN1.ordinal()) < 4)
        player.mo.SetMobjState (statenum_t.S_PLAY);

    mo.momx = 0;
    mo.momy = 0;
    }
    else
    {
    mo.momx = FixedMul (mo.momx, FRICTION);
    mo.momy = FixedMul (mo.momy, FRICTION);
    }
    }
    //
    // P_LineAttack
    //
    /** who got hit (or NULL) */
    public mobj_t     linetarget;
    mobj_t     shootthing;

    // Height if not aiming up or down
    // ???: use slope for monsters?
    int     shootz; // fixed_t

    int     la_damage;
    int     attackrange; // fixed_t

    int     aimslope; // fixed_t

    


    //
    // 
    //
    /** P_AimLineAttack
     * @param t1
     * @param angle long
     * @param distance int
     */
   int
   AimLineAttack
    ( mobj_t   t1,
      long   angle,
      int   distance )
    {
        int x2,y2;
        shootthing = t1;
        
        x2 = t1.x + (distance>>FRACBITS)*finecosine(angle);
        y2 = t1.y + (distance>>FRACBITS)*finesine(angle);
        shootz = t1.z + (t1.height>>1) + 8*FRACUNIT;

        // can't shoot outside view angles
        topslope = 100*FRACUNIT/160;    
        bottomslope = -100*FRACUNIT/160;
        
        attackrange = distance;
        linetarget = null;
        
        PathTraverse ( t1.x, t1.y,
                 x2, y2,
                 PT_ADDLINES|PT_ADDTHINGS,
                 AimTraverse );
            
        if (linetarget!=null)
        return aimslope;

        return 0;
    }
     

    /**
     * P_LineAttack
     * If damage == 0, it is just a test trace
     * that will leave linetarget set.
     * 
     * @param t1
     * @param angle angle_t
     * @param distance fixed_t
     * @param slope fixed_t
     * @param damage
     */
   
    void
    LineAttack
    ( mobj_t   t1,
      long   angle,
      int   distance,
      int   slope,
      int       damage )
    {
        int x2,y2;
        
        shootthing = t1;
        la_damage = damage;
        x2 = t1.x + (distance>>FRACBITS)*finecosine(angle);
        y2 = t1.y + (distance>>FRACBITS)*finesine(angle);
        shootz = t1.z + (t1.height>>1) + 8*FRACUNIT;
        attackrange = distance;
        aimslope = slope;
            
        PathTraverse ( t1.x, t1.y,
                 x2, y2,
                 PT_ADDLINES|PT_ADDTHINGS,
                 ShootTraverse );
    }
     


    //
    // USE LINES
    //
    mobj_t     usething;

    boolean UseTraverse (intercept_t in)
    {
        boolean     side;
        // FIXME: some sanity check here?
        line_t line=(line_t) in.d();
        
        if (line.special==0)
        {
        LineOpening (line);
        if (openrange <= 0)
        {
        	 S.StartSound(usething, sfxenum_t.sfx_noway);
            
            // can't use through a wall
            return false;   
        }
        // not a special line, but keep checking
        return true ;       
        }
        
        side = false;
        if (line.PointOnLineSide (usething.x, usething.y))
        side = true;
        
        //  return false;       // don't use back side
        
        UseSpecialLine (usething, line, side);

        // can't use for than one special line in a row
        return false;
    }

    /**
     * P_UseSpecialLine
     * Called when a thing uses a special line.
     * Only the front sides of lines are usable.
     */
    
    public boolean
    UseSpecialLine
    ( mobj_t   thing,
      line_t   line,
      boolean      side )
    {               

        // Err...
        // Use the back sides of VERY SPECIAL lines...
        if (side)
        {
       switch(line.special)
       {
         case 124:
	           // Sliding door open&close
	           // SL.EV_SlidingDoor(line, thing);
           break;

         default:
           return false;
           //break;
       }
        }

        
        // Switches that other things can activate.
        //_D_: little bug fixed here, see linuxdoom source
        if (thing.player==/*!=*/null)
        {
       // never open secret doors
       if (eval(line.flags& ML_SECRET))
           return false;
       
       switch(line.special)
       {
         case 1:   // MANUAL DOOR RAISE
         case 32:  // MANUAL BLUE
         case 33:  // MANUAL RED
         case 34:  // MANUAL YELLOW
           break;
           
         default:
           return false;
           //break;
       }
        }

        
        // do something  
        switch (line.special)
        {
       // MANUALS
          case 1:      // Vertical Door
          case 26:     // Blue Door/Locked
          case 27:     // Yellow Door /Locked
          case 28:     // Red Door /Locked

          case 31:     // Manual door open
          case 32:     // Blue locked door open
          case 33:     // Red locked door open
          case 34:     // Yellow locked door open

          case 117:        // Blazing door raise
          case 118:        // Blazing door open
       VerticalDoor (line, thing);
       break;
       
       //UNUSED - Door Slide Open&Close
        case 124:
         // NOTE: clashes with secret level exit.
         //SL.EV_SlidingDoor (line, thing);
         break;

       // SWITCHES
          case 7:
       // Build Stairs
       if (BuildStairs(line,stair_e.build8))
           SW.ChangeSwitchTexture(line,false);
       break;

          case 9:
       // Change Donut
       if (DoDonut(line))
           SW.ChangeSwitchTexture(line,false);
       break;
       
          case 11:
       // Exit level
              SW.ChangeSwitchTexture(line,false);
       DM.ExitLevel ();
       break;
       
          case 14:
       // Raise Floor 32 and change texture
       if (PEV.DoPlat(line,plattype_e.raiseAndChange,32))
           SW.ChangeSwitchTexture(line,false);
       break;
       
          case 15:
       // Raise Floor 24 and change texture
       if (PEV.DoPlat(line,plattype_e.raiseAndChange,24))
           SW.ChangeSwitchTexture(line,false);
       break;
       
          case 18:
       // Raise Floor to next highest floor
       if (DoFloor(line, floor_e.raiseFloorToNearest))
           SW.ChangeSwitchTexture(line,false);
       break;
       
          case 20:
       // Raise Plat next highest floor and change texture
       if (PEV.DoPlat(line,plattype_e.raiseToNearestAndChange,0))
           SW.ChangeSwitchTexture(line,false);
       break;
       
          case 21:
       // PlatDownWaitUpStay
       if (PEV.DoPlat(line,plattype_e.downWaitUpStay,0))
           SW.ChangeSwitchTexture(line,false);
       break;
       
          case 23:
       // Lower Floor to Lowest
       if (DoFloor(line,floor_e.lowerFloorToLowest))
           SW.ChangeSwitchTexture(line,false);
       break;
       
          case 29:
       // Raise Door
       if (DoDoor(line,vldoor_e.normal))
           SW.ChangeSwitchTexture(line,false);
       break;
       
          case 41:
       // Lower Ceiling to Floor
       if (DoCeiling(line,ceiling_e.lowerToFloor))
           SW.ChangeSwitchTexture(line,false);
       break;
       
          case 71:
       // Turbo Lower Floor
       if (DoFloor(line,floor_e.turboLower))
           SW.ChangeSwitchTexture(line,false);
       break;
       
          case 49:
       // Ceiling Crush And Raise
       if (DoCeiling(line,ceiling_e.crushAndRaise))
           SW.ChangeSwitchTexture(line,false);
       break;
       
          case 50:
       // Close Door
       if (DoDoor(line,vldoor_e.close))
           SW.ChangeSwitchTexture(line,false);
       break;
       
          case 51:
       // Secret EXIT
              SW.ChangeSwitchTexture(line,false);
       DM.SecretExitLevel ();
       break;
       
          case 55:
       // Raise Floor Crush
       if (DoFloor(line,floor_e.raiseFloorCrush))
           SW.ChangeSwitchTexture(line,false);
       break;
       
          case 101:
       // Raise Floor
       if (DoFloor(line,floor_e.raiseFloor))
           SW.ChangeSwitchTexture(line,false);
       break;
       
          case 102:
       // Lower Floor to Surrounding floor height
       if (DoFloor(line,floor_e.lowerFloor))
           SW.ChangeSwitchTexture(line,false);
       break;
       
          case 103:
       // Open Door
       if (DoDoor(line,vldoor_e.open))
           SW.ChangeSwitchTexture(line,false);
       break;
       
          case 111:
       // Blazing Door Raise (faster than TURBO!)
       if (DoDoor (line,vldoor_e.blazeRaise))
           SW.ChangeSwitchTexture(line,false);
       break;
       
          case 112:
       // Blazing Door Open (faster than TURBO!)
       if (DoDoor (line,vldoor_e.blazeOpen))
           SW.ChangeSwitchTexture(line,false);
       break;
       
          case 113:
       // Blazing Door Close (faster than TURBO!)
       if (DoDoor (line,vldoor_e.blazeClose))
           SW.ChangeSwitchTexture(line,false);
       break;
       
          case 122:
       // Blazing PlatDownWaitUpStay
       if (PEV.DoPlat(line,plattype_e.blazeDWUS,0))
           SW.ChangeSwitchTexture(line,false);
       break;
       
          case 127:
       // Build Stairs Turbo 16
       if (BuildStairs(line,stair_e.turbo16))
           SW.ChangeSwitchTexture(line,false);
       break;
       
          case 131:
       // Raise Floor Turbo
       if (DoFloor(line,floor_e.raiseFloorTurbo))
           SW.ChangeSwitchTexture(line,false);
       break;
       
          case 133:
       // BlzOpenDoor BLUE
          case 135:
       // BlzOpenDoor RED
          case 137:
       // BlzOpenDoor YELLOW
       if (DoLockedDoor (line,vldoor_e.blazeOpen,thing))
           SW.ChangeSwitchTexture(line,false);
       break;
       
          case 140:
       // Raise Floor 512
       if (DoFloor(line,floor_e.raiseFloor512))
           SW.ChangeSwitchTexture(line,false);
       break;
       
       // BUTTONS
          case 42:
       // Close Door
       if (DoDoor(line,vldoor_e.close))
           SW.ChangeSwitchTexture(line,true);
       break;
       
          case 43:
       // Lower Ceiling to Floor
       if (DoCeiling(line,ceiling_e.lowerToFloor))
           SW.ChangeSwitchTexture(line,true);
       break;
       
          case 45:
       // Lower Floor to Surrounding floor height
       if (DoFloor(line,floor_e.lowerFloor))
           SW.ChangeSwitchTexture(line,true);
       break;
       
          case 60:
       // Lower Floor to Lowest
       if (DoFloor(line,floor_e.lowerFloorToLowest))
           SW.ChangeSwitchTexture(line,true);
       break;
       
          case 61:
       // Open Door
       if (DoDoor(line,vldoor_e.open))
           SW.ChangeSwitchTexture(line,true);
       break;
       
          case 62:
       // PlatDownWaitUpStay
       if (PEV.DoPlat(line,plattype_e.downWaitUpStay,1))
           SW.ChangeSwitchTexture(line,true);
       break;
       
          case 63:
       // Raise Door
       if (DoDoor(line,vldoor_e.normal))
           SW.ChangeSwitchTexture(line,true);
       break;
       
          case 64:
       // Raise Floor to ceiling
       if (DoFloor(line,floor_e.raiseFloor))
           SW.ChangeSwitchTexture(line,true);
       break;
       
          case 66:
       // Raise Floor 24 and change texture
       if (PEV.DoPlat(line,plattype_e.raiseAndChange,24))
           SW.ChangeSwitchTexture(line,true);
       break;
       
          case 67:
       // Raise Floor 32 and change texture
       if (PEV.DoPlat(line,plattype_e.raiseAndChange,32))
           SW.ChangeSwitchTexture(line,true);
       break;
       
          case 65:
       // Raise Floor Crush
       if (DoFloor(line,floor_e.raiseFloorCrush))
           SW.ChangeSwitchTexture(line,true);
       break;
       
          case 68:
       // Raise Plat to next highest floor and change texture
       if (PEV.DoPlat(line,plattype_e.raiseToNearestAndChange,0))
           SW.ChangeSwitchTexture(line,true);
       break;
       
          case 69:
       // Raise Floor to next highest floor
       if (DoFloor(line, floor_e.raiseFloorToNearest))
           SW.ChangeSwitchTexture(line,true);
       break;
       
          case 70:
       // Turbo Lower Floor
       if (DoFloor(line,floor_e.turboLower))
           SW.ChangeSwitchTexture(line,true);
       break;
       
          case 114:
       // Blazing Door Raise (faster than TURBO!)
       if (DoDoor (line,vldoor_e.blazeRaise))
           SW.ChangeSwitchTexture(line,true);
       break;
       
          case 115:
       // Blazing Door Open (faster than TURBO!)
       if (DoDoor (line,vldoor_e.blazeOpen))
           SW.ChangeSwitchTexture(line,true);
       break;
       
          case 116:
       // Blazing Door Close (faster than TURBO!)
       if (DoDoor (line,vldoor_e.blazeClose))
           SW.ChangeSwitchTexture(line,true);
       break;
       
          case 123:
       // Blazing PlatDownWaitUpStay
       if (PEV.DoPlat(line,plattype_e.blazeDWUS,0))
           SW.ChangeSwitchTexture(line,true);
       break;
       
          case 132:
       // Raise Floor Turbo
       if (DoFloor(line,floor_e.raiseFloorTurbo))
           SW.ChangeSwitchTexture(line,true);
       break;
       
          case 99:
       // BlzOpenDoor BLUE
          case 134:
       // BlzOpenDoor RED
          case 136:
       // BlzOpenDoor YELLOW
       if (DoLockedDoor (line,vldoor_e.blazeOpen,thing))
           SW.ChangeSwitchTexture(line,true);
       break;
       
          case 138:
       // Light Turn On
       LEV.LightTurnOn(line,255);
       SW.ChangeSwitchTexture(line,true);
       break;
       
          case 139:
       // Light Turn Off
       LEV.LightTurnOn(line,35);
       SW.ChangeSwitchTexture(line,true);
       break;
               
        }
       
        return true;
    }
    

    /**
     * P_UseLines
     * Looks for special lines in front of the player to activate.
     */
    public void UseLines (player_t  player) 
    {
        int     angle;
        int x1,y1,x2,y2;
        //System.out.println("Uselines");
        usething = player.mo;
        
        // Normally this shouldn't cause problems?
        angle = Tables.toBAMIndex(player.mo.angle);

        x1 = player.mo.x;
        y1 = player.mo.y;
        x2 = x1 + (USERANGE>>FRACBITS)*finecosine[angle];
        y2 = y1 + (USERANGE>>FRACBITS)*finesine[angle];
        
        PathTraverse ( x1, y1, x2, y2, PT_ADDLINES, UseTraverse );
    }


    //
    // RADIUS ATTACK
    //
    mobj_t     bombsource;
    mobj_t     bombspot;
    int     bombdamage;

    /**
     * P_RadiusAttack
     * Source is the creature that caused the explosion at spot.
     */
    
    void
    RadiusAttack
    ( mobj_t   spot,
      mobj_t   source,
      int       damage )
    {
        int     x;
        int     y;
        
        int     xl;
        int     xh;
        int     yl;
        int     yh;
        
        int dist; // fixed_t
        
        dist = (damage+MAXRADIUS)<<FRACBITS;
        yh = LL.getSafeBlockY(spot.y + dist - LL.bmaporgy);
        yl = LL.getSafeBlockY(spot.y - dist - LL.bmaporgy);
        xh = LL.getSafeBlockX(spot.x + dist - LL.bmaporgx);
        xl = LL.getSafeBlockX(spot.x - dist - LL.bmaporgx);
        bombspot = spot;
        bombsource = source;
        bombdamage = damage;
        
        for (y=yl ; y<=yh ; y++)
        for (x=xl ; x<=xh ; x++)
            BlockThingsIterator (x, y, RadiusAttack );
    }



    //
    // SECTOR HEIGHT CHANGING
    // After modifying a sectors floor or ceiling height,
    // call this routine to adjust the positions
    // of all things that touch the sector.
    //
    // If anything doesn't fit anymore, true will be returned.
    // If crunch is true, they will take damage
    //  as they are being crushed.
    // If Crunch is false, you should set the sector height back
    //  the way it was and call P_ChangeSector again
    //  to undo the changes.
    //
    boolean     crushchange;
    boolean     nofit;


    //
    // P_ChangeSector
    //
    boolean
    ChangeSector
    ( sector_t sector,
      boolean   crunch )
    {
        int     x;
        int     y;
        
        nofit = false;
        crushchange = crunch;
        
        // re-check heights for all things near the moving sector
        for (x=sector.blockbox[BOXLEFT] ; x<= sector.blockbox[BOXRIGHT] ; x++)
        for (y=sector.blockbox[BOXBOTTOM];y<= sector.blockbox[BOXTOP] ; y++)
            BlockThingsIterator (x, y, ChangeSector);
        
        
        return nofit;
    }
    
    /**
     * P_BlockLinesIterator
     * The validcount flags are used to avoid checking lines
     * that are marked in multiple mapblocks,
     * so increment validcount before the first call
     * to P_BlockLinesIterator, then make one or more calls
     * to it.
     */

    public boolean BlockLinesIterator (int x,int y,PIT_LineFunction func)
    {
     int         offset;
     int 		lineinblock;
     line_t     ld;
     
     if (x<0
     || y<0
     || x>=LL.bmapwidth
     || y>=LL.bmapheight)
     {
     return true;
     }
     
     // This gives us the index to look up (in blockmap)
     
     offset = y*LL.bmapwidth+x;
     
     // The index contains yet another offset, but this time 
     offset = LL.blockmap[offset];

     // MAES: blockmap terminating marker is always -1
     
     final int validcount=R.getValidCount();
     
     // [SYNC ISSUE]: don't skip offset+1 :-/
     for (int list=offset;(lineinblock=LL.blockmap[list])!=-1;list++){
    	 ld = LL.lines[lineinblock];
         //System.out.println(ld);
         if (ld.validcount == validcount)
             continue;   // line has already been checked

         ld.validcount = validcount;
         if ( !func.invoke(ld) )
             return false;
         }
     return true;    // everything was checked
    }

    
    
    /**
     * P_PathTraverse
     * Traces a line from x1,y1 to x2,y2,
     * calling the traverser function for each.
     * Returns true if the traverser function returns true
     * for all lines.
     */
	private final boolean PathTraverse(int x1, int y1, int x2, int y2, int flags,
			PTR_InterceptFunc trav) {
		// System.out.println("Pathtraverse "+x1+" , " +y1+" to "+x2 +" , "
		// +y2);
		final int xt1, yt1;
		final int xt2, yt2;
		final long _x1, _x2, _y1, _y2;
		final int mapx1, mapy1;
		final int xstep, ystep;

		int partial;

		int xintercept, yintercept;

		int mapx;
		int mapy;

		int mapxstep;
		int mapystep;

		int count;

		earlyout = eval(flags& PT_EARLYOUT);

		R.increaseValidCount(1);
		intercept_p = 0;

		if (((x1 - LL.bmaporgx) & (MAPBLOCKSIZE - 1)) == 0)
			x1 += FRACUNIT; // don't side exactly on a line

		if (((y1 - LL.bmaporgy) & (MAPBLOCKSIZE - 1)) == 0)
			y1 += FRACUNIT; // don't side exactly on a line

		trace.x = x1;
		trace.y = y1;
		trace.dx = x2 - x1;
		trace.dy = y2 - y1;

		// Code developed in common with entryway
		// for prBoom+
		
		_x1 = (long) x1 - LL.bmaporgx;
		_y1 = (long) y1 - LL.bmaporgy;
		xt1 = (int) (_x1 >> MAPBLOCKSHIFT);
		yt1 = (int) (_y1 >> MAPBLOCKSHIFT);

		mapx1 = (int) (_x1 >> MAPBTOFRAC);
		mapy1 = (int) (_y1 >> MAPBTOFRAC);

		_x2 = (long) x2 - LL.bmaporgx;
		_y2 = (long) y2 - LL.bmaporgy;
		xt2 = (int) (_x2 >> MAPBLOCKSHIFT);
		yt2 = (int) (_y2 >> MAPBLOCKSHIFT);

		x1 -= LL.bmaporgx;
		y1 -= LL.bmaporgy;
		x2 -= LL.bmaporgx;
		y2 -= LL.bmaporgy;
		
     if (xt2 > xt1)
     {
     mapxstep = 1;
     partial = FRACUNIT - (mapx1&(FRACUNIT-1));
     ystep = FixedDiv (y2-y1,Math.abs(x2-x1));
     }
     else if (xt2 < xt1)
     {
     mapxstep = -1;
     partial = mapx1&(FRACUNIT-1);
     ystep = FixedDiv (y2-y1,Math.abs(x2-x1));
     }
     else
     {
     mapxstep = 0;
     partial = FRACUNIT;
     ystep = 256*FRACUNIT;
     }   

     yintercept = (int) (mapy1 + FixedMul (partial, ystep));

     
     if (yt2 > yt1)
     {
     mapystep = 1;
     partial = FRACUNIT - (mapy1&(FRACUNIT-1));
     xstep = FixedDiv (x2-x1,Math.abs(y2-y1));
     }
     else if (yt2 < yt1)
     {
     mapystep = -1;
     partial = mapy1&(FRACUNIT-1);
     xstep = FixedDiv (x2-x1,Math.abs(y2-y1));
     }
     else
     {
     mapystep = 0;
     partial = FRACUNIT;
     xstep = 256*FRACUNIT;
     }   
     xintercept = (int) (mapx1 + FixedMul (partial, xstep));
     
     // Step through map blocks.
     // Count is present to prevent a round off error
     // from skipping the break.
     mapx = xt1;
     mapy = yt1;
     
     for (count = 0 ; count < 64 ; count++)
     {
     if (eval(flags &PT_ADDLINES))
     {
         if (!BlockLinesIterator (mapx, mapy,AddLineIntercepts))
         return false;   // early out
     }
     
     if (eval(flags &PT_ADDTHINGS))
     {
         if (!BlockThingsIterator (mapx, mapy,AddThingIntercepts))
         return false;   // early out
     }
         
     if (mapx == xt2
         && mapy == yt2)
     {
         break;
     }
     
     boolean changeX = (yintercept >> FRACBITS) == mapy;
     boolean changeY = (xintercept >> FRACBITS) == mapx;
     if (changeX)
     {
         yintercept += ystep;
         mapx += mapxstep;
     } else //[MAES]: this fixed sync issues. Lookup linuxdoom
     if (changeY)
     {
         xintercept += xstep;
         mapy += mapystep;
     }
         
     }
     // go through the sorted list
     //System.out.println("Some intercepts found");
     return TraverseIntercepts ( trav, FRACUNIT );
    } // end method
    
    //
    // FLOORS
    //
    
    private static final int FLOORSPEED= MAPFRACUNIT;

    /** Move a plane (floor or ceiling) and check for crushing
     *  @param sector
     *  @param speed fixed
     *  @param dest fixed
     *  @param crush
     *  @param floorOrCeiling
     *  @param direction
     */
    result_e
    MovePlane
    ( sector_t sector,
      int   speed,
      int   dest,
      boolean   crush,
      int       floorOrCeiling,
      int       direction )
    {
        boolean flag;
        int lastpos; // fixed_t
        
        switch(floorOrCeiling)
        {
          case 0:
        // FLOOR
        switch(direction)
        {
          case -1:
            // DOWN
            if (sector.floorheight - speed < dest)
            {
            lastpos = sector.floorheight;
            sector.floorheight = dest;
            flag = ChangeSector(sector,crush);
            if (flag == true)
            {
                sector.floorheight =lastpos;
                ChangeSector(sector,crush);
                //return crushed;
            }
            return result_e.pastdest;
            }
            else
            {
            lastpos = sector.floorheight;
            sector.floorheight -= speed;
            flag = ChangeSector(sector,crush);
            if (flag == true)
            {
                sector.floorheight = lastpos;
                ChangeSector(sector,crush);
                return result_e.crushed;
            }
            }
            break;
                            
          case 1:
            // UP
            if (sector.floorheight + speed > dest)
            {
            lastpos = sector.floorheight;
            sector.floorheight = dest;
            flag = ChangeSector(sector,crush);
            if (flag == true)
            {
                sector.floorheight = lastpos;
                ChangeSector(sector,crush);
                //return crushed;
            }
            return result_e.pastdest;
            }
            else
            {
            // COULD GET CRUSHED
            lastpos = sector.floorheight;
            sector.floorheight += speed;
            flag =ChangeSector(sector,crush);
            if (flag == true)
            {
                if (crush == true)
                return result_e.crushed;
                sector.floorheight = lastpos;
                ChangeSector(sector,crush);
                return result_e.crushed;
            }
            }
            break;
        }
        break;
                                        
          case 1:
        // CEILING
        switch(direction)
        {
          case -1:
            // DOWN
            if (sector.ceilingheight - speed < dest)
            {
            lastpos = sector.ceilingheight;
            sector.ceilingheight = dest;
            flag = ChangeSector(sector,crush);

            if (flag == true)
            {
                sector.ceilingheight = lastpos;
                ChangeSector(sector,crush);
                //return crushed;
            }
            return result_e.pastdest;
            }
            else
            {
            // COULD GET CRUSHED
            lastpos = sector.ceilingheight;
            sector.ceilingheight -= speed;
            flag = ChangeSector(sector,crush);

            if (flag == true)
            {
                if (crush == true)
                return result_e.crushed;
                sector.ceilingheight = lastpos;
                ChangeSector(sector,crush);
                return result_e.crushed;
            }
            }
            break;
                            
          case 1:
            // UP
            if (sector.ceilingheight + speed > dest)
            {
            lastpos = sector.ceilingheight;
            sector.ceilingheight = dest;
            flag =ChangeSector(sector,crush);
            if (flag == true)
            {
                sector.ceilingheight = lastpos;
                ChangeSector(sector,crush);
                //return crushed;
            }
            return result_e.pastdest;
            }
            else
            {
            lastpos = sector.ceilingheight;
            sector.ceilingheight += speed;
            flag = ChangeSector(sector,crush);
    // UNUSED
    /*
            if (flag == true)
            {
                sector.ceilingheight = lastpos;
                P_ChangeSector(sector,crush);
                return crushed;
            }
    */
            }
            break;
        }
        break;
            
        }
        return result_e.ok;
    }
    
    /** P_CheckMissileSpawn
     * Moves the missile forward a bit
     * and possibly explodes it right there.
     * 
     * @param th
     */

    void CheckMissileSpawn (mobj_t th)
    {
    th.tics -= RND.P_Random()&3;
    if (th.tics < 1)
    th.tics = 1;

    // move a little forward so an angle can
    // be computed if it immediately explodes
    th.x += (th.momx>>1);
    th.y += (th.momy>>1);
    th.z += (th.momz>>1);

    if (!TryMove (th, th.x, th.y))
    ExplodeMissile (th);
    }
    
    //
  //P_Ticker
  //

  public void Ticker ()
  {
     int     i;
     
     // run the tic
     if (DM.paused)
     return;
         
     // pause if in menu and at least one tic has been run
     if ( !DM.netgame
      && DM.menuactive
      && !DM.demoplayback
      && DM.players[DM.consoleplayer].viewz != 1)
     {
     return;
     }
     
         
     for (i=0 ; i<MAXPLAYERS ; i++)
     if (DM.playeringame[i])
         DM.players[i].PlayerThink ();
             
     RunThinkers ();
     SPECS.UpdateSpecials (); // In specials. Merge?
     RespawnSpecials ();

     // for par times
     DM.leveltime++;    
  }
  
  /**
   * P_ShootSpecialLine - IMPACT SPECIALS
   * Called when a thing shoots a special line.
   */
  public void
  ShootSpecialLine
  ( mobj_t   thing,
    line_t   line )
  {
      boolean     ok;
      
      //  Impacts that other things can activate.
      if (thing.player==null)
      {
      ok = false;
      switch(line.special)
      {
        case 46:
          // OPEN DOOR IMPACT
          ok = true;
          break;
      }
      if (!ok)
          return;
      }

      switch(line.special)
      {
        case 24:
      // RAISE FLOOR
      DoFloor(line,floor_e.raiseFloor);
      SW.ChangeSwitchTexture(line,false);
      break;
      
        case 46:
      // OPEN DOOR
      DoDoor(line,vldoor_e.open);
      SW.ChangeSwitchTexture(line,true);
      break;
      
        case 47:
      // RAISE FLOOR NEAR AND CHANGE
      PEV.DoPlat(line,plattype_e.raiseToNearestAndChange,0);
      SW.ChangeSwitchTexture(line,false);
      break;
      }
  }

  /** P_SpawnSpecials
   * After the map has been loaded, scan for specials
   * that spawn thinkers
  */

  void SpawnSpecials ()
  {
	  
	  
      sector_t   sector;
      int     i;
      
      /*int     episode;

      episode = 1;
      if (W.CheckNumForName("texture2") >= 0)
      episode = 2;
      */
      
      // See if -TIMER needs to be used.
      SPECS.levelTimer = false;
      
      i = DM.CM.CheckParm("-avg");
      if (eval(i) && DM.deathmatch)
      {
          SPECS.levelTimer = true;
          SPECS.levelTimeCount = 20 * 60 * 35;
      }
      
      i = DM.CM.CheckParm("-timer");
      if (eval(i) && DM.deathmatch)
      {
      int time;
      time = Integer.parseInt(DM.CM.getArgv(i+1)) * 60 * 35;
      SPECS.levelTimer = true;
      SPECS.levelTimeCount = time;
      }
      
      //  Init special SECTORs.
      //sector = LL.sectors;
      for (i=0 ; i<LL.numsectors ; i++)
      {
          sector=LL.sectors[i];
      if (!eval(sector.special))
          continue;
      
      switch (sector.special)
      {
        case 1:
          // FLICKERING LIGHTS
            sector.SpawnLightFlash ();
          break;

        case 2:
          // STROBE FAST
            sector.SpawnStrobeFlash(FASTDARK,0);
          break;
          
        case 3:
          // STROBE SLOW
            sector.SpawnStrobeFlash(SLOWDARK,0);
          break;
          
        case 4:
          // STROBE FAST/DEATH SLIME
            sector.SpawnStrobeFlash(FASTDARK,0);
          sector.special = 4;
          break;
          
        case 8:
          // GLOWING LIGHT
            sector.SpawnGlowingLight();
          break;
        case 9:
          // SECRET SECTOR
          DM.totalsecret++;
          break;
          
        case 10:
          // DOOR CLOSE IN 30 SECONDS
          sector.SpawnDoorCloseIn30 ();
          break;
          
        case 12:
          // SYNC STROBE SLOW
            sector.SpawnStrobeFlash (SLOWDARK, 1);
          break;

        case 13:
          // SYNC STROBE FAST
            sector.SpawnStrobeFlash ( FASTDARK, 1);
          break;

        case 14:
          // DOOR RAISE IN 5 MINUTES
            sector.SpawnDoorRaiseIn5Mins (i);
          break;
          
        case 17:
            sector.SpawnFireFlicker();
          break;
      }
      }

      
      //  Init line EFFECTs
      SPECS.numlinespecials = 0;
      for (i = 0;i < LL.numlines; i++)
      {
      switch(LL.lines[i].special)
      {
        case 48:
          // EFFECT FIRSTCOL SCROLL+
          // Maes 6/4/2012: removed length limit.
          if (SPECS.numlinespecials==SPECS.linespeciallist.length)
        	  SPECS.resizeLinesSpecialList();
          SPECS.linespeciallist[SPECS.numlinespecials] = LL.lines[i];
          SPECS.numlinespecials++;
          break;
      }
      }

      
      //  Init other misc stuff
      for (i = 0;i < getMaxCeilings();i++)
    	  getActiveCeilings()[i] = null;

      PEV.initActivePlats();
      
      SW.initButtonList();


      // UNUSED: no horizonal sliders.
      // if (SL!=null) {
      // SL.updateStatus(DM);
      //  SL.P_InitSlidingDoorFrames();
      //}
  }
  
  
  
  /**
   * Move a plat up and down
   */
  void PlatRaise(plat_t plat)
  {
      result_e    res;
      
      switch(plat.status)
      {
        case up:
      res = MovePlane(plat.sector,
                plat.speed,
                plat.high,
                plat.crush,0,1);
                      
      if (plat.type == plattype_e.raiseAndChange
          || plat.type == plattype_e.raiseToNearestAndChange)
      {
          if (!eval(DM.leveltime&7))
          S.StartSound(plat.sector.soundorg, sfxenum_t.sfx_stnmov);
      }
      
                  
      if (res == result_e.crushed && (!plat.crush))
      {
          plat.count = plat.wait;
          plat.status = plat_e.down;
          S.StartSound(plat.sector.soundorg, sfxenum_t.sfx_pstart);
      }
      else
      {
          if (res == result_e.pastdest)
          {
          plat.count = plat.wait;
          plat.status = plat_e.waiting;
          S.StartSound(plat.sector.soundorg,  sfxenum_t.sfx_pstop);

          switch(plat.type)
          {
            case blazeDWUS:
            case downWaitUpStay:
              PEV.RemoveActivePlat(plat);
              break;
              
            case raiseAndChange:
            case raiseToNearestAndChange:
              PEV.RemoveActivePlat(plat);
              break;
              
            default:
              break;
          }
          }
      }
      break;
      
        case  down:
      res = MovePlane(plat.sector,plat.speed,plat.low,false,0,-1);

      if (res == result_e.pastdest)
      {
          plat.count = plat.wait;
          plat.status = plat_e.waiting;
          S.StartSound(plat.sector.soundorg,sfxenum_t.sfx_pstop);
      }
      break;
      
        case  waiting:
      if (--plat.count==0)
      {
          if (plat.sector.floorheight == plat.low)
          plat.status = plat_e.up;
          else
          plat.status = plat_e.down;
          S.StartSound(plat.sector.soundorg,sfxenum_t.sfx_pstart);
      }
        case  in_stasis:
      break;
      }
  }
  
  public Actions(DoomStatus DC){
      super(DC);
      this.A=this;      
      SlideTraverse=new PTR_SlideTraverse();
      AimTraverse=new PTR_AimTraverse();
      ShootTraverse=new PTR_ShootTraverse();
      UseTraverse=new PTR_UseTraverse();

      AddLineIntercepts=new PIT_AddLineIntercepts();
       AddThingIntercepts=new PIT_AddThingIntercepts();
       VileCheck=new PIT_VileCheck();
       CheckLine=new PIT_CheckLine();
       StompThing=new PIT_StompThing();
       CheckThing=new PIT_CheckThing();
       RadiusAttack=new PIT_RadiusAttack();
       ChangeSector=new PIT_ChangeSector();
      
	  
  }

  //
  // P_RunThinkers
  //
  public void RunThinkers() {
      thinker_t currentthinker;

      currentthinker = thinkercap.next;
      while (currentthinker != thinkercap) {
          if (currentthinker.function == think_t.NOP) {
              // time to remove it
              currentthinker.next.prev = currentthinker.prev;
              currentthinker.prev.next = currentthinker.next;
              
              // Problem: freeing was done explicitly on think_t's, not mobj_t's.
            /*try {
            	  
            // According to certian gurus, this method is faster than instanceof
            // and almost on par with id checking. 
            // 
            // http://stackoverflow.com/questions/103564/the-performance-impact-of-using-instanceof-in-java
            	  if (currentthinker.getClass()==mobj_t.class)
              
              mobjpool.checkIn((mobj_t)currentthinker);
              } catch (ClassCastException e){
            	  // Object will simply be destroyed without reuse, in this case.
              }  */
              // Z_Free (currentthinker);
          } else {
              if (currentthinker.acp1!=null)
                  // Execute thinker's function.
            	  currentthinker.acp1.invoke((mobj_t) currentthinker);
              else
              if (currentthinker.acpss!=null) {
            	  currentthinker.acpss.invoke(currentthinker);
              }
          }
          currentthinker = currentthinker.next;
      }
  }


public void setActiveceilings(ceiling_t[] activeceilings) {
    this.activeceilings = activeceilings;
}

public final ceiling_t[] getActiveCeilings() {
    return activeceilings;
}

public final int getMaxCeilings() {
    return activeceilings.length;
}

///////////////////// PIT AND PTR FUNCTIONS //////////////////

PTR_InterceptFunc SlideTraverse;
PTR_InterceptFunc AimTraverse;
PTR_InterceptFunc ShootTraverse;
PTR_InterceptFunc UseTraverse;

PIT_AddLineIntercepts AddLineIntercepts;
PIT_AddThingIntercepts AddThingIntercepts;
PIT_VileCheck VileCheck;
PIT_CheckLine CheckLine;
PIT_StompThing StompThing;
PIT_CheckThing CheckThing;
PIT_RadiusAttack RadiusAttack;
PIT_ChangeSector ChangeSector;

/**
 * PIT_VileCheck
 * Detect a corpse that could be raised.
 */


class PIT_VileCheck  implements PIT_MobjFunction {
	
    public mobj_t      corpsehit;
    public mobj_t      vileobj;
    public int     viletryx;
    public int     viletryy;
    
    public boolean invoke (mobj_t   thing)
{
    int     maxdist;
    boolean check;
    
    if (!eval(thing.flags &MF_CORPSE) )
    return true;    // not a monster
    
    if (thing.tics != -1)
    return true;    // not lying still yet
    
    if (thing.info.raisestate == statenum_t.S_NULL)
    return true;    // monster doesn't have a raise state
    
    maxdist = thing.info.radius + mobjinfo[mobjtype_t.MT_VILE.ordinal()].radius;
    
    if ( Math.abs(thing.x - viletryx) > maxdist
     || Math.abs(thing.y - viletryy) > maxdist )
    return true;        // not actually touching
        
    corpsehit = thing;
    corpsehit.momx = corpsehit.momy = 0;
    corpsehit.height <<= 2;
    check = CheckPosition (corpsehit, corpsehit.x, corpsehit.y);
    corpsehit.height >>= 2;

    if (!check)
    return true;        // doesn't fit here
        
    return false;       // got one, so stop checking
	}

}

/**
 * PIT_ChangeSector
 */

private class PIT_ChangeSector implements PIT_MobjFunction {
public boolean invoke (mobj_t   thing)
{
    mobj_t mo;
    
    if (ThingHeightClip (thing))
    {
    // keep checking
    return true;
    }
    

    // crunch bodies to giblets
    if (thing.health <= 0)
    {
    thing.SetMobjState(statenum_t.S_GIBS);

    thing.flags &= ~MF_SOLID;
    thing.height = 0;
    thing.radius = 0;

    // keep checking
    return true;        
    }

    // crunch dropped items
    if (eval(thing.flags& MF_DROPPED))
    {
    A.RemoveMobj (thing);
    
    // keep checking
    return true;        
    }

    if (! eval(thing.flags & MF_SHOOTABLE) )
    {
    // assume it is bloody gibs or something
    return true;            
    }
    
    nofit = true;

    if (crushchange && !eval(DM.leveltime&3) )
    {
    A.DamageMobj( thing,null,null,10);

    // spray blood in a random direction
    mo = A.SpawnMobj (thing.x,
              thing.y,
              thing.z + thing.height/2, mobjtype_t.MT_BLOOD);
    
    mo.momx = (RND.P_Random() - RND.P_Random ())<<12;
    mo.momy = (RND.P_Random() - RND.P_Random ())<<12;
    }

    // keep checking (crush other things)   
    return true;    
	}
}


/** PIT_CheckLine
 * Adjusts tmfloorz and tmceilingz as lines are contacted
 * 
 */

protected class PIT_CheckLine implements PIT_LineFunction {
     public boolean invoke(line_t ld){
    if (tmbbox[BOXRIGHT] <= ld.bbox[BOXLEFT]
    || tmbbox[BOXLEFT] >= ld.bbox[BOXRIGHT]
    || tmbbox[BOXTOP] <= ld.bbox[BOXBOTTOM]
    || tmbbox[BOXBOTTOM] >= ld.bbox[BOXTOP] )
    return true;

    if (ld.BoxOnLineSide (tmbbox) != -1)
    return true;
        
    // A line has been hit
    
    // The moving thing's destination position will cross
    // the given line.
    // If this should not be allowed, return false.
    // If the line is special, keep track of it
    // to process later if the move is proven ok.
    // NOTE: specials are NOT sorted by order,
    // so two special lines that are only 8 pixels apart
    // could be crossed in either order.
    
    if (ld.backsector==null)
    return false;       // one sided line
        
    if (!eval(tmthing.flags& MF_MISSILE) )
    {
    if ( eval(ld.flags& ML_BLOCKING) )
        return false;   // explicitly blocking everything

    if ( (tmthing.player==null) && eval(ld.flags& ML_BLOCKMONSTERS ))
        return false;   // block monsters only
    }

    // set openrange, opentop, openbottom
    LineOpening (ld); 
    
    // adjust floor / ceiling heights
    if (opentop < tmceilingz)
    {
    tmceilingz = opentop;
    ceilingline = ld;
    }

    if (openbottom > tmfloorz)
    tmfloorz = openbottom;  

    if (lowfloor < tmdropoffz)
    tmdropoffz = lowfloor;
        
    // if contacted a special line, add it to the list
    if (ld.special!=0)
    {
    spechit[numspechit] = ld;
    numspechit++;
    // Let's be proactive about this.
    if (numspechit>=spechit.length) ResizeSpechits();
    }

    return true;
    }

}


/**PIT_CheckThing  */

private class PIT_CheckThing  implements PIT_MobjFunction {
    public boolean invoke (mobj_t thing) {
    int     blockdist; // fixed_t
    boolean     solid;
    int         damage;
        
    if ((thing.flags & (MF_SOLID|MF_SPECIAL|MF_SHOOTABLE) )==0)
    return true;
    
    blockdist = thing.radius + tmthing.radius;

    if ( Math.abs(thing.x - tmx) >= blockdist
     || Math.abs(thing.y - tmy) >= blockdist )
    {
    // didn't hit it
    return true;    
    }
    
    // don't clip against self
    if (thing == tmthing)
    return true;
    
    // check for skulls slamming into things
    if ((tmthing.flags & MF_SKULLFLY)!=0)
    {
    damage = ((RND.P_Random()%8)+1)*tmthing.info.damage;
    
    A.DamageMobj (thing, tmthing, tmthing, damage);
    
    tmthing.flags &= ~MF_SKULLFLY;
    tmthing.momx = tmthing.momy = tmthing.momz = 0;
    
    tmthing.SetMobjState (tmthing.info.spawnstate);
    
    return false;       // stop moving
    }

    
    // missiles can hit other things
    if (eval(tmthing.flags & MF_MISSILE))
    {
    // see if it went over / under
    if (tmthing.z > thing.z + thing.height)
        return true;        // overhead
    if (tmthing.z+tmthing.height < thing.z)
        return true;        // underneath
        
    if (tmthing.target!=null && (
        tmthing.target.type == thing.type || 
        (tmthing.target.type == mobjtype_t.MT_KNIGHT && thing.type == mobjtype_t.MT_BRUISER)||
        (tmthing.target.type == mobjtype_t.MT_BRUISER && thing.type == mobjtype_t.MT_KNIGHT) ) )
    {
        // Don't hit same species as originator.
        if (thing == tmthing.target)
        return true;

        if (thing.type != mobjtype_t.MT_PLAYER)
        {
        // Explode, but do no damage.
        // Let players missile other players.
        return false;
        }
    }
    
    if (! eval(thing.flags &MF_SHOOTABLE) )
    {
        // didn't do any damage
        return !eval(thing.flags & MF_SOLID);   
    }
    
    // damage / explode
    damage = ((RND.P_Random()%8)+1)*tmthing.info.damage;
    A.DamageMobj (thing, tmthing, tmthing.target, damage);

    // don't traverse any more
    return false;               
    }
    
    // check for special pickup
    if (eval(thing.flags & MF_SPECIAL))
    {
    solid = eval(thing.flags&MF_SOLID);
    if (eval(tmflags&MF_PICKUP))
    {
        // can remove thing
        A.TouchSpecialThing (thing, tmthing);
    }
    return !solid;
    }
    
    return !eval(thing.flags &MF_SOLID);
}

}

/**
 * PIT_RadiusAttack
 * "bombsource" is the creature
 * that caused the explosion at "bombspot".
 */

private class PIT_RadiusAttack implements PIT_MobjFunction {
    public boolean invoke (mobj_t thing)
{
    int dx,dy,dist; // fixed_t
    
    if (!eval(thing.flags & MF_SHOOTABLE) )
    return true;

    // Boss spider and cyborg
    // take no damage from concussion.
    if (thing.type == mobjtype_t.MT_CYBORG
    || thing.type == mobjtype_t.MT_SPIDER)
    return true;
        
    dx = Math.abs(thing.x - bombspot.x);
    dy = Math.abs(thing.y - bombspot.y);
    
    dist = dx>dy ? dx : dy;
    dist = (dist - thing.radius) >> FRACBITS;

    if (dist < 0)
    dist = 0;

    if (dist >= bombdamage)
    return true;    // out of range

    if ( EN.CheckSight (thing, bombspot) )
    {
    // must be in direct path
    A.DamageMobj (thing, bombspot, bombsource, bombdamage - dist);
    }
    
    return true;
	}
}

/**
 *PIT_StompThing
 */

private class PIT_StompThing implements PIT_MobjFunction {
public boolean invoke (mobj_t thing)
{
 int blockdist; // fixed_t
     
 if ((thing.flags & MF_SHOOTABLE)==0 )
 return true;
     
 blockdist = thing.radius + tmthing.radius;
 
 if ( Math.abs(thing.x - tmx) >= blockdist
  || Math.abs(thing.y - tmy) >= blockdist )
 {
 // didn't hit it
 return true;
 }
 
 // don't clip against self
 if (thing == tmthing)
 return true;
 
 // monsters don't stomp things except on boss level
 if ( (tmthing.player==null) && (DM.gamemap != 30))
 return false;   
     
 A.DamageMobj (thing, tmthing, tmthing, 10000); // in interaction
 
 return true;
}
}



}

package p;

public interface Interceptable {

}

package p;

import rr.line_t;
import s.degenmobj_t;

public class button_t implements Resettable{

        public line_t line;
        public bwhere_e    where;
        public int     btexture;
        public int     btimer;
        public degenmobj_t soundorg;
        
        public button_t(){	
        	this.btexture=0;
        	this.btimer=0;
        	this.where=bwhere_e.top;
        }
 
        public void reset(){
            this.line=null;
            this.where=bwhere_e.top;
            this.btexture=0;
            this.btimer=0;
            this.soundorg=null;
            
        }

    }
package p;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

import w.CacheableDoomObject;
import w.DoomBuffer;

/**
 * Source animation definition. Made readable for compatibility with Boom's
 * SWANTBLS system. 
 * 
 * @author velktron
 */
public class animdef_t
        implements CacheableDoomObject {

    public animdef_t() {

    }

    public animdef_t(boolean istexture, String endname, String startname,
            int speed) {
        super();
        this.istexture = istexture;
        this.endname = endname;
        this.startname = startname;
        this.speed = speed;
    }

    
    /** if false, it is a flat, and will NOT be used as a texture. Unless you
     *  use "flats on walls functionality of course. */
    public boolean istexture;

    /** The END name and START name of a texture, given in this order when reading a lump 
     *  The animation system is agnostic to the actual names of of the "in-between" 
     *  frames, it's purely pointer based, and only the start/end are constant. It only
     *  counts the actual number of existing textures during initialization time.
     * 
     */
    
    public String endname,startname;

    public int speed;

    public String toString() {
        return String.format("%s %s %s %d", istexture, startname, endname,
            speed);
    }

    @Override
    public void unpack(ByteBuffer buf)
            throws IOException {
        // Like most Doom structs...
        buf.order(ByteOrder.LITTLE_ENDIAN);
        this.istexture = (buf.get() != 0);
        this.startname = DoomBuffer.getNullTerminatedString(buf, 9);
        this.endname = DoomBuffer.getNullTerminatedString(buf, 9);
        this.speed = buf.getInt();
    }

    public static int size() {
        return 23;
    }

}

package p;

/** YEAH, I'M USING THE CONSTANTS INTERFACE PATTERN. DEAL WITH IT */

public interface MobjFlags {
 // // MF_ flags for mobjs.

    // Call P_SpecialThing when touched.
    public static final long MF_SPECIAL = 1;
    // Blocks.
    public static final long MF_SOLID = 2;
    // Can be hit.
    public static final long MF_SHOOTABLE = 4;
    // Don't use the sector links (invisible but touchable).
    public static final long MF_NOSECTOR = 8;
    // Don't use the blocklinks (inert but displayable)
    public static final long MF_NOBLOCKMAP = 16;

    // Not to be activated by sound, deaf monster.
    public static final long MF_AMBUSH = 32;
    // Will try to attack right back.
    public static final long MF_JUSTHIT = 64;
    // Will take at least one step before attacking.
    public static final long MF_JUSTATTACKED = 128;
    // On level spawning (initial position),
    // hang from ceiling instead of stand on floor.
    public static final long MF_SPAWNCEILING = 256;
    // Don't apply gravity (every tic),
    // that is, object will float, keeping current height
    // or changing it actively.
    public static final long MF_NOGRAVITY = 512;

    // Movement flags.
    // This allows jumps from high places.
    public static final long MF_DROPOFF = 0x400;
    // For players, will pick up items.
    public static final long MF_PICKUP = 0x800;
    // Player cheat. ???
    public static final int MF_NOCLIP = 0x1000;
    // Player: keep info about sliding along walls.
    public static final int MF_SLIDE = 0x2000;
    // Allow moves to any height, no gravity.
    // For active floaters, e.g. cacodemons, pain elementals.
    public static final int MF_FLOAT = 0x4000;
    // Don't cross lines
    // ??? or look at heights on teleport.
    public static final int MF_TELEPORT = 0x8000;
    // Don't hit same species, explode on block.
    // Player missiles as well as fireballs of various kinds.
    public static final int MF_MISSILE = 0x10000;
    // Dropped by a demon, not level spawned.
    // E.g. ammo clips dropped by dying former humans.
    public static final int MF_DROPPED = 0x20000;
    // Use fuzzy draw (shadow demons or spectres),
    // temporary player invisibility powerup.
    public static final int MF_SHADOW = 0x40000;
    // Flag: don't bleed when shot (use puff),
    // barrels and shootable furniture shall not bleed.
    public static final long MF_NOBLOOD = 0x80000;
    // Don't stop moving halfway off a step,
    // that is, have dead bodies slide down all the way.
    public static final long MF_CORPSE = 0x100000;
    // Floating to a height for a move, ???
    // don't auto float to target's height.
    public static final long MF_INFLOAT = 0x200000;

    // On kill, count this enemy object
    // towards intermission kill total.
    // Happy gathering.
    public static final long MF_COUNTKILL = 0x400000;

    // On picking up, count this item object
    // towards intermission item total.
    public static final long MF_COUNTITEM = 0x800000;

    // Special handling: skull in flight.
    // Neither a cacodemon nor a missile.
    public static final long MF_SKULLFLY = 0x1000000;

    // Don't spawn this object
    // in death match mode (e.g. key cards).
    public static final long MF_NOTDMATCH = 0x2000000;

    // Player sprites in multiplayer modes are modified
    // using an internal color lookup table for re-indexing.
    // If 0x4 0x8 or 0xc,
    // use a translation table for player colormaps
    public static final long MF_TRANSLATION = 0xc000000;
    // Hmm ???.
    public static final long MF_TRANSSHIFT = 26;

    public static final long  MF_UNUSED2      =(0x0000000010000000);
    public static final long  MF_UNUSED3      =(0x0000000020000000);

        // Translucent sprite?                                          // phares
    public static final long  MF_TRANSLUCENT  =(0x0000000040000000);

    // this is free            LONGLONG(0x0000000100000000)

    // these are greater than an int. That's why the flags below are now uint_64_t

    public static final long  MF_TOUCHY = (0x0000000100000000L);
    public static final long  MF_BOUNCES =(0x0000000200000000L);
    public static final long  MF_FRIEND = (0x0000000400000000L);

    public static final long  MF_RESSURECTED =(0x0000001000000000L);
    public static final long  MF_NO_DEPTH_TEST =(0x0000002000000000L);
    public static final long  MF_FOREGROUND = (0x0000004000000000L);
    
}

package p;

import m.IRandom;
import rr.SectorAction;

//
// P_LIGHTS
//

public class fireflicker_t extends SectorAction{
	
     private IRandom RND;
    
     public int     count;
     public int     maxlight;
     public int     minlight;
     
     public fireflicker_t(IRandom RND){
         this.RND=RND;
     }
     
     //
     // T_FireFlicker
     //
     public void FireFlicker() {
         int amount;

         if (--count != 0)
             return;

         amount = (RND.P_Random() & 3) * 16;

         if (sector.lightlevel - amount < minlight)
             sector.lightlevel = (short) minlight;
         else
             sector.lightlevel = (short) (maxlight - amount);

         count = 4;
     }
     
 } 

package p;

//
// P_CEILNG
//

public enum ceiling_e {

     lowerToFloor,
     raiseToHighest,
     lowerAndCrush,
     crushAndRaise,
     fastCrushAndRaise,
     silentCrushAndRaise;

 }

package p;

import doom.player_t;

public interface ActionType2{
	public void invoke ( player_t player, pspdef_t psp );
}
package p;

import static p.DoorDefines.GLOWSPEED;

import java.io.DataInputStream;
import java.io.IOException;
import java.nio.ByteBuffer;

import rr.SectorAction;
import w.DoomIO;

public class glow_t extends SectorAction{
        public int     minlight;
        public int     maxlight;
        public int     direction;

        //
        // Spawn glowing light
        //

        public void Glow() {
            switch (direction) {
            case -1:
                // DOWN
                sector.lightlevel -= GLOWSPEED;
                if (sector.lightlevel <= minlight) {
                    sector.lightlevel += GLOWSPEED;
                    direction = 1;
                }
                break;

            case 1:
                // UP
                sector.lightlevel += GLOWSPEED;
                if (sector.lightlevel >= maxlight) {
                    sector.lightlevel -= GLOWSPEED;
                    direction = -1;
                }
                break;
            }
        }
        
        @Override
        public void read(DataInputStream f) throws IOException{

            super.read(f); // Call thinker reader first            
            super.sectorid=DoomIO.readLEInt(f); // Sector index
            minlight=DoomIO.readLEInt(f);
            maxlight=DoomIO.readLEInt(f);
            direction=DoomIO.readLEInt(f);
            }
        
        @Override
        public void pack(ByteBuffer b) throws IOException{
            super.pack(b); //12            
            b.putInt(super.sectorid); // 16
            b.putInt(minlight);//20
            b.putInt(maxlight);//24
            b.putInt(direction);//38
        }
        
    }
package p;

import static rr.line_t.ML_TWOSIDED;
import static data.Defines.*;
import static data.Limits.MAXPLAYERS;
import static data.Limits.MAXRADIUS;
import static m.BBox.BOXBOTTOM;
import static m.BBox.BOXLEFT;
import static m.BBox.BOXRIGHT;
import static m.BBox.BOXTOP;
import static m.fixed_t.FRACBITS;
import static m.fixed_t.FixedDiv;
import static utils.C2JUtils.flags;
import java.io.IOException;
import java.nio.ByteOrder;

import m.BBox;
import rr.line_t;
import rr.node_t;
import rr.sector_t;
import rr.seg_t;
import rr.side_t;
import rr.subsector_t;
import rr.vertex_t;
import s.degenmobj_t;
import utils.C2JUtils;
import w.DoomBuffer;
import data.maplinedef_t;
import data.mapnode_t;
import data.mapsector_t;
import data.mapseg_t;
import data.mapsidedef_t;
import data.mapsubsector_t;
import data.mapthing_t;
import data.mapvertex_t;
import defines.*;
import doom.DoomStatus;

//Emacs style mode select   -*- C++ -*- 
//-----------------------------------------------------------------------------
//
// $Id: LevelLoader.java,v 1.44 2012/09/24 17:16:23 velktron Exp $
//
// Copyright (C) 1993-1996 by id Software, Inc.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// DESCRIPTION:
//  Do all the WAD I/O, get map description,
//  set up initial state and misc. LUTs.
//
//-----------------------------------------------------------------------------

public class LevelLoader extends AbstractLevelLoader{

public static final String  rcsid = "$Id: LevelLoader.java,v 1.44 2012/09/24 17:16:23 velktron Exp $";


public LevelLoader(DoomStatus<?,?> DC) {
		super(DC);
		// Traditional loader sets limit.
		deathmatchstarts=new mapthing_t[MAX_DEATHMATCH_STARTS];
	}

  /**
  * P_LoadVertexes
 * @throws IOException 
  */
  public void LoadVertexes (int lump) throws IOException
  {
      // Make a lame-ass attempt at loading some vertexes.
      
      // Determine number of lumps:
      //  total lump length / vertex record length.
      numvertexes = W.LumpLength (lump) / mapvertex_t.sizeOf();

      // Load data into cache.
      // MAES: we now have a mismatch between memory/disk: in memory, we need an array.
      // On disk, we have a single lump/blob. Thus, we need to find a way to deserialize this...
      vertexes=W.CacheLumpNumIntoArray(lump,numvertexes,vertex_t.class);
      
     // Copy and convert vertex coordinates,
     // MAES: not needed. Intermediate mapvertex_t struct skipped.
       
  }



  /**
  * P_LoadSegs
 * @throws IOException 
  */
  public void LoadSegs (int lump) throws IOException
  {
   
      mapseg_t[] data;
      mapseg_t       ml;
      seg_t li;
      line_t     ldef;
      int         linedef;
      int         side;
      
      // Another disparity between disk/memory. Treat it the same as VERTEXES.
      numsegs = W.LumpLength (lump) / mapseg_t.sizeOf();
      segs = C2JUtils.createArrayOfObjects(seg_t.class,numsegs);
      data = W.CacheLumpNumIntoArray(lump,numsegs,mapseg_t.class);
      

      // We're not done yet!
      for (int i=0 ; i<numsegs ; i++)
      {
          li=segs[i];
          ml=data[i];
      li.v1 = vertexes[ml.v1];
      li.v2 = vertexes[ml.v2];
      li.assignVertexValues();
      
      li.angle = ((ml.angle)<<16)&0xFFFFFFFFL;
      li.offset = (ml.offset)<<16;
      linedef = ml.linedef;
      li.linedef = ldef= lines[linedef];
      side = ml.side;
      li.sidedef = sides[ldef.sidenum[side]];
      li.frontsector = sides[ldef.sidenum[side]].sector;
      if (flags(ldef.flags,ML_TWOSIDED)){
    	  // MAES: Fix double sided without back side. E.g. Linedef 16103 in Europe.wad
    	  if (ldef.sidenum[side^1]!=line_t.NO_INDEX)
          li.backsector = sides[ldef.sidenum[side^1]].sector;
          // Fix two-sided with no back side.
    	  //else {
    	  //li.backsector=null;
    	  //ldef.flags^=ML_TWOSIDED;
    	  //}
      	}
      else {
          li.backsector = null;
      		}
      }
      
  }


  /**
  * P_LoadSubsectors
 * @throws IOException 
  */
  public void LoadSubsectors (int lump) throws IOException
  {
      mapsubsector_t ms;
      subsector_t    ss;
      mapsubsector_t[] data;
      
      numsubsectors = W.LumpLength (lump) / mapsubsector_t.sizeOf();      
      subsectors = C2JUtils.createArrayOfObjects(subsector_t.class,numsubsectors);

      // Read "mapsubsectors"
      data=W.CacheLumpNumIntoArray(lump,numsubsectors, mapsubsector_t.class);
      
      for (int i=0 ; i<numsubsectors ; i++)
      {
          ms=data[i];
          ss=subsectors[i];
      ss.numlines = ms.numsegs;
      ss.firstline = ms.firstseg;
      }

  }
  
  /**
   * P_LoadSectors
 * @throws IOException 
   */
  public void LoadSectors (int lump) throws IOException
  {
      mapsector_t[]       data;
      mapsector_t    ms;
      sector_t       ss;
      
      numsectors = W.LumpLength (lump) / mapsector_t.sizeOf();
      sectors = C2JUtils.createArrayOfObjects(sector_t.class,numsectors);
     
      // Read "mapsectors"
      data=W.CacheLumpNumIntoArray(lump,numsectors,mapsector_t.class);

      for (int i=0 ; i<numsectors ; i++)
      {
          ms = data[i];
          ss = sectors[i];
      ss.floorheight = ms.floorheight<<FRACBITS;
      ss.ceilingheight = ms.ceilingheight<<FRACBITS;
      ss.floorpic = (short) TM.FlatNumForName(ms.floorpic);
      ss.ceilingpic = (short) TM.FlatNumForName(ms.ceilingpic);
      ss.lightlevel = ms.lightlevel;
      ss.special = ms.special;
      ss.tag = ms.tag;
      ss.thinglist = null;
      ss.id=i;
      ss.TL=this.P;
      ss.RND=this.DM.RND;
      }

  }


  /**
  * P_LoadNodes
 * @throws IOException 
  */
  
  public void LoadNodes (int lump) throws IOException
  {
      mapnode_t[]   data;
      int     i;
      int     j;
      int     k;
      mapnode_t  mn;
      node_t no;
      
      numnodes = W.LumpLength (lump) / mapnode_t.sizeOf();
      nodes = C2JUtils.createArrayOfObjects(node_t.class,numnodes);

      // Read "mapnodes"
      data = W.CacheLumpNumIntoArray(lump,numnodes,mapnode_t.class);
      
      for (i=0 ; i<numnodes ; i++)
      {
          mn=data[i];
          no=nodes[i];
          
      no.x = mn.x<<FRACBITS;
      no.y = mn.y<<FRACBITS;
      no.dx = mn.dx<<FRACBITS;
      no.dy = mn.dy<<FRACBITS;
      for (j=0 ; j<2 ; j++)
      {
       // e6y: support for extended nodes
          no.children[j] = (char) mn.children[j];

          // e6y: support for extended nodes
          if (no.children[j] == 0xFFFF) {
              no.children[j] = 0xFFFFFFFF;
          } else if (flags(no.children[j], NF_SUBSECTOR_CLASSIC)) {
              // Convert to extended type
              no.children[j] &= ~NF_SUBSECTOR_CLASSIC;

              // haleyjd 11/06/10: check for invalid subsector reference
              if (no.children[j] >= numsubsectors) {
                  System.err
                          .printf(
                              "P_LoadNodes: BSP tree references invalid subsector %d.\n",
                              no.children[j]);
                  no.children[j] = 0;
              }

              no.children[j] |= NF_SUBSECTOR;
          }

          
          for (k=0 ; k<4 ; k++)
          no.bbox[j].set(k, mn.bbox[j][k]<<FRACBITS);
      }
      }
      
  }


  /**
   * P_LoadThings
 * @throws IOException 
   */
  public void LoadThings (int lump) throws IOException
  {
      mapthing_t[]       data;
      mapthing_t     mt;
      int         numthings;
      boolean     spawn;      
      
      numthings = W.LumpLength (lump) / mapthing_t.sizeOf();
      // VERY IMPORTANT: since now caching is near-absolute,
      // the mapthing_t instances must be CLONED rather than just
      // referenced, otherwise missing mobj bugs start  happening.
      
      data=W.CacheLumpNumIntoArray(lump,numthings,mapthing_t.class);
      
      for (int i=0 ; i<numthings ; i++)
      {
          mt = data[i];
      spawn = true;

      // Do not spawn cool, new monsters if !commercial
      if ( !DM.isCommercial())
      {
          switch(mt.type)
          {
            case 68:  // Arachnotron
            case 64:  // Archvile
            case 88:  // Boss Brain
            case 89:  // Boss Shooter
            case 69:  // Hell Knight
            case 67:  // Mancubus
            case 71:  // Pain Elemental
            case 65:  // Former Human Commando
            case 66:  // Revenant
            case 84:  // Wolf SS
          spawn = false;
          break;
          }
      }
      if (spawn == false)
          break;

      // Do spawn all other stuff.
      // MAES: we have loaded the shit with the proper endianness, so no fucking around, bitch.
      /*mt.x = SHORT(mt.x);
      mt.y = SHORT(mt.y);
      mt.angle = SHORT(mt.angle);
      mt.type = SHORT(mt.type);
      mt.options = SHORT(mt.options);*/
      
      //System.out.printf("Spawning %d %s\n",i,mt.type);
      
      P.SpawnMapThing (mt);
      }
      
      // Status may have changed. It's better to release the resources anyway
      //W.UnlockLumpNum(lump);
      
  }


  /**
   * P_LoadLineDefs
   * Also counts secret lines for intermissions.
 * @throws IOException 
   */
  public void LoadLineDefs (int lump) throws IOException
  {
      maplinedef_t[]       data;
      maplinedef_t   mld;
      line_t     ld;
      vertex_t       v1;
      vertex_t       v2;
      
      numlines = W.LumpLength (lump) / maplinedef_t.sizeOf();
      lines = C2JUtils.createArrayOfObjects(line_t.class,numlines);
      // Check those actually used in sectors, later on.
      used_lines=new boolean[numlines]; 

      // read "maplinedefs"
      data = W.CacheLumpNumIntoArray(lump,numlines,maplinedef_t.class);
      
      for (int i=0 ; i<numlines ; i++)
      {
          mld = data[i];
          ld = lines[i];
    
      ld.flags = mld.flags;
      ld.special = mld.special;
      ld.tag = mld.tag;
      v1 = ld.v1 = vertexes[(char)mld.v1];
      v2 = ld.v2 = vertexes[(char)mld.v2];
      ld.dx = v2.x - v1.x;
      ld.dy = v2.y - v1.y;
      // Map value semantics.
      ld.assignVertexValues();
      
      if (ld.dx==0)
          ld.slopetype = slopetype_t.ST_VERTICAL;
      else if (ld.dy==0)
          ld.slopetype = slopetype_t.ST_HORIZONTAL;
      else
      {
          if (FixedDiv (ld.dy , ld.dx) > 0)
          ld.slopetype = slopetype_t.ST_POSITIVE;
          else
          ld.slopetype = slopetype_t.ST_NEGATIVE;
      }
          
      if (v1.x < v2.x)
      {
          ld.bbox[BOXLEFT] = v1.x;
          ld.bbox[BOXRIGHT] = v2.x;
      }
      else
      {
          ld.bbox[BOXLEFT] = v2.x;
          ld.bbox[BOXRIGHT] = v1.x;
      }

      if (v1.y < v2.y)
      {
          ld.bbox[BOXBOTTOM] = v1.y;
          ld.bbox[BOXTOP] = v2.y;
      }
      else
      {
          ld.bbox[BOXBOTTOM] = v2.y;
          ld.bbox[BOXTOP] = v1.y;
      }

      ld.sidenum[0] = mld.sidenum[0];
      ld.sidenum[1] = mld.sidenum[1];
      
      // Sanity check for two-sided without two valid sides.      
      if (flags(ld.flags,ML_TWOSIDED)) {
    	  if ((ld.sidenum[0] == line_t.NO_INDEX) || (ld.sidenum[1] == line_t.NO_INDEX)){
    	  // Well, dat ain't so tu-sided now, ey esse?
    	  ld.flags^=ML_TWOSIDED; 
    	  }
      }

      // Front side defined without a valid frontsector.
      if (ld.sidenum[0] != line_t.NO_INDEX){
          ld.frontsector = sides[ld.sidenum[0]].sector;
          if (ld.frontsector==null){ // // Still null? Bad map. Map to dummy.
        	  ld.frontsector=dummy_sector;
          }
          
      }
      else
          ld.frontsector = null;

      // back side defined without a valid backsector.
      if (ld.sidenum[1] != line_t.NO_INDEX){
          ld.backsector = sides[ld.sidenum[1]].sector;
          if (ld.backsector==null){ // Still null? Bad map. Map to dummy.
        	  ld.backsector=dummy_sector;
          }
      }
      else
          ld.backsector = null;
      
      // If at least one valid sector is defined, then it's not null.
      if (ld.frontsector!=null || ld.backsector!=null) {
          this.used_lines[i]=true;
          }
      
      }
      

   }


  /**
  * P_LoadSideDefs
  */
  public void LoadSideDefs (int lump) throws IOException
  {
      mapsidedef_t[]       data;
      mapsidedef_t   msd;
      side_t     sd;
      
      numsides = W.LumpLength (lump) / mapsidedef_t.sizeOf();
      sides = C2JUtils.createArrayOfObjects(side_t.class,numsides);
      
      data= W.CacheLumpNumIntoArray(lump,numsides,mapsidedef_t.class);
      
      for (int i=0 ; i<numsides ; i++)
      {
          msd = data[i];
          sd = sides[i];
          
      sd.textureoffset = (msd.textureoffset)<<FRACBITS;
      sd.rowoffset = (msd.rowoffset)<<FRACBITS;
      sd.toptexture = (short) TM.TextureNumForName(msd.toptexture);
      sd.bottomtexture = (short) TM.TextureNumForName(msd.bottomtexture);
      sd.midtexture = (short) TM.TextureNumForName(msd.midtexture);
      if (msd.sector<0) sd.sector=dummy_sector;
      else
      sd.sector = sectors[msd.sector];
      }
  }

  // MAES 22/5/2011 This hack added for PHOBOS2.WAD, in order to
  // accomodate for some linedefs having a sector number of "-1".
  // Any negative sector will get rewired to this dummy sector.
  // PROBABLY, this will handle unused sector/linedefes cleanly?
  sector_t dummy_sector=new sector_t(); 
  
  /**
   * P_LoadBlockMap
   * @throws IOException
   * 
   *  TODO: generate BLOCKMAP dynamically to
   *  handle missing cases and increase accuracy.
   *  
   */
  public void LoadBlockMap (int lump) throws IOException
  {
      int     count=0;
      
      if (DM.CM.CheckParmBool("-blockmap") || W.LumpLength(lump) < 8
              || (count = W.LumpLength(lump) / 2) >= 0x10000) // e6y
          CreateBlockMap();
      else {
      
      DoomBuffer data=(DoomBuffer)W.CacheLumpNum(lump,PU_LEVEL, DoomBuffer.class);
      count=W.LumpLength(lump)/2;
      blockmaplump=new int[count];

      data.setOrder(ByteOrder.LITTLE_ENDIAN);
      data.rewind();
      data.readCharArray(blockmaplump, count);
      
      // Maes: first four shorts are header data.
          
      bmaporgx = blockmaplump[0]<<FRACBITS;
      bmaporgy = blockmaplump[1]<<FRACBITS;
      bmapwidth = blockmaplump[2];
      bmapheight = blockmaplump[3];
      
      // MAES: use killough's code to convert terminators to -1 beforehand
      for (int i = 4; i < count; i++) {
          short t = (short) blockmaplump[i]; // killough 3/1/98
          blockmaplump[i] = (int) (t == -1 ? -1l : t & 0xffff);
      }
      
      // haleyjd 03/04/10: check for blockmap problems
      // http://www.doomworld.com/idgames/index.php?id=12935
      if (!VerifyBlockMap(count)) {
          System.err
                  .printf("P_LoadBlockMap: erroneous BLOCKMAP lump may cause crashes.\n");
          System.err
                  .printf("P_LoadBlockMap: use \"-blockmap\" command line switch for rebuilding\n");
      }
      
      }
     count = bmapwidth*bmapheight;
      
     // IMPORTANT MODIFICATION: no need to have both blockmaplump AND blockmap.
     // If the offsets in the lump are OK, then we can modify them (remove 4)
     // and copy the rest of the data in one single data array. This avoids
     // reserving memory for two arrays (we can't simply alias one in Java)
      
      blockmap=new int[blockmaplump.length-4];
      
      // Offsets are relative to START OF BLOCKMAP, and IN SHORTS, not bytes.
      for (int i=0;i<blockmaplump.length-4;i++){
    	  // Modify indexes so that we don't need two different lumps.
    	  // Can probably be further optimized if we simply shift everything backwards.
    	  // and reuse the same memory space.
    	  if (i<count)
    		  blockmaplump[i]=blockmaplump[i+4]-4;
    	  else{
    		  // Make terminators definitively -1, different that 0xffff
  	          short t = (short) blockmaplump[i+4];          // killough 3/1/98
  	          blockmaplump[i] = (int) (t == -1 ? -1l : t & 0xffff);  	          
    	  	}
      	}
      
      // clear out mobj chains
      // ATTENTION! BUG!!!
      // If blocklinks are "cleared" to void -but instantiated- objects,
      // very bad bugs happen, especially the second time a level is re-instantiated.
      // Probably caused other bugs as well, as an extra object would appear in iterators.
      
       if (blocklinks!=null && blocklinks.length==count)
          for (int i=0;i<count;i++)
              blocklinks[i]=null;
       else 
           blocklinks = new mobj_t[count];
       
       // Bye bye. Not needed.
       blockmap=blockmaplump;
  }


  /**
   * P_GroupLines
   * Builds sector line lists and subsector sector numbers.
   * Finds block bounding boxes for sectors.
  */

  
  public void GroupLines ()
  {
      int         total;
      line_t     li;
      sector_t       sector;
      subsector_t    ss;
      seg_t      seg;
      int[]     bbox=new int[4];
      int         block;
      
      // look up sector number for each subsector
      
      for (int i=0 ; i<numsubsectors ; i++)
      {
      ss = subsectors[i];
      seg = segs[ss.firstline];
      ss.sector = seg.sidedef.sector;
      }

      //linebuffer=new line_t[numsectors][0];
      // count number of lines in each sector
      
      
      total = 0;

        for (int i = 0; i < numlines; i++) {
            li = lines[i];
            total++;
            li.frontsector.linecount++;

            if ((li.backsector != null) && (li.backsector != li.frontsector)) {
                li.backsector.linecount++;
                total++;
            }

        }
      
      // build line tables for each sector    
      // MAES: we don't really need this in Java.
      // linebuffer = new line_t[total];
      // int linebuffercount=0;
      
      // We scan through ALL sectors.
      for (int i=0 ; i<numsectors ; i++)
      {
          sector = sectors[i];
          BBox.ClearBox(bbox);
          //sector->lines = linebuffer;
          // We can just construct line tables of the correct size
          // for each sector.
          int countlines=0;
      // We scan through ALL lines....
          
         // System.out.println(i+ ": looking for sector -> "+sector);
      for (int j=0 ; j<numlines ; j++)
      {
          li=lines[j];
          
          //System.out.println(j+ " front "+li.frontsector+ " back "+li.backsector);
          
          if (li.frontsector == sector || li.backsector == sector)
          {
              // This sector will have one more line.
              countlines++;
          // Expand bounding box...
          BBox.AddToBox(bbox, li.v1.x, li.v1.y);
          BBox.AddToBox (bbox, li.v2.x, li.v2.y);
          }
      }
      
      // So, this sector must have that many lines.
      sector.lines=new line_t[countlines];

      int addedlines=0;
      int pointline=0;
      
      // Add actual lines into sectors.
      for (int j=0 ; j<numlines ; j++)
      {
          li=lines[j];
          // If
          if (li.frontsector == sector || li.backsector == sector)
          {
              // This sector will have one more line.
              sectors[i].lines[pointline++]=lines[j];
              addedlines++;
          }
      }
      
      if (addedlines != sector.linecount)
    	  I.Error ("P_GroupLines: miscounted");
              
      // set the degenmobj_t to the middle of the bounding box
      sector.soundorg=new degenmobj_t(((bbox[BOXRIGHT]+bbox[BOXLEFT])/2),
    		  						  ((bbox[BOXTOP]+bbox[BOXBOTTOM])/2),(sector.ceilingheight-sector.floorheight)/2);
          
      // adjust bounding box to map blocks
      block = (bbox[BOXTOP]-bmaporgy+MAXRADIUS)>>MAPBLOCKSHIFT;
      block = block >= bmapheight ? bmapheight-1 : block;
      sector.blockbox[BOXTOP]=block;

      block = (bbox[BOXBOTTOM]-bmaporgy-MAXRADIUS)>>MAPBLOCKSHIFT;
      block = block < 0 ? 0 : block;
      sector.blockbox[BOXBOTTOM]=block;

      block = (bbox[BOXRIGHT]-bmaporgx+MAXRADIUS)>>MAPBLOCKSHIFT;
      block = block >= bmapwidth ? bmapwidth-1 : block;
      sector.blockbox[BOXRIGHT]=block;

      block = (bbox[BOXLEFT]-bmaporgx-MAXRADIUS)>>MAPBLOCKSHIFT;
      block = block < 0 ? 0 : block;
      sector.blockbox[BOXLEFT]=block;
      }
      
  }
  
  
  @Override
  public void
  SetupLevel
  ( int       episode,
    int       map,
    int       playermask,
    skill_t   skill) 
  {
      int     i;
      String    lumpname;
      int     lumpnum;
   
      try{
      DM.totalkills = DM.totalitems = DM.totalsecret = DM.wminfo.maxfrags = 0;
      DM.wminfo.partime = 180;
      for (i=0 ; i<MAXPLAYERS ; i++)
      {
      DM.players[i].killcount = DM.players[i].secretcount 
          = DM.players[i].itemcount = 0;
      }

      // Initial height of PointOfView
      // will be set by player think.
      DM.players[DM.consoleplayer].viewz = 1; 

      // Make sure all sounds are stopped before Z_FreeTags.
      S.Start ();         

  /*    
  #if 0 // UNUSED
      if (debugfile)
      {
      Z_FreeTags (PU_LEVEL, MAXINT);
      Z_FileDumpHeap (debugfile);
      }
      else
  #endif
  */
    //  Z_FreeTags (PU_LEVEL, PU_PURGELEVEL-1);


      // UNUSED W_Profile ();
      P.InitThinkers ();

      // if working with a development map, reload it
      W.Reload ();            
         
      // find map name
      if ( DM.isCommercial())
      {
      if (map<10)
          lumpname="MAP0"+map;
      else
          lumpname="MAP"+map;
      }
      else
      {
      lumpname = ("E"+
     (char)( '0' + episode)+
     "M"+
      (char)( '0' + map)
      );
      }

      lumpnum = W.GetNumForName (lumpname);
      
      DM.leveltime = 0;
      
      if (!W.verifyLumpName(lumpnum+ML_BLOCKMAP, LABELS[ML_BLOCKMAP]))
          System.err.println("Blockmap missing!");
      
      // note: most of this ordering is important
      

      this.LoadVertexes (lumpnum+ML_VERTEXES);      
      this.LoadSectors (lumpnum+ML_SECTORS);
      this.LoadSideDefs (lumpnum+ML_SIDEDEFS);
      this.LoadLineDefs (lumpnum+ML_LINEDEFS);
      this.LoadSubsectors (lumpnum+ML_SSECTORS);
      this.LoadNodes (lumpnum+ML_NODES);
      this.LoadSegs (lumpnum+ML_SEGS);
      
      // MAES: in order to apply optimizations and rebuilding, order must be changed.
      this.LoadBlockMap (lumpnum+ML_BLOCKMAP);
      //this.SanitizeBlockmap();
      //this.getMapBoundingBox();
      
      this.LoadReject(lumpnum+ML_REJECT);
      
      this.GroupLines ();

      DM.bodyqueslot = 0;
      // Reset to "deathmatch starts"
      DM.deathmatch_p = 0;
      this.LoadThings (lumpnum+ML_THINGS);
      
      // if deathmatch, randomly spawn the active players
      if (DM.deathmatch)
      {
      for (i=0 ; i<MAXPLAYERS ; i++)
          if (DM.playeringame[i])
          {
          DM.players[i].mo = null;
       //   DM.DeathMatchSpawnPlayer (i);
          }
              
      }

      // clear special respawning que
      P.iquehead = P.iquetail = 0;        
      
      // set up world state
      P.SpawnSpecials ();
      
      // build subsector connect matrix
      //  UNUSED P_ConnectSubsectors ();

      // preload graphics
      if (DM.precache){
      TM.PrecacheLevel ();
      // MAES: thinkers are separate than texture management. Maybe split sprite management as well?
      R.PreCacheThinkers();
      
      }

      } catch (Exception e){
          System.err.println("Error while loading level");
          e.printStackTrace();
      }
  }

}

//$Log: LevelLoader.java,v $
//Revision 1.44  2012/09/24 17:16:23  velktron
//Massive merge between HiColor and HEAD. There's no difference from now on, and development continues on HEAD.
//
//Revision 1.43.2.2  2012/09/24 16:57:16  velktron
//Addressed generics warnings.
//
//Revision 1.43.2.1  2012/03/26 09:53:44  velktron
//Use line_t.NO_INDEX for good measure, when possible.
//
//Revision 1.43  2011/11/03 15:19:51  velktron
//Adapted to using ISpriteManager
//
//Revision 1.42  2011/10/07 16:05:52  velktron
//Now using line_t for ML_* definitions.
//
//Revision 1.41  2011/10/06 16:44:32  velktron
//Proper support for extended nodes, made reject loading into a separate method.
//
//Revision 1.40  2011/09/30 15:20:24  velktron
//Very modified, useless SanitizeBlockmap method ditched. 
//Common utility methods moved to superclass. Shares blockmap checking and generation 
//with Boom-derived code. Now capable of running Europe.wad. 
//TODO: Blockmap generation can be really slow on large levels. 
//Optimize better for Java, or parallelize.
//
//Revision 1.39  2011/09/29 17:22:08  velktron
//Blockchain terminators are now -1 (extended)
//
//Revision 1.38  2011/09/29 17:11:32  velktron
//Blockmap optimizations.
//
//Revision 1.37  2011/09/29 15:17:48  velktron
//SetupLevel can propagate exceptions.
//
//Revision 1.36  2011/09/29 13:28:01  velktron
//Extends AbstractLevelLoader
//
//Revision 1.35  2011/09/27 18:04:36  velktron
//Fixed major blockmap bug
//
//Revision 1.34  2011/09/27 16:00:20  velktron
//Minor blockmap stuff.
//
//Revision 1.33  2011/08/24 15:52:04  velktron
//Sets proper ISoundOrigin for sectors (height, too)
//
//Revision 1.32  2011/08/24 15:00:34  velktron
//Improved version, now using createArrayOfObjects. Much better syntax.
//
//Revision 1.31  2011/08/23 16:17:22  velktron
//Got rid of Z remnants.
//
//Revision 1.30  2011/07/27 21:26:19  velktron
//Quieted down debugging for v1.5 release
//
//Revision 1.29  2011/07/25 19:56:53  velktron
//reject matrix size bugfix, fron danmaku branch.
//
//Revision 1.28  2011/07/22 15:37:52  velktron
//Began blockmap autogen code...still WIP
//
//Revision 1.27  2011/07/20 16:14:45  velktron
//Bullet-proofing vs missing or corrupt REJECT table. TODO: built-in system to re-compute it.
//
//Revision 1.26  2011/06/18 23:25:33  velktron
//Removed debugginess
//
//Revision 1.25  2011/06/18 23:21:26  velktron
//-id
//
//Revision 1.24  2011/06/18 23:18:24  velktron
//Added sanitization for broken two-sided sidedefs, and semi-support for extended blockmaps.
//
//Revision 1.23  2011/05/24 11:31:47  velktron
//Adapted to IDoomStatusBar
//
//Revision 1.22  2011/05/22 21:09:34  velktron
//Added spechit overflow handling, and unused linedefs (with -1 sector) handling.
//
//Revision 1.21  2011/05/21 14:53:57  velktron
//Adapted to use new gamemode system.
//
//Revision 1.20  2011/05/20 14:52:23  velktron
//Moved several function from the Renderer and Action code in here, since it made more sense.
//
//Revision 1.19  2011/05/18 16:55:44  velktron
//TEMPORARY TESTING VERSION, DO NOT USE
//
//Revision 1.18  2011/05/17 16:51:20  velktron
//Switched to DoomStatus
//
//Revision 1.17  2011/05/10 10:39:18  velktron
//Semi-playable Techdemo v1.3 milestone
//
//Revision 1.16  2011/05/05 17:24:22  velktron
//Started merging more of _D_'s changes.
//
//Revision 1.15  2010/12/20 17:15:08  velktron
//Made the renderer more OO -> TextureManager and other changes as well.
//
//Revision 1.14  2010/11/22 21:41:22  velktron
//Parallel rendering...sort of.It works, but either  the barriers are broken or it's simply not worthwhile at this point :-/
//
//Revision 1.13  2010/11/22 14:54:53  velktron
//Greater objectification of sectors etc.
//
//Revision 1.12  2010/11/22 01:17:16  velktron
//Fixed blockmap (for the most part), some actions implemented and functional, ambient animation/lighting functional.
//
//Revision 1.11  2010/11/14 20:00:21  velktron
//Bleeding floor bug fixed!
//
//Revision 1.10  2010/11/03 16:48:04  velktron
//"Bling" view angles fixed (perhaps related to the "bleeding line bug"?)
//
//Revision 1.9  2010/09/27 02:27:29  velktron
//BEASTLY update
//
//Revision 1.8  2010/09/23 20:36:45  velktron
//*** empty log message ***
//
//Revision 1.7  2010/09/23 15:11:57  velktron
//A bit closer...
//
//Revision 1.6  2010/09/22 16:40:02  velktron
//MASSIVE changes in the status passing model.
//DoomMain and DoomGame unified.
//Doomstat merged into DoomMain (now status and game functions are one).
//
//Most of DoomMain implemented. Possible to attempt a "classic type" start but will stop when reading sprites.
//
//Revision 1.5  2010/09/21 15:53:37  velktron
//Split the Map ...somewhat...
//
//Revision 1.4  2010/09/14 15:34:01  velktron
//The enormity of this commit is incredible (pun intended)
//
//Revision 1.3  2010/09/08 15:22:18  velktron
//x,y coords in some structs as value semantics. Possible speed increase?
//
//Revision 1.2  2010/09/02 15:56:54  velktron
//Bulk of unified renderer copyediting done.
//
//Some changes like e.g. global separate limits class and instance methods for seg_t and node_t introduced.
//
//Revision 1.1  2010/09/01 15:53:42  velktron
//Graphics data loader implemented....still need to figure out how column caching works, though.
//
//Revision 1.4  2010/08/19 23:14:49  velktron
//Automap
//
//Revision 1.3  2010/08/13 14:06:36  velktron
//Endlevel screen fully functional!
//
//Revision 1.2  2010/08/11 16:31:34  velktron
//Map loading works! Check out LevelLoaderTester for more.
//
//Revision 1.1  2010/08/10 16:41:57  velktron
//Threw some work into map loading.
//
package p;

public enum result_e {
        ok,
        crushed,
        pastdest
}

package p;

import static rr.line_t.ML_BLOCKING;
import static data.Defines.BT_ATTACK;
import static data.Defines.MAPBLOCKSHIFT;
import static data.Defines.MELEERANGE;
import static data.Defines.MISSILERANGE;
import static data.Defines.PST_DEAD;
import static data.Defines.pw_strength;
import static data.Limits.MAXPLAYERS;
import static data.Limits.MAXRADIUS;
import static data.Limits.MAXSKULLS;
import static data.Limits.NUMBRAINTARGETS;
import static data.Tables.ANG180;
import static data.Tables.ANG270;
import static data.Tables.ANG45;
import static data.Tables.ANG90;
import static data.Tables.BITS32;
import static data.Tables.FINEANGLES;
import static data.Tables.FINEMASK;
import static data.Tables.finecosine;
import static data.Tables.finesine;
import static data.info.mobjinfo;
import static data.info.states;
import static doom.items.weaponinfo;
import static doom.player_t.LOWERSPEED;
import static doom.player_t.RAISESPEED;
import static doom.player_t.WEAPONBOTTOM;
import static doom.player_t.WEAPONTOP;
import static doom.player_t.ps_flash;
import static doom.player_t.ps_weapon;
import static m.fixed_t.FRACUNIT;
import static m.fixed_t.MAPFRACUNIT;
import static m.fixed_t.FixedMul;
import static p.ChaseDirections.DI_NODIR;
import static p.ChaseDirections.xspeed;
import static p.ChaseDirections.yspeed;
import static p.MapUtils.AproxDistance;
import static p.mobj_t.MF_AMBUSH;
import static p.mobj_t.MF_COUNTKILL;
import static p.mobj_t.MF_JUSTATTACKED;
import static p.mobj_t.MF_SHADOW;
import static p.mobj_t.MF_SHOOTABLE;
import static p.mobj_t.MF_SKULLFLY;
import static p.mobj_t.MF_SOLID;
import static utils.C2JUtils.eval;
import static utils.C2JUtils.flags;
import i.DoomStatusAware;
import p.UnifiedGameMap.Enemies;
import m.IRandom;
import rr.Renderer;
import rr.line_t;
import s.IDoomSound;
import data.Tables;
import data.mobjinfo_t;
import data.mobjtype_t;
import data.state_t;
import data.sounds.sfxenum_t;
import defines.skill_t;
import defines.statenum_t;
import doom.DoomStatus;
import doom.IDoomGame;
import doom.player_t;
import doom.think_t;
import doom.thinker_t;
import doom.weapontype_t;

public class ActionFunctions implements DoomStatusAware{


	public ActionFunctions(){
		MobjThinker=new P_MobjThinker();
		  WeaponReady=new A_WeaponReady();
		  Lower=new A_Lower();
		  Raise=new A_Raise();
		  Punch=new A_Punch();
		  ReFire=new A_ReFire();
		  FirePistol=new A_FirePistol();
		  Light0=new A_Light0();
		  Light1=new A_Light1();
		  Light2=new A_Light2();
		   FireShotgun=new A_FireShotgun();	
		   FireShotgun2=new A_FireShotgun2();	
		  CheckReload=new A_CheckReload();
		  OpenShotgun2=new A_OpenShotgun2();
		  LoadShotgun2=new A_LoadShotgun2();
		  CloseShotgun2=new A_CloseShotgun2();
		  FireCGun=new A_FireCGun();
		  GunFlash=new A_GunFlash();
		  FireMissile=new A_FireMissile();
		  Saw=new A_Saw();
		  FirePlasma=new A_FirePlasma();
		  BFGsound=new A_BFGsound();
		  FireBFG=new A_FireBFG();
	      BFGSpray=new A_BFGSpray();
	      Explode=new A_Explode();
	      Pain=new A_Pain();
	      PlayerScream=new A_PlayerScream();
	      Fall=new A_Fall();
	      XScream=new A_XScream();
	      Look=new A_Look();
	      Chase=new A_Chase();
	      FaceTarget=new A_FaceTarget();
	      PosAttack=new A_PosAttack();
	      Scream=new A_Scream();
	      SPosAttack=new A_SPosAttack();
	      VileChase=new A_VileChase();
	      VileStart=new A_VileStart();
	      VileTarget=new A_VileTarget();
	      VileAttack=new A_VileAttack();
	      StartFire=new A_StartFire();
	      Fire=new A_Fire();
	      FireCrackle=new A_FireCrackle();
	      Tracer=new A_Tracer();
	      SkelWhoosh=new A_SkelWhoosh();
	      SkelFist=new A_SkelFist();
	      SkelMissile=new A_SkelMissile();
	      FatRaise=new A_FatRaise();
	      FatAttack1=new A_FatAttack1();
	      FatAttack2=new A_FatAttack2();
	      FatAttack3=new A_FatAttack3();
	      BossDeath=new A_BossDeath();
	      CPosAttack=new A_CPosAttack();
	      CPosRefire=new A_CPosRefire();
	      TroopAttack=new A_TroopAttack();
	      SargAttack=new A_SargAttack();
	      HeadAttack=new A_HeadAttack();
	     BruisAttack=new A_BruisAttack();
	      SkullAttack=new A_SkullAttack();
	      Metal=new A_Metal();
	      SpidRefire=new A_SpidRefire();
	     BabyMetal=new A_BabyMetal();
	     BspiAttack=new A_BspiAttack();
	      Hoof=new A_Hoof();
	      CyberAttack=new A_CyberAttack();
	      PainAttack=new A_PainAttack();
	      PainDie=new A_PainDie();
	      KeenDie=new A_KeenDie();
	      BrainPain=new A_BrainPain();
	      BrainScream=new A_BrainScream();
	      BrainDie=new A_BrainDie();
	      BrainAwake=new A_BrainAwake();
	      BrainSpit=new A_BrainSpit();
	      SpawnSound=new A_SpawnSound();
	      SpawnFly=new A_SpawnFly();
	      BrainExplode=new A_BrainExplode();
	      
	      // these need special handling.
	      FireFlicker=new T_FireFlicker();
	      LightFlash=new T_LightFlash();
		  StrobeFlash=new T_StrobeFlash();
		  Glow=new T_Glow();
		  MoveCeiling=new T_MoveCeiling();
		  MoveFloor=new T_MoveFloor();
		  VerticalDoor=new T_VerticalDoor();
		  PlatRaise=new T_PlatRaise();
		  SlidingDoor=new T_SlidingDoor();
		
	}
		
	public ActionFunctions(DoomStatus<?,?> DS, Enemies EN) {
		this();
		this.EN=EN;     
		updateStatus(DS);
	}

	@Override
	public void updateStatus(DoomStatus<?,?> DS){
		this.A=DS.P;
        this.RND=DS.RND;       
        this.R=DS.R;
        this.S=DS.S;
        this.LL=DS.LL;
        this.DS=DS.DM;
        this.DG=DS.DG;
//        this.SL=DS.SL;
	}
	
	protected Actions A;
	protected IRandom RND;
	protected Renderer<?,?> R;
	protected IDoomSound S;
	protected Enemies EN;
	protected AbstractLevelLoader LL;
	protected DoomStatus<?,?> DS;
	protected IDoomGame DG;
	protected SlideDoor SL;
	

	ActionType2  WeaponReady;
	ActionType2  Lower;
	ActionType2  Raise;
	ActionType2  Punch;
	ActionType2  ReFire;
	ActionType2  FirePistol;
	ActionType2  Light0;
	ActionType2  Light1;
	ActionType2  Light2;
	ActionType2   FireShotgun;	
	ActionType2  FireShotgun2;
	ActionType2  CheckReload;
	ActionType2  OpenShotgun2;
	ActionType2  LoadShotgun2;
	ActionType2  CloseShotgun2;
	ActionType2  FireCGun;
	ActionType2  GunFlash;
	ActionType2  FireMissile;
	ActionType2  Saw;
	ActionType2  FirePlasma;
	ActionType2  BFGsound;
	ActionType2  FireBFG;
    ActionType1  BFGSpray;
    ActionType1  Explode;
    ActionType1  Pain;
    ActionType1  PlayerScream;
    ActionType1 Fall;
    ActionType1  XScream;
    ActionType1  Look;
    ActionType1  Chase;
    ActionType1  FaceTarget;
    ActionType1  PosAttack;
    ActionType1  Scream;
    ActionType1  SPosAttack;
    ActionType1  VileChase;
    ActionType1  VileStart;
    ActionType1  VileTarget;
    ActionType1  VileAttack;
    ActionType1 StartFire;
    ActionType1  Fire;
    ActionType1  FireCrackle;
    ActionType1  Tracer;
    ActionType1  SkelWhoosh;
    ActionType1  SkelFist;
    ActionType1  SkelMissile;
    ActionType1  FatRaise;
    ActionType1  FatAttack1;
    ActionType1  FatAttack2;
    ActionType1 FatAttack3;
    ActionType1  BossDeath;
    ActionType1  CPosAttack;
    ActionType1  CPosRefire;
    ActionType1  TroopAttack;
    ActionType1  SargAttack;
    ActionType1  HeadAttack;
    ActionType1 BruisAttack;
    ActionType1  SkullAttack;
    ActionType1  Metal;
    ActionType1  SpidRefire;
    ActionType1 BabyMetal;
    ActionType1 BspiAttack;
    ActionType1  Hoof;
    ActionType1  CyberAttack;
    ActionType1  PainAttack;
    ActionType1  PainDie;
    ActionType1 KeenDie;
    ActionType1  BrainPain;
    ActionType1  BrainScream;
    ActionType1  BrainDie;
    ActionType1  BrainAwake;
    ActionType1  BrainSpit;
    ActionType1  SpawnSound;
    ActionType1  SpawnFly;
    ActionType1  BrainExplode;
    ActionType1  MobjThinker;
    ActionTypeSS<?>  FireFlicker;
    ActionTypeSS<?>  LightFlash;
	ActionTypeSS<?> StrobeFlash;
	ActionTypeSS<?>  Glow;
	ActionTypeSS<?>  MoveCeiling;
	ActionTypeSS<?>  MoveFloor;
	ActionTypeSS<?>  VerticalDoor;
	ActionTypeSS<?> PlatRaise;
	ActionTypeSS<?> SlidingDoor;
	
	/** Wires a state to an actual callback depending on its
	 *  enum. This eliminates the need to have a giant
	 *  switch statements in Actions, and should allow DEH
	 *  rewiring.
	 * @param st
	 */
	
	public void doWireState(state_t st){
	      //System.out.println("Dispatching: "+action);
		if (st.action==null) return;
	      switch (st.action){
	          case P_MobjThinker:
	        	  st.acp1=MobjThinker;
	        	  break;
	          case A_Light0:
	        	  st.acp2=Light0;
	        	  break;
	          case A_WeaponReady:
	        	  st.acp2=WeaponReady;
	        	  break;        	  
	          case  A_Lower:
	        	  st.acp2=Lower;
	        	  break;
	          case  A_Raise:
	        	  st.acp2=Raise;
	        	  break;
	          case  A_Punch:
	        	  st.acp2=Punch;
	        	  break;
	          case A_ReFire:
	        	  st.acp2=ReFire;
	        	  break;
	          case A_FirePistol:
	        	  st.acp2= FirePistol;
	        	  break;
	          case  A_Light1:
	        	  st.acp2=Light1;
	        	  break;
	          case A_FireShotgun:
	        	  st.acp2=FireShotgun;
	        	  break;
	          case  A_Light2:
	        	  st.acp2=Light2;
	        	  break;
	          case A_FireShotgun2:
	        	  st.acp2=FireShotgun2;
	        	  break;
	          case  A_CheckReload:
	        	  st.acp2=CheckReload;
	        	  break;
	          case A_OpenShotgun2:
	        	  st.acp2=OpenShotgun2;
	        	  break;
	          case A_LoadShotgun2:
	        	  st.acp2=LoadShotgun2;
	        	  break;
	          case A_CloseShotgun2:
	        	  st.acp2=CloseShotgun2;
	        	  break;
	          case A_FireCGun:
	        	  st.acp2=FireCGun;
	        	  break;
	          case A_GunFlash:
	        	  st.acp2=GunFlash;
	        	  break;
	          case A_FireMissile:
	        	  st.acp2=FireMissile;
	        	  break;
	          case A_Saw:
	        	  st.acp2=Saw;
	        	  break;
	          case A_FirePlasma:
	        	  st.acp2=FirePlasma;
	        	  break;
	          case A_BFGsound:
	        	  st.acp2=BFGsound;
	        	  break;
	          case A_FireBFG:
	        	  st.acp2=FireBFG;
	        	  break;
	          case A_BFGSpray:
	        	  st.acp1=BFGSpray;
	        	  break;
	          case A_Explode:
	        	  st.acp1=Explode;
	        	  break;
	          case A_Pain:
	        	  st.acp1=Pain;
	        	  break;
	          case A_PlayerScream:
	        	  st.acp1=PlayerScream;
	        	  break;
	          case A_Fall:
	        	  st.acp1=Fall;
	        	  break;
	          case  A_XScream:
	        	  st.acp1=XScream;
	        	  break;
	          case A_Look:
	        	  st.acp1=Look;
	        	  break;
	          case  A_Chase:
	        	  st.acp1=Chase;
	        	  break;
	          case  A_FaceTarget:
	        	  st.acp1=FaceTarget;
	        	  break;
	          case  A_PosAttack:
	        	  st.acp1=PosAttack;
	        	  break;
	          case  A_Scream:
	        	  st.acp1=Scream;
	        	  break;
	          case  A_SPosAttack:
	        	  st.acp1=SPosAttack;
	        	  break;
	          case  A_VileChase:
	        	  st.acp1=VileChase;
	        	  break;
	          case  A_VileStart:
	        	  st.acp1=VileStart;
	        	  break;
	          case  A_VileTarget:
	        	  st.acp1=VileTarget;
	        	  break;
	          case  A_VileAttack:
	        	  st.acp1=VileAttack;
	        	  break;
	          case  A_StartFire:
	        	  st.acp1=StartFire;
	        	  break;
	          case  A_Fire:
	        	  st.acp1=Fire;
	        	  break;
	          case  A_FireCrackle:
	        	  st.acp1=FireCrackle;
	        	  break;
	          case  A_Tracer:
	        	  st.acp1=Tracer;
	        	  break;
	          case  A_SkelWhoosh:
	        	  st.acp1=SkelWhoosh;
	        	  break;
	          case  A_SkelFist:
	        	  st.acp1=SkelFist;
	        	  break;
	          case  A_SkelMissile:
	        	  st.acp1=SkelMissile;
	        	  break;
	          case  A_FatRaise:
	        	  st.acp1=FatRaise;
	        	  break;
	          case  A_FatAttack1:
	        	  st.acp1=FatAttack1;
	        	  break;
	          case  A_FatAttack2:
	        	  st.acp1=FatAttack2;
	        	  break;
	          case A_FatAttack3:
	        	  st.acp1= FatAttack3;
	        	  break;
	          case  A_BossDeath:
	        	  st.acp1=BossDeath;
	        	  break;
	          case  A_CPosAttack:
	        	  st.acp1=CPosAttack;
	        	  break;
	          case  A_CPosRefire:
	        	  st.acp1=CPosRefire;
	        	  break;
	          case  A_TroopAttack:
	        	  st.acp1=TroopAttack;
	        	  break;
	          case  A_SargAttack:
	        	  st.acp1=SargAttack;
	        	  break;
	          case  A_HeadAttack:
	        	  st.acp1=HeadAttack;
	        	  break;
	          case  A_BruisAttack:
	        	  st.acp1=BruisAttack;
	        	  break;
	          case  A_SkullAttack:
	        	  st.acp1=SkullAttack;
	        	  break;
	          case  A_Metal:
	        	  st.acp1=Metal;
	        	  break;
	          case  A_SpidRefire:
	        	  st.acp1=SpidRefire;
	        	  break;
	          case  A_BabyMetal:
	        	  st.acp1=BabyMetal;
	        	  break;
	          case  A_BspiAttack:
	        	  st.acp1=BspiAttack;
	        	  break;
	          case  A_Hoof:
	        	  st.acp1=Hoof;
	        	  break;
	          case  A_CyberAttack:
	        	  st.acp1=CyberAttack;
	        	  break;
	          case  A_PainAttack:
	        	  st.acp1=PainAttack;
	        	  break;
	          case  A_PainDie:
	        	  st.acp1=PainDie;
	        	  break;
	          case  A_KeenDie:
	        	  st.acp1=KeenDie;
	              break;
	          case  A_BrainPain:
	        	  st.acp1= BrainPain;
	        	  break;
	          case  A_BrainScream:
	        	  st.acp1=BrainScream;
	        	  break;
	          case  A_BrainDie:
	        	  st.acp1= BrainDie;
	        	  break;
	          case  A_BrainAwake:
	        	  st.acp1=BrainAwake;
	        	  break;        	  
	          case  A_BrainSpit:
	        	  st.acp1=BrainSpit;
	        	  break;
	          case  A_SpawnSound:
	        	  st.acp1=SpawnSound;
	        	  break;
	          case  A_SpawnFly:
	        	  st.acp1=SpawnFly;
	        	  break;
	          case  A_BrainExplode:
	        	  st.acp1=BrainExplode;
	        	  break;
	          case  T_FireFlicker:
	        	  st.acpss=FireFlicker;
	        	  break;
	          case 	T_LightFlash:
	        	  st.acpss=LightFlash;
	        	  break;
	          case	T_StrobeFlash:
	        	  st.acpss=StrobeFlash;
	        	  break;
	          case	T_Glow:
	        	  st.acpss=Glow;
	        	  break;
	          case	T_MoveCeiling:
	        	  st.acpss=MoveCeiling;
	        	  break;
	          case	T_MoveFloor:
	        	  st.acpss=MoveFloor;
	        	  break;
	          case    T_VerticalDoor:
	              //_D_: changed this to make it work
	        	  st.acpss=VerticalDoor;
	              break;
	          case	T_PlatRaise:
	        	  st.acpss=PlatRaise;
	          	break;
	      }
	      
	  }
	
	public void doWireThinker(thinker_t st){
	      //System.out.println("Dispatching: "+action);
		if (st==null) return;
		
		// we don't want anything to be called on this.
		if (st.function==null){
			st.acp1=null;
			st.acp2=null;
			st.acpss=null;
			return;
		}
		
	      switch (st.function){
	          case P_MobjThinker:
	        	  st.acp1=MobjThinker;
	        	  break;
	          case A_Light0:
	        	  st.acp2=Light0;
	        	  break;
	          case A_WeaponReady:
	        	  st.acp2=WeaponReady;
	        	  break;        	  
	          case  A_Lower:
	        	  st.acp2=Lower;
	        	  break;
	          case  A_Raise:
	        	  st.acp2=Raise;
	        	  break;
	          case  A_Punch:
	        	  st.acp2=Punch;
	        	  break;
	          case A_ReFire:
	        	  st.acp2=ReFire;
	        	  break;
	          case A_FirePistol:
	        	  st.acp2= FirePistol;
	        	  break;
	          case  A_Light1:
	        	  st.acp2=Light1;
	        	  break;
	          case A_FireShotgun:
	        	  st.acp2=FireShotgun;
	        	  break;
	          case  A_Light2:
	        	  st.acp2=Light2;
	        	  break;
	          case A_FireShotgun2:
	        	  st.acp2=FireShotgun2;
	        	  break;
	          case  A_CheckReload:
	        	  st.acp2=CheckReload;
	        	  break;
	          case A_OpenShotgun2:
	        	  st.acp2=OpenShotgun2;
	        	  break;
	          case A_LoadShotgun2:
	        	  st.acp2=LoadShotgun2;
	        	  break;
	          case A_CloseShotgun2:
	        	  st.acp2=CloseShotgun2;
	        	  break;
	          case A_FireCGun:
	        	  st.acp2=FireCGun;
	        	  break;
	          case A_GunFlash:
	        	  st.acp2=GunFlash;
	        	  break;
	          case A_FireMissile:
	        	  st.acp2=FireMissile;
	        	  break;
	          case A_Saw:
	        	  st.acp2=Saw;
	        	  break;
	          case A_FirePlasma:
	        	  st.acp2=FirePlasma;
	        	  break;
	          case A_BFGsound:
	        	  st.acp2=BFGsound;
	        	  break;
	          case A_FireBFG:
	        	  st.acp2=FireBFG;
	        	  break;
	          case A_BFGSpray:
	        	  st.acp1=BFGSpray;
	        	  break;
	          case A_Explode:
	        	  st.acp1=Explode;
	        	  break;
	          case A_Pain:
	        	  st.acp1=Pain;
	        	  break;
	          case A_PlayerScream:
	        	  st.acp1=PlayerScream;
	        	  break;
	          case A_Fall:
	        	  st.acp1=Fall;
	        	  break;
	          case  A_XScream:
	        	  st.acp1=XScream;
	        	  break;
	          case A_Look:
	        	  st.acp1=Look;
	        	  break;
	          case  A_Chase:
	        	  st.acp1=Chase;
	        	  break;
	          case  A_FaceTarget:
	        	  st.acp1=FaceTarget;
	        	  break;
	          case  A_PosAttack:
	        	  st.acp1=PosAttack;
	        	  break;
	          case  A_Scream:
	        	  st.acp1=Scream;
	        	  break;
	          case  A_SPosAttack:
	        	  st.acp1=SPosAttack;
	        	  break;
	          case  A_VileChase:
	        	  st.acp1=VileChase;
	        	  break;
	          case  A_VileStart:
	        	  st.acp1=VileStart;
	        	  break;
	          case  A_VileTarget:
	        	  st.acp1=VileTarget;
	        	  break;
	          case  A_VileAttack:
	        	  st.acp1=VileAttack;
	        	  break;
	          case  A_StartFire:
	        	  st.acp1=StartFire;
	        	  break;
	          case  A_Fire:
	        	  st.acp1=Fire;
	        	  break;
	          case  A_FireCrackle:
	        	  st.acp1=FireCrackle;
	        	  break;
	          case  A_Tracer:
	        	  st.acp1=Tracer;
	        	  break;
	          case  A_SkelWhoosh:
	        	  st.acp1=SkelWhoosh;
	        	  break;
	          case  A_SkelFist:
	        	  st.acp1=SkelFist;
	        	  break;
	          case  A_SkelMissile:
	        	  st.acp1=SkelMissile;
	        	  break;
	          case  A_FatRaise:
	        	  st.acp1=FatRaise;
	        	  break;
	          case  A_FatAttack1:
	        	  st.acp1=FatAttack1;
	        	  break;
	          case  A_FatAttack2:
	        	  st.acp1=FatAttack2;
	        	  break;
	          case A_FatAttack3:
	        	  st.acp1= FatAttack3;
	        	  break;
	          case  A_BossDeath:
	        	  st.acp1=BossDeath;
	        	  break;
	          case  A_CPosAttack:
	        	  st.acp1=CPosAttack;
	        	  break;
	          case  A_CPosRefire:
	        	  st.acp1=CPosRefire;
	        	  break;
	          case  A_TroopAttack:
	        	  st.acp1=TroopAttack;
	        	  break;
	          case  A_SargAttack:
	        	  st.acp1=SargAttack;
	        	  break;
	          case  A_HeadAttack:
	        	  st.acp1=HeadAttack;
	        	  break;
	          case  A_BruisAttack:
	        	  st.acp1=BruisAttack;
	        	  break;
	          case  A_SkullAttack:
	        	  st.acp1=SkullAttack;
	        	  break;
	          case  A_Metal:
	        	  st.acp1=Metal;
	        	  break;
	          case  A_SpidRefire:
	        	  st.acp1=SpidRefire;
	        	  break;
	          case  A_BabyMetal:
	        	  st.acp1=BabyMetal;
	        	  break;
	          case  A_BspiAttack:
	        	  st.acp1=BspiAttack;
	        	  break;
	          case  A_Hoof:
	        	  st.acp1=Hoof;
	        	  break;
	          case  A_CyberAttack:
	        	  st.acp1=CyberAttack;
	        	  break;
	          case  A_PainAttack:
	        	  st.acp1=PainAttack;
	        	  break;
	          case  A_PainDie:
	        	  st.acp1=PainDie;
	        	  break;
	          case  A_KeenDie:
	        	  st.acp1=KeenDie;
	              break;
	          case  A_BrainPain:
	        	  st.acp1= BrainPain;
	        	  break;
	          case  A_BrainScream:
	        	  st.acp1=BrainScream;
	        	  break;
	          case  A_BrainDie:
	        	  st.acp1= BrainDie;
	        	  break;
	          case  A_BrainAwake:
	        	  st.acp1=BrainAwake;
	        	  break;        	  
	          case  A_BrainSpit:
	        	  st.acp1=BrainSpit;
	        	  break;
	          case  A_SpawnSound:
	        	  st.acp1=SpawnSound;
	        	  break;
	          case  A_SpawnFly:
	        	  st.acp1=SpawnFly;
	        	  break;
	          case  A_BrainExplode:
	        	  st.acp1=BrainExplode;
	        	  break;
	          case  T_FireFlicker:
	        	  st.acpss=FireFlicker;
	        	  break;
	          case 	T_LightFlash:
	        	  st.acpss=LightFlash;
	        	  break;
	          case	T_StrobeFlash:
	        	  st.acpss=StrobeFlash;
	        	  break;
	          case	T_Glow:
	        	  st.acpss=Glow;
	        	  break;
	          case	T_MoveCeiling:
	        	  st.acpss=MoveCeiling;
	        	  break;
	          case	T_MoveFloor:
	        	  st.acpss=MoveFloor;
	        	  break;
	          case    T_VerticalDoor:
	              //_D_: changed this to make it work
	        	  st.acpss=VerticalDoor;
	              break;
	          case	T_PlatRaise:
	        	  st.acpss=PlatRaise;
	          	break;
	          case	T_SlidingDoor:
	        	  st.acpss=SlidingDoor;
	          	break;
	      }
	      
	  }
	
	
	/** Causes object to move and perform actions.
	 *  Can only be called through the Actions dispatcher.
	 * 
	 * @param mobj
	 */
	      

	class P_MobjThinker implements ActionType1{
	    public void invoke(mobj_t  mobj){
			// momentum movement
			if (mobj.momx != 0 || mobj.momy != 0
					|| (eval(mobj.flags& MF_SKULLFLY))) {
				A.XYMovement(mobj);

				// FIXME: decent NOP/NULL/Nil function pointer please.
				if (mobj.function == think_t.NOP){
					return; // mobj was removed
				}
			}
			if ((mobj.z != mobj.floorz) || mobj.momz != 0) {
				mobj.ZMovement();

				// FIXME: decent NOP/NULL/Nil function pointer please.
				if (mobj.function == think_t.NOP){
					return; // mobj was removed
				}	
			}

			// cycle through states,
			// calling action functions at transitions
			if (mobj.tics != -1) {
				mobj.tics--;

				// you can cycle through multiple states in a tic
				if (!eval(mobj.tics))
					if (!mobj.SetMobjState(mobj.state.nextstate))
						return; // freed itself
			} else {
				// check for nightmare respawn
				if (!eval(mobj.flags& MF_COUNTKILL))
					return;

				if (!DS.respawnmonsters)
					return;

				mobj.movecount++;

				if (mobj.movecount < 12 * 35)
					return;

				if (eval(DS.leveltime& 31))
					return;

				if (RND.P_Random() > 4)
					return;

				A.NightmareRespawn(mobj);
			}
		}
	}

	class T_FireFlicker implements ActionTypeSS<fireflicker_t>{

		@Override
		public void invoke(fireflicker_t a) {
			a.FireFlicker();
			
		}
		
	}
	
	class T_LightFlash implements ActionTypeSS<lightflash_t>{

		@Override
		public void invoke(lightflash_t a) {
			a.LightFlash();			
		}
		
	}
	
	class T_StrobeFlash implements ActionTypeSS<strobe_t>{

		@Override
		public void invoke(strobe_t a) {
			a.StrobeFlash();			
		}
		
	}
	
	class T_Glow implements ActionTypeSS<glow_t>{

		@Override
		public void invoke(glow_t a) {
			a.Glow();
			
		}
		
	}
	
	class	T_MoveCeiling implements ActionTypeSS<ceiling_t>{

		@Override
		public void invoke(ceiling_t a) {
			A.MoveCeiling(a);			
		}
	
	}

	class T_MoveFloor implements ActionTypeSS<floormove_t>{

		@Override
		public void invoke(floormove_t a) {
			A.MoveFloor(a);			
		}
	
	}
	
	class	T_VerticalDoor implements ActionTypeSS<vldoor_t>{

		@Override
		public void invoke(vldoor_t a) {
			A.VerticalDoor(a);	
		}
	
	}

	class T_SlidingDoor implements ActionTypeSS<slidedoor_t> {

		@Override
		public void invoke(slidedoor_t door) {

			switch (door.status) {
			case sd_opening:
				if (door.timer-- == 0) {
					if (++door.frame == SlideDoor.SNUMFRAMES) {
						// IF DOOR IS DONE OPENING...
						LL.sides[door.line.sidenum[0]].midtexture = 0;
						LL.sides[door.line.sidenum[1]].midtexture = 0;
						door.line.flags &= ML_BLOCKING ^ 0xff;

						if (door.type == sdt_e.sdt_openOnly) {
							door.frontsector.specialdata = null;
							A.RemoveThinker(door);
							break;
						}

						door.timer = SlideDoor.SDOORWAIT;
						door.status = sd_e.sd_waiting;
					} else {
						// IF DOOR NEEDS TO ANIMATE TO NEXT FRAME...
						door.timer = SlideDoor.SWAITTICS;

						LL.sides[door.line.sidenum[0]].midtexture = (short) SL.slideFrames[door.whichDoorIndex].frontFrames[door.frame];
						LL.sides[door.line.sidenum[1]].midtexture = (short) SL.slideFrames[door.whichDoorIndex].backFrames[door.frame];
					}
				}
				break;

			case sd_waiting:
				// IF DOOR IS DONE WAITING...
				if (door.timer-- == 0) {
					// CAN DOOR CLOSE?
					if (door.frontsector.thinglist != null
							|| door.backsector.thinglist != null) {
						door.timer = SlideDoor.SDOORWAIT;
						break;
					}

					// door.frame = SNUMFRAMES-1;
					door.status = sd_e.sd_closing;
					door.timer = SlideDoor.SWAITTICS;
				}
				break;

			case sd_closing:
				if (door.timer-- == 0) {
					if (--door.frame < 0) {
						// IF DOOR IS DONE CLOSING...
						door.line.flags |= ML_BLOCKING;
						door.frontsector.specialdata = null;
						A.RemoveThinker(door);
						break;
					} else {
						// IF DOOR NEEDS TO ANIMATE TO NEXT FRAME...
						door.timer = SlideDoor.SWAITTICS;

						LL.sides[door.line.sidenum[0]].midtexture = (short) SL.slideFrames[door.whichDoorIndex].frontFrames[door.frame];
						LL.sides[door.line.sidenum[1]].midtexture = (short) SL.slideFrames[door.whichDoorIndex].backFrames[door.frame];
					}
				}
				break;
			}
		}
	}
	
	
	class	T_PlatRaise implements ActionTypeSS<plat_t>{

		@Override
		public void invoke(plat_t a) {
			A.PlatRaise(a);
		}
	
	}

	
	//
    // A_FaceTarget
    //
	
	public class A_FaceTarget implements ActionType1{
    public void invoke(mobj_t  actor)
    {   
        if (actor.target==null)
        return;
        
        actor.flags &= ~MF_AMBUSH;
        
        actor.angle = R.PointToAngle2 (actor.x,
                        actor.y,
                        actor.target.x,
                        actor.target.y)&BITS32;
        
        if (eval(actor.target.flags & MF_SHADOW))
        actor.angle += (RND.P_Random()-RND.P_Random())<<21;
        actor.angle&=BITS32;
    	}
    }


    //
    // A_PosAttack
    //
	class A_PosAttack implements ActionType1{
	    public void invoke(mobj_t  actor)
	    {   
        int     angle;
        int     damage;
        int     slope;
        
        if (actor.target==null)
        return;
        FaceTarget.invoke(actor);
        angle = (int) actor.angle;
        slope = A.AimLineAttack (actor, angle, MISSILERANGE);

        S.StartSound(actor, sfxenum_t.sfx_pistol);
        angle += (RND.P_Random()-RND.P_Random())<<20;
        damage = ((RND.P_Random()%5)+1)*3;
        A.LineAttack (actor, angle, MISSILERANGE, slope, damage);
	    }
	}


	class A_SPosAttack implements ActionType1{
        public void invoke(mobj_t  actor)
        {   
        int     i;
        long     angle;
        long     bangle;
        int     damage;
        int     slope;
        
        if (actor.target==null)
        return;

        S.StartSound(actor, sfxenum_t.sfx_shotgn);
        A_FaceTarget (actor);
        bangle = actor.angle;
        slope = A.AimLineAttack (actor, bangle, MISSILERANGE);

        for (i=0 ; i<3 ; i++)
        {
        angle = bangle + ((RND.P_Random()-RND.P_Random())<<20);
        damage = ((RND.P_Random()%5)+1)*3;
        A.LineAttack (actor, angle, MISSILERANGE, slope, damage);
        }
        }
	}

	class A_CPosAttack implements ActionType1{
        public void invoke(mobj_t  actor)
        {   
        long     angle;
        long     bangle;
        int     damage;
        int     slope;
        
        if (actor.target==null)
        return;

        S.StartSound(actor, sfxenum_t.sfx_shotgn);
        A_FaceTarget (actor);
        bangle = actor.angle;
        slope = A.AimLineAttack (actor, bangle, MISSILERANGE);

        angle = bangle + ((RND.P_Random()-RND.P_Random())<<20);
        damage = ((RND.P_Random()%5)+1)*3;
        A.LineAttack (actor, angle, MISSILERANGE, slope, damage);
        }
	}

	class A_CPosRefire implements ActionType1{
        public void invoke(mobj_t  actor){
        // keep firing unless target got out of sight
        A_FaceTarget (actor);

        if (RND.P_Random () < 40)
        return;

        if (actor.target==null
        || actor.target.health <= 0
        || !EN.CheckSight (actor, actor.target) )
        {
        actor.SetMobjState ( actor.info.seestate);
        }
        }
	}


	class A_SpidRefire implements ActionType1{
        public void invoke(mobj_t  actor){
        // keep firing unless target got out of sight
        A_FaceTarget (actor);

        if (RND.P_Random () < 10)
        return;

        if (actor.target==null
        || actor.target.health <= 0
        || !EN.CheckSight (actor, actor.target) )
        {
        actor.SetMobjState ( actor.info.seestate);
        	}
        }
	}

	class A_BspiAttack implements ActionType1{
        public void invoke(mobj_t  actor){ 
        if (actor.target==null)
        return;
            
        A_FaceTarget (actor);

        // launch a missile
        A.SpawnMissile (actor, actor.target, mobjtype_t.MT_ARACHPLAZ);
        }
	}


    //
    // A_TroopAttack
    //
	class A_TroopAttack implements ActionType1{
        public void invoke(mobj_t  actor){ 
        int     damage;
        
        if (actor.target==null)
        return;
            
        A_FaceTarget (actor);
        if (A.EN.CheckMeleeRange (actor))
        {
        	S.StartSound(actor, sfxenum_t.sfx_claw);
        damage = (RND.P_Random()%8+1)*3;
        A.DamageMobj (actor.target, actor, actor, damage);
        return;
        }

        
        // launch a missile
        A.SpawnMissile (actor, actor.target, mobjtype_t.MT_TROOPSHOT);
        }
	}

    class A_SargAttack implements ActionType1{
        public void invoke(mobj_t  actor){ 
        int     damage;

        if (actor.target==null)
        return;
            
        A_FaceTarget (actor);
        if (EN.CheckMeleeRange (actor))
        {
        damage = ((RND.P_Random()%10)+1)*4;
        A.DamageMobj (actor.target, actor, actor, damage);
        }
        }
    }


    class A_HeadAttack implements ActionType1{
        public void invoke(mobj_t  actor){ 
        int     damage;
        
        if (actor.target==null)
        return;
            
        A_FaceTarget (actor);
        if (EN.CheckMeleeRange (actor))
        {
        damage = (RND.P_Random()%6+1)*10;
        A.DamageMobj (actor.target, actor, actor, damage);
        return;
        }
        
        // launch a missile
        A.SpawnMissile (actor, actor.target, mobjtype_t.MT_HEADSHOT);
        }
    }

    class A_CyberAttack implements ActionType1{
        public void invoke(mobj_t  actor){ 
        if (actor.target==null)
        return;
            
        A_FaceTarget (actor);
        A.SpawnMissile (actor, actor.target, mobjtype_t.MT_ROCKET);
        }
    }

    class A_BruisAttack implements ActionType1{
        public void invoke(mobj_t  actor){ 
        int     damage;
        
        if (actor.target==null)
        return;
            
        if (EN.CheckMeleeRange (actor))
        {
        	S.StartSound(actor, sfxenum_t.sfx_claw);
        damage = (RND.P_Random()%8+1)*10;
        A.DamageMobj (actor.target, actor, actor, damage);
        return;
        }
        
        // launch a missile
        A.SpawnMissile (actor, actor.target, mobjtype_t.MT_BRUISERSHOT);
        }
       }


    //
    // A_SkelMissile
    //
    class A_SkelMissile implements ActionType1{
        public void invoke(mobj_t  actor){ 
        mobj_t  mo;
        
        if (actor.target==null)
        return;
            
        A_FaceTarget (actor);
        actor.z += 16*FRACUNIT;    // so missile spawns higher
        mo = A.SpawnMissile (actor, actor.target, mobjtype_t.MT_TRACER);
        actor.z -= 16*FRACUNIT;    // back to normal

        mo.x += mo.momx;
        mo.y += mo.momy;
        mo.tracer = actor.target;
        }
    }

    private static final int TRACEANGLE = 0xc000000;

    class A_Tracer implements ActionType1{
        public void invoke(mobj_t  actor){ 
        long exact; //angle_t
        int dist,slope; // fixed
        mobj_t  dest;
        mobj_t  th;
            
        if (eval(DS.gametic &3))
        return;
        
        // spawn a puff of smoke behind the rocket      
        A.SpawnPuff (actor.x, actor.y, actor.z);
        
        th = A.SpawnMobj (actor.x-actor.momx,
                  actor.y-actor.momy,
                  actor.z, mobjtype_t.MT_SMOKE);
        
        th.momz = MAPFRACUNIT;
        th.tics -= RND.P_Random()&3;
        if (th.tics < 1)
        th.tics = 1;
        
        // adjust direction
        dest = actor.tracer;
        
        if (dest==null || dest.health <= 0)
        return;
        
        // change angle 
        exact = R.PointToAngle2 (actor.x,
                     actor.y,
                     dest.x,
                     dest.y)&BITS32;
        
        // MAES: let's analyze the logic here...
        // So exact is the angle between the missile and its target. 

        if (exact != actor.angle) // missile is already headed there dead-on.
        {
        if (exact - actor.angle > ANG180)
        {
            actor.angle -= TRACEANGLE;
            actor.angle&=BITS32;
            if (((exact - actor.angle)&BITS32) < ANG180)
            actor.angle = exact;
        }
        else
        {
            actor.angle += TRACEANGLE;
            actor.angle&=BITS32;
            if (((exact - actor.angle)&BITS32) > ANG180)
            actor.angle = exact;
        }
        }
        
        // MAES: fixed and sped up.
        int exact2 = Tables.toBAMIndex(actor.angle);
        actor.momx = FixedMul (actor.info.speed, finecosine[exact2]);
        actor.momy = FixedMul (actor.info.speed, finesine[exact2]);
        
        // change slope
        dist = AproxDistance (dest.x - actor.x,
                    dest.y - actor.y);
        
        dist = dist / actor.info.speed;

        if (dist < 1)
        dist = 1;
        slope = (dest.z+40*FRACUNIT - actor.z) / dist;

        if (slope < actor.momz)
        actor.momz -= FRACUNIT/8;
        else
        actor.momz += FRACUNIT/8;
        }
    }

    class A_SkelWhoosh implements ActionType1{
        public void invoke(mobj_t  actor){ 
        if (actor.target==null)
        return;
        A_FaceTarget (actor);
        S.StartSound(actor,sfxenum_t.sfx_skeswg);
        }
    }
    
    class A_SkelFist implements ActionType1{
        public void invoke(mobj_t  actor){ 
        int     damage;

        if (actor.target==null)
        return;
            
        A_FaceTarget (actor);
        
        if (EN.CheckMeleeRange (actor))
        {
        damage = ((RND.P_Random()%10)+1)*6;
        S.StartSound(actor, sfxenum_t.sfx_skepch);
        A.DamageMobj (actor.target, actor, actor, damage);
        }
        }
    }

    //
    // A_VileChase
    // Check for ressurecting a body
    //
    class A_VileChase implements ActionType1{
        public void invoke(mobj_t  actor){ 
        int         xl;
        int         xh;
        int         yl;
        int         yh;
        
        int         bx;
        int         by;

        mobjinfo_t     info;
        mobj_t      temp;
        
        if (actor.movedir != DI_NODIR)
        {
        // check for corpses to raise
        A.VileCheck.viletryx =
            actor.x + actor.info.speed*xspeed[actor.movedir];
        A.VileCheck.viletryy =
            actor.y + actor.info.speed*yspeed[actor.movedir];

        xl = LL.getSafeBlockX(A.VileCheck.viletryx - LL.bmaporgx - MAXRADIUS*2);
        xh = LL.getSafeBlockX(A.VileCheck.viletryx - LL.bmaporgx + MAXRADIUS*2);
        yl = LL.getSafeBlockY(A.VileCheck.viletryy - LL.bmaporgy - MAXRADIUS*2);
        yh = LL.getSafeBlockY(A.VileCheck.viletryy - LL.bmaporgy + MAXRADIUS*2);
        
        A.VileCheck.vileobj = actor;
        for (bx=xl ; bx<=xh ; bx++)
        {
            for (by=yl ; by<=yh ; by++)
            {
            // Call PIT_VileCheck to check
            // whether object is a corpse
            // that can be raised.
            if (!A.BlockThingsIterator(bx,by,A.VileCheck))
            {
                // got one!
                temp = actor.target;
                actor.target = A.VileCheck.corpsehit;
                A_FaceTarget (actor);
                actor.target = temp;
                        
                actor.SetMobjState ( statenum_t.S_VILE_HEAL1);
                S.StartSound(A.VileCheck.corpsehit, sfxenum_t.sfx_slop);
                info = A.VileCheck.corpsehit.info;
                
                A.VileCheck.corpsehit.SetMobjState (info.raisestate);
                A.VileCheck.corpsehit.height <<= 2;
                A.VileCheck.corpsehit.flags = info.flags;
                A.VileCheck.corpsehit.health = info.spawnhealth;
                A.VileCheck.corpsehit.target = null;

                return;
            }
            }
        }
        }

        // Return to normal attack.
        Chase.invoke (actor);
        }
    }


    //
    // A_VileStart
    //
    class A_VileStart implements ActionType1{
        public void invoke(mobj_t  actor){ 
    	S.StartSound(actor, sfxenum_t.sfx_vilatk);
        }
    }


    //
    // A_Fire
    // Keep fire in front of player unless out of sight
    //
    
    class A_StartFire implements ActionType1{
        public void invoke(mobj_t  actor){ 
    	S.StartSound(actor,sfxenum_t.sfx_flamst);
        Fire.invoke(actor);
        }
    }

    class A_FireCrackle implements ActionType1{
        public void invoke(mobj_t  actor){ 
    	S.StartSound(actor,sfxenum_t.sfx_flame);
        Fire.invoke(actor);
        }
     }
    
    //
    // A_FirePistol
    //
    class A_FirePistol implements ActionType2{
    public void invoke( player_t player, pspdef_t psp ) 
    {
  	  S.StartSound(player.mo, sfxenum_t.sfx_pistol);

        player.mo.SetMobjState ( statenum_t.S_PLAY_ATK2);
        player.ammo[weaponinfo[player.readyweapon.ordinal()].ammo.ordinal()]--;

        player.SetPsprite (
              ps_flash,
              weaponinfo[player.readyweapon.ordinal()].flashstate);

        A.P_BulletSlope (player.mo);
        A.P_GunShot (player.mo, !eval(player.refire));
    	}
    }

    //
    // A_FireShotgun
    //
    class A_FireShotgun implements ActionType2{
        public void invoke( player_t player, pspdef_t psp ) 
        {
        int     i;
        
        S.StartSound(player.mo, sfxenum_t.sfx_shotgn);
        player.mo.SetMobjState ( statenum_t.S_PLAY_ATK2);

        player.ammo[weaponinfo[player.readyweapon.ordinal()].ammo.ordinal()]--;

        player.SetPsprite (
              ps_flash,
              weaponinfo[player.readyweapon.ordinal()].flashstate);

        A.P_BulletSlope (player.mo);
        
        for (i=0 ; i<7 ; i++)
        A.P_GunShot (player.mo, false);
        }
    }


    /**
     * A_FireShotgun2
     */
    
    class A_FireShotgun2 implements ActionType2{
        public void invoke( player_t player, pspdef_t psp ) 
        {
        int     i;
        long angle;
        int     damage;
            
        
        S.StartSound (player.mo, sfxenum_t.sfx_dshtgn);
        player.mo.SetMobjState (statenum_t.S_PLAY_ATK2);

        player.ammo[weaponinfo[player.readyweapon.ordinal()].ammo.ordinal()]-=2;

        player.SetPsprite (
              ps_flash,
              weaponinfo[player.readyweapon.ordinal()].flashstate);

        A.P_BulletSlope (player.mo);
        
        for (i=0 ; i<20 ; i++)
        {
        damage = 5*(RND.P_Random ()%3+1);
        angle = player.mo.angle;
        angle += (RND.P_Random()-RND.P_Random())<<19;
        A.LineAttack (player.mo,
                  angle,
                  MISSILERANGE,
                  A.bulletslope + ((RND.P_Random()-RND.P_Random())<<5), damage);
        }
    }
    }
    
    
    class A_Fire implements ActionType1{
        public void invoke(mobj_t  actor){ 
        mobj_t  dest;
        //long    an;
            
        dest = actor.tracer;
        if (dest==null)
        return;
            
        // don't move it if the vile lost sight
        if (!EN.CheckSight (actor.target, dest) )
        return;

        // an = dest.angle >>> ANGLETOFINESHIFT;

        A.UnsetThingPosition (actor);
        actor.x = dest.x + FixedMul (24*FRACUNIT, finecosine(dest.angle));
        actor.y = dest.y + FixedMul (24*FRACUNIT, finesine(dest.angle));
        actor.z = dest.z;
        LL.SetThingPosition (actor);
        }
    }



    //
    // A_VileTarget
    // Spawn the hellfire
    //
    class A_VileTarget implements ActionType1{
        public void invoke(mobj_t  actor){ 
        mobj_t  fog;
        
        if (actor.target==null)
        return;

        A_FaceTarget (actor);

        fog = A.SpawnMobj (actor.target.x,
                   actor.target.y,
                   actor.target.z, mobjtype_t.MT_FIRE);
        
        actor.tracer = fog;
        fog.target = actor;
        fog.tracer = actor.target;
        Fire.invoke(fog);
        }
    }




    //
    // A_VileAttack
    //
    class A_VileAttack implements ActionType1{
        public void invoke(mobj_t  actor){ 
        mobj_t  fire;
        //int     an;
        
        if (actor.target==null)
        return;
        
        A_FaceTarget (actor);

        if (!EN.CheckSight (actor, actor.target) )
        return;

        S.StartSound(actor, sfxenum_t.sfx_barexp);
        A.DamageMobj (actor.target, actor, actor, 20);
        actor.target.momz = 1000*MAPFRACUNIT/actor.target.info.mass;
        
       // an = actor.angle >> ANGLETOFINESHIFT;

        fire = actor.tracer;

        if (fire==null)
        return;
            
        // move the fire between the vile and the player
        fire.x = actor.target.x - FixedMul (24*FRACUNIT, finecosine(actor.angle));
        fire.y = actor.target.y - FixedMul (24*FRACUNIT, finesine(actor.angle));  
        A.RadiusAttack (fire, actor, 70 );
        }
    }



    //
    // Mancubus attack,
    // firing three missiles (bruisers)
    // in three different directions?
    // Doesn't look like it. 
    //
    private static final long FATSPREAD =  (ANG90/8);

    class A_FatRaise implements ActionType1{
        public void invoke(mobj_t  actor){ 
        A_FaceTarget (actor);
        S.StartSound(actor, sfxenum_t.sfx_manatk);
        }
    }


    class A_FatAttack1 implements ActionType1{
        public void invoke(mobj_t  actor){ 
        mobj_t  mo;
        int     an;
        
        A_FaceTarget (actor);
        // Change direction  to ...
        actor.angle += FATSPREAD;
        A.SpawnMissile (actor, actor.target, mobjtype_t.MT_FATSHOT);

        mo = A.SpawnMissile (actor, actor.target, mobjtype_t.MT_FATSHOT);
        mo.angle += FATSPREAD;
        an = Tables.toBAMIndex(mo.angle);
        mo.momx = FixedMul (mo.info.speed, finecosine[an]);
        mo.momy = FixedMul (mo.info.speed, finesine[an]);
        }
    }

    class A_FatAttack2 implements ActionType1{
        public void invoke(mobj_t  actor){ 
        mobj_t  mo;
        int     an;

        A_FaceTarget (actor);
        // Now here choose opposite deviation.
        actor.angle -= FATSPREAD;
        A.SpawnMissile (actor, actor.target, mobjtype_t.MT_FATSHOT);

        mo = A.SpawnMissile (actor, actor.target, mobjtype_t.MT_FATSHOT);
        mo.angle -= FATSPREAD*2;
        an = Tables.toBAMIndex(mo.angle);
        mo.momx = FixedMul (mo.info.speed, finecosine[an]);
        mo.momy = FixedMul (mo.info.speed, finesine[an]);
        }
    }

    class A_FatAttack3 implements ActionType1{
        public void invoke(mobj_t  actor){ 
        mobj_t  mo;
        int     an;

        A_FaceTarget (actor);
        
        mo = A.SpawnMissile (actor, actor.target, mobjtype_t.MT_FATSHOT);
        mo.angle -= FATSPREAD/2;
        an = Tables.toBAMIndex(mo.angle);
        mo.momx = FixedMul (mo.info.speed, finecosine[an]);
        mo.momy = FixedMul (mo.info.speed, finesine[an]);

        mo = A.SpawnMissile (actor, actor.target, mobjtype_t.MT_FATSHOT);
        mo.angle += FATSPREAD/2;
        an = Tables.toBAMIndex(mo.angle);
        mo.momx = FixedMul (mo.info.speed, finecosine[an]);
        mo.momy = FixedMul (mo.info.speed, finesine[an]);
        }
    }



    private static final int SKULLSPEED  =    (20*MAPFRACUNIT);
    /**      
     * SkullAttack
     * Fly at the player like a missile.
     */
    class A_SkullAttack implements ActionType1{
        public void invoke(mobj_t  actor){ 
        mobj_t      dest;
        int     an;
        int         dist;

        if (actor.target==null)
        return;
            
        dest = actor.target;   
        actor.flags |= MF_SKULLFLY;

        S.StartSound(actor, actor.info.attacksound);
        FaceTarget.invoke(actor);
        an = Tables.toBAMIndex(actor.angle);
        actor.momx = FixedMul (SKULLSPEED, finecosine[an]);
        actor.momy = FixedMul (SKULLSPEED, finesine[an]);
        dist = AproxDistance (dest.x - actor.x, dest.y - actor.y);
        dist = dist / SKULLSPEED;
        
        if (dist < 1)
        dist = 1;
        actor.momz = (dest.z+(dest.height>>1) - actor.z) / dist;
        }
    }


    /**
     * A_PainShootSkull
     * Spawn a lost soul and launch it at the target
     * It's not a valid callback like the others, actually.
     * No idea if some DEH patch does use it to cause
     * mayhem though.
     * 
     */
    
    private void A_PainShootSkull
    ( mobj_t    actor,
      long   angle )
    {
        int x,y, z; // fixed
        
        mobj_t  newmobj;
        int an; // angle
        int     prestep;
        int     count;
        thinker_t  currentthinker;

        // count total number of skull currently on the level
        count = 0;

        currentthinker = A.thinkercap.next;
        while (currentthinker != A.thinkercap)
        {
        if (   (currentthinker.function == think_t.P_MobjThinker)
            && ((mobj_t)currentthinker).type == mobjtype_t.MT_SKULL)
            count++;
        currentthinker = currentthinker.next;
        }

        // if there are allready 20 skulls on the level,
        // don't spit another one
        if (count > MAXSKULLS)
        return;


        // okay, there's playe for another one
        an = Tables.toBAMIndex(angle);
        
        prestep =
        4*FRACUNIT
        + 3*(actor.info.radius + mobjinfo[mobjtype_t.MT_SKULL.ordinal()].radius)/2;
        
        x = actor.x + FixedMul (prestep, finecosine[an]);
        y = actor.y + FixedMul (prestep, finesine[an]);
        z = actor.z + 8*FRACUNIT;
            
        newmobj = A.SpawnMobj (x , y, z, mobjtype_t.MT_SKULL);

        // Check for movements.
        if (!A.TryMove (newmobj, newmobj.x, newmobj.y))
        {
        // kill it immediately
        A.DamageMobj (newmobj,actor,actor,10000);   
        return;
        }
            
        newmobj.target = actor.target;
        SkullAttack.invoke (newmobj);
    }


    //
    // A_PainAttack
    // Spawn a lost soul and launch it at the target
    // 
    class A_PainAttack implements ActionType1{
        public void invoke(mobj_t  actor){ 
        if (actor.target==null)
        return;

        A_FaceTarget (actor);
        A_PainShootSkull (actor, actor.angle);
        }
    }


    class A_PainDie implements ActionType1{
        public void invoke(mobj_t  actor){ 
        Fall.invoke (actor);
        A_PainShootSkull (actor, actor.angle+ANG90);
        A_PainShootSkull (actor, actor.angle+ANG180);
        A_PainShootSkull (actor, actor.angle+ANG270);
        }
    }






    class A_Scream implements ActionType1 {
        public void invoke(mobj_t  actor){ 
        int     sound;
        
        switch (actor.info.deathsound)
        {
          case sfx_None:
        return;
            
          case sfx_podth1:
          case sfx_podth2:
          case sfx_podth3:
        sound = sfxenum_t.sfx_podth1.ordinal() + RND.P_Random ()%3;
        break;
            
          case sfx_bgdth1:
          case sfx_bgdth2:
        sound = sfxenum_t.sfx_bgdth1.ordinal() + RND.P_Random ()%2;
        break;
        
          default:
        sound = actor.info.deathsound.ordinal();
        break;
        }

        // Check for bosses.
        if (actor.type==mobjtype_t.MT_SPIDER
        || actor.type == mobjtype_t.MT_CYBORG)
        {
        // full volume
        S.StartSound (null, sound);
        }
        else
        S.StartSound (actor, sound);
        }
    }


    /**
     * A_WeaponReady
     * The player can fire the weapon
     * or change to another weapon at this time.
     * Follows after getting weapon up,
     * or after previous attack/fire sequence.
     */

    class A_WeaponReady implements ActionType2 {
    	public void invoke( player_t player, pspdef_t psp )
    {   
        statenum_t  newstate;
        int     angle;
        
        // get out of attack state
        if (player.mo.state == states[statenum_t.S_PLAY_ATK1.ordinal()]
        || player.mo.state == states[statenum_t.S_PLAY_ATK2.ordinal()] )
        {
        player.mo.SetMobjState (statenum_t.S_PLAY);
        }
        
        if (player.readyweapon == weapontype_t.wp_chainsaw
        && psp.state == states[statenum_t.S_SAW.ordinal()])
        {
      	  S.StartSound(player.mo, sfxenum_t.sfx_sawidl);
        }
        
        // check for change
        //  if player is dead, put the weapon away
        if (player.pendingweapon != weapontype_t.wp_nochange || !eval(player.health[0]))
        {
        // change weapon
        //  (pending weapon should allready be validated)
        newstate = weaponinfo[player.readyweapon.ordinal()].downstate;
        player.SetPsprite ( player_t.ps_weapon, newstate);
        return; 
        }
        
        // check for fire
        //  the missile launcher and bfg do not auto fire
        if (eval(player.cmd.buttons & BT_ATTACK))
        {
        if ( !player.attackdown
             || (player.readyweapon != weapontype_t.wp_missile
             && player.readyweapon != weapontype_t.wp_bfg) )
        {
            player.attackdown = true;
            EN.FireWeapon (player);      
            return;
        }
        }
        else
        player.attackdown = false;
        
        // bob the weapon based on movement speed
        angle = (128*DS.leveltime)&FINEMASK;
        psp.sx = FRACUNIT + FixedMul (player.bob, finecosine[angle]);
        angle &= FINEANGLES/2-1;
        psp.sy = player_t.WEAPONTOP + FixedMul (player.bob, finesine[angle]);
    	}
    }
    
    //
    // A_Raise
    //
    class A_Raise implements ActionType2 {
    	public void invoke( player_t player, pspdef_t psp )
        {   
        statenum_t  newstate;
        
        //System.out.println("Trying to raise weapon");      
        //System.out.println(player.readyweapon + " height: "+psp.sy);
        psp.sy -= RAISESPEED;

        if (psp.sy > WEAPONTOP ) {
        //System.out.println("Not on top yet, exit and repeat.");
        return;
        }
        
        psp.sy = WEAPONTOP;
        
        // The weapon has been raised all the way,
        //  so change to the ready state.
        newstate = weaponinfo[player.readyweapon.ordinal()].readystate;
        //System.out.println("Weapon raised, setting new state.");
        
        player.SetPsprite (ps_weapon, newstate);
        }
    }


    //
    // A_ReFire
    // The player can re-fire the weapon
    // without lowering it entirely.
    //
    class A_ReFire implements ActionType2 {
    	public void invoke( player_t player, pspdef_t psp )
        {   
        
        // check for fire
        //  (if a weaponchange is pending, let it go through instead)
        if ( eval(player.cmd.buttons & BT_ATTACK) 
         && player.pendingweapon == weapontype_t.wp_nochange
         && eval(player.health[0]))
        {
        player.refire++;
        EN.FireWeapon (player);
        }
        else
        {
        player.refire = 0;
        player.CheckAmmo ();
        }
        }
    }
    
    
    //
    // A_GunFlash
    //
    class A_GunFlash implements ActionType2 {
    	public void invoke( player_t player, pspdef_t psp )
        {    
        player.mo.SetMobjState (statenum_t.S_PLAY_ATK2);
        player.SetPsprite (ps_flash,weaponinfo[player.readyweapon.ordinal()].flashstate);
        }
    }

    //
    // A_Punch
    //
    class A_Punch implements ActionType2 {
    	public void invoke( player_t player, pspdef_t psp )
        {   
        long angle; //angle_t
        int     damage;
        int     slope;
        
        damage = (RND.P_Random ()%10+1)<<1;

        if (eval(player.powers[pw_strength]))    
        damage *= 10;

        angle = player.mo.angle;
        //angle = (angle+(RND.P_Random()-RND.P_Random())<<18)/*&BITS32*/;
        // _D_: for some reason, punch didnt work until I change this
        // I think it's because of "+" VS "<<" prioritys...
        angle += (RND.P_Random()-RND.P_Random())<<18;
        slope = A.AimLineAttack (player.mo, angle, MELEERANGE);
        A.LineAttack (player.mo, angle, MELEERANGE, slope, damage);

        // turn to face target
        if (eval(A.linetarget))
        {
      	  S.StartSound(player.mo, sfxenum_t.sfx_punch);
        player.mo.angle = R.PointToAngle2 (player.mo.x,
                             player.mo.y,
                             A.linetarget.x,
                             A.linetarget.y)&BITS32;
        }
    }	
    }


    //
    // A_Saw
    //
    class A_Saw implements ActionType2 {
    	public void invoke( player_t player, pspdef_t psp )
        {   
        long angle; // angle_t
        int     damage;
        int     slope;

        damage = 2*(RND.P_Random ()%10+1);
        angle = player.mo.angle;
        angle += (RND.P_Random()-RND.P_Random())<<18;
        angle&=BITS32;
        
        // use meleerange + 1 se the puff doesn't skip the flash
        slope = A.AimLineAttack (player.mo, angle, MELEERANGE+1);
        A.LineAttack (player.mo, angle, MELEERANGE+1, slope, damage);

        if (!eval(A.linetarget))
        {
      	  S.StartSound(player.mo, sfxenum_t.sfx_sawful);
        return;
        }
        S.StartSound(player.mo, sfxenum_t.sfx_sawhit);
        
        // turn to face target
        angle = R.PointToAngle2 (player.mo.x, player.mo.y,
                     A.linetarget.x, A.linetarget.y)&BITS32;
        /* FIXME: this comparison is going to fail.... or not?
         If e.g. angle = 359 degrees (which will be mapped to a small negative number),       
         and player.mo.angle = 160 degrees (a large, positive value), the result will be a 
         large negative value, which will still be "greater" than ANG180.
         
         It seems that *differences* between angles will always compare correctly, but
         not direct inequalities.
         
        */
        
        // Yet another screwy place where unsigned BAM angles are used as SIGNED comparisons.
        long dangle= (angle - player.mo.angle);
        dangle&=BITS32;
        if (dangle > ANG180)
        {
        if ((int)dangle < -ANG90/20)
            player.mo.angle = angle + ANG90/21;
        else
            player.mo.angle -= ANG90/20;
        }
        else
        {
        if (dangle > ANG90/20)
            player.mo.angle = angle - ANG90/21;
        else
            player.mo.angle += ANG90/20;
        }
        player.mo.angle&=BITS32;
        player.mo.flags |= MF_JUSTATTACKED;
        }
    }


    //
    // A_FireMissile
    //
    class A_FireMissile implements ActionType2 {
    	public void invoke( player_t player, pspdef_t psp )
        {   
        player.ammo[weaponinfo[player.readyweapon.ordinal()].ammo.ordinal()]--;
        A.SpawnPlayerMissile (player.mo, mobjtype_t.MT_ROCKET);
        }
    }


    //
    // A_FireBFG
    //
    class A_FireBFG implements ActionType2 {
    	 
    	
    	 // plasma cells for a bfg attack
    	 // IDEA: make action functions partially parametrizable?
    	private static final int BFGCELLS      =  40;     
    	
    	public void invoke( player_t player, pspdef_t psp )
        {   
        player.ammo[weaponinfo[player.readyweapon.ordinal()].ammo.ordinal()] -= BFGCELLS;
        A.SpawnPlayerMissile (player.mo, mobjtype_t.MT_BFG);
        }
    }


    //
    // A_FireCGun
    //
    class A_FireCGun implements ActionType2 {
    	public void invoke( player_t player, pspdef_t psp )
        {   
        // For convenience.
        int readyweap=player.readyweapon.ordinal();
        int flashstate=weaponinfo[readyweap].flashstate.ordinal();
        int current_state=psp.state.id;
        
        S.StartSound (player.mo, sfxenum_t.sfx_pistol);      
        if (!eval(player.ammo[weaponinfo[readyweap].ammo.ordinal()]))
        return;
            
        player.mo.SetMobjState (statenum_t.S_PLAY_ATK2);
        player.ammo[weaponinfo[readyweap].ammo.ordinal()]--;
        
        // MAES: Code to alternate between two different gun flashes
        // needed a clear rewrite, as it was way too messy.
        // We know that the flash states are a certain amount away from 
        // the firing states. This amount is two frames.
        player.SetPsprite (ps_flash,statenum_t.values()[flashstate+current_state-statenum_t.S_CHAIN1.ordinal()]
              );

        A.P_BulletSlope (player.mo);
        
        A.P_GunShot (player.mo, !eval(player.refire));
        }
    }
    
    //
    // A_FirePlasma
    //
    class A_FirePlasma implements ActionType2 {
    	public void invoke( player_t player, pspdef_t psp )
        {   
        player.ammo[weaponinfo[player.readyweapon.ordinal()].ammo.ordinal()]--;

        player.SetPsprite (
              ps_flash,
              weaponinfo[player.readyweapon.ordinal()].flashstate );

        A.SpawnPlayerMissile (player.mo, mobjtype_t.MT_PLASMA);
        }
    }
    
    class A_XScream implements ActionType1 {
        public void invoke(mobj_t  actor){ 
        S.StartSound (actor, sfxenum_t.sfx_slop); 
        }
    }

    class A_Pain implements ActionType1 {
        public void invoke(mobj_t  actor){ 
        if (actor.info.painsound!=null)
        	S.StartSound(actor, actor.info.painsound);   
        }
    }



    class A_Fall implements ActionType1 {
        public void invoke(mobj_t  actor){ 
        // actor is on ground, it can be walked over
        actor.flags &= ~MF_SOLID;

        // So change this if corpse objects
        // are meant to be obstacles.
        }
    }


    //
    // A_Explode
    //
    class A_Explode implements ActionType1 {
        public void invoke(mobj_t  thingy){ 
        A.RadiusAttack ( thingy, thingy.target, 128 );
        }
    }


    /**
     * A_BossDeath
     * Possibly trigger special effects
     * if on first boss level
     *
     * TODO: find out how Plutonia/TNT does cope with this.
     * Special clauses?
     *
     */
    class A_BossDeath implements ActionType1 {
        public void invoke(mobj_t  mo){ 
        thinker_t  th;
        mobj_t  mo2;
        line_t  junk = new line_t();
        int     i;
            
        if ( DS.isCommercial())
        {
        if (DS.gamemap != 7)
            return;
            
        if ((mo.type != mobjtype_t.MT_FATSO)
            && (mo.type != mobjtype_t.MT_BABY))
            return;
        }
        else
        {
        switch(DS.gameepisode)
        {
          case 1:
            if (DS.gamemap != 8)
            return;

            if (mo.type != mobjtype_t.MT_BRUISER)
            return;
            break;
            
          case 2:
            if (DS.gamemap != 8)
            return;

            if (mo.type != mobjtype_t.MT_CYBORG)
            return;
            break;
            
          case 3:
            if (DS.gamemap != 8)
            return;
            
            if (mo.type != mobjtype_t.MT_SPIDER)
            return;
            
            break;
            
          case 4:
            switch(DS.gamemap)
            {
              case 6:
            if (mo.type != mobjtype_t.MT_CYBORG)
                return;
            break;
            
              case 8: 
            if (mo.type != mobjtype_t.MT_SPIDER)
                return;
            break;
            
              default:
            return;
            }
            break;
            
          default:
            if (DS.gamemap != 8)
            return;
            break;
        }
            
        }

        
        // make sure there is a player alive for victory
        for (i=0 ; i<MAXPLAYERS ; i++)
        if (DS.playeringame[i] && DS.players[i].health[0] > 0)
            break;
        
        if (i==MAXPLAYERS)
        return; // no one left alive, so do not end game
        
        // scan the remaining thinkers to see
        // if all bosses are dead
        for (th = A.thinkercap.next ; th != A.thinkercap ; th=th.next)
        {
        if (th.function != think_t.P_MobjThinker)
            continue;
        
        mo2 = (mobj_t)th;
        if (mo2 != mo
            && mo2.type == mo.type
            && mo2.health > 0)
        {
            // other boss not dead
            return;
        }
        }
        
        // victory!
        if ( DS.isCommercial())
        {
        if (DS.gamemap == 7)
        {
            if (mo.type == mobjtype_t.MT_FATSO)
            {
            junk.tag = 666;
            A.DoFloor(junk,floor_e.lowerFloorToLowest);
            return;
            }
            
            if (mo.type == mobjtype_t.MT_BABY)
            {
            junk.tag = 667;
            A.DoFloor(junk,floor_e.raiseToTexture);
            return;
            }
        }
        }
        else
        {
        switch(DS.gameepisode)
        {
          case 1:
            junk.tag = 666;
            A.DoFloor (junk, floor_e.lowerFloorToLowest);
            return;
            
          case 4:
            switch(DS.gamemap)
            {
              case 6:
            junk.tag = 666;
            A.DoDoor (junk, vldoor_e.blazeOpen);
            return;
            
              case 8:
            junk.tag = 666;
            A.DoFloor (junk, floor_e.lowerFloorToLowest);
            return;
            }
        }
        }
        
        A.DM.ExitLevel ();
        }
    }

    class A_Hoof implements ActionType1 {
        public void invoke(mobj_t  mo){ 
    	S.StartSound(mo, sfxenum_t.sfx_hoof);
    	 Chase.invoke(mo);
        }
    }
    
    class A_KeenDie implements ActionType1 {
        public void invoke(mobj_t  mo){ 
        thinker_t  th;
        mobj_t mo2;
        line_t  junk = new line_t(); // MAES: fixed null 21/5/2011

        Fall.invoke(mo);
        
        // scan the remaining thinkers
        // to see if all Keens are dead
        for (th = A.thinkercap.next ; th != A.thinkercap ; th=th.next)
        {
        if (th.function != think_t.P_MobjThinker)
            continue;

        mo2 = (mobj_t)th;
        if (mo2 != mo
            && mo2.type == mo.type
            && mo2.health > 0)
        {
            // other Keen not dead
            return;     
        }
        }

        junk.tag = 666;
        A.DoDoor(junk,vldoor_e.open);
        }
    }

    class A_Metal implements ActionType1 {
        public void invoke(mobj_t  mo){ 
    	S.StartSound(mo, sfxenum_t.sfx_metal);
        Chase.invoke(mo);
        }
    }

    class A_BabyMetal implements ActionType1 {
        public void invoke(mobj_t  mo){ 
    	S.StartSound(mo, sfxenum_t.sfx_bspwlk);
        Chase.invoke (mo);
        }
    }
    
    //
    // A_BFGsound
    //
    class A_BFGsound implements ActionType2{
    	public void invoke( player_t player, pspdef_t psp )	{
    		S.StartSound(player.mo, sfxenum_t.sfx_bfg);
    	}	
    }
    
    //
    // A_BFGSpray
    // Spawn a BFG explosion on every monster in view
    //
    class A_BFGSpray implements ActionType1 {
        public void invoke(mobj_t  mo){ 
        int         i;
        int         j;
        int         damage;
        long     an; // angle_t
        
        // offset angles from its attack angle
        for (i=0 ; i<40 ; i++)
        {
        an = (mo.angle - ANG90/2 + ANG90/40*i)&BITS32;

        // mo.target is the originator (player)
        //  of the missile
        A.AimLineAttack (mo.target, an, 16*64*FRACUNIT);

        if (!eval(A.linetarget))
            continue;

        A.SpawnMobj (A.linetarget.x,
            A.linetarget.y,
            A.linetarget.z + (A.linetarget.height>>2),
                 mobjtype_t.MT_EXTRABFG);
        
        damage = 0;
        for (j=0;j<15;j++)
            damage += (RND.P_Random()&7) + 1;

        A.DamageMobj (A.linetarget, mo.target,mo.target, damage);
        }
        }
    }


    class A_OpenShotgun2 implements ActionType2{
		public void invoke (player_t player,pspdef_t psp){
    	S.StartSound(player.mo, sfxenum_t.sfx_dbopn);
		}
    }
    
    class A_LoadShotgun2 implements ActionType2{
		public void invoke (player_t player,pspdef_t psp){
    	S.StartSound(player.mo, sfxenum_t.sfx_dbload);
		}
    }

    //
    // A_Look
    // Stay in state until a player is sighted.
    //
    class A_Look implements ActionType1 {
        public void invoke(mobj_t  actor){ 
        mobj_t targ;
        boolean seeyou=false; // to avoid the fugly goto
        
        actor.threshold = 0;   // any shot will wake up
        targ = actor.subsector.sector.soundtarget;

        if (targ!=null
        && eval(targ.flags& MF_SHOOTABLE) )
        {
        actor.target = targ;

        if ( eval(actor.flags&MF_AMBUSH ))
        {
            seeyou= (EN.CheckSight (actor, actor.target));              
        } else
            seeyou=true;
        }
        if (!seeyou){
        if (!EN.LookForPlayers (actor, false) )
        return;
        }
        
        // go into chase state
      seeyou:
        if (actor.info.seesound!=null && actor.info.seesound!=sfxenum_t.sfx_None)
        {
        int     sound;
            
        switch (actor.info.seesound)
        {
          case sfx_posit1:
          case sfx_posit2:
          case sfx_posit3:
            sound = sfxenum_t.sfx_posit1.ordinal()+RND.P_Random()%3;
            break;

          case sfx_bgsit1:
          case sfx_bgsit2:
            sound = sfxenum_t.sfx_bgsit1.ordinal()+RND.P_Random()%2;
            break;

          default:
            sound = actor.info.seesound.ordinal();
            break;
        }

        if (actor.type==mobjtype_t.MT_SPIDER
            || actor.type == mobjtype_t.MT_CYBORG)
        {
            // full volume
        	S.StartSound(null, sound);
        }
        else
        	S.StartSound(actor, sound);
        }

        actor.SetMobjState(actor.info.seestate);
        }
    }
    
    class A_CloseShotgun2 implements ActionType2{
		public void invoke (player_t player,pspdef_t psp){
    	S.StartSound(player.mo, sfxenum_t.sfx_dbcls);
        ReFire.invoke(player,psp);
		}
    }

    //
    // ?
    //
    class A_Light0 implements ActionType2{
		public void invoke (player_t player,pspdef_t psp){
        player.extralight = 0;
		}
    }

    class A_Light1 implements ActionType2{
		public void invoke (player_t player,pspdef_t psp){
        player.extralight = 1;
		}
    }
    
    class A_Light2 implements ActionType2{
		public void invoke (player_t player,pspdef_t psp){
        player.extralight = 2;
		}
    }

    
    //
    // A_Lower
    // Lowers current weapon,
    //  and changes weapon at bottom.
    //
    class A_Lower implements ActionType2{
		public void invoke (player_t player,pspdef_t psp){
        psp.sy += LOWERSPEED;

        // Is already down.
        if (psp.sy < WEAPONBOTTOM )
        return;

        // Player is dead.
        if (player.playerstate == PST_DEAD)
        {
        psp.sy = WEAPONBOTTOM;

        // don't bring weapon back up
        return;     
        }
        
        // The old weapon has been lowered off the screen,
        // so change the weapon and start raising it
        if (!eval(player.health[0]))
        {
        // Player is dead, so keep the weapon off screen.
        player.SetPsprite (ps_weapon, statenum_t.S_NULL);
        return; 
        }
        
        player.readyweapon = player.pendingweapon; 

        player.BringUpWeapon ();
		}
    }
    
    class A_CheckReload implements ActionType2{
		public void invoke (player_t player,pspdef_t psp){
        player.CheckAmmo ();
    /*
        if (player.ammo[am_shell]<2)
        P_SetPsprite (player, ps_weapon, S_DSNR1);
    */}
    }
    

    // Brain status
    mobj_t[]      braintargets=new mobj_t[NUMBRAINTARGETS];
    int     numbraintargets;
    int     braintargeton;
    
    class A_BrainAwake implements ActionType1{
    	
        public void invoke(mobj_t  mo){ 
        thinker_t  thinker;
        mobj_t  m;
        
        // find all the target spots
        numbraintargets = 0;
        braintargeton = 0;
        
        thinker = A.thinkercap.next;
        for (thinker = A.thinkercap.next ;
         thinker != A.thinkercap ;
         thinker = thinker.next)
        {
        if (thinker.function != think_t.P_MobjThinker)
            continue;   // not a mobj

        m = (mobj_t)thinker;

        if (m.type == mobjtype_t.MT_BOSSTARGET )
        {
            braintargets[numbraintargets] = m;
            numbraintargets++;
        }
        }
        
        S.StartSound(null,sfxenum_t.sfx_bossit);
        }
    }


    class A_BrainPain implements ActionType1 {
        public void invoke(mobj_t  mo){ 
    	S.StartSound(null,sfxenum_t.sfx_bospn);
        }
    }


    class A_BrainScream implements ActionType1{
        public void invoke(mobj_t  mo){ 
        int     x;
        int     y;
        int     z;
        mobj_t  th;
        
        for (x=mo.x - 196*FRACUNIT ; x< mo.x + 320*FRACUNIT ; x+= FRACUNIT*8)
        {
        y = mo.y - 320*FRACUNIT;
        z = 128 + RND.P_Random()*2*FRACUNIT;
        th = A.SpawnMobj (x,y,z, mobjtype_t.MT_ROCKET);
        th.momz = RND.P_Random()*512;

        th.SetMobjState (statenum_t.S_BRAINEXPLODE1);

        th.tics -= RND.P_Random()&7;
        if (th.tics < 1)
            th.tics = 1;
        }
        
        S.StartSound(null,sfxenum_t.sfx_bosdth);
        }
    }



    class A_BrainExplode implements ActionType1{
        public void invoke(mobj_t  mo){ 
        int     x;
        int     y;
        int     z;
        mobj_t  th;
        
        x = mo.x + (RND.P_Random () - RND.P_Random ())*2048;
        y = mo.y;
        z = 128 + RND.P_Random()*2*FRACUNIT;
        th = A.SpawnMobj (x,y,z, mobjtype_t.MT_ROCKET);
        th.momz = RND.P_Random()*512;

        th.SetMobjState (statenum_t.S_BRAINEXPLODE1);

        th.tics -= RND.P_Random()&7;
        if (th.tics < 1)
        th.tics = 1;
        }
    }


    class A_BrainDie implements ActionType1{
        public void invoke(mobj_t  mo){ 
        DG.ExitLevel ();
        }
    }

    private int  easy = 0;
    
    class A_BrainSpit implements ActionType1{
        public void invoke(mobj_t  mo){ 
        mobj_t  targ;
        mobj_t  newmobj;
        
        easy ^= 1;
        if (DS.gameskill.ordinal() <= skill_t.sk_easy.ordinal() && (easy==0))
        return;
            
        // shoot a cube at current target
        targ = braintargets[braintargeton];
        
        // Load-time fix: awake on zero numbrain targets, if A_BrainSpit is called.
        if (numbraintargets==0) {BrainAwake.invoke(mo);
        						 return;
        						}
        braintargeton = (braintargeton+1)%numbraintargets;

        // spawn brain missile
        newmobj = A.SpawnMissile (mo, targ, mobjtype_t.MT_SPAWNSHOT);
        newmobj.target = targ;
        newmobj.reactiontime =
        (int) (((targ.y - mo.y)/newmobj.momy) / newmobj.state.tics);

         S.StartSound(null, sfxenum_t.sfx_bospit);
        }
    }


    // travelling cube sound
    class A_SpawnSound implements ActionType1{
        public void invoke(mobj_t  mo){ 
        S.StartSound (mo,sfxenum_t.sfx_boscub);
        SpawnFly.invoke(mo);
        }
    }

     
    class A_SpawnFly implements ActionType1{
        public void invoke(mobj_t  mo){ 
        mobj_t  newmobj;
        mobj_t  fog;
        mobj_t  targ;
        int     r;
        mobjtype_t  type;
        
        if (--mo.reactiontime!=0)
        return; // still flying
        
        targ = mo.target;

        // First spawn teleport fog.
        fog = A.SpawnMobj (targ.x, targ.y, targ.z, mobjtype_t.MT_SPAWNFIRE);
        S.StartSound (fog, sfxenum_t.sfx_telept);

        // Randomly select monster to spawn.
        r = RND.P_Random ();

        // Probability distribution (kind of :),
        // decreasing likelihood.
        if ( r<50 )
        type = mobjtype_t.MT_TROOP;
        else if (r<90)
        type = mobjtype_t.MT_SERGEANT;
        else if (r<120)
        type = mobjtype_t.MT_SHADOWS;
        else if (r<130)
        type = mobjtype_t.MT_PAIN;
        else if (r<160)
        type = mobjtype_t.MT_HEAD;
        else if (r<162)
        type = mobjtype_t.MT_VILE;
        else if (r<172)
        type = mobjtype_t.MT_UNDEAD;
        else if (r<192)
        type = mobjtype_t.MT_BABY;
        else if (r<222)
        type = mobjtype_t.MT_FATSO;
        else if (r<246)
        type = mobjtype_t.MT_KNIGHT;
        else
        type = mobjtype_t.MT_BRUISER;      

        newmobj = A.SpawnMobj (targ.x, targ.y, targ.z, type);
        if (EN.LookForPlayers (newmobj, true) )
        newmobj.SetMobjState (newmobj.info.seestate);
        
        // telefrag anything in this spot
        A.TeleportMove (newmobj, newmobj.x, newmobj.y);

        // remove self (i.e., cube).
        A.RemoveMobj (mo);
    }
    }
//
//P_MobjThinker
//
  
      class A_PlayerScream implements ActionType1{
          public void invoke(mobj_t  actor){ 
          // Default death sound.
          sfxenum_t     sound = sfxenum_t.sfx_pldeth;
          
          if ( DS.isCommercial()
          &&  (actor.health < -50))
          {
          // IF THE PLAYER DIES
          // LESS THAN -50% WITHOUT GIBBING
          sound =  sfxenum_t.sfx_pdiehi;
          }
          
          S.StartSound(actor, sound);
          }
      }

      //
      // A_FaceTarget. This is called by many other functions anyway, 
      // other than autonomously.
      //
      void A_FaceTarget (mobj_t  actor)
      {   
          if (actor.target==null)
          return;
          
          actor.flags &= ~MF_AMBUSH;
          
          actor.angle = R.PointToAngle2 (actor.x,
                          actor.y,
                          actor.target.x,
                          actor.target.y)&BITS32;
          
          if (eval(actor.target.flags & MF_SHADOW))
          actor.angle += (RND.P_Random()-RND.P_Random())<<21;
          actor.angle&=BITS32;
      }
      
      /**
       * A_Chase
       * Actor has a melee attack,
       * so it tries to close as fast as possible
       */
      
      class A_Chase implements ActionType1{
          public void invoke(mobj_t  actor){ 
          int     delta;
          boolean nomissile=false; // for the fugly goto

          if (actor.reactiontime!=0)
              actor.reactiontime--;


          // modify target threshold
          if  (actor.threshold!=0)
          {
              if (actor.target==null
                      || actor.target.health <= 0)
              {
                  actor.threshold = 0;
              }
              else
                  actor.threshold--;
          }

          // turn towards movement direction if not there yet
          if (actor.movedir < 8)
          {
              actor.angle &= (7<<29);
              actor.angle&=BITS32;
              // Nice problem, here!
              delta = (int) (actor.angle - (actor.movedir << 29));

              if (delta > 0)
                  actor.angle -= ANG45;
              else if (delta < 0)
                  actor.angle += ANG45;
              
              actor.angle&=BITS32;
          }

          
          
          if (actor.target==null
                  || !eval(actor.target.flags&MF_SHOOTABLE))
          {
              // look for a new target
              if (EN.LookForPlayers(actor,true))
                  return;     // got a new target

              actor.SetMobjState (actor.info.spawnstate);
              return;
          }

          // do not attack twice in a row
          if (eval(actor.flags & MF_JUSTATTACKED))
          {
              actor.flags &= ~MF_JUSTATTACKED;
              if (DS.gameskill != skill_t.sk_nightmare && !DS.fastparm)
                  A.NewChaseDir (actor);
              return;
          }

          // check for melee attack
          if (actor.info.meleestate!=statenum_t.S_NULL /*null*/
                  && EN.CheckMeleeRange (actor))
          {
              if (actor.info.attacksound!=null){
                  S.StartSound (actor, actor.info.attacksound);
              }
              actor.SetMobjState(actor.info.meleestate);
              return;
          }

          // check for missile attack
          if (actor.info.missilestate!=statenum_t.S_NULL /*!= null*/) //_D_: this caused a bug where Demon for example were disappearing
          {
              // Assume that a missile attack is possible
              if (DS.gameskill.ordinal() < skill_t.sk_nightmare.ordinal()
                      && !DS.fastparm && actor.movecount!=0)
              {
                  // Uhm....no.
                  nomissile=true;
              }
              else
                  if (!EN.CheckMissileRange (actor))
                      nomissile=true; // Out of range

              if (!nomissile){
                  // Perform the attack
                  actor.SetMobjState ( actor.info.missilestate);
                  actor.flags |= MF_JUSTATTACKED;
                  return;
              }
          }

          // This should be executed always, if not averted by returns.
          
              // possibly choose another target
              if (DS.netgame
                      && actor.threshold==0
                      && !EN.CheckSight (actor, actor.target) )
              {
                  if (EN.LookForPlayers(actor,true))
                      return; // got a new target
              }

          // chase towards player
          if (--actor.movecount<0
                  || !A.Move (actor))
          {
             A.NewChaseDir (actor);
          }

          // make active sound
          if (actor.info.activesound!=null
                  && RND.P_Random() < 3)
          {
              S.StartSound (actor, actor.info.activesound);
          }
          }
      }
      

      
}

package p;

import java.io.DataInputStream;
import java.io.IOException;
import java.nio.ByteBuffer;

import m.IRandom;
import rr.SectorAction;
import w.DoomIO;

//
// BROKEN LIGHT EFFECT
//

public class lightflash_t extends SectorAction{
	
    private IRandom RND;
    
    public int     count;
    public int     maxlight;
    public int     minlight;
    public int     maxtime;
    public int     mintime;
    
    public lightflash_t(){
    
    }
    
    public lightflash_t(IRandom RND){
        this.RND=RND;
    }
    
    /**
     * T_LightFlash
     * Do flashing lights.
     */
    
    public void LightFlash() {
        if (--count != 0)
            return;

        if (sector.lightlevel == maxlight) {
            sector.lightlevel = (short)minlight;
            count = (RND.P_Random() & mintime) + 1;
        } else {
            sector.lightlevel = (short)maxlight;
            count = (RND.P_Random() & maxtime) + 1;
        }

    }
    
    @Override
    public void read(DataInputStream f) throws IOException{

        super.read(f); // Call thinker reader first            
        super.sectorid=DoomIO.readLEInt(f); // Sector index
        count=DoomIO.readLEInt(f);
        maxlight=DoomIO.readLEInt(f);
        minlight=DoomIO.readLEInt(f);
        maxtime=DoomIO.readLEInt(f);
        mintime=DoomIO.readLEInt(f);
        }
    
    @Override
    public void pack(ByteBuffer b) throws IOException{
        super.pack(b); //12            
        b.putInt(super.sectorid); // 16
        b.putInt(count); //20
        b.putInt(maxlight);//24
        b.putInt(minlight);//28
        b.putInt(maxtime);//32
        b.putInt(mintime);//36
    }
    
}
package p;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Arrays;

import m.BBox;
import m.fixed_t;

import boom.DeepBSPNodesV4;
import boom.mapglvertex_t;
import boom.mapnode_v4_t;
import boom.mapnode_znod_t;
import boom.mapseg_v4_t;
import boom.mapseg_znod_t;
import boom.mapsubsector_v4_t;
import boom.mapsubsector_znod_t;

import data.Limits;
import data.maplinedef_t;
import data.mapnode_t;
import data.mapsector_t;
import data.mapseg_t;
import data.mapsidedef_t;
import data.mapsubsector_t;
import data.mapthing_t;
import data.mapvertex_t;
import defines.skill_t;
import defines.slopetype_t;
import doom.DoomStatus;
import rr.RendererState;
import rr.line_t;
import static rr.line_t.ML_TWOSIDED;
import rr.node_t;
import rr.sector_t;
import rr.seg_t;
import rr.side_t;
import rr.subsector_t;
import rr.vertex_t;
import rr.z_vertex_t;
import s.degenmobj_t;
import utils.C2JUtils;
import w.CacheableDoomObjectContainer;
import w.DoomBuffer;
import w.wadfile_info_t;
import static m.fixed_t.FRACUNIT;
import static m.fixed_t.FRACBITS;
import static data.Defines.*;
import static utils.C2JUtils.flags;
import static utils.C2JUtils.unsigned;
import static boom.E6Y.NO_INDEX;
import static m.BBox.*;
import static boom.Compatibility.*;

/*
 * Emacs style mode select -*- C++ -*-
 * -----------------------------------------------------------------------------
 * PrBoom: a Doom port merged with LxDoom and LSDLDoom based on BOOM, a modified
 * and improved DOOM engine Copyright (C) 1999 by id Software, Chi Hoang, Lee
 * Killough, Jim Flynn, Rand Phares, Ty Halderman Copyright (C) 1999-2000 by
 * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze Copyright 2005,
 * 2006 by Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko This
 * program is free software; you can redistribute it and/or modify it under the
 * terms of the GNU General Public License as published by the Free Software
 * Foundation; either version 2 of the License, or (at your option) any later
 * version. This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
 * details. You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. DESCRIPTION: Do all
 * the WAD I/O, get map description, set up initial state and misc. LUTs.
 * 
 * MAES 30/9/2011: This is a direct translation of prBoom+'s 2.5.0.8 p_setup.c
 * and p_setup.h.
 * 
 * 
 * 
 * -----------------------------------------------------------------------------
 */

public class BoomLevelLoader
        extends AbstractLevelLoader {

    public BoomLevelLoader(DoomStatus<?,?> DC) {
        super(DC);
        // TODO Auto-generated constructor stub
    }

    // OpenGL related.
    byte[] map_subsectors;

    // //////////////////////////////////////////////////////////////////////////////////////////
    // figgi 08/21/00 -- finalants and globals for glBsp support
    public static final int gNd2 = 0x32644E67; // figgi -- suppport for new
                                               // GL_VERT format v2.0

    public static final int gNd3 = 0x33644E67;

    public static final int gNd4 = 0x34644E67;

    public static final int gNd5 = 0x35644E67;

    public static final int ZNOD = 0x444F4E5A;

    public static final int ZGLN = 0x4E4C475A;

    public static final int GL_VERT_OFFSET = 4;

    int firstglvertex = 0;

    int nodesVersion = 0;

    boolean forceOldBsp = false;

    // figgi 08/21/00 -- glSegs
    class glseg_t {
        char v1; // start vertex (16 bit)

        char v2; // end vertex (16 bit)

        char linedef; // linedef, or -1 for minisegs

        short side; // side on linedef: 0 for right, 1 for left

        short partner; // corresponding partner seg, or -1 on one-sided walls
    }

    public static final int ML_GL_LABEL = 0; // A separator name, GL_ExMx or
                                             // GL_MAPxx

    public static final int ML_GL_VERTS = 1; // Extra Vertices

    public static final int ML_GL_SEGS = 2; // Segs, from linedefs & minisegs

    public static final int ML_GL_SSECT = 3; // SubSectors, list of segs

    public static final int ML_GL_NODES = 4; // GL BSP nodes

    // //////////////////////////////////////////////////////////////////////////////////////////

    //
    // REJECT
    // For fast sight rejection.
    // Speeds up enemy AI by skipping detailed
    // LineOf Sight calculation.
    // Without the special effect, this could
    // be used as a PVS lookup as well.
    //

    private int rejectlump = -1;// cph - store reject lump num if cached

    private int current_episode = -1;

    private int current_map = -1;

    private int current_nodesVersion = -1;

    private boolean samelevel = false;

    /**
     * e6y: Smart malloc Used by P_SetupLevel() for smart data loading. Do
     * nothing if level is the same. Passing a null array forces allocation.
     * 
     * @param p
     *        generically typed array to consider
     * @param numstuff
     *        elements to realloc
     */

    private <T> T[] malloc_IfSameLevel(T[] p, int numstuff, Class<T> type) {
        if (!samelevel || (p == null)) {
            return (T[]) C2JUtils.createArrayOfObjects(type, numstuff);
        }
        return p;
    }

    // e6y: Smart calloc
    // Used by P_SetupLevel() for smart data loading
    // Clear the memory without allocation if level is the same
    private <T> T[] calloc_IfSameLevel(T[] p, int numstuff, Class<T> type) {
        if (!samelevel) {
            return (T[]) C2JUtils.createArrayOfObjects(type, numstuff);
        } else {

            // TODO: stuff should be resetted!
            C2JUtils.resetAll((Resettable[]) p);
            return p;
        }
    }

    //
    // P_CheckForZDoomNodes
    //

    private boolean P_CheckForZDoomNodes(int lumpnum, int gl_lumpnum) {
        byte[] data;
        int check;
        
        data = W.CacheLumpNumAsRawBytes(lumpnum + ML_NODES, 0);
        check=ByteBuffer.wrap(data).getInt();
        
        if (check == ZNOD)
            I.Error("P_CheckForZDoomNodes: ZDoom nodes not supported yet");

        data = W.CacheLumpNumAsRawBytes(lumpnum + ML_SSECTORS, 0);
        if (check == ZGLN)
            I.Error("P_CheckForZDoomNodes: ZDoom GL nodes not supported yet");

        // Unlock them to force different buffering interpretation.
        W.UnlockLumpNum(lumpnum + ML_NODES);
        W.UnlockLumpNum(lumpnum + ML_SSECTORS);

        return false;
    }

    //
    // P_CheckForDeePBSPv4Nodes
    // http://www.sbsoftware.com/files/DeePBSPV4specs.txt
    //

    private boolean P_CheckForDeePBSPv4Nodes(int lumpnum, int gl_lumpnum) {
        byte[] data;
        boolean result = false;

        data = W.CacheLumpNumAsRawBytes(lumpnum + ML_NODES, 0);
        byte[] compare = Arrays.copyOfRange(data, 0, 7);

        if (Arrays.equals(compare, DeepBSPNodesV4.DeepBSPHeader)) {
            System.out
                    .println("P_CheckForDeePBSPv4Nodes: DeePBSP v4 Extended nodes are detected\n");
            result = true;
        }

        W.UnlockLumpNum(lumpnum + ML_NODES);

        return result;
    }

    //
    // P_CheckForZDoomUncompressedNodes
    // http://zdoom.org/wiki/ZDBSP#Compressed_Nodes
    //

    private static final int XNOD = 0x584e4f44;

    private boolean P_CheckForZDoomUncompressedNodes(int lumpnum, int gl_lumpnum) {
        byte[] data;
        int wrapper;
        boolean result = false;

        data = W.CacheLumpNumAsRawBytes(lumpnum + ML_NODES, 0);
        wrapper=ByteBuffer.wrap(data).getInt();

        if (wrapper==XNOD) {
            System.out.println("P_CheckForZDoomUncompressedNodes: ZDoom uncompressed normal nodes are detected\n");
            result = true;
        }

        W.UnlockLumpNum(lumpnum + ML_NODES);

        return result;
    }

    //
    // P_GetNodesVersion
    //

    public void P_GetNodesVersion(int lumpnum, int gl_lumpnum) {
        int ver = -1;
        nodesVersion = 0;

        if ((gl_lumpnum > lumpnum) && (forceOldBsp == false)
            &&(DoomStatus.compatibility_level>=prboom_2_compatibility)
                                                             ) {

            byte[] data = W.CacheLumpNumAsRawBytes(gl_lumpnum + ML_GL_VERTS, 0);
            int wrapper = ByteBuffer.wrap(data).getInt();
            if (wrapper == gNd2) {
                data = W.CacheLumpNumAsRawBytes(gl_lumpnum + ML_GL_SEGS, 0);
                wrapper = ByteBuffer.wrap(data).getInt();
                if (wrapper == gNd3) {
                    ver = 3;
                } else {
                    nodesVersion = gNd2;
                    System.out
                            .println("P_GetNodesVersion: found version 2 nodes\n");
                }
            }
            if (wrapper == gNd4) {
                ver = 4;
            }
            if (wrapper == gNd5) {
                ver = 5;
            }
            // e6y: unknown gl nodes will be ignored
            if (nodesVersion == 0 && ver != -1) {
                System.out.printf(
                    "P_GetNodesVersion: found version %d nodes\n", ver);
                System.out.printf(
                    "P_GetNodesVersion: version %d nodes not supported\n", ver);
            }
        } else {
            nodesVersion = 0;
            System.out.println("P_GetNodesVersion: using normal BSP nodes\n");
            if (P_CheckForZDoomNodes(lumpnum, gl_lumpnum))
                I.Error("P_GetNodesVersion: ZDoom nodes not supported yet");
        }
    }

    //
    // P_LoadVertexes
    //
    // killough 5/3/98: reformatted, cleaned up
    //
    private void P_LoadVertexes(int lump) {
        final mapvertex_t[] data; // cph - final

        // Determine number of lumps:
        // total lump length / vertex record length.
        numvertexes = W.LumpLength(lump) / mapvertex_t.sizeOf();

        // Allocate zone memory for buffer.
        vertexes =
            (vertex_t[]) calloc_IfSameLevel(vertexes, numvertexes,
                vertex_t.class);

        // Load data into cache.
        // cph 2006/07/29 - cast to mapvertex_t here, making the loop below much
        // neater
        data = W.CacheLumpNumIntoArray(lump, numvertexes, mapvertex_t.class);

        // Copy and convert vertex coordinates,
        // internal representation as fixed.
        for (int i = 0; i < numvertexes; i++) {
            vertexes[i].x = data[i].x << m.fixed_t.FRACBITS;
            vertexes[i].y = data[i].y << FRACBITS;
        }

        // Free buffer memory.
        W.UnlockLumpNum(lump);
    }

    /*******************************************
     * Name : P_LoadVertexes2 * modified : 09/18/00, adapted for PrBoom * author
     * : figgi * what : support for gl nodes
     * 
     * @throws IOException
     *         *
     *******************************************/

    // figgi -- FIXME: Automap showes wrong zoom boundaries when starting game
    // when P_LoadVertexes2 is used with classic BSP nodes.

    private void P_LoadVertexes2(int lump, int gllump)
            throws IOException {
        final ByteBuffer gldata;
        int i;
        mapvertex_t[] ml;

        // GL vertexes come after regular ones.
        firstglvertex = W.LumpLength(lump) / mapvertex_t.sizeOf();
        numvertexes = W.LumpLength(lump) / mapvertex_t.sizeOf();

        if (gllump >= 0) // check for glVertices
        {
            // Read GL lump into buffer. This allows some flexibility
            gldata = W.CacheLumpNumAsDoomBuffer(gllump).getBuffer();

            if (nodesVersion == gNd2) // 32 bit GL_VERT format (16.16 fixed)
            {

                // These vertexes are double in size than regular Doom vertexes.
                // Furthermore, we have to skip the first 4 bytes
                // (GL_VERT_OFFSET)
                // of the gl lump.
                numvertexes +=
                    (W.LumpLength(gllump) - GL_VERT_OFFSET)
                            / mapglvertex_t.sizeOf();

                // Vertexes size accomodates both normal and GL nodes.
                vertexes =
                    malloc_IfSameLevel(vertexes, numvertexes, vertex_t.class);

                final mapglvertex_t[] mgl =
                    C2JUtils.createArrayOfObjects(mapglvertex_t.class,
                        numvertexes - firstglvertex);

                // Get lump and skip first 4 bytes
                gldata.rewind();
                gldata.position(GL_VERT_OFFSET);

                CacheableDoomObjectContainer.unpack(gldata, mgl);

                int mgl_count = 0;

                for (i = firstglvertex; i < numvertexes; i++) {
                    vertexes[i].x = mgl[mgl_count].x;
                    vertexes[i].y = mgl[mgl_count].y;
                    mgl_count++;
                }
            } else {
                // Vertexes size accomodates both normal and GL nodes.
                numvertexes += W.LumpLength(gllump) / mapvertex_t.sizeOf();
                vertexes =
                    malloc_IfSameLevel(vertexes, numvertexes, vertex_t.class);

                ml =
                    C2JUtils.createArrayOfObjects(mapvertex_t.class,
                        numvertexes - firstglvertex);

                // We can read this "directly" because no skipping is involved.
                gldata.rewind();
                CacheableDoomObjectContainer.unpack(gldata, ml);
                // ml = W.CacheLumpNumIntoArray(gllump,
                // numvertexes-firstglvertex,mapvertex_t.class);
                int ml_count = 0;

                for (i = firstglvertex; i < numvertexes; i++) {
                    vertexes[i].x = ml[ml_count].x;
                    vertexes[i].y = ml[ml_count].y;
                    ml_count++;
                }
            }
            W.UnlockLumpNum(gllump);
        }

        // Loading of regular lumps (sheesh!)
        ml = W.CacheLumpNumIntoArray(lump, firstglvertex, mapvertex_t.class);

        for (i = 0; i < firstglvertex; i++) {
            vertexes[i].x = ml[i].x;
            vertexes[i].y = ml[i].y;
        }

        W.UnlockLumpNum(lump);

    }

    /*******************************************
     * created : 08/13/00 * modified : 09/18/00, adapted for PrBoom * author :
     * figgi * what : basic functions needed for * computing gl nodes *
     *******************************************/

    public int checkGLVertex(int num) {
        if ((num & 0x8000) != 0)
            num = (num & 0x7FFF) + firstglvertex;
        return num;
    }

    public static float GetDistance(int dx, int dy) {
        float fx = (float) (dx) / FRACUNIT, fy = (float) (dy) / FRACUNIT;
        return (float) Math.sqrt(fx * fx + fy * fy);
    }

    public static float GetTexelDistance(int dx, int dy) {
        // return (float)((int)(GetDistance(dx, dy) + 0.5f));
        float fx = (float) (dx) / FRACUNIT, fy = (float) (dy) / FRACUNIT;
        return (float) ((int) (0.5f + (float) Math.sqrt(fx * fx + fy * fy)));
    }

    public static int GetOffset(vertex_t v1, vertex_t v2) {
        float a, b;
        int r;
        a = (float) (v1.x - v2.x) / (float) FRACUNIT;
        b = (float) (v1.y - v2.y) / (float) FRACUNIT;
        r = (int) (Math.sqrt(a * a + b * b) * (float) FRACUNIT);
        return r;
    }

    //
    // P_LoadSegs
    //
    // killough 5/3/98: reformatted, cleaned up

    private void P_LoadSegs(int lump) {
        int i;
        final mapseg_t[] data; // cph - final

        numsegs = W.LumpLength(lump) / mapseg_t.sizeOf();
        segs = (seg_t[]) calloc_IfSameLevel(segs, numsegs, seg_t.class);

        data = W.CacheLumpNumIntoArray(lump, numsegs, mapseg_t.class); // cph -
                                                                       // wad
                                                                       // lump
                                                                       // handling
                                                                       // updated

        if ((data == null) || (numsegs == 0))
            I.Error("P_LoadSegs: no segs in level");

        for (i = 0; i < numsegs; i++) {
            seg_t li = segs[i];
            final mapseg_t ml = data[i];
            char v1, v2;

            int side, linedef;
            line_t ldef;

            li.iSegID = i; // proff 11/05/2000: needed for OpenGL

            v1 = (char) ml.v1;
            v2 = (char) ml.v2;

            // e6y
            // moved down for additional checks to avoid overflow
            // if wrong vertexe's indexes are in SEGS lump
            // see below for more detailed information
            // li.v1 = &vertexes[v1];
            // li.v2 = &vertexes[v2];

            li.miniseg = false; // figgi -- there are no minisegs in classic BSP
                                // nodes

            // e6y: moved down, see below
            // li.length = GetDistance(li.v2.x - li.v1.x, li.v2.y - li.v1.y);

            li.angle = ml.angle << 16;
            li.offset = ml.offset << 16;
            linedef = (char) ml.linedef;

            // e6y: check for wrong indexes
            if (linedef >= numlines) {
                I.Error(
                    "P_LoadSegs: seg %d references a non-existent linedef %d",
                    i, linedef);
            }

            ldef = lines[linedef];
            li.linedef = ldef;
            side = ml.side;

            // e6y: fix wrong side index
            if (side != 0 && side != 1) {
                System.err
                        .printf(
                            "P_LoadSegs: seg %d contains wrong side index %d. Replaced with 1.\n",
                            i, side);
                side = 1;
            }

            // e6y: check for wrong indexes
            if ((char) ldef.sidenum[side] >= (char) numsides) {
                I.Error(
                    "P_LoadSegs: linedef %d for seg %d references a non-existent sidedef %d",
                    linedef, i, ldef.sidenum[side]);
            }

            li.sidedef = sides[ldef.sidenum[side]];

            /*
             * cph 2006/09/30 - our frontsector can be the second side of the
             * linedef, so must check for NO_INDEX in case we are incorrectly
             * referencing the back of a 1S line
             */
            if (ldef.sidenum[side] != NO_INDEX)
                li.frontsector = sides[ldef.sidenum[side]].sector;
            else {
                li.frontsector = null;
                System.err.printf(
                    "P_LoadSegs: front of seg %i has no sidedef\n", i);
            }

            if (flags(ldef.flags, ML_TWOSIDED)
                    && ldef.sidenum[side ^ 1] != NO_INDEX)
                li.backsector = sides[ldef.sidenum[side ^ 1]].sector;
            else
                li.backsector = null;

            // e6y
            // check and fix wrong references to non-existent vertexes
            // see e1m9 @ NIVELES.WAD
            // http://www.doomworld.com/idgames/index.php?id=12647
            if (v1 >= numvertexes || v2 >= numvertexes) {
                String str =
                    "P_LoadSegs: compatibility loss - seg %d references a non-existent vertex %d\n";

                if (DM.demorecording) {
                    I.Error(
                        str
                                + "Demo recording on levels with invalid nodes is not allowed",
                        i, (v1 >= numvertexes ? v1 : v2));
                }

                if (v1 >= numvertexes)
                    System.err.printf(str, i, v1);
                if (v2 >= numvertexes)
                    System.err.printf(str, i, v2);

                if (li.sidedef == sides[li.linedef.sidenum[0]]) {
                    li.v1 = lines[ml.linedef].v1;
                    li.v2 = lines[ml.linedef].v2;
                } else {
                    li.v1 = lines[ml.linedef].v2;
                    li.v2 = lines[ml.linedef].v1;
                }
            } else {
                li.v1 = vertexes[v1];
                li.v2 = vertexes[v2];
            }

            li.assignVertexValues();

            // e6y: now we can calculate it
            li.length = GetDistance(li.v2x - li.v1x, li.v2y - li.v1y);

            // Recalculate seg offsets that are sometimes incorrect
            // with certain nodebuilders. Fixes among others, line 20365
            // of DV.wad, map 5
            li.offset = GetOffset(li.v1, (ml.side != 0 ? ldef.v2 : ldef.v1));
        }

        W.UnlockLumpNum(lump); // cph - release the data
    }

    private void P_LoadSegs_V4(int lump) {
        int i;
        mapseg_v4_t[] data;

        numsegs = W.LumpLength(lump) / mapseg_v4_t.sizeOf();
        segs = (seg_t[]) calloc_IfSameLevel(segs, numsegs, seg_t.class);
        data = W.CacheLumpNumIntoArray(lump, numsegs, mapseg_v4_t.class);

        if ((data == null) || (numsegs == 0))
            I.Error("P_LoadSegs_V4: no segs in level");

        for (i = 0; i < numsegs; i++) {
            seg_t li = segs[i];
            final mapseg_v4_t ml = data[i];
            int v1, v2;

            int side, linedef;
            line_t ldef;

            li.iSegID = i; // proff 11/05/2000: needed for OpenGL

            v1 = ml.v1;
            v2 = ml.v2;

            li.miniseg = false; // figgi -- there are no minisegs in classic BSP
                                // nodes

            li.angle = ml.angle << 16;
            li.offset = ml.offset << 16;
            linedef = ml.linedef;

            // e6y: check for wrong indexes
            if (unsigned(linedef) >= unsigned(numlines)) {
                I.Error(
                    "P_LoadSegs_V4: seg %d references a non-existent linedef %d",
                    i, unsigned(linedef));
            }

            ldef = lines[linedef];
            li.linedef = ldef;
            side = ml.side;

            // e6y: fix wrong side index
            if (side != 0 && side != 1) {
                System.err
                        .printf(
                            "P_LoadSegs_V4: seg %d contains wrong side index %d. Replaced with 1.\n",
                            i, side);
                side = 1;
            }

            // e6y: check for wrong indexes
            if (unsigned(ldef.sidenum[side]) >= unsigned(numsides)) {
                I.Error(
                    "P_LoadSegs_V4: linedef %d for seg %d references a non-existent sidedef %d",
                    linedef, i, unsigned(ldef.sidenum[side]));
            }

            li.sidedef = sides[ldef.sidenum[side]];

            /*
             * cph 2006/09/30 - our frontsector can be the second side of the
             * linedef, so must check for NO_INDEX in case we are incorrectly
             * referencing the back of a 1S line
             */
            if (ldef.sidenum[side] != NO_INDEX) {
                li.frontsector = sides[ldef.sidenum[side]].sector;
            } else {
                li.frontsector = null;
                System.err.printf(
                    "P_LoadSegs_V4: front of seg %i has no sidedef\n", i);
            }

            if (flags(ldef.flags, ML_TWOSIDED)
                    && ldef.sidenum[side ^ 1] != NO_INDEX)
                li.backsector = sides[ldef.sidenum[side ^ 1]].sector;
            else
                li.backsector = null;

            // e6y
            // check and fix wrong references to non-existent vertexes
            // see e1m9 @ NIVELES.WAD
            // http://www.doomworld.com/idgames/index.php?id=12647
            if (v1 >= numvertexes || v2 >= numvertexes) {
                String str =
                    "P_LoadSegs_V4: compatibility loss - seg %d references a non-existent vertex %d\n";

                if (DM.demorecording) {
                    I.Error(
                        (str + "Demo recording on levels with invalid nodes is not allowed"),
                        i, (v1 >= numvertexes ? v1 : v2));
                }

                if (v1 >= numvertexes)
                    System.err.printf(str, i, v1);
                if (v2 >= numvertexes)
                    System.err.printf(str, i, v2);

                if (li.sidedef == sides[li.linedef.sidenum[0]]) {
                    li.v1 = lines[ml.linedef].v1;
                    li.v2 = lines[ml.linedef].v2;
                } else {
                    li.v1 = lines[ml.linedef].v2;
                    li.v2 = lines[ml.linedef].v1;
                }
            } else {
                li.v1 = vertexes[v1];
                li.v2 = vertexes[v2];
            }

            // e6y: now we can calculate it
            li.length = GetDistance(li.v2.x - li.v1.x, li.v2.y - li.v1.y);

            // Recalculate seg offsets that are sometimes incorrect
            // with certain nodebuilders. Fixes among others, line 20365
            // of DV.wad, map 5
            li.offset = GetOffset(li.v1, (ml.side != 0 ? ldef.v2 : ldef.v1));
        }

        W.UnlockLumpNum(lump); // cph - release the data
    }

    /*******************************************
     * Name : P_LoadGLSegs * created : 08/13/00 * modified : 09/18/00, adapted
     * for PrBoom * author : figgi * what : support for gl nodes *
     *******************************************/
    /*
     * private void P_LoadGLSegs(int lump) { int i; final glseg_t ml; line_t
     * ldef; numsegs = W.LumpLength(lump) / sizeof(glseg_t); segs =
     * malloc_IfSameLevel(segs, numsegs * sizeof(seg_t)); memset(segs, 0,
     * numsegs * sizeof(seg_t)); ml = (final glseg_t*)W.CacheLumpNum(lump); if
     * ((!ml) || (!numsegs)) I_Error("P_LoadGLSegs: no glsegs in level"); for(i
     * = 0; i < numsegs; i++) { // check for gl-vertices segs[i].v1 =
     * &vertexes[checkGLVertex(LittleShort(ml.v1))]; segs[i].v2 =
     * &vertexes[checkGLVertex(LittleShort(ml.v2))]; segs[i].iSegID = i;
     * if(ml.linedef != (unsigned short)-1) // skip minisegs { ldef =
     * &lines[ml.linedef]; segs[i].linedef = ldef; segs[i].miniseg = false;
     * segs[i].angle =
     * R_PointToAngle2(segs[i].v1.x,segs[i].v1.y,segs[i].v2.x,segs[i].v2.y);
     * segs[i].sidedef = &sides[ldef.sidenum[ml.side]]; segs[i].length =
     * GetDistance(segs[i].v2.x - segs[i].v1.x, segs[i].v2.y - segs[i].v1.y);
     * segs[i].frontsector = sides[ldef.sidenum[ml.side]].sector; if (ldef.flags
     * & ML_TWOSIDED) segs[i].backsector =
     * sides[ldef.sidenum[ml.side^1]].sector; else segs[i].backsector = 0; if
     * (ml.side) segs[i].offset = GetOffset(segs[i].v1, ldef.v2); else
     * segs[i].offset = GetOffset(segs[i].v1, ldef.v1); } else { segs[i].miniseg
     * = true; segs[i].angle = 0; segs[i].offset = 0; segs[i].length = 0;
     * segs[i].linedef = NULL; segs[i].sidedef = NULL; segs[i].frontsector =
     * NULL; segs[i].backsector = NULL; } ml++; } W.UnlockLumpNum(lump); }
     */

    //
    // P_LoadSubsectors
    //
    // killough 5/3/98: reformatted, cleaned up

    private void P_LoadSubsectors(int lump) {
        /*
         * cph 2006/07/29 - make data a final mapsubsector_t *, so the loop
         * below is simpler & gives no finalness warnings
         */
        final mapsubsector_t[] data;
        int i;

        numsubsectors = W.LumpLength(lump) / mapsubsector_t.sizeOf();
        subsectors =
            (subsector_t[]) calloc_IfSameLevel(subsectors, numsubsectors,
                subsector_t.class);
        data =
            W.CacheLumpNumIntoArray(lump, numsubsectors, mapsubsector_t.class);

        if ((data == null) || (numsubsectors == 0))
            I.Error("P_LoadSubsectors: no subsectors in level");

        for (i = 0; i < numsubsectors; i++) {
            // e6y: support for extended nodes
            subsectors[i].numlines = (char) data[i].numsegs;
            subsectors[i].firstline = (char) data[i].firstseg;
        }

        W.UnlockLumpNum(lump); // cph - release the data
    }

    private void P_LoadSubsectors_V4(int lump) {
        /*
         * cph 2006/07/29 - make data a final mapsubsector_t *, so the loop
         * below is simpler & gives no finalness warnings
         */
        final mapsubsector_v4_t[] data;
        int i;

        numsubsectors = W.LumpLength(lump) / mapsubsector_v4_t.sizeOf();
        subsectors =
            calloc_IfSameLevel(subsectors, numsubsectors, subsector_t.class);
        data =
            W.CacheLumpNumIntoArray(lump, numsubsectors,
                mapsubsector_v4_t.class);

        if ((data == null) || (numsubsectors == 0))
            I.Error("P_LoadSubsectors_V4: no subsectors in level");

        for (i = 0; i < numsubsectors; i++) {
            subsectors[i].numlines = (int) data[i].numsegs;
            subsectors[i].firstline = (int) data[i].firstseg;
        }

        W.UnlockLumpNum(lump); // cph - release the data
    }

    //
    // P_LoadSectors
    //
    // killough 5/3/98: reformatted, cleaned up

    private void P_LoadSectors(int lump) {
        final mapsector_t[] data; // cph - final*
        int i;

        numsectors = W.LumpLength(lump) / mapsector_t.sizeOf();
        sectors = calloc_IfSameLevel(sectors, numsectors, sector_t.class);
        data = W.CacheLumpNumIntoArray(lump, numsectors, mapsector_t.class); // cph
                                                                             // -
                                                                             // wad
                                                                             // lump
                                                                             // handling
                                                                             // updated

        for (i = 0; i < numsectors; i++) {
            sector_t ss = sectors[i];
            final mapsector_t ms = data[i];

            ss.id = i; // proff 04/05/2000: needed for OpenGL
            ss.floorheight = ms.floorheight << FRACBITS;
            ss.ceilingheight = ms.ceilingheight << FRACBITS;
            ss.floorpic = (short) TM.FlatNumForName(ms.floorpic);
            ss.ceilingpic = (short) TM.FlatNumForName(ms.ceilingpic);
            ss.lightlevel = ms.lightlevel;
            ss.special = ms.special;
            // ss.oldspecial = ms.special; huh?
            ss.tag = ms.tag;
            ss.thinglist = null;
            // MAES: link to thinker list and RNG
            ss.TL = this.P;
            ss.RND = this.DM.RND;

            // ss.touching_thinglist = null; // phares 3/14/98

            // ss.nextsec = -1; //jff 2/26/98 add fields to support locking out
            // ss.prevsec = -1; // stair retriggering until build completes

            // killough 3/7/98:
            // ss.floor_xoffs = 0;
            // ss.floor_yoffs = 0; // floor and ceiling flats offsets
            // ss.ceiling_xoffs = 0;
            // ss.ceiling_yoffs = 0;
            // ss.heightsec = -1; // sector used to get floor and ceiling height
            // ss.floorlightsec = -1; // sector used to get floor lighting
            // killough 3/7/98: end changes

            // killough 4/11/98 sector used to get ceiling lighting:
            // ss.ceilinglightsec = -1;

            // killough 4/4/98: colormaps:
            // ss.bottommap = ss.midmap = ss.topmap = 0;

            // killough 10/98: sky textures coming from sidedefs:
            // ss.sky = 0;
        }

        W.UnlockLumpNum(lump); // cph - release the data
    }

    //
    // P_LoadNodes
    //
    // killough 5/3/98: reformatted, cleaned up

    private void P_LoadNodes(int lump) {
        final mapnode_t[] data; // cph - final*
        int i;

        numnodes = W.LumpLength(lump) / mapnode_t.sizeOf();
        nodes = (node_t[]) malloc_IfSameLevel(nodes, numnodes, node_t.class);
        data = W.CacheLumpNumIntoArray(lump, numnodes, mapnode_t.class); // cph
                                                                         // -
                                                                         // wad
                                                                         // lump
                                                                         // handling
                                                                         // updated

        if ((data == null) || (numnodes == 0)) {
            // allow trivial maps
            if (numsubsectors == 1)
                System.out
                        .print("P_LoadNodes: trivial map (no nodes, one subsector)\n");
            else
                I.Error("P_LoadNodes: no nodes in level");
        }

        for (i = 0; i < numnodes; i++) {
            node_t no = nodes[i];
            final mapnode_t mn = data[i];
            int j;

            no.x = mn.x << FRACBITS;
            no.y = mn.y << FRACBITS;
            no.dx = mn.dx << FRACBITS;
            no.dy = mn.dy << FRACBITS;

            for (j = 0; j < 2; j++) {
                int k;
                // e6y: support for extended nodes
                no.children[j] = (char) mn.children[j];

                // e6y: support for extended nodes
                if (no.children[j] == 0xFFFF) {
                    no.children[j] = 0xFFFFFFFF;
                } else if (flags(no.children[j], NF_SUBSECTOR_CLASSIC)) {
                    // Convert to extended type
                    no.children[j] &= ~NF_SUBSECTOR_CLASSIC;

                    // haleyjd 11/06/10: check for invalid subsector reference
                    if (no.children[j] >= numsubsectors) {
                        System.err
                                .printf(
                                    "P_LoadNodes: BSP tree references invalid subsector %d.\n",
                                    no.children[j]);
                        no.children[j] = 0;
                    }

                    no.children[j] |= NF_SUBSECTOR;
                }

                for (k = 0; k < 4; k++)
                    no.bbox[j].set(k, mn.bbox[j][k] << FRACBITS);
            }
        }

        W.UnlockLumpNum(lump); // cph - release the data
    }

    private void P_LoadNodes_V4(int lump) {
        final DeepBSPNodesV4 data; // cph - final*
        int i;

        numnodes = (W.LumpLength(lump) - 8) / mapnode_v4_t.sizeOf();
        nodes = (node_t[]) malloc_IfSameLevel(nodes, numnodes, node_t.class);
        data = (DeepBSPNodesV4) W.CacheLumpNum(lump, 0, DeepBSPNodesV4.class); // cph
                                                                               // -
                                                                               // wad
                                                                               // lump
                                                                               // handling
                                                                               // updated

        if ((data == null) || (numnodes == 0)) {
            // allow trivial maps
            if (numsubsectors == 1)
                System.out
                        .print("P_LoadNodes_V4: trivial map (no nodes, one subsector)\n");
            else
                I.Error("P_LoadNodes_V4: no nodes in level");
        }

        for (i = 0; i < numnodes; i++) {
            node_t no = nodes[i];
            final mapnode_v4_t mn = data.getNodes()[i];
            int j;

            no.x = mn.x << FRACBITS;
            no.y = mn.y << FRACBITS;
            no.dx = mn.dx << FRACBITS;
            no.dy = mn.dy << FRACBITS;

            for (j = 0; j < 2; j++) {
                int k;
                no.children[j] = mn.children[j];

                for (k = 0; k < 4; k++)
                    no.bbox[j].bbox[k] = mn.bbox[j][k] << FRACBITS;
            }
            
            
        }

        W.UnlockLumpNum(lump); // cph - release the data
    }

     private void P_LoadZSegs(ByteBuffer data) throws IOException {
        int i;

        final mapseg_znod_t nodes[] = C2JUtils.createArrayOfObjects(mapseg_znod_t.class,numsegs);
        CacheableDoomObjectContainer.unpack(data,nodes);

        for (i = 0; i < numsegs; i++) {
            line_t ldef;
            int v1, v2;
            int linedef;
            char side;
            seg_t li = segs[i];
            final mapseg_znod_t ml = nodes[i];

            v1 = ml.v1;
            v2 = ml.v2;

            li.iSegID = i; // proff 11/05/2000: needed for OpenGL
            li.miniseg = false;

            linedef = (char) ml.linedef;

            // e6y: check for wrong indexes
            if (unsigned(linedef) >= unsigned(numlines)) {
                I.Error(
                    "P_LoadZSegs: seg %d references a non-existent linedef %d",
                    i, unsigned(linedef));
            }

            ldef = lines[linedef];
            li.linedef = ldef;
            side = (char) ml.side;

            // e6y: fix wrong side index
            if (side != 0 && side != 1) {
                System.err
                        .printf(
                            "P_LoadZSegs: seg %d contains wrong side index %d. Replaced with 1.\n",
                            i, side);
                side = 1;
            }

            // e6y: check for wrong indexes
            if (unsigned(ldef.sidenum[side]) >= unsigned(numsides)) {
                I.Error(
                    "P_LoadZSegs: linedef %d for seg %d references a non-existent sidedef %d",
                    linedef, i, unsigned(ldef.sidenum[side]));
            }

            li.sidedef = sides[ldef.sidenum[side]];

            /*
             * cph 2006/09/30 - our frontsector can be the second side of the
             * linedef, so must check for NO_INDEX in case we are incorrectly
             * referencing the back of a 1S line
             */
            if (ldef.sidenum[side] != NO_INDEX) {
                li.frontsector = sides[ldef.sidenum[side]].sector;
            } else {
                li.frontsector = null;
                System.err.printf(
                    "P_LoadZSegs: front of seg %i has no sidedef\n", i);
            }

            if (flags(ldef.flags, ML_TWOSIDED)
                    && (ldef.sidenum[side ^ 1] != NO_INDEX))
                li.backsector = sides[ldef.sidenum[side ^ 1]].sector;
            else
                li.backsector = null;

            li.v1 = vertexes[v1];
            li.v2 = vertexes[v2];

            li.length = GetDistance(li.v2.x - li.v1.x, li.v2.y - li.v1.y);
            li.offset = GetOffset(li.v1, (side != 0 ? ldef.v2 : ldef.v1));
            li.angle =
                RendererState.PointToAngle(segs[i].v1.x, segs[i].v1.y,
                    segs[i].v2.x, segs[i].v2.y);
            // li.angle = (int)((float)atan2(li.v2.y - li.v1.y,li.v2.x -
            // li.v1.x) * (ANG180 / M_PI));
        }
    }

    private int CheckZNodesOverflow(int size, int count)
    {
      size -= count;

      if (size < 0)
      {
        I.Error("P_LoadZNodes: incorrect nodes");
      }
      
      return size;
    }
    
    private void P_LoadZNodes(int lump, int glnodes) throws IOException
    {
      ByteBuffer data;
      int i, len;
      int header; // for debugging
      
      int orgVerts, newVerts;
      int numSubs, currSeg;
      int numSegs;
      int numNodes;
      vertex_t[] newvertarray = null;

      data = W.CacheLumpNumAsDoomBuffer(lump).getBuffer();
      data.order(ByteOrder.LITTLE_ENDIAN);
      len =  W.LumpLength(lump);
      
      // skip header
      len=CheckZNodesOverflow(len, 4);
      header=data.getInt();

      // Read extra vertices added during node building
      len=CheckZNodesOverflow(len, 4);
      orgVerts = data.getInt();

      len=CheckZNodesOverflow(len, 4);
      newVerts = data.getInt();

      if (!samelevel)
      {
        if (orgVerts + newVerts == numvertexes)
        {
          newvertarray = vertexes;
        }
        else
        {
          newvertarray = C2JUtils.createArrayOfObjects(vertex_t.class, orgVerts + newVerts);
          System.arraycopy(vertexes, 0, newvertarray, 0, orgVerts);
        }

        //(sizeof(newvertarray[0].x) + sizeof(newvertarray[0].y))
        len=CheckZNodesOverflow(len, newVerts * vertex_t.sizeOf());
        z_vertex_t tmp=new z_vertex_t();
        
        for (i = 0; i < newVerts; i++)
        {
            tmp.unpack(data);
            newvertarray[i + orgVerts].x=tmp.x;
            newvertarray[i + orgVerts].y=tmp.y;
        }

        // Extra vertexes read in
        if (vertexes != newvertarray)
        {
          for (i = 0; i < numlines; i++)
          {
            //lines[i].v1 = lines[i].v1 - vertexes + newvertarray;
            //lines[i].v2 = lines[i].v2 - vertexes + newvertarray;
              // Find indexes of v1 & v2 inside old vertexes array
              // (.v1-vertexes) and use that index to re-point inside newvertarray              
              lines[i].v1=newvertarray[C2JUtils.indexOf(vertexes,lines[i].v1)];
              lines[i].v2=newvertarray[C2JUtils.indexOf(vertexes,lines[i].v2)];
          }
          // free(vertexes);
          vertexes = newvertarray;
          numvertexes = orgVerts + newVerts;
        }
      }
      else
      {
        // Skip the reading of all these new vertices and the expensive indexOf searches.
        int size = newVerts * z_vertex_t.sizeOf();
        len=CheckZNodesOverflow(len, size);
        data.position(data.position()+ size);
      }

      // Read the subsectors
      len=CheckZNodesOverflow(len, 4);
      numSubs=data.getInt();

      numsubsectors = numSubs;
      if (numsubsectors <= 0)
        I.Error("P_LoadZNodes: no subsectors in level");
      subsectors = calloc_IfSameLevel(subsectors, numsubsectors,subsector_t.class);

      len=CheckZNodesOverflow(len, numSubs * mapsubsector_znod_t.sizeOf());
      final mapsubsector_znod_t mseg=new mapsubsector_znod_t();
      for (i = currSeg = 0; i < numSubs; i++)
      {
         mseg.unpack(data);

        subsectors[i].firstline = currSeg;
        subsectors[i].numlines = (int) mseg.numsegs;
        currSeg += mseg.numsegs;
      }      

      // Read the segs
      len=CheckZNodesOverflow(len, 4);
      numSegs = data.getInt();

      // The number of segs stored should match the number of
      // segs used by subsectors.
      if (numSegs != currSeg)
      {
        I.Error("P_LoadZNodes: Incorrect number of segs in nodes.");
      }

      numsegs = numSegs;
      segs = calloc_IfSameLevel(segs, numsegs, seg_t.class);

      if (glnodes == 0)
      {
        len=CheckZNodesOverflow(len, numsegs *mapseg_znod_t.sizeOf());
        P_LoadZSegs(data);
      }
      else
      {
        //P_LoadGLZSegs (data, glnodes);
        I.Error("P_LoadZNodes: GL segs are not supported.");
      }

      // Read nodes
      len=CheckZNodesOverflow(len, 4);
      numNodes = data.getInt();      

      numnodes = numNodes;
      nodes = calloc_IfSameLevel(nodes, numNodes, node_t.class);

      len=CheckZNodesOverflow(len, numNodes * mapnode_znod_t.sizeOf());
      
      mapnode_znod_t[] znodes=C2JUtils.createArrayOfObjects(mapnode_znod_t.class, numNodes);
      CacheableDoomObjectContainer.unpack(data,znodes);      
      
      for (i = 0; i < numNodes; i++)
      {
        int j, k;
        node_t no = nodes[i];
        final mapnode_znod_t mn = znodes[i];

        no.x = mn.x << FRACBITS;
        no.y = mn.y << FRACBITS;
        no.dx = mn.dx<< FRACBITS;
        no.dy = mn.dy<< FRACBITS;

        for (j = 0; j < 2; j++)
        {
          no.children[j] = mn.children[j];

          for (k = 0; k < 4; k++)
            no.bbox[j].bbox[k] = mn.bbox[j][k] << FRACBITS;
        }
      }

      W.UnlockLumpNum(lump); // cph - release the data
    }
    
    private boolean no_overlapped_sprites;

    private final int GETXY(mobj_t mobj) {
        return (mobj.x + (mobj.y >> 16));
    }

    private final int dicmp_sprite_by_pos(final Object a, final Object b) {
        mobj_t m1 = (mobj_t) a;
        mobj_t m2 = (mobj_t) b;

        int res = GETXY(m2) - GETXY(m1);
        no_overlapped_sprites = no_overlapped_sprites && (res != 0);
        return res;
    }

    /*
     * P_LoadThings killough 5/3/98: reformatted, cleaned up cph 2001/07/07 -
     * don't write into the lump cache, especially non-idepotent changes like
     * byte order reversals. Take a copy to edit.
     */

    private void P_LoadThings(int lump) {
        int i, numthings = W.LumpLength(lump) / mapthing_t.sizeOf();
        final mapthing_t[] data =
            W.CacheLumpNumIntoArray(lump, numthings, mapthing_t.class);

        mobj_t mobj;
        int mobjcount = 0;
        mobj_t[] mobjlist =
            C2JUtils.createArrayOfObjects(mobj_t.class, numthings);

        if ((data == null) || (numthings == 0))
            I.Error("P_LoadThings: no things in level");

        for (i = 0; i < numthings; i++) {
            mapthing_t mt = data[i];

            /*
             * Not needed. Handled during unmarshaling. mt.x =
             * LittleShort(mt.x); mt.y = LittleShort(mt.y); mt.angle =
             * LittleShort(mt.angle); mt.type = LittleShort(mt.type); mt.options
             * = LittleShort(mt.options);
             */

            if (!P_IsDoomnumAllowed(mt.type))
                continue;

            // Do spawn all other stuff.
            mobj = P.SpawnMapThing(mt/* , i */);
            if (mobj != null && mobj.info.speed == 0)
                mobjlist[mobjcount++] = mobj;
        }

        W.UnlockLumpNum(lump); // cph - release the data
        /*
         * #ifdef GL_DOOM if (V_GetMode() == VID_MODEGL) { no_overlapped_sprites
         * = true; qsort(mobjlist, mobjcount, sizeof(mobjlist[0]),
         * dicmp_sprite_by_pos); if (!no_overlapped_sprites) { i = 1; while (i <
         * mobjcount) { mobj_t *m1 = mobjlist[i - 1]; mobj_t *m2 = mobjlist[i -
         * 0]; if (GETXY(m1) == GETXY(m2)) { mobj_t *mo = (m1.index < m2.index ?
         * m1 : m2); i++; while (i < mobjcount && GETXY(mobjlist[i]) ==
         * GETXY(m1)) { if (mobjlist[i].index < mo.index) { mo = mobjlist[i]; }
         * i++; } // 'nearest' mo.flags |= MF_FOREGROUND; } i++; } } } #endif
         */

    }

    /*
     * P_IsDoomnumAllowed() Based on code taken from P_LoadThings() in
     * src/p_setup.c Return TRUE if the thing in question is expected to be
     * available in the gamemode used.
     */

    boolean P_IsDoomnumAllowed(int doomnum) {
        // Do not spawn cool, new monsters if !commercial
        if (!DM.isCommercial())
            switch (doomnum) {
            case 64: // Archvile
            case 65: // Former Human Commando
            case 66: // Revenant
            case 67: // Mancubus
            case 68: // Arachnotron
            case 69: // Hell Knight
            case 71: // Pain Elemental
            case 84: // Wolf SS
            case 88: // Boss Brain
            case 89: // Boss Shooter
                return false;
            }

        return true;
    }

    //
    // P_LoadLineDefs
    // Also counts secret lines for intermissions.
    // ^^^
    // ??? killough ???
    // Does this mean secrets used to be linedef-based, rather than
    // sector-based?
    //
    // killough 4/4/98: split into two functions, to allow sidedef overloading
    //
    // killough 5/3/98: reformatted, cleaned up

    private void P_LoadLineDefs(int lump) {
        final maplinedef_t[] data; // cph - final*
        int i;

        numlines = W.LumpLength(lump) / maplinedef_t.sizeOf();
        lines = calloc_IfSameLevel(lines, numlines, line_t.class);
        data = W.CacheLumpNumIntoArray(lump, numlines, maplinedef_t.class); // cph
                                                                            // -
                                                                            // wad
                                                                            // lump
                                                                            // handling
                                                                            // updated

        for (i = 0; i < numlines; i++) {
            final maplinedef_t mld = data[i];
            line_t ld = lines[i];
            ld.id=i;
            vertex_t v1, v2;

            ld.flags = mld.flags;
            ld.special = mld.special;
            ld.tag = mld.tag;
            v1 = ld.v1 = vertexes[(char) mld.v1];
            v2 = ld.v2 = vertexes[(char) mld.v2];
            ld.dx = v2.x - v1.x;
            ld.dy = v2.y - v1.y;
            // Maes: map value semantics.
            ld.assignVertexValues();

            /*
             * #ifdef GL_DOOM // e6y // Rounding the wall length to the nearest
             * integer // when determining length instead of always rounding
             * down // There is no more glitches on seams between identical
             * textures. ld.texel_length = GetTexelDistance(ld.dx, ld.dy);
             * #endif
             */

            ld.tranlump = -1; // killough 4/11/98: no translucency by default

            ld.slopetype =
                (ld.dx == 0) ? slopetype_t.ST_VERTICAL
                        : (ld.dy == 0) ? slopetype_t.ST_HORIZONTAL
                                : fixed_t.FixedDiv(ld.dy, ld.dx) > 0 ? slopetype_t.ST_POSITIVE
                                        : slopetype_t.ST_NEGATIVE;

            if (v1.x < v2.x) {
                ld.bbox[BBox.BOXLEFT] = v1.x;
                ld.bbox[BBox.BOXRIGHT] = v2.x;
            } else {
                ld.bbox[BBox.BOXLEFT] = v2.x;
                ld.bbox[BBox.BOXRIGHT] = v1.x;
            }
            if (v1.y < v2.y) {
                ld.bbox[BBox.BOXBOTTOM] = v1.y;
                ld.bbox[BBox.BOXTOP] = v2.y;
            } else {
                ld.bbox[BBox.BOXBOTTOM] = v2.y;
                ld.bbox[BBox.BOXTOP] = v1.y;
            }

            /* calculate sound origin of line to be its midpoint */
            // e6y: fix sound origin for large levels
            // no need for comp_sound test, these are only used when comp_sound
            // = 0
            ld.soundorg =
                new degenmobj_t(ld.bbox[BBox.BOXLEFT] / 2
                        + ld.bbox[BBox.BOXRIGHT] / 2, ld.bbox[BBox.BOXTOP] / 2
                        + ld.bbox[BBox.BOXBOTTOM] / 2, 0);

            // TODO
            // ld.iLineID=i; // proff 04/05/2000: needed for OpenGL
            ld.sidenum[0] = mld.sidenum[0];
            ld.sidenum[1] = mld.sidenum[1];

            {
                /*
                 * cph 2006/09/30 - fix sidedef errors right away. cph
                 * 2002/07/20 - these errors are fatal if not fixed, so apply
                 * them in compatibility mode - a desync is better than a crash!
                 */
                int j;

                for (j = 0; j < 2; j++) {
                    if (ld.sidenum[j] != NO_INDEX && ld.sidenum[j] >= numsides) {
                        ld.sidenum[j] = NO_INDEX;
                        System.err
                                .printf(
                                    "P_LoadLineDefs: linedef %d has out-of-range sidedef number\n",
                                    numlines - i - 1);
                    }
                }

                // killough 11/98: fix common wad errors (missing sidedefs):

                if (ld.sidenum[0] == NO_INDEX) {
                    ld.sidenum[0] = 0; // Substitute dummy sidedef for missing
                                       // right side
                    // cph - print a warning about the bug
                    System.err.printf(
                        "P_LoadLineDefs: linedef %d missing first sidedef\n",
                        numlines - i - 1);
                }

                if ((ld.sidenum[1] == NO_INDEX) && flags(ld.flags, ML_TWOSIDED)) {
                    // e6y
                    // ML_TWOSIDED flag shouldn't be cleared for compatibility
                    // purposes
                    // see CLNJ-506.LMP at http://doomedsda.us/wad1005.html
                    // TODO: we don't really care, but still...
                    // if (!demo_compatibility ||
                    // !overflows[OVERFLOW.MISSEDBACKSIDE].emulate)
                    // {
                    ld.flags &= ~ML_TWOSIDED; // Clear 2s flag for missing left
                                              // side
                    // }
                    // Mark such lines and do not draw them only in
                    // demo_compatibility,
                    // because Boom's behaviour is different
                    // See OTTAWAU.WAD E1M1, sectors 226 and 300
                    // http://www.doomworld.com/idgames/index.php?id=1651
                    // TODO ehhh?
                    // ld.r_flags = RF_IGNORE_COMPAT;
                    // cph - print a warning about the bug
                    System.err
                            .printf(
                                "P_LoadLineDefs: linedef %d has two-sided flag set, but no second sidedef\n",
                                numlines - i - 1);
                }
            }

            // killough 4/4/98: support special sidedef interpretation below
            // TODO:
            // if (ld.sidenum[0] != NO_INDEX && ld.special!=0)
            // sides[(ld.sidenum[0]<<16)& (0x0000FFFF&ld.sidenum[1])].special =
            // ld.special;
        }

        W.UnlockLumpNum(lump); // cph - release the lump
    }

    // killough 4/4/98: delay using sidedefs until they are loaded
    // killough 5/3/98: reformatted, cleaned up

    private void P_LoadLineDefs2(int lump) {
        line_t ld;

        for (int i = 0; i < numlines; i++) {
            ld = lines[i];
            ld.frontsector = sides[ld.sidenum[0]].sector; // e6y: Can't be
                                                          // NO_INDEX here
            ld.backsector =
                ld.sidenum[1] != NO_INDEX ? sides[ld.sidenum[1]].sector : null;
            switch (ld.special) { // killough 4/11/98: handle special types
            case 260: // killough 4/11/98: translucent 2s textures
                // TODO: transparentpresent = true;//e6y
                // int lmp = sides[ld.getSpecialSidenum()].special; //
                // translucency from sidedef
                // if (!ld.tag) // if tag==0,
                // ld.tranlump = lmp; // affect this linedef only
                // else
                // for (int j=0;j<numlines;j++) // if tag!=0,
                // if (lines[j].tag == ld.tag) // affect all matching linedefs
                // lines[j].tranlump = lump;
                // break;
            }
        }
    }

    //
    // P_LoadSideDefs
    //
    // killough 4/4/98: split into two functions

    private void P_LoadSideDefs(int lump) {
        numsides = W.LumpLength(lump) / mapsidedef_t.sizeOf();
        sides = calloc_IfSameLevel(sides, numsides, side_t.class);
    }

    // killough 4/4/98: delay using texture names until
    // after linedefs are loaded, to allow overloading.
    // killough 5/3/98: reformatted, cleaned up

    private void P_LoadSideDefs2(int lump) {
        // cph - final*, wad lump handling updated
        final mapsidedef_t[] data =
            W.CacheLumpNumIntoArray(lump, numsides, mapsidedef_t.class);
        int i;

        for (i = 0; i < numsides; i++) {
            final mapsidedef_t msd = data[i];
            side_t sd = sides[i];
            sector_t sec;

            sd.textureoffset = msd.textureoffset << FRACBITS;
            sd.rowoffset = msd.rowoffset << FRACBITS;

            { /*
               * cph 2006/09/30 - catch out-of-range sector numbers; use sector
               * 0 instead
               */
                char sector_num = (char) msd.sector;
                if (sector_num >= numsectors) {
                    System.err
                            .printf(
                                "P_LoadSideDefs2: sidedef %i has out-of-range sector num %u\n",
                                i, sector_num);
                    sector_num = 0;
                }
                sd.sector = sec = sectors[sector_num];
            }

            // killough 4/4/98: allow sidedef texture names to be overloaded
            // killough 4/11/98: refined to allow colormaps to work as wall
            // textures if invalid as colormaps but valid as textures.
            switch (sd.special) {
            case 242: // variable colormap via 242 linedef
                /*
                 * sd.bottomtexture = (sec.bottommap =
                 * R.ColormapNumForName(msd.bottomtexture)) < 0 ? sec.bottommap
                 * = 0, R.TextureNumForName(msd.bottomtexture): 0 ;
                 * sd.midtexture = (sec.midmap =
                 * R.ColormapNumForName(msd.midtexture)) < 0 ? sec.midmap = 0,
                 * R.TextureNumForName(msd.midtexture) : 0 ; sd.toptexture =
                 * (sec.topmap = R.ColormapNumForName(msd.toptexture)) < 0 ?
                 * sec.topmap = 0, R.TextureNumForName(msd.toptexture) : 0 ;
                 */

                break;

            case 260: // killough 4/11/98: apply translucency to 2s normal
                      // texture
                if (msd.midtexture.compareToIgnoreCase("TRANMAP") == 0) {
                    if ((sd.special = W.CheckNumForName(msd.midtexture)) < 0
                            || W.LumpLength(sd.special) != 65536) {
                        sd.special = 0;
                        sd.midtexture =
                            (short) TM.TextureNumForName(msd.midtexture);
                    } else {
                        sd.special++;
                        sd.midtexture = 0;
                    }
                } else
                    sd.midtexture = (short) (sd.special = 0);
                sd.toptexture = (short) TM.TextureNumForName(msd.toptexture);
                sd.bottomtexture =
                    (short) TM.TextureNumForName(msd.bottomtexture);
                break;

            /*
             * #ifdef GL_DOOM case 271: case 272: if
             * (R_CheckTextureNumForName(msd.toptexture) == -1) {
             * sd.skybox_index = R_BoxSkyboxNumForName(msd.toptexture); } #endif
             */

            default: // normal cases
                // TODO: Boom uses "SafeTextureNumForName" here. Find out what
                // it does.
                sd.midtexture =
                    (short) TM.CheckTextureNumForName(msd.midtexture);
                sd.toptexture =
                    (short) TM.CheckTextureNumForName(msd.toptexture);
                sd.bottomtexture =
                    (short) TM.CheckTextureNumForName(msd.bottomtexture);
                break;
            }
        }

        W.UnlockLumpNum(lump); // cph - release the lump
    }

    //
    // P_LoadBlockMap
    //
    // killough 3/1/98: substantially modified to work
    // towards removing blockmap limit (a wad limitation)
    //
    // killough 3/30/98: Rewritten to remove blockmap limit,
    // though current algorithm is brute-force and unoptimal.
    //

    private void P_LoadBlockMap(int lump)
            throws IOException {
        int count = 0;

        if (DM.CM.CheckParmBool("-blockmap") || W.LumpLength(lump) < 8
                || (count = W.LumpLength(lump) / 2) >= 0x10000) // e6y
            CreateBlockMap();
        else {
            int i;
            // cph - final*, wad lump handling updated
            final char[] wadblockmaplump;

            DoomBuffer data =
                (DoomBuffer) W.CacheLumpNum(lump, PU_LEVEL, DoomBuffer.class);
            count = W.LumpLength(lump) / 2;
            wadblockmaplump = new char[count];

            data.setOrder(ByteOrder.LITTLE_ENDIAN);
            data.rewind();
            data.readCharArray(wadblockmaplump, count);

            if (!samelevel) // Reallocate if required.
                blockmaplump = new int[count];

            // killough 3/1/98: Expand wad blockmap into larger internal one,
            // by treating all offsets except -1 as unsigned and zero-extending
            // them. This potentially doubles the size of blockmaps allowed,
            // because Doom originally considered the offsets as always signed.

            blockmaplump[0] = wadblockmaplump[0];
            blockmaplump[1] = wadblockmaplump[1];
            blockmaplump[2] = (int) (wadblockmaplump[2] & 0xffff);
            blockmaplump[3] = (int) (wadblockmaplump[3] & 0xffff);

            for (i = 4; i < count; i++) {
                short t = (short) wadblockmaplump[i]; // killough 3/1/98
                blockmaplump[i] = (int) (t == -1 ? -1l : t & 0xffff);
            }

            W.UnlockLumpNum(lump); // cph - unlock the lump

            bmaporgx = blockmaplump[0] << FRACBITS;
            bmaporgy = blockmaplump[1] << FRACBITS;
            bmapwidth = blockmaplump[2];
            bmapheight = blockmaplump[3];

            // haleyjd 03/04/10: check for blockmap problems
            // http://www.doomworld.com/idgames/index.php?id=12935
            if (!VerifyBlockMap(count)) {
                System.err
                        .printf("P_LoadBlockMap: erroneous BLOCKMAP lump may cause crashes.\n");
                System.err
                        .printf("P_LoadBlockMap: use \"-blockmap\" command line switch for rebuilding\n");
            }
        }

        // MAES: blockmap was generated, rather than loaded.
        if (count == 0)
            count = blockmaplump.length - 4;

        // clear out mobj chains - CPhipps - use calloc
        // blocklinks = calloc_IfSameLevel(blocklinks, bmapwidth *
        // bmapheight.mobj_t.);
        // clear out mobj chains
        // ATTENTION! BUG!!!
        // If blocklinks are "cleared" to void -but instantiated- objects,
        // very bad bugs happen, especially the second time a level is
        // re-instantiated.
        // Probably caused other bugs as well, as an extra object would appear
        // in iterators.

        if (blocklinks != null && samelevel)
            for (int i = 0; i < bmapwidth * bmapheight; i++)
                blocklinks[i] = null;
        else
            blocklinks = new mobj_t[bmapwidth * bmapheight];

        // IMPORTANT MODIFICATION: no need to have both blockmaplump AND
        // blockmap.
        // If the offsets in the lump are OK, then we can modify them (remove 4)
        // and copy the rest of the data in one single data array. This avoids
        // reserving memory for two arrays (we can't simply alias one in Java)

        blockmap = new int[blockmaplump.length - 4];
        count = bmapwidth * bmapheight;
        // Offsets are relative to START OF BLOCKMAP, and IN SHORTS, not bytes.
        for (int i = 0; i < blockmaplump.length - 4; i++) {
            // Modify indexes so that we don't need two different lumps.
            // Can probably be further optimized if we simply shift everything
            // backwards.
            // and reuse the same memory space.
            if (i < count)
                blockmaplump[i] = blockmaplump[i + 4] - 4;
            else
                blockmaplump[i] = blockmaplump[i + 4];
        }

        
        // MAES: set blockmapxneg and blockmapyneg
        // E.g. for a full 512x512 map, they should be both
        // -1. For a 257*257, they should be both -255 etc.
        if (bmapwidth>255)
            blockmapxneg= bmapwidth-512;
        if (bmapheight>255)
            blockmapyneg= bmapheight-512;
        
        blockmap = blockmaplump;

    }

    //
    // P_LoadReject - load the reject table
    //

    private void P_LoadReject(int lumpnum, int totallines) {
        // dump any old cached reject lump, then cache the new one
        if (rejectlump != -1)
            W.UnlockLumpNum(rejectlump);
        rejectlump = lumpnum + ML_REJECT;
        rejectmatrix = W.CacheLumpNumAsRawBytes(rejectlump, 0);

        // e6y: check for overflow
        // TODO: g.Overflow.RejectOverrun(rejectlump, rejectmatrix,
        // totallines,numsectors);
    }

    //
    // P_GroupLines
    // Builds sector line lists and subsector sector numbers.
    // Finds block bounding boxes for sectors.
    //
    // killough 5/3/98: reformatted, cleaned up
    // cph 18/8/99: rewritten to avoid O(numlines * numsectors) section
    // It makes things more complicated, but saves seconds on big levels
    // figgi 09/18/00 -- adapted for gl-nodes

    
    // modified to return totallines (needed by P_LoadReject)
    private int P_GroupLines() {
        line_t li;
        sector_t sector;
        int i, j, total = numlines;

        // figgi
        for (i = 0; i < numsubsectors; i++) {
            int seg = subsectors[i].firstline;
            subsectors[i].sector = null;
            for (j = 0; j < subsectors[i].numlines; j++) {
                if (segs[seg].sidedef != null) {
                    subsectors[i].sector = segs[seg].sidedef.sector;
                    break;
                }
                seg++;
            }
            if (subsectors[i].sector == null)
                I.Error("P_GroupLines: Subsector a part of no sector!\n");
        }

        // count number of lines in each sector
        for (i = 0; i < numlines; i++) {
            li = lines[i];
            li.frontsector.linecount++;
            if (li.backsector != null && (li.backsector != li.frontsector)) {
                li.backsector.linecount++;
                total++;
            }
        }

         // allocate line tables for each sector
          // line_t[] linebuffer =
          // C2JUtils.createArrayOfObjects(line_t.class,total);
          // e6y: REJECT overrun emulation code
          // moved to P_LoadReject

            for (i = 0; i < numsectors; i++) {
                sector = sectors[i];
                sector.lines =
                    C2JUtils.createArrayOfObjects(line_t.class,
                        sector.linecount);
                // linebuffer += sector.linecount;
                sector.linecount = 0;
                BBox.ClearBox(sector.blockbox);
            }
        

        // Enter those lines
        for (i = 0; i < numlines; i++) {
            li = lines[i];
            AddLineToSector(li, li.frontsector);
            if (li.backsector != null && li.backsector != li.frontsector)
                AddLineToSector(li, li.backsector);
        }

        for (i = 0; i < numsectors; i++) {
            sector = sectors[i];
            int[] bbox = sector.blockbox; // cph - For convenience, so
                                          // I can sue the old code unchanged
            int block;

            // set the degenmobj_t to the middle of the bounding box
            // TODO
            if (true/* comp[comp_sound] */) {
                sector.soundorg =
                    new degenmobj_t((bbox[BOXRIGHT] + bbox[BOXLEFT]) / 2,
                            (bbox[BOXTOP] + bbox[BOXBOTTOM]) / 2);
            } else {
                // e6y: fix sound origin for large levels
                sector.soundorg =
                    new degenmobj_t((bbox[BOXRIGHT] / 2 + bbox[BOXLEFT] / 2),
                            bbox[BOXTOP] / 2 + bbox[BOXBOTTOM] / 2);
            }

            // adjust bounding box to map blocks
            block =getSafeBlockY(bbox[BOXTOP] - bmaporgy + Limits.MAXRADIUS);
            block = block >= bmapheight ? bmapheight - 1 : block;
            sector.blockbox[BOXTOP] = block;

            block =getSafeBlockY(bbox[BOXBOTTOM] - bmaporgy - Limits.MAXRADIUS);
            block = block < 0 ? 0 : block;
            sector.blockbox[BOXBOTTOM] = block;

            block =getSafeBlockX(bbox[BOXRIGHT] - bmaporgx + Limits.MAXRADIUS);
            block = block >= bmapwidth ? bmapwidth - 1 : block;
            sector.blockbox[BOXRIGHT] = block;

            block =getSafeBlockX(bbox[BOXLEFT] - bmaporgx - Limits.MAXRADIUS);
            block = block < 0 ? 0 : block;
            sector.blockbox[BOXLEFT] = block;
        }

        return total; // this value is needed by the reject overrun emulation
                      // code
    }

    //
    // killough 10/98
    //
    // Remove slime trails.
    //
    // Slime trails are inherent to Doom's coordinate system -- i.e. there is
    // nothing that a node builder can do to prevent slime trails ALL of the
    // time,
    // because it's a product of the integer coodinate system, and just because
    // two lines pass through exact integer coordinates, doesn't necessarily
    // mean
    // that they will intersect at integer coordinates. Thus we must allow for
    // fractional coordinates if we are to be able to split segs with node
    // lines,
    // as a node builder must do when creating a BSP tree.
    //
    // A wad file does not allow fractional coordinates, so node builders are
    // out
    // of luck except that they can try to limit the number of splits (they
    // might
    // also be able to detect the degree of roundoff error and try to avoid
    // splits
    // with a high degree of roundoff error). But we can use fractional
    // coordinates
    // here, inside the engine. It's like the difference between square inches
    // and
    // square miles, in terms of granularity.
    //
    // For each vertex of every seg, check to see whether it's also a vertex of
    // the linedef associated with the seg (i.e, it's an endpoint). If it's not
    // an endpoint, and it wasn't already moved, move the vertex towards the
    // linedef by projecting it using the law of cosines. Formula:
    //
    // 2 2 2 2
    // dx x0 + dy x1 + dx dy (y0 - y1) dy y0 + dx y1 + dx dy (x0 - x1)
    // {---------------------------------, ---------------------------------}
    // 2 2 2 2
    // dx + dy dx + dy
    //
    // (x0,y0) is the vertex being moved, and (x1,y1)-(x1+dx,y1+dy) is the
    // reference linedef.
    //
    // Segs corresponding to orthogonal linedefs (exactly vertical or horizontal
    // linedefs), which comprise at least half of all linedefs in most wads,
    // don't
    // need to be considered, because they almost never contribute to slime
    // trails
    // (because then any roundoff error is parallel to the linedef, which
    // doesn't
    // cause slime). Skipping simple orthogonal lines lets the code finish
    // quicker.
    //
    // Please note: This section of code is not interchangable with TeamTNT's
    // code which attempts to fix the same problem.
    //
    // Firelines (TM) is a Rezistered Trademark of MBF Productions
    //

    private void P_RemoveSlimeTrails() // killough 10/98
    {
        // Hitlist for vertices
        boolean[] hit = new boolean[numvertexes];

        // Searchlist for

        int i;
        for (i = 0; i < numsegs; i++) // Go through each seg
        {
            final line_t l;

            if (segs[i].miniseg == true) // figgi -- skip minisegs
                return;

            l = segs[i].linedef; // The parent linedef
            if (l.dx != 0 && l.dy != 0) // We can ignore orthogonal lines
            {
                vertex_t v = segs[i].v1;
                do {
                    int index = C2JUtils.indexOf(vertexes, v);
                    if (!hit[index]) // If we haven't processed vertex
                    {
                        hit[index] = true; // Mark this vertex as processed
                        if (v != l.v1 && v != l.v2) // Exclude endpoints of
                                                    // linedefs
                        { // Project the vertex back onto the parent linedef
                            long dx2 = (l.dx >> FRACBITS) * (l.dx >> FRACBITS);
                            long dy2 = (l.dy >> FRACBITS) * (l.dy >> FRACBITS);
                            long dxy = (l.dx >> FRACBITS) * (l.dy >> FRACBITS);
                            long s = dx2 + dy2;
                            int x0 = v.x, y0 = v.y, x1 = l.v1.x, y1 = l.v1.y;
                            v.x =
                                (int) ((dx2 * x0 + dy2 * x1 + dxy * (y0 - y1)) / s);
                            v.y =
                                (int) ((dy2 * y0 + dx2 * y1 + dxy * (x0 - x1)) / s);
                        }
                    } // Obsfucated C contest entry: :)
                } while ((v != segs[i].v2) && ((v = segs[i].v2) != null));
            }
            // Assign modified vertex values.
            l.assignVertexValues();
        }
    }

    //
    // P_CheckLumpsForSameSource
    //
    // Are these lumps in the same wad file?
    //

    boolean P_CheckLumpsForSameSource(int lump1, int lump2) {
        int wad1_index, wad2_index;
        wadfile_info_t wad1, wad2;

        if ((unsigned(lump1) >= unsigned(W.NumLumps()))
                || (unsigned(lump2) >= unsigned(W.NumLumps())))
            return false;

        wad1 = W.GetLumpInfo(lump1).wadfile;
        wad2 = W.GetLumpInfo(lump2).wadfile;

        if (wad1 == null || wad2 == null)
            return false;

        wad1_index = W.GetWadfileIndex(wad1);
        wad2_index = W.GetWadfileIndex(wad2);

        if (wad1_index != wad2_index)
            return false;

        if ((wad1_index < 0) || (wad1_index >= W.GetNumWadfiles()))
            return false;

        if ((wad2_index < 0) || (wad2_index >= W.GetNumWadfiles()))
            return false;

        return true;
    }

    private static final String[] ml_labels = { "ML_LABEL", // A separator,
                                                            // name, ExMx or
                                                            // MAPxx
            "ML_THINGS", // Monsters, items..
            "ML_LINEDEFS", // LineDefs, from editing
            "ML_SIDEDEFS", // SideDefs, from editing
            "ML_VERTEXES", // Vertices, edited and BSP splits generated
            "ML_SEGS", // LineSegs, from LineDefs split by BSP
            "ML_SSECTORS", // SubSectors, list of LineSegs
            "ML_NODES", // BSP nodes
            "ML_SECTORS", // Sectors, from editing
            "ML_REJECT", // LUT, sector-sector visibility
            "ML_BLOCKMAP", // LUT, motion clipping, walls/grid element
    };

    private static final boolean GL_DOOM = false;

    //
    // P_CheckLevelFormat
    //
    // Checking for presence of necessary lumps
    //

    void P_CheckLevelWadStructure(final String mapname) {
        int i, lumpnum;

        if (mapname == null) {
            I.Error("P_SetupLevel: Wrong map name");
        }

        lumpnum = W.CheckNumForName(mapname.toUpperCase());

        if (lumpnum < 0) {
            I.Error("P_SetupLevel: There is no %s map.", mapname);
        }

        for (i = ML_THINGS + 1; i <= ML_SECTORS; i++) {
            if (!P_CheckLumpsForSameSource(lumpnum, lumpnum + i)) {
                I.Error(
                    "P_SetupLevel: Level wad structure is incomplete. There is no %s lump. (%s)",
                    ml_labels[i], W.GetNameForLump(lumpnum));
            }
        }

        // refuse to load Hexen-format maps, avoid segfaults
        i = lumpnum + ML_BLOCKMAP + 1;
        if (P_CheckLumpsForSameSource(lumpnum, i)) {
            if (W.GetLumpInfo(i).name.compareToIgnoreCase("BEHAVIOR") == 0) {
                I.Error("P_SetupLevel: %s: Hexen format not supported", mapname);
            }
        }
    }

    //
    // P_SetupLevel
    //
    // killough 5/3/98: reformatted, cleaned up

    public void SetupLevel(int episode, int map, int playermask, skill_t skill)
            throws IOException {
        int i;
        String lumpname;
        int lumpnum;

        String gl_lumpname;
        int gl_lumpnum;

        // e6y
        DM.totallive = 0;
        // TODO: transparentpresent = false;

        // R_StopAllInterpolations();

        DM.totallive =
            DM.totalkills =
                DM.totalitems = DM.totalsecret = DM.wminfo.maxfrags = 0;
        DM.wminfo.partime = 180;

        for (i = 0; i < Limits.MAXPLAYERS; i++) {
            DM.players[i].killcount =
                DM.players[i].secretcount = DM.players[i].itemcount = 0;
            // TODO DM.players[i].resurectedkillcount = 0;//e6y
        }

        // Initial height of PointOfView will be set by player think.
        DM.players[DM.consoleplayer].viewz = 1;

        // Make sure all sounds are stopped before Z_FreeTags.
        S.Start();

        // Z_FreeTags(PU_LEVEL, PU_PURGELEVEL-1);
        if (rejectlump != -1) { // cph - unlock the reject table
            W.UnlockLumpNum(rejectlump);
            rejectlump = -1;
        }

        P.InitThinkers();

        // if working with a devlopment map, reload it
        // W.Reload (); killough 1/31/98: W.Reload obsolete

        // find map name
        if (DM.isCommercial()) {
            lumpname = String.format("map%02d", map); // killough 1/24/98:
                                                      // simplify
            gl_lumpname = String.format("gl_map%02d", map); // figgi
        } else {
            lumpname = String.format("E%dM%d", episode, map); // killough
                                                              // 1/24/98:
                                                              // simplify
            gl_lumpname = String.format("GL_E%dM%d", episode, map); // figgi
        }

        lumpnum = W.GetNumForName(lumpname);
        gl_lumpnum = W.CheckNumForName(gl_lumpname); // figgi

        // e6y
        // Refuse to load a map with incomplete pwad structure.
        // Avoid segfaults on levels without nodes.
        P_CheckLevelWadStructure(lumpname);

        DM.leveltime = 0;
        DM.totallive = 0;

        // note: most of this ordering is important

        // killough 3/1/98: P_LoadBlockMap call moved down to below
        // killough 4/4/98: split load of sidedefs into two parts,
        // to allow texture names to be used in special linedefs

        // figgi 10/19/00 -- check for gl lumps and load them
        P_GetNodesVersion(lumpnum, gl_lumpnum);

        // e6y: speedup of level reloading
        // Most of level's structures now are allocated with PU_STATIC instead
        // of PU_LEVEL
        // It is important for OpenGL, because in case of the same data in
        // memory
        // we can skip recalculation of much stuff

        samelevel =
            (map == current_map) && (episode == current_episode)
                    && (nodesVersion == current_nodesVersion);

        current_episode = episode;
        current_map = map;
        current_nodesVersion = nodesVersion;

        if (!samelevel) {

            /*
             * if (GL_DOOM){ // proff 11/99: clean the memory from textures etc.
             * gld_CleanMemory(); }
             */

            // free(segs);
            // free(nodes);
            // free(subsectors);
            /*
             * #ifdef GL_DOOM free(map_subsectors); #endif
             */

            // free(blocklinks);
            // free(blockmaplump);

            // free(lines);
            // free(sides);
            // free(sectors);
            // free(vertexes);
        }

        if (nodesVersion > 0)
            this.P_LoadVertexes2(lumpnum + ML_VERTEXES, gl_lumpnum
                    + ML_GL_VERTS);
        else
            P_LoadVertexes(lumpnum + ML_VERTEXES);
        P_LoadSectors(lumpnum + ML_SECTORS);
        P_LoadSideDefs(lumpnum + ML_SIDEDEFS);
        P_LoadLineDefs(lumpnum + ML_LINEDEFS);
        P_LoadSideDefs2(lumpnum + ML_SIDEDEFS);
        P_LoadLineDefs2(lumpnum + ML_LINEDEFS);

        // e6y: speedup of level reloading
        // Do not reload BlockMap for same level,
        // because in case of big level P_CreateBlockMap eats much time
        if (!samelevel) {
            P_LoadBlockMap(lumpnum + ML_BLOCKMAP);
        } else {
            // clear out mobj chains
            if (blocklinks != null
                    && blocklinks.length == bmapwidth * bmapheight) {
                for (i = 0; i < bmapwidth * bmapheight; i++) {
                    blocklinks[i] = null;
                }
            } else
                blocklinks =
                    C2JUtils.createArrayOfObjects(mobj_t.class, bmapwidth
                            * bmapheight);
        }

        if (nodesVersion > 0) {
            P_LoadSubsectors(gl_lumpnum + ML_GL_SSECT);
            P_LoadNodes(gl_lumpnum + ML_GL_NODES);
            // TODO: P_LoadGLSegs(gl_lumpnum + ML_GL_SEGS);
        } else {
            if (P_CheckForZDoomUncompressedNodes(lumpnum, gl_lumpnum)) {
                P_LoadZNodes(lumpnum + ML_NODES, 0);
            } else if (P_CheckForDeePBSPv4Nodes(lumpnum, gl_lumpnum)) {
                P_LoadSubsectors_V4(lumpnum + ML_SSECTORS);
                P_LoadNodes_V4(lumpnum + ML_NODES);
                P_LoadSegs_V4(lumpnum + ML_SEGS);
            } else {
                P_LoadSubsectors(lumpnum + ML_SSECTORS);
                P_LoadNodes(lumpnum + ML_NODES);
                P_LoadSegs(lumpnum + ML_SEGS);
            }
        }

        /*
         * if (GL_DOOM){ map_subsectors = calloc_IfSameLevel(map_subsectors,
         * numsubsectors); }
         */

        // reject loading and underflow padding separated out into new function
        // P_GroupLines modified to return a number the underflow padding needs
        // P_LoadReject(lumpnum, P_GroupLines());
        P_GroupLines();
        super.LoadReject(lumpnum+ML_REJECT);

        // e6y
        // Correction of desync on dv04-423.lmp/dv.wad
        // http://www.doomworld.com/vb/showthread.php?s=&postid=627257#post627257
        // if (DoomStatus.compatibility_level>=lxdoom_1_compatibility ||
        // Compatibility.prboom_comp[PC.PC_REMOVE_SLIME_TRAILS.ordinal()].state)
        P_RemoveSlimeTrails(); // killough 10/98: remove slime trails from wad

        // Note: you don't need to clear player queue slots --
        // a much simpler fix is in g_game.c -- killough 10/98

        DM.bodyqueslot = 0;

        /* cph - reset all multiplayer starts */

        for (i = 0; i < playerstarts.length; i++) {
            DM.playerstarts[i] = null;
        }

        deathmatch_p = 0;

        for (i = 0; i < Limits.MAXPLAYERS; i++)
            DM.players[i].mo = null;
        // TODO: TracerClearStarts();

        // Hmm? P_MapStart();

        P_LoadThings(lumpnum + ML_THINGS);

        // if deathmatch, randomly spawn the active players
        if (DM.deathmatch) {
            for (i = 0; i < Limits.MAXPLAYERS; i++)
                if (DM.playeringame[i]) {
                    DM.players[i].mo = null; // not needed? - done before
                                             // P_LoadThings
                    DM.DG.DeathMatchSpawnPlayer(i);
                }
        } else // if !deathmatch, check all necessary player starts actually
               // exist
        {
            for (i = 0; i < Limits.MAXPLAYERS; i++)
                if (DM.playeringame[i] && !C2JUtils.eval(DM.players[i].mo))
                    I.Error("P_SetupLevel: missing player %d start\n", i + 1);
        }

        // killough 3/26/98: Spawn icon landings:
        // TODO: if (DM.isCommercial())
        // P.SpawnBrainTargets();

        if (!DM.isShareware()) {
            // TODO: S.ParseMusInfo(lumpname);
        }

        // clear special respawning que
        P.iquehead = P.iquetail = 0;

        // set up world state
        P.SpawnSpecials();

        // TODO: P.MapEnd();

        // preload graphics
        if (DM.precache)
            TM.PrecacheLevel();

        // MAES: thinkers are separate than texture management. Maybe split
        // sprite management as well?
        R.PreCacheThinkers();

        /*
         * if (GL_DOOM){ if (V_GetMode() == VID_MODEGL) { // e6y // Do not
         * preprocess GL data during skipping, // because it potentially will
         * not be used. // But preprocessing must be called immediately after
         * stop of skipping. if (!doSkip) { // proff 11/99: calculate all OpenGL
         * specific tables etc. gld_PreprocessLevel(); } } }
         */
        // e6y
        // TODO P_SyncWalkcam(true, true);
        // TODO R_SmoothPlaying_Reset(NULL);
    }

}

package p;

public enum sd_e {
	    sd_opening,
	    sd_waiting,
	    sd_closing
	}
package p;

import java.io.DataInputStream;
import java.io.IOException;
import java.nio.ByteBuffer;

import rr.SectorAction;
import rr.sector_t;
import w.DoomIO;
import w.IReadableDoomObject;

public class plat_t extends SectorAction implements IReadableDoomObject{
        
        public sector_t   sector;
        /** fixed_t */
        public int speed,low,high;
        int     wait;
        int     count;
        public plat_e  status;
        public plat_e  oldstatus;
        boolean crush;
        public int     tag;
        public plattype_e  type;
        
        public plat_t (){
        	// These must never be null so they get the lowest ordinal value.
        	// by default.
        	this.status=plat_e.up;
        	this.oldstatus=plat_e.up;
        }
        
        @Override
        public void read(DataInputStream f) throws IOException{

            super.read(f); // Call thinker reader first            
            super.sectorid=DoomIO.readLEInt(f); // Sector index
            speed=DoomIO.readLEInt(f);
            low=DoomIO.readLEInt(f);
            high=DoomIO.readLEInt(f);
            wait=DoomIO.readLEInt(f);
            count=DoomIO.readLEInt(f);
            status=plat_e.values()[DoomIO.readLEInt(f)];
            oldstatus=plat_e.values()[DoomIO.readLEInt(f)];
            System.out.println(status);
            System.out.println(oldstatus);
            crush=DoomIO.readIntBoolean(f);
            tag=DoomIO.readLEInt(f);
            type=plattype_e.values()[DoomIO.readLEInt(f)];        
            }
        
        @Override
        public void pack(ByteBuffer b) throws IOException{
            super.pack(b); //12            
            b.putInt(super.sectorid); // 16
            b.putInt(speed);//20
            b.putInt(low); // 24
            b.putInt(high); //28
            b.putInt(wait); //32
            b.putInt(count); //36
            b.putInt(status.ordinal()); //40
            b.putInt(oldstatus.ordinal()); //44
            System.out.println(status);
            System.out.println(oldstatus);
            b.putInt(crush?1:0); // 48
            b.putInt(tag); // 52
            b.putInt(type.ordinal()); // 56
        }
        
        public vldoor_t asVlDoor(sector_t[] sectors){
        	/*
        	typedef struct
        	{
        	    thinker_t	thinker;
        	    vldoor_e	type;
        	    sector_t*	sector;
        	    fixed_t	topheight;
        	    fixed_t	speed;

        	    // 1 = up, 0 = waiting at top, -1 = down
        	    int             direction;
        	    
        	    // tics to wait at the top
        	    int             topwait;
        	    // (keep in case a door going down is reset)
        	    // when it reaches 0, start going down
        	    int             topcountdown;
        	    
        	} vldoor_t;
        	*/
        	
        	vldoor_t tmp=new vldoor_t();
        	tmp.next=this.next;
        	tmp.prev=this.prev;
        	tmp.function=this.function;
        	tmp.type=vldoor_e.values()[sector.id%vldoor_e.VALUES];
        	tmp.sector=sectors[this.speed%sectors.length];
        	tmp.topheight=this.low;
        	tmp.speed=this.high;
        	tmp.direction=this.wait;
        	tmp.topwait=this.count;
        	tmp.topcountdown=this.status.ordinal();

        	
        	return tmp;
        }
        
    } 
package p;

public class slidename_t {

	public slidename_t() {

	}

	public slidename_t(String frontFrame1, String frontFrame2,
			String frontFrame3, String frontFrame4, String backFrame1,
			String backFrame2, String backFrame3, String backFrame4) {
		this.frontFrame1 = frontFrame1;
		this.frontFrame2 = frontFrame2;
		this.frontFrame3 = frontFrame3;
		this.frontFrame4 = frontFrame4;
		this.backFrame1 = backFrame1;
		this.backFrame2 = backFrame2;
		this.backFrame3 = backFrame3;
		this.backFrame4 = backFrame4;
	}

	String frontFrame1;
	String frontFrame2;
	String frontFrame3;
	String frontFrame4;
	String backFrame1;
	String backFrame2;
	String backFrame3;
	String backFrame4;

}
package p;

//
// P_DOORS
//

public enum vldoor_e {
     normal,
     close30ThenOpen,
     close,
     open,
     raiseIn5Mins,
     blazeRaise,
     blazeOpen,
     blazeClose;
     
     public static final int VALUES=vldoor_e.values().length;
 }
package p;

import doom.thinker_t;

/** Generic single-thinker argument action function. 
 *  Useful for handling stuff such as sector actions, doors, etc.
 *  or special thinker objects that don't qualify as mobj_t's.
 * 
 * @author velktron
 *
 * @param <T>
 */

public interface ActionTypeSS<T extends thinker_t>{
	public void invoke (T a);
}
package p;

import static rr.line_t.*;
import static data.Defines.ITEMQUESIZE;
import static data.Defines.MELEERANGE;
import static data.Defines.NF_SUBSECTOR;
import static data.Defines.NUMAMMO;
import static data.Defines.RANGECHECK;
import static data.Defines.pw_allmap;
import static data.Defines.pw_infrared;
import static data.Defines.pw_invisibility;
import static data.Defines.pw_invulnerability;
import static data.Defines.pw_ironfeet;
import static data.Defines.pw_strength;
import static data.Limits.BUTTONTIME;
import static data.Limits.MAXANIMS;
import static data.Limits.MAXBUTTONS;
import static data.Limits.MAXINT;
import static data.Limits.MAXINTERCEPTS;
import static data.Limits.MAXSPECIALCROSS;
import static data.Limits.MAXSWITCHES;
import static data.Limits.PLATSPEED;
import static data.Limits.PLATWAIT;
import static data.Tables.ANG270;
import static data.Tables.ANG90;
import static data.Tables.BITS32;
import static data.info.mobjinfo;
import static data.info.states;
import static doom.englsh.GOTARMBONUS;
import static doom.englsh.GOTARMOR;
import static doom.englsh.GOTBACKPACK;
import static doom.englsh.GOTBERSERK;
import static doom.englsh.GOTBFG9000;
import static doom.englsh.GOTBLUECARD;
import static doom.englsh.GOTBLUESKUL;
import static doom.englsh.GOTCELL;
import static doom.englsh.GOTCELLBOX;
import static doom.englsh.GOTCHAINGUN;
import static doom.englsh.GOTCHAINSAW;
import static doom.englsh.GOTCLIP;
import static doom.englsh.GOTCLIPBOX;
import static doom.englsh.GOTHTHBONUS;
import static doom.englsh.GOTINVIS;
import static doom.englsh.GOTINVUL;
import static doom.englsh.GOTLAUNCHER;
import static doom.englsh.GOTMAP;
import static doom.englsh.GOTMEDIKIT;
import static doom.englsh.GOTMEDINEED;
import static doom.englsh.GOTMEGA;
import static doom.englsh.GOTMSPHERE;
import static doom.englsh.GOTPLASMA;
import static doom.englsh.GOTREDCARD;
import static doom.englsh.GOTREDSKULL;
import static doom.englsh.GOTROCKBOX;
import static doom.englsh.GOTROCKET;
import static doom.englsh.GOTSHELLBOX;
import static doom.englsh.GOTSHELLS;
import static doom.englsh.GOTSHOTGUN;
import static doom.englsh.GOTSHOTGUN2;
import static doom.englsh.GOTSTIM;
import static doom.englsh.GOTSUIT;
import static doom.englsh.GOTSUPER;
import static doom.englsh.GOTVISOR;
import static doom.englsh.GOTYELWCARD;
import static doom.englsh.GOTYELWSKUL;
import static doom.items.weaponinfo;
import static m.fixed_t.FRACUNIT;
import static m.fixed_t.MAPFRACUNIT;
import static m.fixed_t.FixedDiv;
import static p.DoorDefines.SLOWDARK;
import static p.MapUtils.AproxDistance;
import static p.MapUtils.InterceptVector;
import static p.MobjFlags.*;
import static utils.C2JUtils.eval;
import static utils.C2JUtils.flags;
import java.util.Arrays;

import hu.HU;
import i.DoomStatusAware;
import i.IDoomSystem;
import m.IRandom;
import rr.ISpriteManager;
import rr.Renderer;
import rr.TextureManager;
import rr.line_t;
import rr.node_t;
import rr.sector_t;
import rr.side_t;
import rr.subsector_t;
import s.IDoomSound;
import st.StatusBar;
import utils.C2JUtils;
import w.IWadLoader;
import automap.IAutoMap;
import data.Limits;
import data.mapthing_t;
import data.mobjtype_t;
import data.state_t;
import data.sounds.sfxenum_t;
import defines.ammotype_t;
import defines.card_t;
import defines.statenum_t;
import doom.DoomMain;
import doom.DoomStatus;
import doom.IDoomGame;
import doom.player_t;
import doom.th_class;
import doom.think_t;
import doom.thinker_t;
import doom.weapontype_t;

// // FROM SIGHT

public abstract class UnifiedGameMap implements ThinkerList,DoomStatusAware{
    
    
    public UnifiedGameMap(DoomStatus<?,?> DS){
        this.SW=new Switches();
        this.LEV=new Lights();
        this.SPECS=new Specials();
        this.PEV=new Plats();
        this.See=new Sight(); // Didn't initialize that.
        this.EN=new Enemies();
        this.thinkercap=new thinker_t();
        for (int i=0; i<th_class.NUMTHCLASS; i++)  // killough 8/29/98: initialize threaded lists
            thinkerclasscap[i]=new thinker_t();
        
        this.RemoveThinkerDelayed=new P_RemoveThinkerDelayed();
        
        intercepts = new intercept_t[MAXINTERCEPTS];
        C2JUtils.initArrayOfObjects(intercepts,intercept_t.class);

        this.updateStatus(DS);
        // Normally unused. It clashes with line attribute 124, and looks like ass
        // anyway. However it's fully implemented.
        //this.SL=new SlideDoor(DS);
        //DS.SL=SL;
        this.FUNS=new ActionFunctions(DS,EN);
        
        // "Wire" all states to the proper functions.
        for (int i=0;i<states.length;i++){
        	FUNS.doWireState(states[i]);
        }

        
    }
    
    /////////////////// STATUS ///////////////////

    IWadLoader W;

    IAutoMap<?,?> AM;

    IRandom RND;

    Renderer<?,?> R;
    
    TextureManager<?> TM;

    AbstractLevelLoader LL;

    DoomMain<?,?> DM;

    IDoomGame DG;
    
    StatusBar ST;

    HU HU;
    
    IDoomSystem I;
    
    IDoomSound S;
    
    ISpriteManager SM;

    @Override
    public void updateStatus(DoomStatus<?,?> DC) {
            this.I=DC.I;
            this.DG=DC.DG;
            this.S=DC.S;
            this.LL=DC.LL;
            this.RND=DC.RND;
            this.DM=DC.DM;
            this.R=DC.R;
            this.W=DC.W;
            this.AM=DC.AM;
            this.ST= (StatusBar) DC.ST;
            this.AM=DC.AM;
            this.HU=DC.HU;
            this.TM=DC.TM;
            this.SM=DC.SM;
            if (FUNS!=null)
            FUNS.updateStatus(DC);
            
            }
      
    
    // //////////// Internal singletons //////////////
    public Actions A;

    protected Specials SPECS;

    // DoorsFloors EV;
    protected Plats PEV;

    protected Lights LEV;

    protected Switches SW;

    protected Sight See;

    protected Enemies EN;
    
    protected ActionFunctions FUNS;
    
    protected SlideDoor SL;

    // ////////////////////////////////////////////

    public int topslope;

    public int bottomslope; // slopes to top and bottom of target

    int attackrange;
    
    

    //
    // UTILITIES
    //

    //
    // getSide()
    // Will return a side_t*
    // given the number of the current sector,
    // the line number, and the side (0/1) that you want.
    //
    side_t getSide(int currentSector, int line, int side) {
        return LL.sides[(LL.sectors[currentSector].lines[line]).sidenum[side]];
    }

    /**
     * getSector()
     * Will return a sector_t
     * given the number of the current sector,
     * the line number and the side (0/1) that you want.
     */
    
    sector_t getSector(int currentSector, int line, int side) {
        return LL.sides[(LL.sectors[currentSector].lines[line]).sidenum[side]].sector;
    }

    /**
     * twoSided()
     * Given the sector number and the line number,
     * it will tell you whether the line is two-sided or not.
     */
    
    protected boolean twoSided(int sector, int line) {
        return eval((LL.sectors[sector].lines[line]).flags& ML_TWOSIDED);
    }

    /**
     * RETURN NEXT SECTOR # THAT LINE TAG REFERS TO
     */
    
    protected int FindSectorFromLineTag(line_t line, int start) {
        int i;

        for (i = start + 1; i < LL.numsectors; i++)
            if (LL.sectors[i].tag == line.tag)
                return i;

        return -1;
    }

    

    // //////////////////// FROM p_maputl.c ////////////////////

    /** fixed_t */
    protected int opentop, openbottom, openrange, lowfloor;

    /**
     * P_LineOpening Sets opentop and openbottom to the window through a two
     * sided line. OPTIMIZE: keep this precalculated
     */

    public void LineOpening(line_t linedef) {
        sector_t front;
        sector_t back;

        if (linedef.sidenum[1] == line_t.NO_INDEX) {
            // single sided line
            openrange = 0;
            return;
        }

        front = linedef.frontsector;
        back = linedef.backsector;

        if (front.ceilingheight < back.ceilingheight)
            opentop = front.ceilingheight;
        else
            opentop = back.ceilingheight;

        if (front.floorheight > back.floorheight) {
            openbottom = front.floorheight;
            lowfloor = back.floorheight;
        } else {
            openbottom = back.floorheight;
            lowfloor = front.floorheight;
        }

        openrange = opentop - openbottom;
    }

    //
    // THING POSITION SETTING
    //

    /**
     * P_UnsetThingPosition Unlinks a thing from block map and sectors. On each
     * position change, BLOCKMAP and other lookups maintaining lists ot things
     * inside these structures need to be updated.
     */

    public void UnsetThingPosition(mobj_t thing) {
        final int blockx;
        final int blocky;

        if (!eval(thing.flags& MF_NOSECTOR)) {
            // inert things don't need to be in blockmap?
            // unlink from subsector
            if (thing.snext != null)
                ((mobj_t) thing.snext).sprev = thing.sprev;

            if (thing.sprev != null)
                ((mobj_t) thing.sprev).snext = thing.snext;
            else
                thing.subsector.sector.thinglist = (mobj_t) thing.snext;
        }

        if (!eval(thing.flags& MF_NOBLOCKMAP)) {
            // inert things don't need to be in blockmap
            // unlink from block map
            if (thing.bnext != null)
                ((mobj_t) thing.bnext).bprev = thing.bprev;

            if (thing.bprev != null)
                ((mobj_t) thing.bprev).bnext = thing.bnext;
            else {
                blockx = LL.getSafeBlockX(thing.x - LL.bmaporgx);
                blocky = LL.getSafeBlockY(thing.y - LL.bmaporgy);
                
                if (blockx >= 0 && blockx < LL.bmapwidth && blocky >= 0
                        && blocky < LL.bmapheight) {
                    LL.blocklinks[blocky * LL.bmapwidth + blockx] =
                        (mobj_t) thing.bnext;
                }
            }
        }
    }

    //
    // BLOCK MAP ITERATORS
    // For each line/thing in the given mapblock,
    // call the passed PIT_* function.
    // If the function returns false,
    // exit with false without checking anything else.
    //

    //
    // INTERCEPT ROUTINES
    //
    protected intercept_t[] intercepts;

    int intercept_p;

    public divline_t trace=new divline_t();

    boolean earlyout;

    int ptflags;
    
    //
    //P_TraverseIntercepts
    //Returns true if the traverser function returns true
    //for all lines.
    //
    boolean
    TraverseIntercepts
    ( PTR_InterceptFunc   func,
    int   maxfrac )
    {
     int         count;
     int     dist; //fixed_t
     intercept_t    in=null;  // shut up compiler warning
     
     count = intercept_p;

     while (count-->0)
     {
     dist = MAXINT;
     for (int scan = 0 ; scan<intercept_p ; scan++)
     {
         if (intercepts[scan].frac < dist)
         {
         dist = intercepts[scan].frac;
         in = intercepts[scan];
         }
     }
     
     if (dist > maxfrac)
         return true;    // checked everything in range      

    /*  // UNUSED
     {
     // don't check these yet, there may be others inserted
     in = scan = intercepts;
     for ( scan = intercepts ; scan<intercept_p ; scan++)
         if (scan.frac > maxfrac)
         *in++ = *scan;
     intercept_p = in;
     return false;
     }
    */

         if ( !func.invoke(in) )
         return false;   // don't bother going farther

     in.frac = MAXINT;
     }
     
     return true;        // everything was traversed
    }
    
    protected void UpdateThinker(thinker_t thinker)
    {
      thinker_t th;
      // find the class the thinker belongs to

      th_class cls =
        thinker.function == think_t.NOP ? th_class.th_delete :
        thinker.function == think_t.P_MobjThinker &&
        ((mobj_t) thinker).health > 0 &&
        (eval((((mobj_t) thinker).flags) & MF_COUNTKILL) ||
         ((mobj_t) thinker).type == mobjtype_t.MT_SKULL) ?
        eval((((mobj_t) thinker).flags) & MF_FRIEND) ?
        th_class.th_friends : th_class.th_enemies : th_class.th_misc;

      {
        /* Remove from current thread, if in one */
        if ((th = thinker.cnext)!= null)
          (th.cprev = thinker.cprev).cnext = th;
      }

      // Add to appropriate thread
      th = thinkerclasscap[cls.ordinal()];
      th.cprev.cnext = thinker;
      thinker.cnext = th;
      thinker.cprev = th.cprev;
      th.cprev = thinker;
    }
    
    protected final thinker_t[] thinkerclasscap=new thinker_t[th_class.NUMTHCLASS];

    public boolean sight_debug;
    
    protected final void ResizeIntercepts() {
        intercepts=C2JUtils.resize(intercepts[0],intercepts,intercepts.length*2);
    	}    

    class Lights {

        //
        // Start strobing lights (usually from a trigger)
        //
        void StartLightStrobing(line_t line) {
            int secnum;
            sector_t sec;

            secnum = -1;
            while ((secnum = FindSectorFromLineTag(line, secnum)) >= 0) {
                sec = LL.sectors[secnum];
                if (sec.specialdata != null)
                    continue;

                sec.SpawnStrobeFlash(SLOWDARK, 0);
            }
        }

        //
        // TURN LINE'S TAG LIGHTS OFF
        //
        void TurnTagLightsOff(line_t line) {
            int i;
            int min;
            sector_t sector;
            sector_t tsec;
            line_t templine;

            for (int j = 0; j < LL.numsectors; j++) {
                sector = LL.sectors[j];
                if (sector.tag == line.tag) {

                    min = sector.lightlevel;
                    for (i = 0; i < sector.linecount; i++) {
                        templine = sector.lines[i];
                        tsec = templine.getNextSector(sector);
                        if (tsec == null)
                            continue;
                        if (tsec.lightlevel < min)
                            min = tsec.lightlevel;
                    }
                    sector.lightlevel = (short) min;
                }
            }
        }

        //
        // TURN LINE'S TAG LIGHTS ON
        //
        void LightTurnOn(line_t line, int bright) {

            sector_t sector;
            sector_t temp;
            line_t templine;

            for (int i = 0; i < LL.numsectors; i++) {
                sector = LL.sectors[i];
                if (sector.tag == line.tag) {
                    // bright = 0 means to search
                    // for highest light level
                    // surrounding sector
                    if (bright == 0) {
                        for (int j = 0; j < sector.linecount; j++) {
                            templine = sector.lines[j];
                            temp = templine.getNextSector( sector);

                            if (temp == null)
                                continue;

                            if (temp.lightlevel > bright)
                                bright = temp.lightlevel;
                        }
                    }
                    sector.lightlevel = (short) bright;
                }
            }
        }

    }

    class Enemies {

        // void A_Fall (mobj_t *actor);

        //
        // ENEMY THINKING
        // Enemies are allways spawned
        // with targetplayer = -1, threshold = 0
        // Most monsters are spawned unaware of all players,
        // but some can be made preaware
        //

        /**
         * P_CheckMeleeRange
         */

        boolean CheckMeleeRange(mobj_t actor) {
            mobj_t pl;
            int dist; // fixed_t

            if (actor.target == null)
                return false;

            pl = actor.target;
            dist = AproxDistance(pl.x - actor.x, pl.y - actor.y);

            if (dist >= MELEERANGE - 20 * FRACUNIT + pl.info.radius)
                return false;

            if (!EN.CheckSight(actor, actor.target))
                return false;

            return true;
        }

        /**
         * P_CheckMissileRange
         */

        boolean CheckMissileRange(mobj_t actor) {
            int dist; // fixed_t

            if (!CheckSight(actor, actor.target))
                return false;

            if ((actor.flags & MF_JUSTHIT) != 0) {
                // the target just hit the enemy,
                // so fight back!
                actor.flags &= ~MF_JUSTHIT;
                return true;
            }

            if (actor.reactiontime != 0)
                return false; // do not attack yet

            // OPTIMIZE: get this from a global checksight
            dist =
                AproxDistance(actor.x - actor.target.x, actor.y
                        - actor.target.y)
                        - 64 * FRACUNIT;

            // [SYNC}: Major desync cause of desyncs.
            // DO NOT compare with null!
            if (actor.info.meleestate == statenum_t.S_NULL)
                dist -= 128 * FRACUNIT; // no melee attack, so fire more

            dist >>= 16;

            if (actor.type == mobjtype_t.MT_VILE) {
                if (dist > 14 * 64)
                    return false; // too far away
            }

            if (actor.type == mobjtype_t.MT_UNDEAD) {
                if (dist < 196)
                    return false; // close for fist attack
                dist >>= 1;
            }

            if (actor.type == mobjtype_t.MT_CYBORG
                    || actor.type == mobjtype_t.MT_SPIDER
                    || actor.type == mobjtype_t.MT_SKULL) {
                dist >>= 1;
            }

            if (dist > 200)
                dist = 200;

            if (actor.type == mobjtype_t.MT_CYBORG && dist > 160)
                dist = 160;

            if (RND.P_Random() < dist)
                return false;

            return true;
        }

        /**
         * P_CheckSight Returns true if a straight line between t1 and t2 is
         * unobstructed. Uses REJECT.
         */

        boolean CheckSight(mobj_t t1, mobj_t t2) {
            int s1;
            int s2;
            int pnum;
            int bytenum;
            int bitnum;

            // First check for trivial rejection.

            // Determine subsector entries in REJECT table.
            s1 = t1.subsector.sector.id; // (t1.subsector.sector - sectors);
            s2 = t2.subsector.sector.id;// - sectors);
            pnum = s1 * LL.numsectors + s2;
            bytenum = pnum >> 3;
            bitnum = 1 << (pnum & 7);

            // Check in REJECT table.
            if (eval(LL.rejectmatrix[bytenum]& bitnum)) {
                See.sightcounts[0]++;

                // can't possibly be connected
                return false;
            }

            // An unobstructed LOS is possible.
            // Now look from eyes of t1 to any part of t2.
            See.sightcounts[1]++;

            R.increaseValidCount(1);

            See.sightzstart = t1.z + t1.height - (t1.height >> 2);
            topslope = (t2.z + t2.height) - See.sightzstart;
            bottomslope = (t2.z) - See.sightzstart;

            See.strace.x = t1.x;
            See.strace.y = t1.y;
            See.t2x = t2.x;
            See.t2y = t2.y;
            See.strace.dx = t2.x - t1.x;
            See.strace.dy = t2.y - t1.y;

            // the head node is the last node output
            return See.CrossBSPNode(LL.numnodes - 1);
        }

        //
        // Called by P_NoiseAlert.
        // Recursively traverse adjacent sectors,
        // sound blocking lines cut off traversal.
        //

        mobj_t soundtarget;

        private void RecursiveSound(sector_t sec, int soundblocks) {
            int i;
            line_t check;
            sector_t other;

            // wake up all monsters in this sector
            if (sec.validcount == R.getValidCount()
                    && sec.soundtraversed <= soundblocks + 1) {
                return; // already flooded
            }

            sec.validcount = R.getValidCount();
            sec.soundtraversed = soundblocks + 1;
            sec.soundtarget = soundtarget;

            // "peg" to the level loader for syntactic sugar
            side_t[] sides = LL.sides;

            for (i = 0; i < sec.linecount; i++) {
                check = sec.lines[i];
                if ((check.flags & ML_TWOSIDED) == 0)
                    continue;

                LineOpening(check);

                if (openrange <= 0)
                    continue; // closed door

                if (sides[check.sidenum[0]].sector == sec)
                    other = sides[check.sidenum[1]].sector;
                else
                    other = sides[check.sidenum[0]].sector;

                if ((check.flags & ML_SOUNDBLOCK) != 0) {
                    if (soundblocks == 0)
                        RecursiveSound(other, 1);
                } else
                    RecursiveSound(other, soundblocks);
            }
        }

        /**
         * P_NoiseAlert
         * If a monster yells at a player,
         * it will alert other monsters to the player.
         */
        
        void NoiseAlert(mobj_t target, mobj_t emmiter) {
            soundtarget = target;
            R.increaseValidCount(1);
            RecursiveSound(emmiter.subsector.sector, 0);
        }

        /**
         * P_FireWeapon. Originally in pspr
         */
        public void FireWeapon(player_t player) {
            statenum_t newstate;

            if (!player.CheckAmmo())
                return;

            player.mo.SetMobjState(statenum_t.S_PLAY_ATK1);
            newstate = weaponinfo[player.readyweapon.ordinal()].atkstate;
            player.SetPsprite(player_t.ps_weapon, newstate);
            NoiseAlert(player.mo, player.mo);
        }

        //
        // P_Move
        // Move in the current direction,
        // returns false if the move is blocked.
        //

        // Peg to map movement
        line_t[] spechitp = new line_t[MAXSPECIALCROSS];

        int numspechit;

        /**
         * P_LookForPlayers If allaround is false, only look 180 degrees in
         * front. Returns true if a player is targeted.
         */

        boolean LookForPlayers(mobj_t actor, boolean allaround) {
            int c;
            int stop;
            player_t player;
            // sector_t sector;
            long an; // angle
            int dist; // fixed

            // sector = actor.subsector.sector;

            c = 0;
            stop = (actor.lastlook - 1) & 3;

            for (;; actor.lastlook = (actor.lastlook + 1) & 3) {
                if (!DM.playeringame[actor.lastlook])
                    continue;

                if (c++ == 2 || actor.lastlook == stop) {
                    // done looking
                    return false;
                }

                player = DM.players[actor.lastlook];

                if (player.health[0] <= 0)
                    continue; // dead

                if (!CheckSight(actor, player.mo))
                    continue; // out of sight

                if (!allaround) {
                    an =
                        (R.PointToAngle2(actor.x, actor.y, player.mo.x,
                            player.mo.y)
                                - actor.angle)&BITS32;

                    if (an > ANG90 && an < ANG270) {
                        dist =
                            AproxDistance(player.mo.x - actor.x, player.mo.y
                                    - actor.y);
                        // if real close, react anyway
                        if (dist > MELEERANGE)
                            continue; // behind back
                    }
                }

                actor.target = player.mo;
                return true;
            }
            // The compiler complains that this is unreachable
            // return false;
        }

    }

    class Plats {

        public Plats() {
        	initActivePlats();
        }

        plat_t[] activeplats;

        //
        // Do Platforms
        // "amount" is only used for SOME platforms.
        //
        boolean DoPlat(line_t line, plattype_e type, int amount) {
            plat_t plat;
            int secnum = -1;
            boolean rtn = false;
            sector_t sec;

            // Activate all <type> plats that are in_stasis
            switch (type) {
            case perpetualRaise:
                ActivateInStasis(line.tag);
                break;

            default:
                break;
            }

            while ((secnum = FindSectorFromLineTag(line, secnum)) >= 0) {
                sec = LL.sectors[secnum];

                if (sec.specialdata != null)
                    continue;

                // Find lowest & highest floors around sector
                rtn = true;
                plat = new plat_t();


                plat.type = type;
                plat.sector = sec;
                plat.sector.specialdata = plat;
                plat.function = think_t.T_PlatRaise;
                AddThinker(plat);
                plat.crush = false;
                plat.tag = line.tag;

                switch (type) {
                case raiseToNearestAndChange:
                    plat.speed = PLATSPEED / 2;
                    sec.floorpic = LL.sides[line.sidenum[0]].sector.floorpic;
                    plat.high = sec.FindNextHighestFloor(sec.floorheight);
                    plat.wait = 0;
                    plat.status = plat_e.up;
                    // NO MORE DAMAGE, IF APPLICABLE
                    sec.special = 0;

                    S.StartSound(sec.soundorg,sfxenum_t.sfx_stnmov);
                    break;

                case raiseAndChange:
                    plat.speed = PLATSPEED / 2;
                    sec.floorpic = LL.sides[line.sidenum[0]].sector.floorpic;
                    plat.high = sec.floorheight + amount * FRACUNIT;
                    plat.wait = 0;
                    plat.status = plat_e.up;

                    S.StartSound(sec.soundorg,sfxenum_t.sfx_stnmov);
                    break;

                case downWaitUpStay:
                    plat.speed = PLATSPEED * 4;
                    plat.low = sec.FindLowestFloorSurrounding();

                    if (plat.low > sec.floorheight)
                        plat.low = sec.floorheight;

                    plat.high = sec.floorheight;
                    plat.wait = 35 * PLATWAIT;
                    plat.status = plat_e.down;
                    S.StartSound(sec.soundorg,sfxenum_t.sfx_pstart);
                    break;

                case blazeDWUS:
                    plat.speed = PLATSPEED * 8;
                    plat.low = sec.FindLowestFloorSurrounding();

                    if (plat.low > sec.floorheight)
                        plat.low = sec.floorheight;

                    plat.high = sec.floorheight;
                    plat.wait = 35 * PLATWAIT;
                    plat.status = plat_e.down;
                    S.StartSound(sec.soundorg,sfxenum_t.sfx_pstart);
                    break;

                case perpetualRaise:
                    plat.speed = PLATSPEED;
                    plat.low = sec.FindLowestFloorSurrounding();

                    if (plat.low > sec.floorheight)
                        plat.low = sec.floorheight;

                    plat.high = sec.FindHighestFloorSurrounding();

                    if (plat.high < sec.floorheight)
                        plat.high = sec.floorheight;

                    plat.wait = 35 * PLATWAIT;
                    // Guaranteed to be 0 or 1.
                    plat.status = plat_e.values()[RND.P_Random() & 1];

                    S.StartSound(sec.soundorg,sfxenum_t.sfx_pstart);;
                    break;
                }
                AddActivePlat(plat);
            }
            return rtn;
        }

        void ActivateInStasis(int tag) {
            int i;

            for (i = 0; i < activeplats.length; i++)
                if ((activeplats[i] != null) && (activeplats[i].tag == tag)
                        && (activeplats[i].status == plat_e.in_stasis)) {
                    (activeplats[i]).status = (activeplats[i]).oldstatus;
                    (activeplats[i]).function = think_t.T_PlatRaise;
                    FUNS.doWireThinker(activeplats[i]);
                }
        }

        void StopPlat(line_t line) {
            int j;

            for (j = 0; j < activeplats.length; j++)
                if ((activeplats[j] != null)
                        && (activeplats[j].status != plat_e.in_stasis)
                        && (activeplats[j].tag == line.tag)) {
                    (activeplats[j]).oldstatus = (activeplats[j]).status;
                    (activeplats[j]).status = plat_e.in_stasis;
                    (activeplats[j]).function = null;
                    FUNS.doWireThinker(activeplats[j]);
                }
        }

        
        /*
        void AddActivePlat(plat_t plat) {
            int i;

            for (i = 0; i < MAXPLATS; i++)
                if (activeplats[i] == null) {
                    activeplats[i] = plat;
                    return;
                }
            I.Error("P_AddActivePlat: no more plats!");
        } */

        void RemoveActivePlat(plat_t plat) {
            int i;
            for (i = 0; i < activeplats.length; i++)
                if (plat == activeplats[i]) {
                    (activeplats[i]).sector.specialdata = null;
                    RemoveThinker(activeplats[i]);
                    activeplats[i] = null;

                    return;
                }
            I.Error("P_RemoveActivePlat: can't find plat!");
        }

		public void initActivePlats() {
			// activeplats is just a placeholder. Plat objects aren't
			// actually reused, so we don't need an initialized array.
			// Same rule when resizing.
			activeplats=new plat_t[data.Limits.MAXPLATS];
		}

    }

    class Sight {
        
        public Sight(){
            strace=new divline_t();
            sightcounts= new int[2];
        }
        
        int sightzstart; // eye z of looker

        divline_t strace; // from t1 to t2

        int t2x;

        int t2y;

        int[] sightcounts ;

        

        /**
         * P_CrossSubsector Returns true if strace crosses the given subsector
         * successfully.
         */

        boolean CrossSubsector(int num) {
            int seg; // pointer inside segs
            line_t line;
            int s1;
            int s2;
            int count;
            subsector_t sub;
            sector_t front;
            sector_t back;
            int opentop; // fixed_t
            int openbottom;
            divline_t divl = new divline_t();
            //vertex_t v1;
            //vertex_t v2;
            int frac; // fixed_t
            int slope;

            if (RANGECHECK) {
                if (num >= LL.numsubsectors)
                    I.Error("P_CrossSubsector: ss %d with numss = %d",
                        num, LL.numsubsectors);
            }

            sub = LL.subsectors[num];

            // check lines
            count = sub.numlines;
            seg = sub.firstline;// LL.segs[sub.firstline];

            for (; count > 0; seg++, count--) {
                line = LL.segs[seg].linedef;

                // allready checked other side?
                if (line.validcount == R.getValidCount())
                    continue;

                line.validcount = R.getValidCount();

                //v1 = line.v1;
                //v2 = line.v2;
                s1 = strace.DivlineSide(line.v1x, line.v1y);
                s2 = strace.DivlineSide(line.v2x, line.v2y);

                // line isn't crossed?
                if (s1 == s2)
                    continue;

                divl.x = line.v1x;
                divl.y = line.v1y;
                divl.dx = line.v2x - line.v1x;
                divl.dy = line.v2y - line.v1y;
                s1 = divl.DivlineSide(strace.x, strace.y);
                s2 = divl.DivlineSide(t2x, t2y);

                // line isn't crossed?
                if (s1 == s2)
                    continue;

                // stop because it is not two sided anyway
                // might do this after updating validcount?
                if (!flags(line.flags,ML_TWOSIDED))
                    return false;

                // crosses a two sided line
                front = LL.segs[seg].frontsector;
                back = LL.segs[seg].backsector;

                // no wall to block sight with?
                if (front.floorheight == back.floorheight
                        && front.ceilingheight == back.ceilingheight)
                    continue;

                // possible occluder
                // because of ceiling height differences
                if (front.ceilingheight < back.ceilingheight)
                    opentop = front.ceilingheight;
                else
                    opentop = back.ceilingheight;

                // because of ceiling height differences
                if (front.floorheight > back.floorheight)
                    openbottom = front.floorheight;
                else
                    openbottom = back.floorheight;

                // quick test for totally closed doors
                if (openbottom >= opentop)
                    return false; // stop

                frac = MapUtils.P_InterceptVector(strace, divl);

                if (front.floorheight != back.floorheight) {
                    slope = FixedDiv(openbottom - sightzstart, frac);
                    if (slope > bottomslope)
                        bottomslope = slope;
                }

                if (front.ceilingheight != back.ceilingheight) {
                    slope = FixedDiv(opentop - sightzstart, frac);
                    if (slope < topslope)
                        topslope = slope;
                }

                if (topslope <= bottomslope)
                    return false; // stop
            }
            // passed the subsector ok
            return true;
        }

        /**
         * P_CrossBSPNode Returns true if strace crosses the given node
         * successfully.
         */

        boolean CrossBSPNode(int bspnum) {
            node_t bsp;
            int side;

            if (eval(bspnum& NF_SUBSECTOR)) {
                if (bspnum == -1)
                    return CrossSubsector(0);
                else
                    return CrossSubsector(bspnum & (~NF_SUBSECTOR));
            }

            bsp = LL.nodes[bspnum];

            // decide which side the start point is on
            side = bsp.DivlineSide(strace.x, strace.y);
            if (side == 2)
                side = 0; // an "on" should cross both sides

            // cross the starting side
            if (!CrossBSPNode(bsp.children[side]))
                return false;

            // the partition plane is crossed here
            if (side == bsp.DivlineSide(t2x, t2y)) {
                // the line doesn't touch the other side
                return true;
            }

            // cross the ending side
            return CrossBSPNode(bsp.children[side ^ 1]);
        }

    }

    //
    // P_InitPicAnims
    //

    /**
     * Floor/ceiling animation sequences, defined by first and last frame, i.e.
     * the flat (64x64 tile) name to be used. The full animation sequence is
     * given using all the flats between the start and end entry, in the order
     * found in the WAD file.
     */

    private final animdef_t[] animdefs =
        {
                new animdef_t(false, "NUKAGE3", "NUKAGE1", 8),
                new animdef_t(false, "FWATER4", "FWATER1", 8),
                new animdef_t(false, "SWATER4", "SWATER1", 8),
                new animdef_t(false, "LAVA4", "LAVA1", 8),
                new animdef_t(false, "BLOOD3", "BLOOD1", 8),

                // DOOM II flat animations.
                new animdef_t(false, "RROCK08", "RROCK05", 8),
                new animdef_t(false, "SLIME04", "SLIME01", 8),
                new animdef_t(false, "SLIME08", "SLIME05", 8),
                new animdef_t(false, "SLIME12", "SLIME09", 8),

                new animdef_t(true, "BLODGR4", "BLODGR1", 8),
                new animdef_t(true, "SLADRIP3", "SLADRIP1", 8),

                new animdef_t(true, "BLODRIP4", "BLODRIP1", 8),
                new animdef_t(true, "FIREWALL", "FIREWALA", 8),
                new animdef_t(true, "GSTFONT3", "GSTFONT1", 8),
                new animdef_t(true, "FIRELAVA", "FIRELAV3", 8),
                new animdef_t(true, "FIREMAG3", "FIREMAG1", 8),
                new animdef_t(true, "FIREBLU2", "FIREBLU1", 8),
                new animdef_t(true, "ROCKRED3", "ROCKRED1", 8),

                new animdef_t(true, "BFALL4", "BFALL1", 8),
                new animdef_t(true, "SFALL4", "SFALL1", 8),
                new animdef_t(true, "WFALL4", "WFALL1", 8),
                new animdef_t(true, "DBRAIN4", "DBRAIN1", 8)
        };
                // MAES: this was a cheap trick to mark the end of the sequence
                // with a value of "-1".
                // It won't work in Java, so just use animdefs.length-1
                // new animdef_t(false, "", "", 0) };

    

    //
    // SPECIAL SPAWNING
    //

    class Specials {
        public static final int ok = 0, crushed = 1, pastdest = 2;

        protected line_t[] linespeciallist = new line_t[Limits.MAXLINEANIMS];
        public short numlinespecials;
        
        /**
         * These are NOT the same anims found in defines. Dunno why they fucked up
         * this one so badly. Even the type has the same name, but is entirely
         * different. No way they could be overlapped/unionized either. So WTF.
         * Really. WTF.
         */
        public anim_t[] anims = new anim_t[MAXANIMS];

        // MAES: was a pointer
        public int lastanim;

        //
        // P_UpdateSpecials
        // Animate planes, scroll walls, etc.
        //
        boolean levelTimer;

        int levelTimeCount;

        public void UpdateSpecials() {
            int pic;
            line_t line;
            anim_t anim;

            // LEVEL TIMER
            if (levelTimer == true) {
                levelTimeCount--;
                if (levelTimeCount == 0)
                    DG.ExitLevel();
            }

            // ANIMATE FLATS AND TEXTURES GLOBALLY

            for (int j = 0; j < lastanim; j++) {
                anim = anims[j];
                
                for (int i = anim.basepic; i < anim.basepic + anim.numpics; i++) {
                    pic =
                        anim.basepic
                                + ((DM.leveltime / anim.speed + i) % anim.numpics);
                    if (anim.istexture)
                        TM.setTextureTranslation(i,pic);
                    else
                        TM.setFlatTranslation(i,pic);
                }
            }

            // ANIMATE LINE SPECIALS
            for (int i = 0; i < numlinespecials; i++) {
                line = linespeciallist[i];
                switch (line.special) {
                case 48:
                    // EFFECT FIRSTCOL SCROLL +
                    LL.sides[line.sidenum[0]].textureoffset += MAPFRACUNIT;
                    break;
                }
            }

            // DO BUTTONS
            SW.doButtons();
        }

        public void InitPicAnims() {
        	C2JUtils.initArrayOfObjects(anims);
            anim_t lstanim; 
            // Init animation. MAES: sneaky base pointer conversion ;-)
            this.lastanim = 0;
            // MAES: for (i=0 ; animdefs[i].istexture != -1 ; i++)
            for (int i = 0; i<animdefs.length-1; i++) {
            	lstanim= anims[this.lastanim];
                if (animdefs[i].istexture) {
                    // different episode ?
                    if (TM.CheckTextureNumForName(animdefs[i].startname) == -1)
                        continue;
                    // So, if it IS a valid texture, it goes straight into anims.
                    lstanim.picnum = TM.TextureNumForName(animdefs[i].endname);
                    lstanim.basepic = TM.TextureNumForName(animdefs[i].startname);
                } else { // If not a texture, it's a flat.
                    if (W.CheckNumForName(animdefs[i].startname) == -1)
                        continue;
                    System.out.println(animdefs[i]);
                    // Otherwise, lstanim seems to go nowhere :-/
                    lstanim.picnum = TM.FlatNumForName(animdefs[i].endname);
                    lstanim.basepic = TM.FlatNumForName(animdefs[i].startname);
                }

                lstanim.istexture = animdefs[i].istexture;
                lstanim.numpics = lstanim.picnum - lstanim.basepic + 1;

                if (lstanim.numpics < 2)
                    I.Error("P_InitPicAnims: bad cycle from %s to %s",
                        animdefs[i].startname, animdefs[i].endname);

                lstanim.speed = animdefs[i].speed;
                this.lastanim++;
            }
        }

        protected final void resizeLinesSpecialList() {
        	linespeciallist=C2JUtils.resize(linespeciallist[0],linespeciallist,linespeciallist.length*2);
            }   
        
        
    }

    class Switches {

    	public Switches(){
    		switchlist= new int[MAXSWITCHES];
    		initButtonList();
    	}
        public void doButtons() {
        	for (int i = 0; i < buttonlist.length; i++)
                if (eval(buttonlist[i].btimer)) {
                    buttonlist[i].btimer--;
                    if (!eval(buttonlist[i].btimer)) {
                        switch (buttonlist[i].where) {
                        case top:
                            LL.sides[buttonlist[i].line.sidenum[0]].toptexture =
                                (short) buttonlist[i].btexture;
                            break;

                        case middle:
                            LL.sides[buttonlist[i].line.sidenum[0]].midtexture =
                                (short) buttonlist[i].btexture;
                            break;

                        case bottom:
                            LL.sides[buttonlist[i].line.sidenum[0]].bottomtexture =
                                (short) buttonlist[i].btexture;
                            break;
                        }
                        S.StartSound(buttonlist[i].soundorg,sfxenum_t.sfx_swtchn);
                        buttonlist[i].reset();
                    }
                }
			
		}
		//
        // CHANGE THE TEXTURE OF A WALL SWITCH TO ITS OPPOSITE
        //
        switchlist_t[] alphSwitchList =
            {
                    // Doom shareware episode 1 switches
                    new switchlist_t("SW1BRCOM", "SW2BRCOM", 1),
                    new switchlist_t("SW1BRN1", "SW2BRN1", 1),
                    new switchlist_t("SW1BRN2", "SW2BRN2", 1),
                    new switchlist_t("SW1BRNGN", "SW2BRNGN", 1),
                    new switchlist_t("SW1BROWN", "SW2BROWN", 1),
                    new switchlist_t("SW1COMM", "SW2COMM", 1),
                    new switchlist_t("SW1COMP", "SW2COMP", 1),
                    new switchlist_t("SW1DIRT", "SW2DIRT", 1),
                    new switchlist_t("SW1EXIT", "SW2EXIT", 1),
                    new switchlist_t("SW1GRAY", "SW2GRAY", 1),
                    new switchlist_t("SW1GRAY1", "SW2GRAY1", 1),
                    new switchlist_t("SW1METAL", "SW2METAL", 1),
                    new switchlist_t("SW1PIPE", "SW2PIPE", 1),
                    new switchlist_t("SW1SLAD", "SW2SLAD", 1),
                    new switchlist_t("SW1STARG", "SW2STARG", 1),
                    new switchlist_t("SW1STON1", "SW2STON1", 1),
                    new switchlist_t("SW1STON2", "SW2STON2", 1),
                    new switchlist_t("SW1STONE", "SW2STONE", 1),
                    new switchlist_t("SW1STRTN", "SW2STRTN", 1),

                    // Doom registered episodes 2&3 switches
                    new switchlist_t("SW1BLUE", "SW2BLUE", 2),
                    new switchlist_t("SW1CMT", "SW2CMT", 2),
                    new switchlist_t("SW1GARG", "SW2GARG", 2),
                    new switchlist_t("SW1GSTON", "SW2GSTON", 2),
                    new switchlist_t("SW1HOT", "SW2HOT", 2),
                    new switchlist_t("SW1LION", "SW2LION", 2),
                    new switchlist_t("SW1SATYR", "SW2SATYR", 2),
                    new switchlist_t("SW1SKIN", "SW2SKIN", 2),
                    new switchlist_t("SW1VINE", "SW2VINE", 2),
                    new switchlist_t("SW1WOOD", "SW2WOOD", 2),

                    // Doom II switches
                    new switchlist_t("SW1PANEL", "SW2PANEL", 3),
                    new switchlist_t("SW1ROCK", "SW2ROCK", 3),
                    new switchlist_t("SW1MET2", "SW2MET2", 3),
                    new switchlist_t("SW1WDMET", "SW2WDMET", 3),
                    new switchlist_t("SW1BRIK", "SW2BRIK", 3),
                    new switchlist_t("SW1MOD1", "SW2MOD1", 3),
                    new switchlist_t("SW1ZIM", "SW2ZIM", 3),
                    new switchlist_t("SW1STON6", "SW2STON6", 3),
                    new switchlist_t("SW1TEK", "SW2TEK", 3),
                    new switchlist_t("SW1MARB", "SW2MARB", 3),
                    new switchlist_t("SW1SKULL", "SW2SKULL", 3),

                    new switchlist_t("\0", "\0", 0) };

        /** A (runtime generated) list of the KNOWN button types */
        int[] switchlist;

        int numswitches;

        button_t[] buttonlist;
        //
        // P_InitSwitchList
        // Only called at game initialization.
        //
        public void InitSwitchList() {
            int i;
            int index;
            int episode;

            episode = 1;

            // MAES: if this isn't changed Ultimate Doom's switches
            // won't work visually.
            if (DM.isRegistered())
                episode = 2;
            else if (DM.isCommercial())
                episode = 3;

            for (index = 0, i = 0; i < MAXSWITCHES; i++) {
            	
            	if (index>=switchlist.length) {
            		// Remove limit
            		switchlist=Arrays.copyOf(switchlist,switchlist.length>0?switchlist.length*2:8);
            		}
            	// Trickery. Looks for "end of list" marker
            	// Since the list has pairs of switches, the
            	// actual number of distinct switches is index/2
                if (alphSwitchList[i].episode == 0) {
                    numswitches = index / 2;
                    switchlist[index] = -1;
                    break;
                }

                if (alphSwitchList[i].episode <= episode) {
                    /*
                     * // UNUSED - debug? int value; if
                     * (R_CheckTextureNumForName(alphSwitchList[i].name1) < 0) {
                     * system.Error("Can't find switch texture '%s'!",
                     * alphSwitchList[i].name1); continue; } value =
                     * R_TextureNumForName(alphSwitchList[i].name1);
                     */
                    switchlist[index++] =
                        TM.TextureNumForName(alphSwitchList[i].name1);
                    switchlist[index++] =
                        TM.TextureNumForName(alphSwitchList[i].name2);
                }
            }
        }

        //
        // Start a button counting down till it turns off.
        //
        void StartButton(line_t line, bwhere_e w, int texture, int time) {
            int i;

            // See if button is already pressed
            for (i = 0; i < buttonlist.length; i++) {
                if (buttonlist[i].btimer != 0 && buttonlist[i].line == line) {

                    return;
                }
            }
            
            // At this point, it may mean that THE button of that particular
            // line was not active, or simply that there were not enough 
            // buttons in buttonlist to support an additional entry.

            // Search for a free button slot.
            for (i = 0; i < buttonlist.length; i++) {
                if (buttonlist[i].btimer == 0) {
                    buttonlist[i].line = line;
                    buttonlist[i].where = w;
                    buttonlist[i].btexture = texture;
                    buttonlist[i].btimer = time;
                    buttonlist[i].soundorg = line.soundorg;
                    return;
                }
            }
            
            // Extremely rare event, We must be able to push more than MAXBUTTONS buttons
            // in one tic, which can't normally happen except in really pathological maps.
            // In any case, resizing should solve this problem.
            buttonlist=C2JUtils.resize(buttonlist[0], buttonlist, buttonlist.length*2);
            // Try again
            StartButton(line,w,texture,time);
            // I.Error("P_StartButton: no button slots left!");
        }

        //
        // Function that changes wall texture.
        // Tell it if switch is ok to use again (true=yes, it's a button).
        //
        void ChangeSwitchTexture(line_t line, boolean useAgain) {
            int texTop;
            int texMid;
            int texBot;
            int i;
            int sound;

            if (!useAgain)
                line.special = 0;

            texTop = LL.sides[line.sidenum[0]].toptexture;
            texMid = LL.sides[line.sidenum[0]].midtexture;
            texBot = LL.sides[line.sidenum[0]].bottomtexture;

            sound = sfxenum_t.sfx_swtchn.ordinal();

            // EXIT SWITCH?
            if (line.special == 11)
                sound = sfxenum_t.sfx_swtchx.ordinal();

            for (i = 0; i < numswitches * 2; i++) {
                if (switchlist[i] == texTop) {
                    S.StartSound(buttonlist[0].soundorg,sound);
                    LL.sides[line.sidenum[0]].toptexture =
                        (short) switchlist[i ^ 1];

                    if (useAgain)
                        StartButton(line, bwhere_e.top, switchlist[i],
                            BUTTONTIME);

                    return;
                } else {
                    if (switchlist[i] == texMid) {
                    	 S.StartSound(buttonlist[0].soundorg,sound);
                        LL.sides[line.sidenum[0]].midtexture =
                            (short) switchlist[i ^ 1];

                        if (useAgain)
                            StartButton(line, bwhere_e.middle, switchlist[i],
                                BUTTONTIME);

                        return;
                    } else {
                        if (switchlist[i] == texBot) {
                        	 S.StartSound(buttonlist[0].soundorg,sound);
                            LL.sides[line.sidenum[0]].bottomtexture =
                                (short) switchlist[i ^ 1];

                            if (useAgain)
                                StartButton(line, bwhere_e.bottom,
                                    switchlist[i], BUTTONTIME);

                            return;
                        }
                    }
                }
            }
        }
		public void initButtonList() {
			// Unlike plats, buttonlist needs statically allocated and reusable
			// objects. The MAXBUTTONS limit actually applied to buttons PRESSED
			// or ACTIVE at once, not how many there can actually be in a map.
			
    		buttonlist =  C2JUtils.createArrayOfObjects(button_t.class,MAXBUTTONS);
			}

    }

    //
    // MOVEMENT ITERATOR FUNCTIONS
    //

    interface PIT_LineFunction {
        public boolean invoke(line_t ld);
    }

    interface PIT_MobjFunction {
        public boolean invoke(mobj_t thing);
    }
    
    interface PTR_InterceptFunc {
    	public boolean invoke(intercept_t in);
    }

   /* enum PTR {
        SlideTraverse,
        AimTraverse,
        ShootTraverse,
        UseTraverse
    } */

    //////////////// PIT FUNCTION OBJECTS ///////////////////
    
    //
 // PIT_AddLineIntercepts.
 // Looks for lines in the given block
 // that intercept the given trace
 // to add to the intercepts list.
 //
 // A line is crossed if its endpoints
 // are on opposite sides of the trace.
 // Returns true if earlyout and a solid line hit.
 //

 protected class PIT_AddLineIntercepts implements PIT_LineFunction{

 divline_t dl = new divline_t();

 public boolean invoke(line_t ld) {
     boolean s1;
     boolean s2;
     int frac;
     // avoid precision problems with two routines
     if (trace.dx > FRACUNIT * 16 || trace.dy > FRACUNIT * 16
             || trace.dx < -FRACUNIT * 16 || trace.dy < -FRACUNIT * 16) {
         s1 = trace.PointOnDivlineSide(ld.v1x, ld.v1y);
         s2 = trace.PointOnDivlineSide(ld.v2x, ld.v2y);
         //s1 = trace.DivlineSide(ld.v1x, ld.v1.y);
         //s2 = trace.DivlineSide(ld.v2x, ld.v2y);
     } else {
         s1 = ld.PointOnLineSide(trace.x, trace.y);
         s2 = ld.PointOnLineSide(trace.x + trace.dx, trace.y + trace.dy);
         //s1 = new divline_t(ld).DivlineSide(trace.x, trace.y);
         //s2 = new divline_t(ld).DivlineSide(trace.x + trace.dx, trace.y + trace.dy);
     }

     if (s1 == s2)
         return true; // line isn't crossed

     // hit the line
     dl.MakeDivline(ld);
     frac = InterceptVector(trace, dl);

     if (frac < 0)
         return true; // behind source

     // try to early out the check
     if (earlyout && frac < FRACUNIT && ld.backsector == null) {
         return false; // stop checking
     }

     // "create" a new intercept in the static intercept pool.
     if (intercept_p>=intercepts.length){
         ResizeIntercepts();
     }
     
     intercepts[intercept_p].frac = frac;
     intercepts[intercept_p].isaline = true;
     intercepts[intercept_p].line = ld;
     intercept_p++;

     return true; // continue
 	}

 }


 //
 // PIT_AddThingIntercepts
 //

 protected class PIT_AddThingIntercepts implements PIT_MobjFunction{
	 

     // maybe make this a shared instance variable?
     private divline_t dl = new divline_t();
	 
 public boolean invoke(mobj_t thing) {
	 // FIXME: is false ever returned?
     int x1, y1, x2, y2; // fixed_t

     boolean s1, s2;

     boolean tracepositive;


     int frac; // fixed_t

     tracepositive = (trace.dx ^ trace.dy) > 0;

     // check a corner to corner crossection for hit
     if (tracepositive) {
         x1 = thing.x - thing.radius;
         y1 = thing.y + thing.radius;

         x2 = thing.x + thing.radius;
         y2 = thing.y - thing.radius;
     } else {
         x1 = thing.x - thing.radius;
         y1 = thing.y - thing.radius;

         x2 = thing.x + thing.radius;
         y2 = thing.y + thing.radius;
     }

     s1 = trace.PointOnDivlineSide(x1, y1);
     s2 = trace.PointOnDivlineSide(x2, y2);

     if (s1 == s2)
         return true; // line isn't crossed

     dl.x = x1;
     dl.y = y1;
     dl.dx = x2 - x1;
     dl.dy = y2 - y1;

     frac = InterceptVector(trace, dl);

     if (frac < 0)
         return true; // behind source

     // "create" a new intercept in the static intercept pool.
     // FIXME: this is certainly NOT vanilla compatible behavior.
     // TODO: add overflow emulation for this?
     if (intercept_p>=intercepts.length){
         ResizeIntercepts();
     }
     intercepts[intercept_p].frac = frac;
     intercepts[intercept_p].isaline = false;
     intercepts[intercept_p].thing = thing;
     intercept_p++;

     return true; // keep going
 	}
 }

   
    
    
   /////////// BEGIN MAP OBJECT CODE, USE AS BASIC

    /**
     * P_ExplodeMissile
     */
    
    protected void ExplodeMissile(mobj_t mo) {
        mo.momx = mo.momy = mo.momz = 0;

        // MAES 9/5/2011: using mobj code for that.
        mo.SetMobjState(mobjinfo[mo.type.ordinal()].deathstate);

        mo.tics -= RND.P_Random() & 3;

        if (mo.tics < 1)
            mo.tics = 1;

        mo.flags &= ~MF_MISSILE;

        if (mo.info.deathsound != null)
        S.StartSound(mo, mo.info.deathsound);
    }

    //
    // P_RemoveMobj
    //
    mapthing_t[] itemrespawnque = new mapthing_t[ITEMQUESIZE];

    int[] itemrespawntime = new int[ITEMQUESIZE];

    int iquehead;

    int iquetail;

    public void RemoveMobj(mobj_t mobj) {
        if (eval(mobj.flags& MF_SPECIAL) && !eval(mobj.flags& MF_DROPPED)
                && (mobj.type != mobjtype_t.MT_INV)
                && (mobj.type != mobjtype_t.MT_INS)) {
            itemrespawnque[iquehead] = mobj.spawnpoint;
            itemrespawntime[iquehead] = DM.leveltime;
            iquehead = (iquehead + 1) & (ITEMQUESIZE - 1);

            // lose one off the end?
            if (iquehead == iquetail)
                iquetail = (iquetail + 1) & (ITEMQUESIZE - 1);
        }

        // unlink from sector and block lists
        UnsetThingPosition(mobj);
        
        // stop any playing sound
        S.StopSound (mobj);
        
        // free block
        RemoveThinker((thinker_t) mobj);
    }

    // //////////////////////////////// THINKER CODE, GLOBALLY VISIBLE
    // /////////////////

    //
    // THINKERS
    // All thinkers should be allocated by Z_Malloc
    // so they can be operated on uniformly.
    // The actual structures will vary in size,
    // but the first element must be thinker_t.
    //

    /** Both the head and the tail of the thinkers list */
    public thinker_t thinkercap;
    
    /* An approximation of the active number of thinkers */ 
    private int numthinkers;
    

    //
    // P_InitThinkers
    //
    @Override
    public void InitThinkers() {
    	
        // mobjpool.drain();
        
        for (int i=0; i<th_class.NUMTHCLASS; i++)  // killough 8/29/98: initialize threaded lists
            thinkerclasscap[i].cprev = thinkerclasscap[i].cnext = thinkerclasscap[i];
        
    	thinker_t next=thinkercap.next;
    	thinker_t prev=thinkercap.prev;
    	
    	// Unlink the "dangling" thinkers that may still be attached
    	// to the thinkercap. When loading a new level, they DO NOT get unloaded,
    	// wtf...
    	if (next!=null && next!=thinkercap) {
    		//System.err.println("Next link to thinkercap nulled");
    		next.prev=null;
    	}

    	if (prev!=null && prev!=thinkercap) {
    		//System.err.println("Prev link to thinkercap nulled");
    		prev.next=null;
    	}
    	
        thinkercap.next = thinkercap;
        thinkercap.prev = thinkercap;
    }

    /** cph 2002/01/13 - iterator for thinker list
     * WARNING: Do not modify thinkers between calls to this functin
     */
    thinker_t NextThinker(thinker_t th, th_class cl)
    {
      thinker_t top = thinkerclasscap[cl.ordinal()];
      if (th==null) th = top;
      th = cl == th_class.th_all ? th.next : th.cnext;
      return th == top ? null : th;
    }
    
    /**
     * P_AddThinker Adds a new thinker at the end of the list.
     */

    public void AddThinker(thinker_t thinker) {
    	
    	// If something was too weird to be wired before, it will
    	// be wired here for sure, so don't worry about searching 
    	// all of the code.
    	if (thinker.function!=null && (thinker.acp1==null && thinker.acp2==null))
    		FUNS.doWireThinker(thinker);
    	
        thinkercap.prev.next = thinker;
        thinker.next = thinkercap;
        thinker.prev = thinkercap.prev;
        thinkercap.prev = thinker;
        
        // killough 8/29/98: set sentinel pointers, and then add to appropriate list
        thinker.cnext = thinker.cprev = null;
        UpdateThinker(thinker);
        
        // [Maes] seems only used for interpolations
        //newthinkerpresent = true;
        
        numthinkers++;
    }
   
    public void ClearPlatsBeforeLoading(){
        for (int i = 0; i < PEV.activeplats.length; i++) {
                PEV.activeplats[i] = null;
            }
    }
    
    public void AddActivePlat(plat_t plat) {
        int i;

        for (i = 0; i < PEV.activeplats.length; i++)
            if (PEV.activeplats[i] == null) {
                PEV.activeplats[i] = plat;
                return;
            }
        // Uhh... lemme guess. Needs to resize?
        // Resize but leave extra items empty.
        PEV.activeplats=C2JUtils.resizeNoAutoInit(PEV.activeplats,2*PEV.activeplats.length);
        AddActivePlat(plat);
        
        //I.Error("P_AddActivePlat: no more plats!");
    }
        
    // MAES: works, but not worth it.
    // MobjPool mobjpool;
        
    //
    // P_RemoveThinker
    // Deallocation is lazy -- it will not actually be freed
    // until its thinking turn comes up.
    //
    //
    // killough 4/25/98:
    //
    // Instead of marking the function with -1 value cast to a function pointer,
    // set the function to P_RemoveThinkerDelayed(), so that later, it will be
    // removed automatically as part of the thinker process.
    //
    
    public void RemoveThinker(thinker_t thinker) {
        //thinker.function = think_t.NOP;
        // Wire to this special function.
        thinker.function=think_t.NOP;
        thinker.acpss = this.RemoveThinkerDelayed;
        // Remove any type 1 or 2 special functions.
        thinker.acp1 = null;

        thinker.acp2 = null;
        numthinkers--;
        
    }

    //
    // P_AllocateThinker
    // Allocates memory and adds a new thinker at the end of the list.
    //
    public void AllocateThinker(thinker_t thinker) {
        // UNUSED
    }

    public thinker_t getRandomThinker() {    	
    	
    	int pick=(int) (Math.random()*numthinkers);
    	thinker_t th=this.getThinkerCap();
    	
    	for (int i=0;i<pick;i++){
    		th=th.next;
    	}
    	
    	return th;
    }
    
    
    public int getNumThinkers(){
    	return numthinkers;
    	
    }

    //
    // P_Init
    //
    public void Init() {
        SW.InitSwitchList();
        SPECS.InitPicAnims();
        SM.InitSprites(ISpriteManager.doomsprnames);
    }

    /**
     * P_TouchSpecialThing LIKE ROMERO's ASS!!!
     */
    public void TouchSpecialThing(mobj_t special, mobj_t toucher) {
        player_t player;
        int i;
        int delta;// fixed_t
        sfxenum_t sound;

        delta = special.z - toucher.z;

        if (delta > toucher.height || delta < -8 * FRACUNIT) {
            // out of reach
            return;
        }

        sound = sfxenum_t.sfx_itemup;
        player = toucher.player;

        // Dead thing touching.
        // Can happen with a sliding player corpse.
        if (toucher.health <= 0)
            return;

        // Identify by sprite.
        switch (special.sprite) {
        // armor
        case SPR_ARM1:
            if (!player.GiveArmor(1))
                return;
            player.message = GOTARMOR;
            break;

        case SPR_ARM2:
            if (!player.GiveArmor(2))
                return;
            player.message = GOTMEGA;
            break;

        // bonus items
        case SPR_BON1:
            player.health[0]++; // can go over 100%
            if (player.health[0] > 200)
                player.health[0] = 200;
            player.mo.health = player.health[0];
            player.message = GOTHTHBONUS;
            break;

        case SPR_BON2:
            player.armorpoints[0]++; // can go over 100%
            if (player.armorpoints[0] > 200)
                player.armorpoints[0] = 200;
            if (player.armortype == 0)
                player.armortype = 1;
            player.message = GOTARMBONUS;
            break;

        case SPR_SOUL:
            player.health[0] += 100;
            if (player.health[0] > 200)
                player.health[0] = 200;
            player.mo.health = player.health[0];
            player.message = GOTSUPER;
            sound = sfxenum_t.sfx_getpow;
            break;

        case SPR_MEGA:
            if (!DM.isCommercial())
                return;
            player.health[0] = 200;
            player.mo.health = player.health[0];
            player.GiveArmor(2);
            player.message = GOTMSPHERE;
            sound = sfxenum_t.sfx_getpow;
            break;

        // cards
        // leave cards for everyone
        case SPR_BKEY:
            if (!player.cards[card_t.it_bluecard.ordinal()])
                player.message = GOTBLUECARD;
            player.GiveCard(card_t.it_bluecard);
            if (!DM.netgame)
                break;
            return;

        case SPR_YKEY:
            if (!player.cards[card_t.it_yellowcard.ordinal()])
                player.message = GOTYELWCARD;
            player.GiveCard(card_t.it_yellowcard);
            if (!DM.netgame)
                break;
            return;

        case SPR_RKEY:
            if (!player.cards[card_t.it_redcard.ordinal()])
                player.message = GOTREDCARD;
            player.GiveCard(card_t.it_redcard);
            if (!DM.netgame)
                break;
            return;

        case SPR_BSKU:
            if (!player.cards[card_t.it_blueskull.ordinal()])
                player.message = GOTBLUESKUL;
            player.GiveCard(card_t.it_blueskull);
            if (!DM.netgame)
                break;
            return;

        case SPR_YSKU:
            if (!player.cards[card_t.it_yellowskull.ordinal()])
                player.message = GOTYELWSKUL;
            player.GiveCard(card_t.it_yellowskull);
            if (!DM.netgame)
                break;
            return;

        case SPR_RSKU:
            if (!player.cards[card_t.it_redskull.ordinal()])
                player.message = GOTREDSKULL;
            player.GiveCard(card_t.it_redskull);
            if (!DM.netgame)
                break;
            return;

            // medikits, heals
        case SPR_STIM:
            if (!player.GiveBody(10))
                return;
            player.message = GOTSTIM;
            break;

        case SPR_MEDI:
            if (!player.GiveBody(25))
                return;

            if (player.health[0] < 25)
                player.message = GOTMEDINEED;
            else
                player.message = GOTMEDIKIT;
            break;

        // power ups
        case SPR_PINV:
            if (!player.GivePower(pw_invulnerability))
                return;
            player.message = GOTINVUL;
            sound = sfxenum_t.sfx_getpow;
            break;

        case SPR_PSTR:
            if (!player.GivePower(pw_strength))
                return;
            player.message = GOTBERSERK;
            if (player.readyweapon != weapontype_t.wp_fist)
                player.pendingweapon = weapontype_t.wp_fist;
            sound = sfxenum_t.sfx_getpow;
            break;

        case SPR_PINS:
            if (!player.GivePower(pw_invisibility))
                return;
            player.message = GOTINVIS;
            sound = sfxenum_t.sfx_getpow;
            break;

        case SPR_SUIT:
            if (!player.GivePower(pw_ironfeet))
                return;
            player.message = GOTSUIT;
            sound = sfxenum_t.sfx_getpow;
            break;

        case SPR_PMAP:
            if (!player.GivePower(pw_allmap))
                return;
            player.message = GOTMAP;
            sound = sfxenum_t.sfx_getpow;
            break;

        case SPR_PVIS:
            if (!player.GivePower(pw_infrared))
                return;
            player.message = GOTVISOR;
            sound = sfxenum_t.sfx_getpow;
            break;

        // ammo
        case SPR_CLIP:
            if ((special.flags & MF_DROPPED) != 0) {
                if (!player.GiveAmmo(ammotype_t.am_clip, 0))
                    return;
            } else {
                if (!player.GiveAmmo(ammotype_t.am_clip, 1))
                    return;
            }
            player.message = GOTCLIP;
            break;

        case SPR_AMMO:
            if (!player.GiveAmmo(ammotype_t.am_clip, 5))
                return;
            player.message = GOTCLIPBOX;
            break;

        case SPR_ROCK:
            if (!player.GiveAmmo(ammotype_t.am_misl, 1))
                return;
            player.message = GOTROCKET;
            break;

        case SPR_BROK:
            if (!player.GiveAmmo(ammotype_t.am_misl, 5))
                return;
            player.message = GOTROCKBOX;
            break;

        case SPR_CELL:
            if (!player.GiveAmmo(ammotype_t.am_cell, 1))
                return;
            player.message = GOTCELL;
            break;

        case SPR_CELP:
            if (!player.GiveAmmo(ammotype_t.am_cell, 5))
                return;
            player.message = GOTCELLBOX;
            break;

        case SPR_SHEL:
            if (!player.GiveAmmo(ammotype_t.am_shell, 1))
                return;
            player.message = GOTSHELLS;
            break;

        case SPR_SBOX:
            if (!player.GiveAmmo(ammotype_t.am_shell, 5))
                return;
            player.message = GOTSHELLBOX;
            break;

        case SPR_BPAK:
            if (!player.backpack) {
                for (i = 0; i < NUMAMMO; i++)
                    player.maxammo[i] *= 2;
                player.backpack = true;
            }
            for (i = 0; i < NUMAMMO; i++)
                player.GiveAmmo(ammotype_t.values()[i], 1);
            player.message = GOTBACKPACK;
            break;

        // weapons
        case SPR_BFUG:
            if (!player.GiveWeapon(weapontype_t.wp_bfg, false))
                return;
            player.message = GOTBFG9000;
            sound = sfxenum_t.sfx_wpnup;
            break;

        case SPR_MGUN:
            if (!player.GiveWeapon(weapontype_t.wp_chaingun,
                (special.flags & MF_DROPPED) != 0))
                return;
            player.message = GOTCHAINGUN;
            sound = sfxenum_t.sfx_wpnup;
            break;

        case SPR_CSAW:
            if (!player.GiveWeapon(weapontype_t.wp_chainsaw, false))
                return;
            player.message = GOTCHAINSAW;
            sound = sfxenum_t.sfx_wpnup;
            break;

        case SPR_LAUN:
            if (!player.GiveWeapon(weapontype_t.wp_missile, false))
                return;
            player.message = GOTLAUNCHER;
            sound = sfxenum_t.sfx_wpnup;
            break;

        case SPR_PLAS:
            if (!player.GiveWeapon(weapontype_t.wp_plasma, false))
                return;
            player.message = GOTPLASMA;
            sound = sfxenum_t.sfx_wpnup;
            break;

        case SPR_SHOT:
            if (!player.GiveWeapon(weapontype_t.wp_shotgun,
                (special.flags & MF_DROPPED) != 0))
                return;
            player.message = GOTSHOTGUN;
            sound = sfxenum_t.sfx_wpnup;
            break;

        case SPR_SGN2:
            if (!player.GiveWeapon(weapontype_t.wp_supershotgun,
                (special.flags & MF_DROPPED) != 0))
                return;
            player.message = GOTSHOTGUN2;
            sound = sfxenum_t.sfx_wpnup;
            break;

        default:
            I.Error("P_SpecialThing: Unknown gettable thing");
        }

        if ((special.flags & MF_COUNTITEM) != 0)
            player.itemcount++;
        RemoveMobj(special);
        player.bonuscount += player_t.BONUSADD;
        if (player == DM.players[DM.consoleplayer])
        S.StartSound (null, sound);
    }

    
    @Override
    public thinker_t getThinkerCap() {
        return thinkercap;
    }
    
 /**
  * killough 11/98:
  *
  * Make currentthinker external, so that P_RemoveThinkerDelayed
  * can adjust currentthinker when thinkers self-remove.
  */
    
    protected thinker_t currentthinker;
    
    protected final P_RemoveThinkerDelayed RemoveThinkerDelayed; 
    
    public class P_RemoveThinkerDelayed implements ActionTypeSS <thinker_t>{
        
    @Override
    public void invoke(thinker_t thinker) {
        
    	/*
        try {
        System.err.printf("Delete: %s %d<= %s %d => %s %d\n",
            ((mobj_t)thinker.prev).type,((mobj_t)thinker.prev).thingnum,
            ((mobj_t)thinker).type,((mobj_t)thinker).thingnum,
            ((mobj_t)thinker.next).type,((mobj_t)thinker.next).thingnum);
        } catch (ClassCastException e){
            
        } */
        
        // Unlike Boom, if we reach here it gets zapped anyway
        //if (!thinker->references)
        //{
          { /* Remove from main thinker list */
            thinker_t next = thinker.next;
            /* Note that currentthinker is guaranteed to point to us,
             * and since we're freeing our memory, we had better change that. So
             * point it to thinker->prev, so the iterator will correctly move on to
             * thinker->prev->next = thinker->next */
            (next.prev = currentthinker = thinker.prev).next = next;
            //thinker.next=thinker.prev=null;
            try {
           // System.err.printf("Delete: %s %d <==> %s %d\n",
           //     ((mobj_t)currentthinker.prev).type,((mobj_t)currentthinker.prev).thingnum,
           //     ((mobj_t)currentthinker.next).type,((mobj_t)currentthinker.next).thingnum);
            } catch (ClassCastException e){
                
            }
            
          }
          {
            /* Remove from current thinker class list */
            thinker_t th = thinker.cnext;
            (th.cprev = thinker.cprev).cnext = th;
            //thinker.cnext=thinker.cprev=null;
          }
        }
    }
    
} // End unified map
package p;

import static m.fixed_t.*;
import rr.line_t;
import static utils.C2JUtils.eval;
//
// P_MAPUTL
//

public class divline_t {

    /** fixed_t */
     public int x, y, dx, dy;
     

     /**
      *P_PointOnDivlineSide
      *Returns 0 or 1. (false or true)
      *@param x fixed
      *@param y fixed
      *@param divline_t
      */
     public boolean
     PointOnDivlineSide
     ( int   x,
     int   y
     )
     {
    	 
    	 
    	 // Using Killough's version.
    	  return
    	    (dx==0) ? x <= this.x ? dy > 0 : dy < 0 :
    	    (dy==0) ? y <= this.y ? dx < 0 : dx > 0 :
    	    (dy^dx^(x -= this.x)^(y -= this.y)) < 0 ? (dy^x) < 0 :
    	    FixedMul(y>>8, this.dx>>8) >= FixedMul(this.dy>>8, x>>8);
    	    /*
    	    int PUREFUNC P_PointOnDivlineSide(fixed_t x, fixed_t y, const divline_t *line)
    	    {
    	      return
    	        !line->dx ? x <= line->x ? line->dy > 0 : line->dy < 0 :
    	        !line->dy ? y <= line->y ? line->dx < 0 : line->dx > 0 :
    	        (line->dy^line->dx^(x -= line->x)^(y -= line->y)) < 0 ? (line->dy^x) < 0 :
    	        FixedMul(y>>8, line->dx>>8) >= FixedMul(line->dy>>8, x>>8);
    	    }*/
    	    
    /*
      int dx;
      int dy;
      int left;
      int right;
      
      if (this.dx==0)
      {
      if (x <= this.x)
          return this.dy > 0;
      
      return this.dy < 0;
      }
      if (this.dy==0)
      {
      if (y <= this.y)
          return this.dx < 0;

      return this.dx > 0;
      }
      
      dx = (x - this.x);
      dy = (y - this.y);
      
      // try to quickly decide by looking at sign bits
      if ( ((this.dy ^ this.dx ^ dx ^ dy)&0x80000000) !=0)
      {
      if (((this.dy ^ dx) & 0x80000000) !=0)
          return true;       // (left is negative)
      return false;
      }
      
      left = FixedMul ( this.dy>>8, dx>>8 );
      right = FixedMul ( dy>>8 , this.dx>>8 );
      
      if (right < left)
      return false;       // front side
      return true;           // back side
      */
     }



     //
     //P_MakeDivline
     //
     public void
     MakeDivline
     ( line_t   li)
     {
      this.x = li.v1x;
      this.y = li.v1y;
      this.dx = li.dx;
      this.dy = li.dy;
     }

     public divline_t(line_t   li)
     {
      this.x = li.v1x;
      this.y = li.v1y;
      this.dx = li.dx;
      this.dy = li.dy;
     }

     public divline_t() {
		// TODO Auto-generated constructor stub
	}



	/**
 	  * P_DivlineSide
 	  * Returns side 0 (front), 1 (back), or 2 (on).
 	 */
 	public int
 	DivlineSide
 	( int	x,
 	  int	y)
 	{
 	    
 	   int left,right;
 	    // Boom-style code. Da fack.
 	   // [Maes:] it is MUCH more corrent than the linuxdoom one, for whatever reason.
 	    
 	   return
 	  (this.dx==0) ? x == this.x ? 2 : x <= this.x ? eval(this.dy > 0) : eval(this.dy < 0) :
 	  (this.dy==0) ? (olddemo ? x : y) == this.y ? 2 : y <= this.y ? eval(this.dx < 0) : eval(this.dx > 0) :
 	  (this.dy==0) ? y == this.y ? 2 : y <= this.y ? eval(this.dx < 0) : eval(this.dx > 0) :
 	  (right = ((y - this.y) >> FRACBITS) * (this.dx >> FRACBITS)) <
 	  (left  = ((x - this.x) >> FRACBITS) * (this.dy >> FRACBITS)) ? 0 :
 	  right == left ? 2 : 1;
 	  
 	  /*  
 	    
 	    int	left,right,dx,dy;

 	    if (this.dx==0)
 	    {
 	    if (x==this.x)
 	        return 2;
 	    
 	    if (x <= this.x)
 	        return eval(this.dy > 0);

 	    return eval(this.y < 0);
 	    }
 	    
 	    if (this.dy==0)
 	    {
 	    if (x==this.y)
 	        return 2;

 	    if (y <= this.y)
 	        return eval(this.dx < 0);

 	    return eval(this.dx > 0);
 	    }
 	    
 	    dx = (x - this.x);
 	    dy = (y - this.y);

 	    left =  (this.dy>>FRACBITS) * (dx>>FRACBITS);
 	    right = (dy>>FRACBITS) * (this.dx>>FRACBITS);
 	    
 	    if (right < left)
 	    return 0;   // front side
 	    
 	    if (left == right)
 	    return 2;
 	    return 1;       // back side
 	    */
 	}
 	
 	private static final boolean olddemo = true;
     
     
 }

package p;

import java.io.DataInputStream;
import java.io.IOException;
import java.nio.ByteBuffer;

import rr.SectorAction;
import w.DoomIO;
import w.IReadableDoomObject;

public class floormove_t extends SectorAction implements IReadableDoomObject{
    
    public floormove_t(){
        // MAES HACK: floors are implied to be at least of "lowerFloor" type
        // unless set otherwise, due to implicit zero-enum value.
        this.type=floor_e.lowerFloor;
    }
    
    public floor_e type;
    public boolean crush;

    public int     direction;
    public int     newspecial;
    public short   texture;
    /** fixed_t */
    public int floordestheight;
    /** fixed_t */
    public int speed;

    @Override
    public void read(DataInputStream f) throws IOException{

        super.read(f); // Call thinker reader first            
        type=floor_e.values()[DoomIO.readLEInt(f)];
        crush=DoomIO.readIntBoolean(f);
        super.sectorid=DoomIO.readLEInt(f); // Sector index (or pointer?)
        direction=DoomIO.readLEInt(f);
        newspecial=DoomIO.readLEInt(f);
        texture=DoomIO.readLEShort(f);
        floordestheight=DoomIO.readLEInt(f);
        speed=DoomIO.readLEInt(f);        
        }
   
    @Override
    public void pack(ByteBuffer b) throws IOException{
        super.pack(b); //12            
        b.putInt(type.ordinal()); // 16
        b.putInt(crush?1:0); //20
        b.putInt(super.sectorid); // 24
        b.putInt(direction); // 28
        b.putInt(newspecial); // 32
        b.putShort(texture); // 34
        b.putInt(floordestheight); // 38
        b.putInt(speed); // 42
    }
    
}
package p;

import static m.fixed_t.FRACUNIT;
import static m.fixed_t.MAPFRACUNIT;

public final class DoorDefines {
    
    // Doors
    public static final int VDOORSPEED = MAPFRACUNIT * 2;
    public static final int VDOORWAIT = 150;
    
    
    // Lights
    public static final int GLOWSPEED = 5;

    public static final int STROBEBRIGHT = 5;

    public static final int FASTDARK = 15;

    public static final int SLOWDARK = 35;
    
    
}

package p;

public enum plattype_e {
        perpetualRaise,
        downWaitUpStay,
        raiseAndChange,
        raiseToNearestAndChange,
        blazeDWUS

    } 
package p;

public enum stair_e
{
    build8, // slowly build by 8
    turbo16 // quickly build by 16    
} 
package p;

import i.DoomStatusAware;
import i.IDoomSystem;
import doom.DoomStatus;
import doom.think_t;
import rr.TextureManager;
import rr.line_t;
import rr.sector_t;
import utils.C2JUtils;

public class SlideDoor implements DoomStatusAware {
	
		protected TextureManager<?> TM;
	
		protected AbstractLevelLoader LL;
		
		protected DoomStatus<?,?> DM;
		
		protected Actions P;
		
		protected IDoomSystem I;
	
	      // UNUSED
	      // Separate into p_slidoor.c?

	      // ABANDONED TO THE MISTS OF TIME!!!
	      //
	      // EV_SlidingDoor : slide a door horizontally
	      // (animate midtexture, then set noblocking line)
	      //

	      public static final int MAXSLIDEDOORS	=5;
	   // how many frames of animation
	      public static final int SNUMFRAMES	=	4;

	      public static final int SDOORWAIT		=35*3;
	      public static final int SWAITTICS	=	4;
	      
	      final slidename_t[]    slideFrameNames= {
		          new slidename_t("GDOORF1","GDOORF2","GDOORF3","GDOORF4",  // front
		    	           "GDOORB1","GDOORB2","GDOORB3","GDOORB4"), // back
		    	          
		    	          new slidename_t(),new slidename_t(),new slidename_t(),new slidename_t()
		    	      };
	    	  
	      final slideframe_t[] slideFrames;

	      public SlideDoor(){
	    	  slideFrames=new slideframe_t[MAXSLIDEDOORS];
	    	  C2JUtils.initArrayOfObjects(slideFrames);
	    	     
	      }
	      
	      public SlideDoor(DoomStatus<?,?> DS) {
	    	this();
			this.updateStatus(DS);
		}

		void P_InitSlidingDoorFrames()
	      {
	          int        i;
	          int        f1;
	          int        f2;
	          int        f3;
	          int        f4;
	         
	          // DOOM II ONLY...
	          if (!DM.isCommercial())
	         return;
	         
	          for (i = 0;i < MAXSLIDEDOORS; i++)
	          {
	         if (slideFrameNames[i].frontFrame1==null)
	             break;
	                 
	         f1 = TM.TextureNumForName(slideFrameNames[i].frontFrame1);
	         f2 = TM.TextureNumForName(slideFrameNames[i].frontFrame2);
	         f3 = TM.TextureNumForName(slideFrameNames[i].frontFrame3);
	         f4 = TM.TextureNumForName(slideFrameNames[i].frontFrame4);

	         slideFrames[i].frontFrames[0] = f1;
	         slideFrames[i].frontFrames[1] = f2;
	         slideFrames[i].frontFrames[2] = f3;
	         slideFrames[i].frontFrames[3] = f4;
	             
	         f1 = TM.TextureNumForName(slideFrameNames[i].backFrame1);
	         f2 = TM.TextureNumForName(slideFrameNames[i].backFrame2);
	         f3 = TM.TextureNumForName(slideFrameNames[i].backFrame3);
	         f4 = TM.TextureNumForName(slideFrameNames[i].backFrame4);

	         slideFrames[i].backFrames[0] = f1;
	         slideFrames[i].backFrames[1] = f2;
	         slideFrames[i].backFrames[2] = f3;
	         slideFrames[i].backFrames[3] = f4;
	          }
	      }


	      //
	      // Return index into "slideFrames" array
	      // for which door type to use
	      //
	      int P_FindSlidingDoorType(line_t  line)
	      {
	          int        i;
	          int        val;
	         
	          for (i = 0;i < MAXSLIDEDOORS;i++)
	          {
	         val = LL.sides[line.sidenum[0]].midtexture;
	         if (val == slideFrames[i].frontFrames[0])
	             return i;
	          }
	         
	          return -1;
	      }

	     



	     public void
	      EV_SlidingDoor
	      ( line_t  line,
	        mobj_t  thing )
	      {
	          sector_t       sec;
	          slidedoor_t   door;
	         
	          // DOOM II ONLY...
	          if (!DM.isCommercial())
	         return;
	          
	          System.err.println("EV_SlidingDoor");
	          
	          // Make sure door isn't already being animated
	          sec = line.frontsector;
	          door = null;
	          if (sec.specialdata!=null)
	          {
	         if (thing.player==null)
	             return;
	                 
	         door = (slidedoor_t) sec.specialdata;
	         if (door.type == sdt_e.sdt_openAndClose)
	         {
	             if (door.status == sd_e.sd_waiting)
	             door.status = sd_e.sd_closing;
	         }
	         else
	             return;
	          }
	          
	          // Init sliding door vars
	          if (door==null)
	          {
	         door = new slidedoor_t();
	         P.AddThinker (door);
	         sec.specialdata = door;
	             
	         door.type = sdt_e.sdt_openAndClose;
	         door.status = sd_e.sd_opening;
	         door.whichDoorIndex = P_FindSlidingDoorType(line);

	         if (door.whichDoorIndex < 0)
	             I.Error("EV_SlidingDoor: Can't use texture for sliding door!");
	                 
	         door.frontsector = sec;
	         door.backsector = line.backsector;
	         door.function = think_t.T_SlidingDoor;
	         door.timer = SWAITTICS;
	         door.frame = 0;
	         door.line = line;
	          }
	      }

		@Override
		public void updateStatus(DoomStatus<?,?> DS) {
			this.DM=DS;
			this.I=DM.I;
			this.LL=DM.LL;
			this.P=DM.P;
			this.TM=DM.TM;			
		}
	  }

package p;

/** For objects that needed to be memset to 0 in C,
 * rather than being reallocated. */

public interface Resettable {
    public void reset();
}

package p;

public interface ActionType1{
	public void invoke (mobj_t actor);
}

package p;

import java.io.DataInputStream;
import java.io.IOException;
import java.nio.ByteBuffer;

import rr.SectorAction;
import w.DoomIO;
import w.IReadableDoomObject;

public class vldoor_t extends SectorAction implements IReadableDoomObject{
        
        public vldoor_e    type;
        /** fixed_t */
        public int topheight, speed;

        /** 1 = up, 0 = waiting at top, -1 = down */
        public int direction;
        
        /** tics to wait at the top */
        public int             topwait;
        
        /**(keep in case a door going down is reset)
           when it reaches 0, start going down */        
        public int             topcountdown;
        
        @Override
        public void read(DataInputStream f) throws IOException{

            super.read(f); // Call thinker reader first            
            type=vldoor_e.values()[DoomIO.readLEInt(f)];
            super.sectorid=DoomIO.readLEInt(f); // Sector index (or pointer?)
            topheight=DoomIO.readLEInt(f);
            speed=DoomIO.readLEInt(f);
            direction=DoomIO.readLEInt(f);
            topwait=DoomIO.readLEInt(f);
            topcountdown=DoomIO.readLEInt(f);            
            }
        
        @Override
        public void pack(ByteBuffer b) throws IOException{
            super.pack(b); //12            
            b.putInt(type.ordinal()); // 16
            b.putInt(super.sectorid); // 20
            b.putInt(topheight); // 24
            b.putInt(speed); //28
            b.putInt(direction); // 32
            b.putInt(topwait); //36
            b.putInt(topcountdown); //40
        }
        
    }
package p;

import static data.Defines.FLOATSPEED;
import static data.Defines.GRAVITY;
import static data.Defines.VIEWHEIGHT;
import static data.info.states;
import static p.MapUtils.AproxDistance;
import static utils.C2JUtils.pointer;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

import rr.subsector_t;
import s.ISoundOrigin;
import w.IPackableDoomObject;
import w.IReadableDoomObject;
import w.IWritableDoomObject;
import data.Tables;
import data.mapthing_t;
import data.mobjinfo_t;
import data.mobjtype_t;
import data.sounds.sfxenum_t;
import data.spritenum_t;
import data.state_t;
import defines.*;
import doom.player_t;
import doom.thinker_t;

/**
 * 
 * NOTES: mobj_t
 * 
 * mobj_ts are used to tell the refresh where to draw an image, tell the world
 * simulation when objects are contacted, and tell the sound driver how to
 * position a sound.
 * 
 * The refresh uses the next and prev links to follow lists of things in sectors
 * as they are being drawn. The sprite, frame, and angle elements determine
 * which patch_t is used to draw the sprite if it is visible. The sprite and
 * frame values are allmost allways set from state_t structures. The
 * statescr.exe utility generates the states.h and states.c files that contain
 * the sprite/frame numbers from the statescr.txt source file. The xyz origin
 * point represents a point at the bottom middle of the sprite (between the feet
 * of a biped). This is the default origin position for patch_ts grabbed with
 * lumpy.exe. A walking creature will have its z equal to the floor it is
 * standing on.
 * 
 * The sound code uses the x,y, and subsector fields to do stereo positioning of
 * any sound effited by the mobj_t.
 * 
 * The play simulation uses the blocklinks, x,y,z, radius, height to determine
 * when mobj_ts are touching each other, touching lines in the map, or hit by
 * trace lines (gunshots, lines of sight, etc). The mobj_t->flags element has
 * various bit flags used by the simulation.
 * 
 * Every mobj_t is linked into a single sector based on its origin coordinates.
 * The subsector_t is found with R_PointInSubsector(x,y), and the sector_t can
 * be found with subsector->sector. The sector links are only used by the
 * rendering code, the play simulation does not care about them at all.
 * 
 * Any mobj_t that needs to be acted upon by something else in the play world
 * (block movement, be shot, etc) will also need to be linked into the blockmap.
 * If the thing has the MF_NOBLOCK flag set, it will not use the block links. It
 * can still interact with other things, but only as the instigator (missiles
 * will run into other things, but nothing can run into a missile). Each block
 * in the grid is 128*128 units, and knows about every line_t that it contains a
 * piece of, and every interactable mobj_t that has its origin contained.
 * 
 * A valid mobj_t is a mobj_t that has the proper subsector_t filled in for its
 * xy coordinates and is linked into the sector from which the subsector was
 * made, or has the MF_NOSECTOR flag set (the subsector_t needs to be valid even
 * if MF_NOSECTOR is set), and is linked into a blockmap block or has the
 * MF_NOBLOCKMAP flag set. Links should only be modified by the
 * P_[Un]SetThingPosition() functions. Do not change the MF_NO? flags while a
 * thing is valid.
 * 
 * Any questions?
 * 
 * @author admin
 * 
 */

public class mobj_t extends thinker_t implements ISoundOrigin, Interceptable,
		IWritableDoomObject, IPackableDoomObject, IReadableDoomObject {

	Actions A;

	public mobj_t() {
		this.spawnpoint = new mapthing_t();
	}

	public mobj_t(Actions A) {
		this();
		this.A = A;
		// A mobj_t is ALSO a thinker, as it always contains the struct.
		// Don't fall for C's trickery ;-)
		// this.thinker=new thinker_t();
	}

	/* List: thinker links. */
	// public thinker_t thinker;

	/** (fixed_t) Info for drawing: position. */
	public int x, y, z;

	/** More list: links in sector (if needed) */
	public thinker_t snext, sprev;

	// More drawing info: to determine current sprite.
	/**
	 * orientation. This needs to be long or else certain checks will fail...but
	 * I need to see it working in order to confirm
	 */
	public long angle;

	/** used to find patch_t and flip value */
	public spritenum_t sprite;
	/** might be ORed with FF_FULLBRIGHT */
	public int frame;

	/** Interaction info, by BLOCKMAP. Links in blocks (if needed). */
	public thinker_t bnext, bprev;

	/** MAES: was actually a pointer to a struct subsector_s */
	public subsector_t subsector;

	/** (fixed_t) The closest interval over all contacted Sectors. */
	public int floorz, ceilingz;

	/** (fixed_t) For movement checking. */
	public int radius, height;

	/** (fixed_t) Momentums, used to update position. */
	public int momx, momy, momz;

	/** If == validcount, already checked. */
	public int validcount;

	public mobjtype_t type;
	// MAES: was a pointer
	public mobjinfo_t info; // &mobjinfo[mobj.type]

	public long tics; // state tic counter
	// MAES: was a pointer
	public state_t state;
	public long flags;
	public int health;

	/** Movement direction, movement generation (zig-zagging). */
	public int movedir; // 0-7
	public int movecount; // when 0, select a new dir

	/**
	 * Thing being chased/attacked (or NULL), also the originator for missiles.
	 * MAES: was a pointer
	 */
	public mobj_t target;
	public int p_target; // for savegames

	/**
	 * Reaction time: if non 0, don't attack yet. Used by player to freeze a bit
	 * after teleporting.
	 */
	public int reactiontime;

	/**
	 * If >0, the target will be chased no matter what (even if shot)
	 */
	public int threshold;

	/**
	 * Additional info record for player avatars only. Only valid if type ==
	 * MT_PLAYER struct player_s* player;
	 */

	public player_t player;

	/** Player number last looked for. */
	public int lastlook;

	/** For nightmare respawn. */
	public mapthing_t spawnpoint; // struct

	/** Thing being chased/attacked for tracers. */

	public mobj_t tracer; // MAES: was a pointer

	// // MF_ flags for mobjs.

	// Call P_SpecialThing when touched.
	public static final int MF_SPECIAL = 1;
	// Blocks.
	public static final int MF_SOLID = 2;
	// Can be hit.
	public static final int MF_SHOOTABLE = 4;
	// Don't use the sector links (invisible but touchable).
	public static final int MF_NOSECTOR = 8;
	// Don't use the blocklinks (inert but displayable)
	public static final int MF_NOBLOCKMAP = 16;

	// Not to be activated by sound, deaf monster.
	public static final int MF_AMBUSH = 32;
	// Will try to attack right back.
	public static final int MF_JUSTHIT = 64;
	// Will take at least one step before attacking.
	public static final int MF_JUSTATTACKED = 128;
	// On level spawning (initial position),
	// hang from ceiling instead of stand on floor.
	public static final int MF_SPAWNCEILING = 256;
	// Don't apply gravity (every tic),
	// that is, object will float, keeping current height
	// or changing it actively.
	public static final int MF_NOGRAVITY = 512;

	// Movement flags.
	// This allows jumps from high places.
	public static final int MF_DROPOFF = 0x400;
	// For players, will pick up items.
	public static final int MF_PICKUP = 0x800;
	// Player cheat. ???
	public static final int MF_NOCLIP = 0x1000;
	// Player: keep info about sliding along walls.
	public static final int MF_SLIDE = 0x2000;
	// Allow moves to any height, no gravity.
	// For active floaters, e.g. cacodemons, pain elementals.
	public static final int MF_FLOAT = 0x4000;
	// Don't cross lines
	// ??? or look at heights on teleport.
	public static final int MF_TELEPORT = 0x8000;
	// Don't hit same species, explode on block.
	// Player missiles as well as fireballs of various kinds.
	public static final int MF_MISSILE = 0x10000;
	// Dropped by a demon, not level spawned.
	// E.g. ammo clips dropped by dying former humans.
	public static final int MF_DROPPED = 0x20000;
	// Use fuzzy draw (shadow demons or spectres),
	// temporary player invisibility powerup.
	public static final int MF_SHADOW = 0x40000;
	// Flag: don't bleed when shot (use puff),
	// barrels and shootable furniture shall not bleed.
	public static final int MF_NOBLOOD = 0x80000;
	// Don't stop moving halfway off a step,
	// that is, have dead bodies slide down all the way.
	public static final int MF_CORPSE = 0x100000;
	// Floating to a height for a move, ???
	// don't auto float to target's height.
	public static final int MF_INFLOAT = 0x200000;

	// On kill, count this enemy object
	// towards intermission kill total.
	// Happy gathering.
	public static final int MF_COUNTKILL = 0x400000;

	// On picking up, count this item object
	// towards intermission item total.
	public static final int MF_COUNTITEM = 0x800000;

	// Special handling: skull in flight.
	// Neither a cacodemon nor a missile.
	public static final int MF_SKULLFLY = 0x1000000;

	// Don't spawn this object
	// in death match mode (e.g. key cards).
	public static final int MF_NOTDMATCH = 0x2000000;

	// Player sprites in multiplayer modes are modified
	// using an internal color lookup table for re-indexing.
	// If 0x4 0x8 or 0xc,
	// use a translation table for player colormaps
	public static final int MF_TRANSLATION = 0xc000000;
	// Hmm ???.
	public static final int MF_TRANSSHIFT = 26;

	/*
	 * The following methods were for the most part "contextless" and
	 * instance-specific, so they were implemented here rather that being
	 * scattered all over the package.
	 */

	/**
	 * P_SetMobjState Returns true if the mobj is still present.
	 */

	public boolean SetMobjState(statenum_t state) {
		state_t st;

		do {
			if (state == statenum_t.S_NULL) {
				state = null;
				// MAES/_D_: uncommented this as it should work by now (?).
				A.RemoveMobj(this);
				return false;
			}

			st = states[state.ordinal()];
			this.state = st;
			tics = st.tics;
			sprite = st.sprite;
			frame = (int) st.frame;

			// Modified handling.
			// Call action functions when the state is set

			if (st.acp1 != null) {
				st.acp1.invoke(this);
			}

			state = st.nextstate;
		} while (tics == 0);

		return true;
	}

	/**
	 * P_ZMovement
	 */

	public void ZMovement() {
		// fixed_t
		int dist;
		int delta;

		// check for smooth step up
		if ((player != null) && z < floorz) {
			player.viewheight -= floorz - z;

			player.deltaviewheight = (VIEWHEIGHT - player.viewheight) >> 3;
		}

		// adjust height
		z += momz;

		if (((flags & MF_FLOAT) != 0) && target != null) {
			// float down towards target if too close
			if ((flags & MF_SKULLFLY) == 0 && (flags & MF_INFLOAT) == 0) {
				dist = AproxDistance(x - target.x, y - target.y);

				delta = (target.z + (height >> 1)) - z;

				if (delta < 0 && dist < -(delta * 3))
					z -= FLOATSPEED;
				else if (delta > 0 && dist < (delta * 3))
					z += FLOATSPEED;
			}

		}

		// clip movement
		if (z <= floorz) {
			// hit the floor

			// Note (id):
			// somebody left this after the setting momz to 0,
			// kinda useless there.
			if ((flags & MF_SKULLFLY) != 0) {
				// the skull slammed into something
				momz = -momz;
			}

			if (momz < 0) {
				if (player != null && (momz < -GRAVITY * 8)) {
					// Squat down.
					// Decrease viewheight for a moment
					// after hitting the ground (hard),
					// and utter appropriate sound.
					player.deltaviewheight = momz >> 3;
					A.S.StartSound(this, sfxenum_t.sfx_oof);
				}
				momz = 0;
			}
			z = floorz;

			if ((flags & MF_MISSILE) != 0 && (flags & MF_NOCLIP) == 0) {
				A.ExplodeMissile(this);
				return;
			}
		} else if ((flags & MF_NOGRAVITY) == 0) {
			if (momz == 0)
				momz = -GRAVITY * 2;
			else
				momz -= GRAVITY;
		}

		if (z + height > ceilingz) {
			// hit the ceiling
			if (momz > 0)
				momz = 0;
			{
				z = ceilingz - height;
			}

			if ((flags & MF_SKULLFLY) != 0) { // the skull slammed into
												// something
				momz = -momz;
			}

			if ((flags & MF_MISSILE) != 0 && (flags & MF_NOCLIP) == 0) {
				A.ExplodeMissile(this);
				return;
			}
		}
	}

	public int eflags; // DOOM LEGACY

	// Fields used only during DSG unmarshalling
	public int stateid;
	public int playerid;
	public int p_tracer;

	/** Unique thing id, used during sync debugging */
    public int thingnum;

	public void clear() {
		fastclear.rewind();
		try {
			this.unpack(mobj_t.fastclear);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	// _D_: to permit this object to save/load
	public void read(DataInputStream f) throws IOException {
		// More efficient, avoids duplicating code and
		// handles little endian better.
		buffer.position(0);
		buffer.order(ByteOrder.LITTLE_ENDIAN);
		f.read(buffer.array());
		this.unpack(buffer);
	}

	@Override
	public void write(DataOutputStream f) throws IOException {

		// More efficient, avoids duplicating code and
		// handles little endian better.
		buffer.position(0);
		buffer.order(ByteOrder.LITTLE_ENDIAN);
		this.pack(buffer);
		f.write(buffer.array());

	}

	public void pack(ByteBuffer b) throws IOException {
		b.order(ByteOrder.LITTLE_ENDIAN);
		super.pack(b); // Pack the head thinker.
		b.putInt(x);
		b.putInt(y);
		b.putInt(z);
		b.putInt(pointer(snext));
		b.putInt(pointer(sprev));
		b.putInt((int) (this.angle & Tables.BITS32));
		b.putInt(this.sprite.ordinal());
		b.putInt(this.frame);
		b.putInt(pointer(bnext));
		b.putInt(pointer(bprev));
		b.putInt(pointer(subsector));
		b.putInt(floorz);
		b.putInt(ceilingz);
		b.putInt(radius);
		b.putInt(height);
		b.putInt(momx);
		b.putInt(momy);
		b.putInt(momz);
		b.putInt(validcount);
		b.putInt(type.ordinal());
		b.putInt(pointer(info)); // TODO: mobjinfo
		b.putInt((int) (this.tics & Tables.BITS32));
		b.putInt(this.state.id); // TODO: state OK?
		b.putInt((int) this.flags); // truncate
		b.putInt(this.health);
		b.putInt(this.movedir);
		b.putInt(this.movecount);
		b.putInt(pointer(target)); // TODO: p_target?
		b.putInt(this.reactiontime);
		b.putInt(this.threshold);
		// Check for player.
		if (this.player != null) {
			b.putInt(1 + this.player.identify());

			// System.out.printf("Mobj with hashcode %d is player %d",pointer(this),1+this.player.identify());
		} else
			b.putInt(0);
		b.putInt(lastlook);
		spawnpoint.pack(b);
		b.putInt(pointer(tracer)); // tracer pointer stored.

	}

	public void unpack(ByteBuffer b) throws IOException {
		b.order(ByteOrder.LITTLE_ENDIAN);
		super.unpack(b); // 12 Read the head thinker.
		this.x = b.getInt(); // 16
		this.y = b.getInt(); // 20
		this.z = b.getInt(); // 24
		b.getLong(); // TODO: snext, sprev. When are those set? 32
		this.angle = Tables.BITS32 & b.getInt(); // 36
		this.sprite = spritenum_t.values()[b.getInt()]; // 40
		this.frame = b.getInt(); // 44
		b.getLong(); // TODO: bnext, bprev. When are those set? 52
		b.getInt(); // TODO: subsector 56
		this.floorz = b.getInt(); // 60
		this.ceilingz = b.getInt(); // 64
		this.radius = b.getInt(); // 68
		this.height = b.getInt(); // 72
		this.momx = b.getInt(); // 76
		this.momy = b.getInt(); // 80
		this.momz = b.getInt(); // 84
		this.validcount = b.getInt(); // 88
		this.type = mobjtype_t.values()[b.getInt()]; // 92
		b.getInt(); // TODO: mobjinfo (deduced from type) //96
		this.tics = Tables.BITS32 & b.getInt(); // 100
		// System.out.println("State"+f.readLEInt());
		this.stateid = b.getInt(); // TODO: state OK?
		this.flags = b.getInt()&Tables.BITS32; // Only 32-bit flags can be restored
		this.health = b.getInt();
		this.movedir = b.getInt();
		this.movecount = b.getInt();
		this.p_target = b.getInt();
		this.reactiontime = b.getInt();
		this.threshold = b.getInt();
		this.playerid = b.getInt(); // TODO: player. Non null should mean that
									// it IS a player.
		this.lastlook = b.getInt();
		spawnpoint.unpack(b);
		this.p_tracer = b.getInt(); // TODO: tracer
	}

	private static ByteBuffer buffer = ByteBuffer.allocate(154);
	private static ByteBuffer fastclear = ByteBuffer.allocate(154);

	/*
	 * @Override protected void finalize(){ count++; if (count%100==0)
	 * System.err
	 * .printf("Total %d Mobj %s@%d finalized free memory: %d\n",count,
	 * this.type.name(),this.hashCode(),Runtime.getRuntime().freeMemory()); }
	 */
	protected static int count = 0;

	// TODO: a linked list of sectors where this object appears
	// public msecnode_t touching_sectorlist;

	// Sound origin stuff
	
	@Override
	public final int getX() {
		return x;
	}

	@Override
	public final int getY() {
		return y;
	}

	@Override
	public final int getZ() {
		return z;
	}
	
	public String toString(){
	    return String.format("%s %d",this.type,this.thingnum);
	}

}

package p;

import doom.thinker_t;

public interface ThinkerList {

    void AddThinker(thinker_t thinker);
    void RemoveThinker(thinker_t thinker);
    void InitThinkers();
    int getNumThinkers();
    thinker_t getRandomThinker();
    thinker_t getThinkerCap();
}

package p;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

import w.CacheableDoomObject;
import w.DoomBuffer;
//
// P_SWITCH
//

public class switchlist_t
        implements CacheableDoomObject {
    
    public switchlist_t(){
        
    }
    
    // Were char[9]
    public String name1;

    public String name2;

    public short episode;

    public switchlist_t(String name1, String name2, int episode) {
        super();
        this.name1 = name1;
        this.name2 = name2;
        this.episode = (short) episode;
    }

    @Override
    public void unpack(ByteBuffer buf)
            throws IOException {
        // Like most Doom structs...
        buf.order(ByteOrder.LITTLE_ENDIAN);
        this.name1 = DoomBuffer.getNullTerminatedString(buf, 9);
        this.name2 = DoomBuffer.getNullTerminatedString(buf, 9);
        this.episode = buf.getShort();
    }

    public final static int size() {
        return 20;
    }

    public String toString() {
        return String.format("%s %s %d", name1, name2, episode);
    }
}
package p;

import rr.line_t;

/** An object that carries...interception information, I guess...with either a line
 *  or an object?
 *  
 * @author Velktron
 *
 */

public class intercept_t {
		
		/** most intercepts will belong to a static pool */
	
		public intercept_t() {
		
		}
	
		public intercept_t(int frac, mobj_t thing){
			this.frac=frac;
			this.thing=thing;
			this.isaline=false;
		}
		
		public intercept_t(int frac, line_t line){
			this.frac=frac;
			this.line=line;
			this.isaline=true;
		}
		
		/** fixed_t, along trace line */
        public int frac; 
        public boolean isaline;
        // MAES: this was an union of a mobj_t and a line_t,
        // returned as "d".
        mobj_t thing;
        line_t line;
        
        public Interceptable d(){
            return (isaline)? line: thing;
        }
        
    }

package p;

import m.fixed_t;

public interface DoomPlayer {
	
	public fixed_t
	AimLineAttack
	( mobj_t	t1,
	  int	angle,
	  fixed_t	distance );

	public void
	LineAttack
	( mobj_t	t1,
	  int	angle,
	  fixed_t	distance,
	  fixed_t	slope,
	  int		damage );

	void
	RadiusAttack
	( mobj_t	spot,
	  mobj_t	source,
	  int		damage );
	
	void
	TouchSpecialThing
	( mobj_t	special,
	  mobj_t	toucher );

	void
	DamageMobj
	( mobj_t	target,
	  mobj_t	inflictor,
	  mobj_t	source,
	  int		damage );
	}

package p;

/** Animating textures and planes
 *  There is another anim_t used in wi_stuff, unrelated.
 * 
 * @author admin
 *
 */



public class anim_t {
	
		public anim_t(){
			
		}
    
      public anim_t(boolean istexture, int picnum, int basepic, int numpics,
            int speed) {
        super();
        this.istexture = istexture;
        this.picnum = picnum;
        this.basepic = basepic;
        this.numpics = numpics;
        this.speed = speed;
    }
    public  boolean istexture;
      public   int     picnum;
      public  int     basepic;
      public  int     numpics;
      public int     speed;
        
    }

package p;

import java.io.DataInputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

import rr.SectorAction;
import w.CacheableDoomObject;
import w.IPackableDoomObject;
import w.IReadableDoomObject;

public class ceiling_t extends SectorAction implements CacheableDoomObject,IReadableDoomObject,IPackableDoomObject{

        public ceiling_e   type;
        /** fixed_t */
        public int bottomheight;
        /** fixed_t */
        public int topheight;
        /** fixed_t */
        public int speed;
        boolean crush;

        // 1 = up, 0 = waiting, -1 = down
        public int     direction;

        // ID
        public int     tag;                   
        public int     olddirection;
        
        public ceiling_t(){
        	// Set to the smallest ordinal type.
        	this.type=ceiling_e.lowerToFloor;
        }
        
        // HACK for speed.
        public static final ceiling_e[] values=ceiling_e.values();
        
        @Override
        public void read(DataInputStream f) throws IOException{
        	// Read 48 bytes.
        	readbuffer.position(0);
        	readbuffer.order(ByteOrder.LITTLE_ENDIAN);
        	f.read(readbuffer.array(), 0,48);
        	unpack(readbuffer);        	
            }

        @Override
        public void pack(ByteBuffer b) throws IOException{
        	b.order(ByteOrder.LITTLE_ENDIAN);
            super.pack(b); //12            
            b.putInt(type.ordinal()); // 16            
            b.putInt(super.sectorid); // 20
            b.putInt(bottomheight); 
            b.putInt(topheight); // 28
            b.putInt(speed);
            b.putInt(crush?1:0); 
            b.putInt(direction); // 40
            b.putInt(tag);
            b.putInt(olddirection); //48
        	}

		@Override
		public void unpack(ByteBuffer b) throws IOException {
			b.order(ByteOrder.LITTLE_ENDIAN);
			super.unpack(b); // Call thinker reader first
			type=values[b.getInt()];
            super.sectorid=b.getInt(); // sector pointer.
            bottomheight=b.getInt();
            topheight=b.getInt();
            speed=b.getInt();
            crush=(b.getInt()!=0);
            direction=b.getInt();
            tag=b.getInt();
            olddirection=b.getInt();			
			}
        
		private static ByteBuffer readbuffer=ByteBuffer.allocate(48);
		
        }

package p;

public enum sdt_e {
	    sdt_openOnly,
	    sdt_closeOnly,
	    sdt_openAndClose
	}
package p;

public enum bwhere_e {
        top,
        middle,
        bottom
        }
package p;

import java.io.DataInputStream;
import java.io.IOException;
import java.nio.ByteBuffer;

import rr.SectorAction;
import w.DoomIO;

public class strobe_t extends SectorAction{
		
        public int     count;
        public int     minlight;
        public int     maxlight;
        public int     darktime;
        public int     brighttime;
        
        //
        // T_StrobeFlash
        //
        public void StrobeFlash() {
            if (--count != 0)
                return;

            if (sector.lightlevel == minlight) {
                sector.lightlevel = (short) maxlight;
                count = brighttime;
            } else {
                sector.lightlevel = (short) minlight;
                count = darktime;
            }

        }
        
        @Override
        public void read(DataInputStream f) throws IOException{

            super.read(f); // Call thinker reader first            
            super.sectorid=DoomIO.readLEInt(f); // Sector index
            count=DoomIO.readLEInt(f);
            maxlight=DoomIO.readLEInt(f);
            minlight=DoomIO.readLEInt(f);
            darktime=DoomIO.readLEInt(f);
            brighttime=DoomIO.readLEInt(f);       
            }
        
        @Override
        public void pack(ByteBuffer b) throws IOException{
            super.pack(b); //12            
            b.putInt(super.sectorid); // 16
            b.putInt(count); //20
            b.putInt(maxlight);//24
            b.putInt(minlight);//28
            b.putInt(darktime);//32
            b.putInt(brighttime);//36
        }
        
    };

download  show line numbers  debug dex  old transpilations   

Travelled to 15 computer(s): aoiabmzegqzx, bhatertpkbcr, cbybwowwnfue, cfunsshuasjs, ddnzoavkxhuk, gwrvuhgaqvyk, ishqpsrjomds, lpdgvwnxivlt, mqqgnosmbjvj, onxytkatvevr, pyentgdyhuwx, pzhvpgtvlbxg, tslmcundralx, tvejysmllsmz, vouqrxazstgt

No comments. add comment

Snippet ID: #1005016
Snippet name: Mocha Doom Single Source (include)
Eternal ID of this version: #1005016/1
Text MD5: 73ca03f98e9862a606ae662c56a197f1
Transpilation MD5: 73ca03f98e9862a606ae662c56a197f1
Author: stefan
Category: javax / games
Type: JavaX source code
Public (visible to everyone): Yes
Archived (hidden from active list): No
Created/modified: 2016-09-28 22:00:15
Source code size: 3225853 bytes / 104211 lines
Pitched / IR pitched: No / No
Views / Downloads: 11555 / 21542
Referenced in: [show references]