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