--- /dev/null
+/.idea
+/built
+/target/
+/.classpath
+/.settings/
+/.project
\ No newline at end of file
--- /dev/null
+<!doctype html>
+<html lang="en">
+<head>
+<title>LWJake2 README</title>
+<!-- 2017-07-07 Fri 14:10 -->
+<meta charset="utf-8">
+<meta name="viewport" content="width=device-width, initial-scale=1">
+<meta name="generator" content="Org-mode">
+<meta name="author" content="Svjatoslav Agejenko">
+
+<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.5/css/bootstrap.min.css" rel="stylesheet">
+<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
+<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.5/js/bootstrap.min.js"></script>
+<style type="text/css">
+/* org mode styles on top of twbs */
+
+html {
+ position: relative;
+ min-height: 100%;
+}
+
+body {
+ font-size: 18px;
+ margin-bottom: 105px;
+}
+
+footer {
+ position: absolute;
+ bottom: 0;
+ width: 100%;
+ height: 101px;
+ background-color: #f5f5f5;
+}
+
+footer > div {
+ padding: 10px;
+}
+
+footer p {
+ margin: 0 0 5px;
+ text-align: center;
+ font-size: 16px;
+}
+
+#table-of-contents {
+ margin-top: 20px;
+ margin-bottom: 20px;
+}
+
+blockquote p {
+ font-size: 18px;
+}
+
+pre {
+ font-size: 16px;
+}
+
+.footpara {
+ display: inline-block;
+}
+
+figcaption {
+ font-size: 16px;
+ color: #666;
+ font-style: italic;
+ padding-bottom: 15px;
+}
+
+/* from twbs docs */
+
+.bs-docs-sidebar.affix {
+ position: static;
+}
+@media (min-width: 768px) {
+ .bs-docs-sidebar {
+ padding-left: 20px;
+ }
+}
+
+/* All levels of nav */
+.bs-docs-sidebar .nav > li > a {
+ display: block;
+ padding: 4px 20px;
+ font-size: 14px;
+ font-weight: 500;
+ color: #999;
+}
+.bs-docs-sidebar .nav > li > a:hover,
+.bs-docs-sidebar .nav > li > a:focus {
+ padding-left: 19px;
+ color: #A1283B;
+ text-decoration: none;
+ background-color: transparent;
+ border-left: 1px solid #A1283B;
+}
+.bs-docs-sidebar .nav > .active > a,
+.bs-docs-sidebar .nav > .active:hover > a,
+.bs-docs-sidebar .nav > .active:focus > a {
+ padding-left: 18px;
+ font-weight: bold;
+ color: #A1283B;
+ background-color: transparent;
+ border-left: 2px solid #A1283B;
+}
+
+/* Nav: second level (shown on .active) */
+.bs-docs-sidebar .nav .nav {
+ display: none; /* Hide by default, but at >768px, show it */
+ padding-bottom: 10px;
+}
+.bs-docs-sidebar .nav .nav > li > a {
+ padding-top: 1px;
+ padding-bottom: 1px;
+ padding-left: 30px;
+ font-size: 12px;
+ font-weight: normal;
+}
+.bs-docs-sidebar .nav .nav > li > a:hover,
+.bs-docs-sidebar .nav .nav > li > a:focus {
+ padding-left: 29px;
+}
+.bs-docs-sidebar .nav .nav > .active > a,
+.bs-docs-sidebar .nav .nav > .active:hover > a,
+.bs-docs-sidebar .nav .nav > .active:focus > a {
+ padding-left: 28px;
+ font-weight: 500;
+}
+
+/* Nav: third level (shown on .active) */
+.bs-docs-sidebar .nav .nav .nav {
+ padding-bottom: 10px;
+}
+.bs-docs-sidebar .nav .nav .nav > li > a {
+ padding-top: 1px;
+ padding-bottom: 1px;
+ padding-left: 40px;
+ font-size: 12px;
+ font-weight: normal;
+}
+.bs-docs-sidebar .nav .nav .nav > li > a:hover,
+.bs-docs-sidebar .nav .nav .nav > li > a:focus {
+ padding-left: 39px;
+}
+.bs-docs-sidebar .nav .nav .nav > .active > a,
+.bs-docs-sidebar .nav .nav .nav > .active:hover > a,
+.bs-docs-sidebar .nav .nav .nav > .active:focus > a {
+ padding-left: 38px;
+ font-weight: 500;
+}
+
+/* Show and affix the side nav when space allows it */
+@media (min-width: 992px) {
+ .bs-docs-sidebar .nav > .active > ul {
+ display: block;
+ }
+ /* Widen the fixed sidebar */
+ .bs-docs-sidebar.affix,
+ .bs-docs-sidebar.affix-bottom {
+ width: 213px;
+ }
+ .bs-docs-sidebar.affix {
+ position: fixed; /* Undo the static from mobile first approach */
+ top: 20px;
+ }
+ .bs-docs-sidebar.affix-bottom {
+ position: absolute; /* Undo the static from mobile first approach */
+ }
+ .bs-docs-sidebar.affix .bs-docs-sidenav,.bs-docs-sidebar.affix-bottom .bs-docs-sidenav {
+ margin-top: 0;
+ margin-bottom: 0
+ }
+}
+@media (min-width: 1200px) {
+ /* Widen the fixed sidebar again */
+ .bs-docs-sidebar.affix-bottom,
+ .bs-docs-sidebar.affix {
+ width: 263px;
+ }
+}
+</style>
+<script type="text/javascript">
+$(function() {
+ 'use strict';
+
+ $('.bs-docs-sidebar li').first().addClass('active');
+
+ $(document.body).scrollspy({target: '.bs-docs-sidebar'});
+
+ $('.bs-docs-sidebar').affix();
+});
+</script>
+</head>
+<body>
+<div id="content" class="container">
+<div class="row"><div class="col-md-9"><h1 class="title">LWJake2 README</h1>
+<div id="outline-container-sec-1" class="outline-2">
+<h2 id="sec-1"><span class="section-number-2">1</span> LWJake2 README</h2>
+<div class="outline-text-2" id="text-1">
+<p>
+LWJake2 Website: <a href="https://github.com/flibitijibibo/LWJake2">https://github.com/flibitijibibo/LWJake2</a>
+</p>
+
+<p>
+LWJake2 is a fork of Jake2, a port of the GPL'd Quake 2 engine from id Software
+to Java.
+</p>
+
+<p>
+LWJake2 is distributed under the terms of the GPLv2 (see LICENSE).
+</p>
+
+<p>
+The port was done completely in Java. No native libraries are used for the
+game functionality.
+</p>
+
+<p>
+This version of Jake2 uses the Lightweight Java Game Library (LWJGL) for
+OpenGL/OpenAL bindings, rather than JOGL/JOAL.
+</p>
+
+<p>
+LWJGL Website - <a href="http://www.lwjgl.org/">http://www.lwjgl.org/</a>
+</p>
+
+<p>
+LWJake2 is still under development. Send bug reports and feedback to
+flibitijibibo@flibitijibibo.com.
+</p>
+
+<p>
+Currently LWJake2 supports GNU/Linux, Windows and Mac OSX. The LWJake2 dedicated
+server runs on every Java supported platform.
+</p>
+
+<p>
+System Requirements:
+</p>
+<ul class="org-ul">
+<li>A computer from the last ~5 years.
+</li>
+<li>The latest version of Java.
+</li>
+</ul>
+</div>
+</div>
+<div id="outline-container-sec-2" class="outline-2">
+<h2 id="sec-2"><span class="section-number-2">2</span> Installation</h2>
+<div class="outline-text-2" id="text-2">
+<ul class="org-ul">
+<li>Extract the archive that this is in.
+</li>
+<li>Install Quake 2 data (see below).
+</li>
+<li>Run your respective executable (LWJake2.sh, for example)
+</li>
+<li>Play!
+</li>
+</ul>
+
+<p>
+Installation of Quake 2 data:
+By default, LWJake2 looks in its own directory for baseq2/, so by default you
+need to move your baseq2/ to that folder. However, you can change this by
+modifying the "cddir" cvar in your config.cfg. For example, if I wanted to
+host my data in ~/.quake2, I would add the following to my config.cfg:
+</p>
+
+<p>
+set cddir "/home/flibitijibibo/.quake2"
+</p>
+
+<p>
+Be sure to check for "set cddir" first! LWJake2 adds this in the default config.
+</p>
+</div>
+</div>
+<div id="outline-container-sec-3" class="outline-2">
+<h2 id="sec-3"><span class="section-number-2">3</span> Credits</h2>
+<div class="outline-text-2" id="text-3">
+<p>
+Bytonic Software - Original Jake2 authors
+</p>
+<hr >
+<p>
+Holger Zickner <hoz@bytonic.de>
+Carsten Weisse <cwei@bytonic.de>
+Rene Stoeckel <rst@bytonic.de>
+</p>
+
+<p>
+Jake2 Community - Other Jake2 contributors
+</p>
+<hr >
+<p>
+David Sanders - Original LWJGL implementation
+</p>
+
+<p>
+12characters Games - LWJake2 authors
+</p>
+<hr >
+<p>
+Ethan Lee <flibitijibibo@flibitijibibo.com>
+</p>
+</div>
+</div>
+</div><div class="col-md-3"><nav id="table-of-contents">
+<div id="text-table-of-contents" class="bs-docs-sidebar">
+<ul class="nav">
+<li><a href="#sec-1">1. LWJake2 README</a></li>
+<li><a href="#sec-2">2. Installation</a></li>
+<li><a href="#sec-3">3. Credits</a></li>
+</ul>
+</div>
+</nav>
+</div></div></div>
+<footer id="postamble" class="">
+<div><p class="author">Author: Svjatoslav Agejenko</p>
+<p class="date">Created: 2017-07-07 Fri 14:10</p>
+<p class="creator"><a href="http://www.gnu.org/software/emacs/">Emacs</a> 25.1.1 (<a href="http://orgmode.org">Org-mode</a> 8.2.10)</p>
+</div>
+</footer>
+</body>
+</html>
--- /dev/null
+* LWJake2 README
+LWJake2 Website: https://github.com/flibitijibibo/LWJake2
+
+LWJake2 is a fork of Jake2, a port of the GPL'd Quake 2 engine from id Software
+to Java.
+
+LWJake2 is distributed under the terms of the GPLv2 (see LICENSE).
+
+The port was done completely in Java. No native libraries are used for the
+game functionality.
+
+This version of Jake2 uses the Lightweight Java Game Library (LWJGL) for
+OpenGL/OpenAL bindings, rather than JOGL/JOAL.
+
+LWJGL Website - http://www.lwjgl.org/
+
+LWJake2 is still under development. Send bug reports and feedback to
+flibitijibibo@flibitijibibo.com.
+
+Currently LWJake2 supports GNU/Linux, Windows and Mac OSX. The LWJake2 dedicated
+server runs on every Java supported platform.
+
+System Requirements:
+- A computer from the last ~5 years.
+- The latest version of Java.
+* Installation
+
+- Extract the archive that this is in.
+- Install Quake 2 data (see below).
+- Run your respective executable (LWJake2.sh, for example)
+- Play!
+
+Installation of Quake 2 data:
+By default, LWJake2 looks in its own directory for baseq2/, so by default you
+need to move your baseq2/ to that folder. However, you can change this by
+modifying the "cddir" cvar in your config.cfg. For example, if I wanted to
+host my data in ~/.quake2, I would add the following to my config.cfg:
+
+set cddir "/home/flibitijibibo/.quake2"
+
+Be sure to check for "set cddir" first! LWJake2 adds this in the default config.
+* Credits
+Bytonic Software - Original Jake2 authors
+-----------------------------------------
+Holger Zickner <hoz@bytonic.de>
+Carsten Weisse <cwei@bytonic.de>
+Rene Stoeckel <rst@bytonic.de>
+
+Jake2 Community - Other Jake2 contributors
+------------------------------------------
+David Sanders - Original LWJGL implementation
+
+12characters Games - LWJake2 authors
+------------------------------------
+Ethan Lee <flibitijibibo@flibitijibibo.com>
--- /dev/null
+/usr/share/games/quake2/baseq2/
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4">
+ <component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_8" inherit-compiler-output="false">
+ <output url="file://$MODULE_DIR$/target/classes" />
+ <output-test url="file://$MODULE_DIR$/target/test-classes" />
+ <content url="file://$MODULE_DIR$">
+ <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
+ <excludeFolder url="file://$MODULE_DIR$/target" />
+ </content>
+ <orderEntry type="inheritedJdk" />
+ <orderEntry type="sourceFolder" forTests="false" />
+ <orderEntry type="library" scope="TEST" name="Maven: junit:junit:4.8.1" level="project" />
+ <orderEntry type="library" name="Maven: org.apache.commons:commons-lang3:3.0" level="project" />
+ <orderEntry type="library" name="Maven: org.lwjgl.lwjgl:lwjgl:2.9.3" level="project" />
+ <orderEntry type="library" name="Maven: org.lwjgl.lwjgl:lwjgl-platform:natives-windows:2.9.3" level="project" />
+ <orderEntry type="library" name="Maven: org.lwjgl.lwjgl:lwjgl-platform:natives-linux:2.9.3" level="project" />
+ <orderEntry type="library" name="Maven: org.lwjgl.lwjgl:lwjgl-platform:natives-osx:2.9.3" level="project" />
+ <orderEntry type="library" name="Maven: net.java.jinput:jinput:2.0.5" level="project" />
+ <orderEntry type="library" name="Maven: net.java.jutils:jutils:1.0.0" level="project" />
+ <orderEntry type="library" scope="RUNTIME" name="Maven: net.java.jinput:jinput-platform:natives-linux:2.0.5" level="project" />
+ <orderEntry type="library" scope="RUNTIME" name="Maven: net.java.jinput:jinput-platform:natives-windows:2.0.5" level="project" />
+ <orderEntry type="library" scope="RUNTIME" name="Maven: net.java.jinput:jinput-platform:natives-osx:2.0.5" level="project" />
+ <orderEntry type="library" name="Maven: org.lwjgl.lwjgl:lwjgl_util:2.9.3" level="project" />
+ <orderEntry type="library" name="Maven: com.flibit:EFX:1.0" level="project" />
+ </component>
+</module>
\ No newline at end of file
--- /dev/null
+<!doctype html>
+<html lang="en">
+<head>
+<title>LWJake2 - experiments</title>
+<!-- 2019-01-27 P 00:36 -->
+<meta charset="utf-8">
+<meta name="viewport" content="width=device-width, initial-scale=1">
+<meta name="generator" content="Org-mode">
+<meta name="author" content="Svjatoslav Agejenko">
+<link href="https://bootswatch.com/3/darkly/bootstrap.min.css" rel="stylesheet">
+<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
+<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.5/js/bootstrap.min.js"></script>
+<style type="text/css">
+footer {background-color: #111 !important;}
+pre {background-color: #111; color: #ccc;}
+</style>
+<style type="text/css">
+/* org mode styles on top of twbs */
+
+html {
+ position: relative;
+ min-height: 100%;
+}
+
+body {
+ font-size: 18px;
+ margin-bottom: 105px;
+}
+
+footer {
+ position: absolute;
+ bottom: 0;
+ width: 100%;
+ height: 101px;
+ background-color: #f5f5f5;
+}
+
+footer > div {
+ padding: 10px;
+}
+
+footer p {
+ margin: 0 0 5px;
+ text-align: center;
+ font-size: 16px;
+}
+
+#table-of-contents {
+ margin-top: 20px;
+ margin-bottom: 20px;
+}
+
+blockquote p {
+ font-size: 18px;
+}
+
+pre {
+ font-size: 16px;
+}
+
+.footpara {
+ display: inline-block;
+}
+
+figcaption {
+ font-size: 16px;
+ color: #666;
+ font-style: italic;
+ padding-bottom: 15px;
+}
+
+/* from twbs docs */
+
+.bs-docs-sidebar.affix {
+ position: static;
+}
+@media (min-width: 768px) {
+ .bs-docs-sidebar {
+ padding-left: 20px;
+ }
+}
+
+/* All levels of nav */
+.bs-docs-sidebar .nav > li > a {
+ display: block;
+ padding: 4px 20px;
+ font-size: 14px;
+ font-weight: 500;
+ color: #999;
+}
+.bs-docs-sidebar .nav > li > a:hover,
+.bs-docs-sidebar .nav > li > a:focus {
+ padding-left: 19px;
+ color: #A1283B;
+ text-decoration: none;
+ background-color: transparent;
+ border-left: 1px solid #A1283B;
+}
+.bs-docs-sidebar .nav > .active > a,
+.bs-docs-sidebar .nav > .active:hover > a,
+.bs-docs-sidebar .nav > .active:focus > a {
+ padding-left: 18px;
+ font-weight: bold;
+ color: #A1283B;
+ background-color: transparent;
+ border-left: 2px solid #A1283B;
+}
+
+/* Nav: second level (shown on .active) */
+.bs-docs-sidebar .nav .nav {
+ display: none; /* Hide by default, but at >768px, show it */
+ padding-bottom: 10px;
+}
+.bs-docs-sidebar .nav .nav > li > a {
+ padding-top: 1px;
+ padding-bottom: 1px;
+ padding-left: 30px;
+ font-size: 12px;
+ font-weight: normal;
+}
+.bs-docs-sidebar .nav .nav > li > a:hover,
+.bs-docs-sidebar .nav .nav > li > a:focus {
+ padding-left: 29px;
+}
+.bs-docs-sidebar .nav .nav > .active > a,
+.bs-docs-sidebar .nav .nav > .active:hover > a,
+.bs-docs-sidebar .nav .nav > .active:focus > a {
+ padding-left: 28px;
+ font-weight: 500;
+}
+
+/* Nav: third level (shown on .active) */
+.bs-docs-sidebar .nav .nav .nav {
+ padding-bottom: 10px;
+}
+.bs-docs-sidebar .nav .nav .nav > li > a {
+ padding-top: 1px;
+ padding-bottom: 1px;
+ padding-left: 40px;
+ font-size: 12px;
+ font-weight: normal;
+}
+.bs-docs-sidebar .nav .nav .nav > li > a:hover,
+.bs-docs-sidebar .nav .nav .nav > li > a:focus {
+ padding-left: 39px;
+}
+.bs-docs-sidebar .nav .nav .nav > .active > a,
+.bs-docs-sidebar .nav .nav .nav > .active:hover > a,
+.bs-docs-sidebar .nav .nav .nav > .active:focus > a {
+ padding-left: 38px;
+ font-weight: 500;
+}
+
+/* Show and affix the side nav when space allows it */
+@media (min-width: 992px) {
+ .bs-docs-sidebar .nav > .active > ul {
+ display: block;
+ }
+ /* Widen the fixed sidebar */
+ .bs-docs-sidebar.affix,
+ .bs-docs-sidebar.affix-bottom {
+ width: 213px;
+ }
+ .bs-docs-sidebar.affix {
+ position: fixed; /* Undo the static from mobile first approach */
+ top: 20px;
+ }
+ .bs-docs-sidebar.affix-bottom {
+ position: absolute; /* Undo the static from mobile first approach */
+ }
+ .bs-docs-sidebar.affix .bs-docs-sidenav,.bs-docs-sidebar.affix-bottom .bs-docs-sidenav {
+ margin-top: 0;
+ margin-bottom: 0
+ }
+}
+@media (min-width: 1200px) {
+ /* Widen the fixed sidebar again */
+ .bs-docs-sidebar.affix-bottom,
+ .bs-docs-sidebar.affix {
+ width: 263px;
+ }
+}
+</style>
+<script type="text/javascript">
+$(function() {
+ 'use strict';
+
+ $('.bs-docs-sidebar li').first().addClass('active');
+
+ $(document.body).scrollspy({target: '.bs-docs-sidebar'});
+
+ $('.bs-docs-sidebar').affix();
+});
+</script>
+</head>
+<body>
+<div id="content" class="container">
+<div class="row"><div class="col-md-9"><h1 class="title">LWJake2 - experiments</h1>
+
+<div id="outline-container-sec-1" class="outline-2">
+<h2 id="sec-1"><span class="section-number-2">1</span> General</h2>
+<div class="outline-text-2" id="text-1">
+<ul class="org-ul">
+<li>This program is free software: you can redistribute it and/or modify
+it under the terms of the GPLv2 as published by the Free Software
+Foundation.
+</li>
+
+<li>Program authors:
+<ol class="org-ol">
+<li>Id Software is very kind for releasing <a href="https://github.com/id-Software/Quake-2">source code</a> for their <a href="https://en.wikipedia.org/wiki/Quake_II">Quake 2</a>
+first person shooter game.
+</li>
+
+<li>Quake 2 source code got ported to Java as <a href="https://bytonic.de/html/jake2.html">jake2</a>.
+<ul class="org-ul">
+<li>Bytonic Software - Original Jake2 authors:
+<ul class="org-ul">
+<li>Holger Zickner <hoz@bytonic.de>
+</li>
+<li>Carsten Weisse <cwei@bytonic.de>
+</li>
+<li>Rene Stoeckel <rst@bytonic.de>
+</li>
+</ul>
+</li>
+</ul>
+</li>
+
+<li>jake2 got forked and ported to <a href="https://www.lwjgl.org/">LightWeight Java Gaming Library</a> as <a href="https://github.com/flibitijibibo/LWJake2">LWJake2</a>.
+<ul class="org-ul">
+<li>David Sanders
+</li>
+<li>Ethan Lee <flibitijibibo@flibitijibibo.com>
+</li>
+</ul>
+</li>
+
+<li>Svjatoslav Agejenko took <a href="https://github.com/flibitijibibo/LWJake2">LWJake2</a> as a base for <a href="#sec-2">exeriments</a>
+<ul class="org-ul">
+<li>Homepage: <a href="https://svjatoslav.eu">https://svjatoslav.eu</a>
+</li>
+<li>Email: <a href="mailto://svjatoslav@svjatoslav.eu">mailto://svjatoslav@svjatoslav.eu</a>
+</li>
+<li><a href="https://www.svjatoslav.eu/projects/">Other software projects hosted at svjatoslav.eu</a>
+</li>
+</ul>
+</li>
+</ol>
+</li>
+</ul>
+</div>
+<div id="outline-container-sec-1-1" class="outline-3">
+<h3 id="sec-1-1"><span class="section-number-3">1.1</span> Source code</h3>
+<div class="outline-text-3" id="text-1-1">
+<ul class="org-ul">
+<li><a href="https://www2.svjatoslav.eu/gitweb/?p=quake2.git;a=snapshot;h=HEAD;sf=tgz">Download latest snapshot in TAR GZ format</a>
+</li>
+<li><a href="https://www2.svjatoslav.eu/gitweb/?p=quake2.git;a=summary">Browse Git repository online</a>
+</li>
+<li>Clone Git repository using command:
+<pre class="example">
+git clone http://www2.svjatoslav.eu/git/quake2.git
+
+</pre>
+</li>
+</ul>
+</div>
+</div>
+</div>
+
+<div id="outline-container-sec-2" class="outline-2">
+<h2 id="sec-2"><a id="ID-a4bce93d-9a44-4197-82dc-4c9c163f80fb" name="ID-a4bce93d-9a44-4197-82dc-4c9c163f80fb"></a><span class="section-number-2">2</span> Current status and goals</h2>
+<div class="outline-text-2" id="text-2">
+<p>
+Recent screenshot:
+<img src="screenshot.png" class="img-responsive" alt="screenshot.png">
+</p>
+
+<p>
+Goals:
+</p>
+<ul class="org-ul">
+<li>Remove all violence and overall Quake game logic and get minimal,
+easy to understand and experiment with 3D engine.
+</li>
+
+<li>Remove dependency on Quake proprietary game files.
+<ul class="org-ul">
+<li>Create world programmatically using <a href="https://en.wikipedia.org/wiki/Constructive_solid_geometry">CSG</a> instead.
+</li>
+<li>Add support for other formats (Perhaps <a href="https://en.wikipedia.org/wiki/Wavefront_.obj_file">Wavefront OBJ</a>).
+</li>
+</ul>
+</li>
+
+<li>Learn in the process, how 3D engine is done.
+<ul class="org-ul">
+<li>Perhaps integrate or transfer some knowledge to <a href="https://www3.svjatoslav.eu/projects/sixth-3d/">Sixth 3D engine</a>
+</li>
+</ul>
+</li>
+</ul>
+
+
+<p>
+Done:
+</p>
+<ul class="org-ul">
+<li>Removed Microsoft Windows support.
+</li>
+<li>Converted from previously Ant to Maven project.
+</li>
+<li>Code refactoring according to Java conventions.
+</li>
+<li>Deleted lots of weapons, monsters, kill & damage related code.
+</li>
+</ul>
+</div>
+</div>
+</div><div class="col-md-3"><nav id="table-of-contents">
+<div id="text-table-of-contents" class="bs-docs-sidebar">
+<ul class="nav">
+<li><a href="#sec-1">1. General</a>
+<ul class="nav">
+<li><a href="#sec-1-1">1.1. Source code</a></li>
+</ul>
+</li>
+<li><a href="#sec-2">2. Current status and goals</a></li>
+</ul>
+</div>
+</nav>
+</div></div></div>
+<footer id="postamble" class="">
+<div><p class="author">Author: Svjatoslav Agejenko</p>
+<p class="date">Created: 2019-01-27 P 00:36</p>
+<p class="creator"><a href="http://www.gnu.org/software/emacs/">Emacs</a> 26.1 (<a href="http://orgmode.org">Org-mode</a> 9.1.9)</p>
+</div>
+</footer>
+</body>
+</html>
--- /dev/null
+#+TITLE: LWJake2 - experiments
+
+* (document settings) :noexport:
+** use dark style for TWBS-HTML exporter
+#+HTML_HEAD: <link href="https://bootswatch.com/3/darkly/bootstrap.min.css" rel="stylesheet">
+#+HTML_HEAD: <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
+#+HTML_HEAD: <script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.5/js/bootstrap.min.js"></script>
+#+HTML_HEAD: <style type="text/css">
+#+HTML_HEAD: footer {background-color: #111 !important;}
+#+HTML_HEAD: pre {background-color: #111; color: #ccc;}
+#+HTML_HEAD: </style>
+
+* General
+
+- This program is free software: you can redistribute it and/or modify
+ it under the terms of the GPLv2 as published by the Free Software
+ Foundation.
+
+- Program authors:
+ 1. Id Software is very kind for releasing [[https://github.com/id-Software/Quake-2][source code]] for their [[https://en.wikipedia.org/wiki/Quake_II][Quake 2]]
+ first person shooter game.
+
+ 2. Quake 2 source code got ported to Java as [[https://bytonic.de/html/jake2.html][jake2]].
+ - Bytonic Software - Original Jake2 authors:
+ - Holger Zickner <hoz@bytonic.de>
+ - Carsten Weisse <cwei@bytonic.de>
+ - Rene Stoeckel <rst@bytonic.de>
+
+ 3. jake2 got forked and ported to [[https://www.lwjgl.org/][LightWeight Java Gaming Library]] as [[https://github.com/flibitijibibo/LWJake2][LWJake2]].
+ - David Sanders
+ - Ethan Lee <flibitijibibo@flibitijibibo.com>
+
+ 4. Svjatoslav Agejenko took [[https://github.com/flibitijibibo/LWJake2][LWJake2]] as a base for [[id:a4bce93d-9a44-4197-82dc-4c9c163f80fb][exeriments]]
+ - Homepage: https://svjatoslav.eu
+ - Email: mailto://svjatoslav@svjatoslav.eu
+ - [[https://www.svjatoslav.eu/projects/][Other software projects hosted at svjatoslav.eu]]
+** Source code
+- [[https://www2.svjatoslav.eu/gitweb/?p=quake2.git;a=snapshot;h=HEAD;sf=tgz][Download latest snapshot in TAR GZ format]]
+- [[https://www2.svjatoslav.eu/gitweb/?p=quake2.git;a=summary][Browse Git repository online]]
+- Clone Git repository using command:
+ : git clone http://www2.svjatoslav.eu/git/quake2.git
+
+* Current status and goals
+ :PROPERTIES:
+ :ID: a4bce93d-9a44-4197-82dc-4c9c163f80fb
+ :END:
+
+Recent screenshot:
+[[file:screenshot.png]]
+
+Goals:
++ Remove all violence and overall Quake game logic and get minimal,
+ easy to understand and experiment with 3D engine.
+
++ Remove dependency on Quake proprietary game files.
+ + Create world programmatically using [[https://en.wikipedia.org/wiki/Constructive_solid_geometry][CSG]] instead.
+ + Add support for other formats (Perhaps [[https://en.wikipedia.org/wiki/Wavefront_.obj_file][Wavefront OBJ]]).
+
++ Learn in the process, how 3D engine is done.
+ + Perhaps integrate or transfer some knowledge to [[https://www3.svjatoslav.eu/projects/sixth-3d/][Sixth 3D engine]]
+
+
+Done:
++ Removed Microsoft Windows support.
++ Converted from previously Ant to Maven project.
++ Code refactoring according to Java conventions.
++ Deleted lots of weapons, monsters, kill & damage related code.
--- /dev/null
+<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>lwjake2</groupId>
+ <artifactId>quake2</artifactId>
+ <version>1.0-SNAPSHOT</version>
+ <name>Quake2</name>
+ <packaging>jar</packaging>
+
+
+ <dependencies>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <version>4.8.1</version>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-lang3</artifactId>
+ <version>3.0</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.lwjgl.lwjgl</groupId>
+ <artifactId>lwjgl</artifactId>
+ <version>2.9.3</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.lwjgl.lwjgl</groupId>
+ <artifactId>lwjgl_util</artifactId>
+ <version>2.9.3</version>
+ </dependency>
+
+ <dependency>
+ <groupId>eu.svjatoslav</groupId>
+ <artifactId>javainspect</artifactId>
+ <version>1.6</version>
+ </dependency>
+ </dependencies>
+
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <version>3.1</version>
+ <configuration>
+ <source>1.8</source>
+ <target>1.8</target>
+ <optimize>true</optimize>
+ <encoding>UTF-8</encoding>
+ </configuration>
+ </plugin>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-source-plugin</artifactId>
+ <version>2.2.1</version>
+ <executions>
+ <execution>
+ <id>attach-sources</id>
+ <goals>
+ <goal>jar</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-javadoc-plugin</artifactId>
+ <version>2.9</version>
+ <executions>
+ <execution>
+ <id>attach-javadocs</id>
+ <goals>
+ <goal>jar</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-jar-plugin</artifactId>
+ <configuration>
+ <archive>
+ <manifest>
+ <addClasspath>true</addClasspath>
+ <classpathPrefix>lib/</classpathPrefix>
+ <mainClass>lwjake2.LWJake2</mainClass>
+ </manifest>
+ </archive>
+ </configuration>
+ </plugin>
+
+ <plugin>
+ <groupId>com.googlecode.mavennatives</groupId>
+ <artifactId>maven-nativedependencies-plugin</artifactId>
+ <version>0.0.5</version>
+ <executions>
+ <execution>
+ <id>unpacknatives</id>
+ <phase>generate-resources</phase>
+ <goals>
+ <goal>copy</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+
+ </build>
+
+ <repositories>
+ <repository>
+ <id>svjatoslav.eu</id>
+ <name>Svjatoslav repository</name>
+ <url>http://www2.svjatoslav.eu/maven/</url>
+ </repository>
+ </repositories>
+</project>
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/**
+ * Contains the definitions for the game engine.
+ */
+
+package lwjake2;
+
+import java.nio.ByteOrder;
+
+public class Defines {
+ // -----------------
+ // client/q_shared.h
+
+ // can accelerate and turn
+ public final static int PM_NORMAL = 0;
+ public final static int PM_SPECTATOR = 1;
+ // no acceleration or turning
+ public final static int PM_DEAD = 2;
+ public final static int PM_GIB = 3; // different bounding box
+ public final static int PM_FREEZE = 4;
+
+ public final static int EV_ITEM_RESPAWN = 1;
+ public final static int EV_FOOTSTEP = 2;
+ public final static int EV_FALLSHORT = 3;
+ public final static int EV_FALL = 4;
+ public final static int EV_FALLFAR = 5;
+ public final static int EV_PLAYER_TELEPORT = 6;
+ public final static int EV_OTHER_TELEPORT = 7;
+
+ // angle indexes
+ public final static int PITCH = 0; // up / down
+ public final static int YAW = 1; // left / right
+ public final static int ROLL = 2; // fall over
+
+ public final static int MAX_STRING_CHARS = 1024; // max length of a string passed to Cmd_TokenizeString
+ public final static int MAX_STRING_TOKENS = 80; // max tokens resulting from Cmd_TokenizeString
+ public final static int MAX_TOKEN_CHARS = 1024; // max length of an individual token
+
+ public final static int MAX_QPATH = 64; // max length of a quake game pathname
+ public final static int MAX_OSPATH = 128; // max length of a filesystem pathname
+
+ // per-level limits
+ public final static int MAX_CLIENTS = 256; // absolute limit
+ public final static int MAX_EDICTS = 1024; // must change protocol to increase more
+ public final static int MAX_LIGHTSTYLES = 256;
+ public final static int MAX_MODELS = 256; // these are sent over the net as bytes
+ public final static int MAX_SOUNDS = 256; // so they cannot be blindly increased
+ public final static int MAX_IMAGES = 256;
+ public final static int MAX_ITEMS = 256;
+ public final static int MAX_GENERAL = (MAX_CLIENTS * 2); // general config strings
+
+ public final static int PRINT_MEDIUM = 1; // death messages
+ public final static int PRINT_HIGH = 2; // critical messages
+ public final static int PRINT_CHAT = 3; // chat messages
+
+ public final static int ERR_FATAL = 0; // exit the entire game with a popup window
+ public final static int ERR_DROP = 1; // print to console and disconnect from game
+ public final static int ERR_DISCONNECT = 2; // don't kill server
+
+ public final static int PRINT_ALL = 0;
+ public final static int PRINT_DEVELOPER = 1; // only print when "developer 1"
+
+ // key / value info strings
+ public final static int MAX_INFO_KEY = 64;
+ public final static int MAX_INFO_STRING = 512;
+
+ public final static int SFF_HIDDEN = 0x02;
+ public final static int SFF_SUBDIR = 0x08;
+ public final static int SFF_SYSTEM = 0x10;
+
+ public final static int CVAR_ARCHIVE = 1; // set to cause it to be saved to vars.rc
+ public final static int CVAR_USERINFO = 2; // added to userinfo when changed
+ public final static int CVAR_SERVERINFO = 4; // added to serverinfo when changed
+ public final static int CVAR_NOSET = 8; // don't allow change from console at all,
+ // but can be set from the command line
+ public final static int CVAR_LATCH = 16; // save changes until server restart
+
+ // lower bits are stronger, and will eat weaker brushes completely
+ public final static int CONTENTS_SOLID = 1; // an eye is never valid in a solid
+ public final static int CONTENTS_WINDOW = 2; // translucent, but not watery
+ public final static int CONTENTS_LAVA = 8;
+ public final static int CONTENTS_SLIME = 16;
+ public final static int CONTENTS_WATER = 32;
+
+ public final static int CONTENTS_PLAYERCLIP = 0x10000;
+ public final static int CONTENTS_MONSTERCLIP = 0x20000;
+
+ // currents can be added to any other contents, and may be mixed
+ public final static int CONTENTS_CURRENT_0 = 0x40000;
+ public final static int CONTENTS_CURRENT_90 = 0x80000;
+ public final static int CONTENTS_CURRENT_180 = 0x100000;
+ public final static int CONTENTS_CURRENT_270 = 0x200000;
+ public final static int CONTENTS_CURRENT_UP = 0x400000;
+ public final static int CONTENTS_CURRENT_DOWN = 0x800000;
+
+
+ public final static int CONTENTS_MONSTER = 0x2000000; // should never be on a brush, only in game
+ public final static int CONTENTS_DEADMONSTER = 0x4000000;
+ public final static int CONTENTS_LADDER = 0x20000000;
+
+ public final static int SURF_SLICK = 0x2; // effects game physics
+
+ public final static int SURF_SKY = 0x4; // don't draw, but add to skybox
+ public final static int SURF_WARP = 0x8; // turbulent water warp
+ public final static int SURF_TRANS33 = 0x10;
+ public final static int SURF_TRANS66 = 0x20;
+ public final static int SURF_FLOWING = 0x40; // scroll towards angle
+
+ //
+ // button bits
+ //
+ public final static int BUTTON_ATTACK = 1;
+ public final static int BUTTON_USE = 2;
+ public final static int BUTTON_ANY = 128; // any key whatsoever
+
+ public final static int MAXTOUCH = 32;
+
+ // entity_state_t->effects
+ // Effects are things handled on the client side (lights, particles, frame animations)
+ // that happen constantly on the given entity.
+ // An entity that has effects will be sent to the client
+ // even if it has a zero index model.
+ public final static int EF_ROTATE = 0x00000001; // rotate (bonus items)
+ public final static int EF_GIB = 0x00000002; // leave a trail
+ public final static int EF_BLASTER = 0x00000008; // redlight + trail
+ public final static int EF_ROCKET = 0x00000010; // redlight + trail
+ public final static int EF_GRENADE = 0x00000020;
+ public final static int EF_HYPERBLASTER = 0x00000040;
+ public final static int EF_BFG = 0x00000080;
+ public final static int EF_COLOR_SHELL = 0x00000100;
+ public final static int EF_POWERSCREEN = 0x00000200;
+ public final static int EF_ANIM01 = 0x00000400; // automatically cycle between frames 0 and 1 at 2 hz
+ public final static int EF_ANIM23 = 0x00000800; // automatically cycle between frames 2 and 3 at 2 hz
+ public final static int EF_ANIM_ALL = 0x00001000; // automatically cycle through all frames at 2hz
+ public final static int EF_ANIM_ALLFAST = 0x00002000; // automatically cycle through all frames at 10hz
+ public final static int EF_FLIES = 0x00004000;
+ public final static int EF_QUAD = 0x00008000;
+ public final static int EF_PENT = 0x00010000;
+ public final static int EF_TELEPORTER = 0x00020000; // particle fountain
+ public final static int EF_FLAG1 = 0x00040000;
+ public final static int EF_FLAG2 = 0x00080000;
+ // RAFAEL
+ public final static int EF_IONRIPPER = 0x00100000;
+ public final static int EF_GREENGIB = 0x00200000;
+ public final static int EF_BLUEHYPERBLASTER = 0x00400000;
+ public final static int EF_SPINNINGLIGHTS = 0x00800000;
+ public final static int EF_PLASMA = 0x01000000;
+ public final static int EF_TRAP = 0x02000000;
+
+ //ROGUE
+ public final static int EF_TRACKER = 0x04000000;
+ public final static int EF_DOUBLE = 0x08000000;
+ public final static int EF_SPHERETRANS = 0x10000000;
+ public final static int EF_TAGTRAIL = 0x20000000;
+ public final static int EF_HALF_DAMAGE = 0x40000000;
+ public final static int EF_TRACKERTRAIL = 0x80000000;
+ //ROGUE
+
+ // entity_state_t->renderfx flags
+ public final static int RF_MINLIGHT = 1; // allways have some light (viewmodel)
+ public final static int RF_VIEWERMODEL = 2; // don't draw through eyes, only mirrors
+ public final static int RF_FULLBRIGHT = 8; // allways draw full intensity
+ public final static int RF_TRANSLUCENT = 32;
+ public final static int RF_FRAMELERP = 64;
+ public final static int RF_SHELL_RED = 1024;
+ public final static int RF_SHELL_GREEN = 2048;
+ public final static int RF_SHELL_BLUE = 4096;
+
+ //ROGUE
+ public final static int RF_IR_VISIBLE = 0x00008000; // 32768
+ public final static int RF_SHELL_DOUBLE = 0x00010000; // 65536
+ public final static int RF_SHELL_HALF_DAM = 0x00020000;
+ public final static int RF_USE_DISGUISE = 0x00040000;
+ //ROGUE
+
+ // player_state_t->refdef flags
+ public final static int RDF_UNDERWATER = 1; // warp the screen as apropriate
+ public final static int RDF_NOWORLDMODEL = 2; // used for player configuration screen
+
+ //ROGUE
+ public final static int RDF_IRGOGGLES = 4;
+ //ROGUE
+
+ public final static int MZ_LOGIN = 9;
+ public final static int MZ_LOGOUT = 10;
+
+ public final static int CHAN_AUTO = 0;
+ public final static int CHAN_VOICE = 2;
+ public final static int CHAN_ITEM = 3;
+ public final static int CHAN_BODY = 4;
+ // modifier flags
+ public final static int CHAN_NO_PHS_ADD = 8;
+ // send to all clients, not just ones in PHS (ATTN 0 will also do this)
+ public final static int CHAN_RELIABLE = 16; // send by reliable message, not datagram
+
+ // sound attenuation values
+ public final static int ATTN_NONE = 0; // full volume the entire level
+ public final static int ATTN_NORM = 1;
+ public final static int ATTN_STATIC = 3; // diminish very rapidly with distance
+
+ // player_state->stats[] indexes
+ public final static int STAT_HEALTH_ICON = 0;
+ public final static int STAT_HEALTH = 1;
+ public final static int STAT_PICKUP_ICON = 7;
+ public final static int STAT_PICKUP_STRING = 8;
+ public final static int STAT_TIMER_ICON = 9;
+ public final static int STAT_TIMER = 10;
+ public final static int STAT_HELPICON = 11;
+ public final static int STAT_SELECTED_ITEM = 12;
+ public final static int STAT_LAYOUTS = 13;
+ public final static int STAT_FRAGS = 14;
+ public final static int STAT_FLASHES = 15; // cleared each frame, 1 = health, 2 = armor
+ public final static int STAT_CHASE = 16;
+ public final static int STAT_SPECTATOR = 17;
+
+ public final static int MAX_STATS = 32;
+
+ // dmflags->value flags
+ public final static int DF_NO_ITEMS = 0x00000002; // 2
+ public final static int DF_NO_FALLING = 0x00000008; // 8
+ public final static int DF_INSTANT_ITEMS = 0x00000010; // 16
+ public final static int DF_SAME_LEVEL = 0x00000020; // 32
+ public final static int DF_SKINTEAMS = 0x00000040; // 64
+ public final static int DF_MODELTEAMS = 0x00000080; // 128
+ public final static int DF_NO_FRIENDLY_FIRE = 0x00000100; // 256
+ public final static int DF_SPAWN_FARTHEST = 0x00000200; // 512
+ public final static int DF_FORCE_RESPAWN = 0x00000400; // 1024
+ public final static int DF_NO_ARMOR = 0x00000800; // 2048
+ public final static int DF_ALLOW_EXIT = 0x00001000; // 4096
+ public final static int DF_INFINITE_AMMO = 0x00002000; // 8192
+ public final static int DF_QUAD_DROP = 0x00004000; // 16384
+ public final static int DF_FIXED_FOV = 0x00008000; // 32768
+
+ // ROGUE
+ public final static int DF_NO_MINES = 0x00020000;
+ public final static int DF_NO_STACK_DOUBLE = 0x00040000;
+ public final static int DF_NO_NUKES = 0x00080000;
+ public final static int DF_NO_SPHERES = 0x00100000;
+ // ROGUE
+
+ //
+ // config strings are a general means of communication from
+ // the server to all connected clients.
+ // Each config string can be at most MAX_QPATH characters.
+ //
+ public final static int CS_NAME = 0;
+ public final static int CS_CDTRACK = 1;
+ public final static int CS_SKY = 2;
+ public final static int CS_SKYAXIS = 3; // %f %f %f format
+ public final static int CS_SKYROTATE = 4;
+ public final static int CS_STATUSBAR = 5; // display program string
+
+ public final static int CS_AIRACCEL = 29; // air acceleration control
+ public final static int CS_MAXCLIENTS = 30;
+ public final static int CS_MAPCHECKSUM = 31; // for catching cheater maps
+
+ public final static int CS_MODELS = 32;
+ public final static int CS_SOUNDS = (CS_MODELS + MAX_MODELS);
+ public final static int CS_IMAGES = (CS_SOUNDS + MAX_SOUNDS);
+ public final static int CS_LIGHTS = (CS_IMAGES + MAX_IMAGES);
+ public final static int CS_ITEMS = (CS_LIGHTS + MAX_LIGHTSTYLES);
+ public final static int CS_PLAYERSKINS = (CS_ITEMS + MAX_ITEMS);
+ public final static int CS_GENERAL = (CS_PLAYERSKINS + MAX_CLIENTS);
+ public final static int MAX_CONFIGSTRINGS = (CS_GENERAL + MAX_GENERAL);
+
+ // gi.BoxEdicts() can return a list of either solid or trigger entities
+ // FIXME: eliminate AREA_ distinction?
+ public final static int AREA_SOLID = 1;
+ public final static int AREA_TRIGGERS = 2;
+
+ public final static int TE_GUNSHOT = 0;
+ public final static int TE_BLOOD = 1;
+ public final static int TE_BLASTER = 2;
+ public final static int TE_RAILTRAIL = 3;
+ public final static int TE_SHOTGUN = 4;
+ public final static int TE_EXPLOSION1 = 5;
+ public final static int TE_EXPLOSION2 = 6;
+ public final static int TE_ROCKET_EXPLOSION = 7;
+ public final static int TE_GRENADE_EXPLOSION = 8;
+ public final static int TE_SPARKS = 9;
+ public final static int TE_SPLASH = 10;
+ public final static int TE_BUBBLETRAIL = 11;
+ public final static int TE_SCREEN_SPARKS = 12;
+ public final static int TE_SHIELD_SPARKS = 13;
+ public final static int TE_BULLET_SPARKS = 14;
+ public final static int TE_LASER_SPARKS = 15;
+ public final static int TE_PARASITE_ATTACK = 16;
+ public final static int TE_ROCKET_EXPLOSION_WATER = 17;
+ public final static int TE_GRENADE_EXPLOSION_WATER = 18;
+ public final static int TE_MEDIC_CABLE_ATTACK = 19;
+ public final static int TE_BFG_EXPLOSION = 20;
+ public final static int TE_BFG_BIGEXPLOSION = 21;
+ public final static int TE_BOSSTPORT = 22; // used as '22' in a map, so DON'T RENUMBER!!!
+ public final static int TE_BFG_LASER = 23;
+ public final static int TE_GRAPPLE_CABLE = 24;
+ public final static int TE_WELDING_SPARKS = 25;
+ public final static int TE_GREENBLOOD = 26;
+ public final static int TE_BLUEHYPERBLASTER = 27;
+ public final static int TE_PLASMA_EXPLOSION = 28;
+ public final static int TE_TUNNEL_SPARKS = 29;
+ //ROGUE
+ public final static int TE_BLASTER2 = 30;
+ public final static int TE_LIGHTNING = 33;
+ public final static int TE_DEBUGTRAIL = 34;
+ public final static int TE_PLAIN_EXPLOSION = 35;
+ public final static int TE_FLASHLIGHT = 36;
+ public final static int TE_FORCEWALL = 37;
+ public final static int TE_HEATBEAM = 38;
+ public final static int TE_MONSTER_HEATBEAM = 39;
+ public final static int TE_STEAM = 40;
+ public final static int TE_BUBBLETRAIL2 = 41;
+ public final static int TE_MOREBLOOD = 42;
+ public final static int TE_HEATBEAM_SPARKS = 43;
+ public final static int TE_HEATBEAM_STEAM = 44;
+ public final static int TE_CHAINFIST_SMOKE = 45;
+ public final static int TE_ELECTRIC_SPARKS = 46;
+ public final static int TE_TRACKER_EXPLOSION = 47;
+ public final static int TE_TELEPORT_EFFECT = 48;
+ public final static int TE_DBALL_GOAL = 49;
+ public final static int TE_WIDOWBEAMOUT = 50;
+ public final static int TE_NUKEBLAST = 51;
+ public final static int TE_WIDOWSPLASH = 52;
+ public final static int TE_EXPLOSION1_BIG = 53;
+ public final static int TE_EXPLOSION1_NP = 54;
+ public final static int TE_FLECHETTE = 55;
+
+ // content masks
+ public final static int MASK_SOLID = (CONTENTS_SOLID | CONTENTS_WINDOW);
+ public final static int MASK_PLAYERSOLID = (CONTENTS_SOLID | CONTENTS_PLAYERCLIP | CONTENTS_WINDOW | CONTENTS_MONSTER);
+ public final static int MASK_DEADSOLID = (CONTENTS_SOLID | CONTENTS_PLAYERCLIP | CONTENTS_WINDOW);
+ public final static int MASK_MONSTERSOLID = (CONTENTS_SOLID | CONTENTS_MONSTERCLIP | CONTENTS_WINDOW | CONTENTS_MONSTER);
+ public final static int MASK_WATER = (CONTENTS_WATER | CONTENTS_LAVA | CONTENTS_SLIME);
+ public final static int MASK_OPAQUE = (CONTENTS_SOLID | CONTENTS_SLIME | CONTENTS_LAVA);
+ public final static int MASK_SHOT = (CONTENTS_SOLID | CONTENTS_MONSTER | CONTENTS_WINDOW | CONTENTS_DEADMONSTER);
+ public final static int MASK_CURRENT =
+ (CONTENTS_CURRENT_0
+ | CONTENTS_CURRENT_90
+ | CONTENTS_CURRENT_180
+ | CONTENTS_CURRENT_270
+ | CONTENTS_CURRENT_UP
+ | CONTENTS_CURRENT_DOWN);
+
+
+ // (machen nur GL)
+ public final static int VIDREF_GL = 1;
+ public final static int VIDREF_SOFT = 2;
+
+
+ public final static int ANIM_BASIC = 0; // stand / run
+ public final static int ANIM_WAVE = 1;
+ public final static int ANIM_JUMP = 2;
+ public final static int ANIM_DEATH = 5;
+ public final static int ANIM_REVERSE = 6;
+
+ // view pitching times
+ public final static float DAMAGE_TIME = 0.5f;
+ public final static float FALL_TIME = 0.3f;
+
+ public final static int DAMAGE_NO_PROTECTION = 0x00000020;
+ // armor, shields, invulnerability, and godmode have no effect
+
+ public final static int DAMAGE_NO = 0;
+ public final static int DAMAGE_YES = 1; // will take damage if hit
+ public final static int DAMAGE_AIM = 2; // auto targeting recognizes this
+
+ public final static int MOD_BLASTER = 1;
+ public final static int MOD_SHOTGUN = 2;
+ public final static int MOD_SSHOTGUN = 3;
+ public final static int MOD_MACHINEGUN = 4;
+ public final static int MOD_CHAINGUN = 5;
+ public final static int MOD_GRENADE = 6;
+ public final static int MOD_G_SPLASH = 7;
+ public final static int MOD_ROCKET = 8;
+ public final static int MOD_R_SPLASH = 9;
+ public final static int MOD_HYPERBLASTER = 10;
+ public final static int MOD_RAILGUN = 11;
+ public final static int MOD_BFG_LASER = 12;
+ public final static int MOD_BFG_BLAST = 13;
+ public final static int MOD_BFG_EFFECT = 14;
+ public final static int MOD_HANDGRENADE = 15;
+ public final static int MOD_HG_SPLASH = 16;
+ public final static int MOD_WATER = 17;
+ public final static int MOD_SLIME = 18;
+ public final static int MOD_LAVA = 19;
+ public final static int MOD_CRUSH = 20;
+ public final static int MOD_TELEFRAG = 21;
+ public final static int MOD_FALLING = 22;
+ public final static int MOD_SUICIDE = 23;
+ public final static int MOD_HELD_GRENADE = 24;
+ public final static int MOD_EXPLOSIVE = 25;
+ public final static int MOD_BARREL = 26;
+ public final static int MOD_BOMB = 27;
+ public final static int MOD_EXIT = 28;
+ public final static int MOD_SPLASH = 29;
+ public final static int MOD_TARGET_LASER = 30;
+ public final static int MOD_TRIGGER_HURT = 31;
+ public final static int MOD_TARGET_BLASTER = 33;
+ public final static int MOD_FRIENDLY_FIRE = 0x8000000;
+
+ // edict->spawnflags
+ // these are set with checkboxes on each entity in the map editor
+ public final static int SPAWNFLAG_NOT_EASY = 0x00000100;
+ public final static int SPAWNFLAG_NOT_MEDIUM = 0x00000200;
+ public final static int SPAWNFLAG_NOT_HARD = 0x00000400;
+ public final static int SPAWNFLAG_NOT_DEATHMATCH = 0x00000800;
+ public final static int SPAWNFLAG_NOT_COOP = 0x00001000;
+
+ // edict->flags
+ public final static int FL_FLY = 0x00000001;
+ public final static int FL_SWIM = 0x00000002; // implied immunity to drowining
+ public final static int FL_INWATER = 0x00000008;
+ public final static int FL_GODMODE = 0x00000010;
+ public final static int FL_NOTARGET = 0x00000020;
+ public final static int FL_PARTIALGROUND = 0x00000100; // not all corners are valid
+ public final static int FL_TEAMSLAVE = 0x00000400; // not the first on the team
+ public final static int FL_NO_KNOCKBACK = 0x00000800;
+ public final static int FL_POWER_ARMOR = 0x00001000; // power armor (if any) is active
+
+ public final static float FRAMETIME = 0.1f;
+
+
+ public final static int BODY_QUEUE_SIZE = 8;
+
+ public final static int AI_GOOD_GUY = 0x00000100;
+ public final static int AI_NOSTEP = 0x00000400;
+ public final static int AI_COMBAT_POINT = 0x00001000;
+
+ public final static int SFL_CROSS_TRIGGER_MASK = 0x000000ff;
+ // edict->movetype values
+ public final static int MOVETYPE_NONE = 0; // never moves
+ public final static int MOVETYPE_NOCLIP = 1; // origin and angles change with no interaction
+ public final static int MOVETYPE_PUSH = 2; // no clip to world, push on box contact
+ public final static int MOVETYPE_STOP = 3; // no clip to world, stops on box contact
+
+ public final static int MOVETYPE_WALK = 4; // gravity
+ public final static int MOVETYPE_STEP = 5; // gravity, special edge handling
+ public final static int MOVETYPE_FLY = 6;
+ public final static int MOVETYPE_TOSS = 7; // gravity
+ public final static int MOVETYPE_FLYMISSILE = 8; // extra size to monsters
+ public final static int MOVETYPE_BOUNCE = 9;
+
+ public final static int MULTICAST_ALL = 0;
+ public final static int MULTICAST_PHS = 1;
+ public final static int MULTICAST_PVS = 2;
+ public final static int MULTICAST_ALL_R = 3;
+ public final static int MULTICAST_PHS_R = 4;
+ public final static int MULTICAST_PVS_R = 5;
+
+ // -------------
+ // client/game.h
+
+ public final static int SOLID_NOT = 0; // no interaction with other objects
+ public final static int SOLID_TRIGGER = 1; // only touch when inside, after moving
+ public final static int SOLID_BBOX = 2; // touch on edge
+ public final static int SOLID_BSP = 3; // bsp clip, touch on edge
+
+ // edict->svflags
+ public final static int SVF_NOCLIENT = 0x00000001; // don't send entity to clients, even if it has effects
+ public final static int SVF_DEADMONSTER = 0x00000002; // treat as CONTENTS_DEADMONSTER for collision
+ public final static int SVF_MONSTER = 0x00000004; // treat as CONTENTS_MONSTER for collision
+
+ public final static int MAX_ENT_CLUSTERS = 16;
+
+ public final static int sv_stopspeed = 100;
+ public final static int sv_friction = 6;
+ public final static int sv_waterfriction = 1;
+
+
+ // R E N D E R E R
+ ////////////////////
+ public static final int MAX_DLIGHTS = 32;
+ public static final int MAX_ENTITIES = 128;
+ public static final int MAX_PARTICLES = 4096;
+
+ // gl_model.h
+ public static final int SURF_PLANEBACK = 2;
+ public static final int SURF_DRAWSKY = 4;
+ public static final int SURF_DRAWTURB = 0x10;
+
+ public static final float POWERSUIT_SCALE = 4.0f;
+
+ // protocol bytes that can be directly added to messages
+
+ public final static int svc_temp_entity = 3;
+ public final static int svc_layout = 4;
+ public final static int svc_inventory = 5;
+
+ // the rest are private to the client and server
+ public final static int svc_nop = 6;
+ public final static int svc_disconnect = 7;
+ public final static int svc_reconnect = 8;
+ public final static int svc_sound = 9; // <see code>
+ public final static int svc_print = 10; // [byte] id [string] null terminated string
+ public final static int svc_stufftext = 11;
+ // [string] stuffed into client's console buffer, should be \n terminated
+ public final static int svc_serverdata = 12; // [long] protocol ...
+ public final static int svc_configstring = 13; // [short] [string]
+ public final static int svc_spawnbaseline = 14;
+ public final static int svc_centerprint = 15; // [string] to put in center of the screen
+ public final static int svc_download = 16; // [short] size [size bytes]
+ public final static int svc_playerinfo = 17; // variable
+ public final static int svc_packetentities = 18; // [...]
+ public final static int svc_deltapacketentities = 19; // [...]
+ public final static int svc_frame = 20;
+
+ public static final int NUMVERTEXNORMALS = 162;
+ public static final int PROTOCOL_VERSION = 34;
+ public final static int PORT_MASTER = 27900;
+ public final static int PORT_CLIENT = 27901;
+ public final static int PORT_SERVER = 27910;
+ public final static int PORT_ANY = -1;
+
+ public final static int PS_M_TYPE = (1);
+ public final static int PS_M_ORIGIN = (1 << 1);
+ public final static int PS_M_VELOCITY = (1 << 2);
+ public final static int PS_M_TIME = (1 << 3);
+ public final static int PS_M_FLAGS = (1 << 4);
+ public final static int PS_M_GRAVITY = (1 << 5);
+ public final static int PS_M_DELTA_ANGLES = (1 << 6);
+
+ public final static int UPDATE_BACKUP = 16; // copies of entity_state_t to keep buffered
+ // must be power of two
+ public final static int UPDATE_MASK = (UPDATE_BACKUP - 1);
+
+ public final static int PS_VIEWOFFSET = (1 << 7);
+ public final static int PS_VIEWANGLES = (1 << 8);
+ public final static int PS_KICKANGLES = (1 << 9);
+ public final static int PS_BLEND = (1 << 10);
+ public final static int PS_FOV = (1 << 11);
+ public final static int PS_WEAPONINDEX = (1 << 12);
+ public final static int PS_RDFLAGS = (1 << 14);
+
+ public static final int CM_ANGLE1 = (1);
+ public static final int CM_ANGLE2 = (1 << 1);
+ public static final int CM_ANGLE3 = (1 << 2);
+ public static final int CM_FORWARD = (1 << 3);
+ public static final int CM_SIDE = (1 << 4);
+ public static final int CM_UP = (1 << 5);
+ public static final int CM_BUTTONS = (1 << 6);
+ public static final int CM_IMPULSE = (1 << 7);
+
+ // try to pack the common update flags into the first byte
+ public final static int U_ORIGIN1 = (1);
+ public final static int U_ORIGIN2 = (1 << 1);
+ public final static int U_ANGLE2 = (1 << 2);
+ public final static int U_ANGLE3 = (1 << 3);
+ public final static int U_FRAME8 = (1 << 4); // frame is a byte
+ public final static int U_EVENT = (1 << 5);
+ public final static int U_REMOVE = (1 << 6); // REMOVE this entity, don't add it
+ public final static int U_MOREBITS1 = (1 << 7); // read one additional byte
+
+ // second byte
+ public final static int U_NUMBER16 = (1 << 8); // NUMBER8 is implicit if not set
+ public final static int U_ORIGIN3 = (1 << 9);
+ public final static int U_ANGLE1 = (1 << 10);
+ public final static int U_MODEL = (1 << 11);
+ public final static int U_RENDERFX8 = (1 << 12); // fullbright, etc
+ public final static int U_EFFECTS8 = (1 << 14); // autorotate, trails, etc
+ public final static int U_MOREBITS2 = (1 << 15); // read one additional byte
+
+ // third byte
+ public final static int U_SKIN8 = (1 << 16);
+ public final static int U_FRAME16 = (1 << 17); // frame is a short
+ public final static int U_RENDERFX16 = (1 << 18); // 8 + 16 = 32
+ public final static int U_EFFECTS16 = (1 << 19); // 8 + 16 = 32
+ public final static int U_MODEL2 = (1 << 20); // weapons, flags, etc
+ public final static int U_MODEL3 = (1 << 21);
+ public final static int U_MODEL4 = (1 << 22);
+ public final static int U_MOREBITS3 = (1 << 23); // read one additional byte
+
+ // fourth byte
+ public final static int U_OLDORIGIN = (1 << 24); // FIXME: get rid of this
+ public final static int U_SKIN16 = (1 << 25);
+ public final static int U_SOUND = (1 << 26);
+ public final static int U_SOLID = (1 << 27);
+
+ public static final int MAX_MD2SKINS = 32;
+ public static final int MAX_SKINNAME = 64;
+
+ public static final int MAXLIGHTMAPS = 4;
+
+ public static final int clc_nop = 1;
+ public static final int clc_move = 2; // [[usercmd_t]
+ public static final int clc_userinfo = 3; // [[userinfo string]
+ public static final int clc_stringcmd = 4; // [string] message
+
+ public static final int NS_CLIENT = 0;
+ public static final int NS_SERVER = 1;
+
+ public static final int NA_LOOPBACK = 0;
+ public static final int NA_BROADCAST = 1;
+ public static final int NA_IP = 2;
+ public static final int NA_BROADCAST_IPX = 4;
+
+ public final static int SND_VOLUME = (1); // a byte
+ public final static int SND_ATTENUATION = (1 << 1); // a byte
+ public final static int SND_POS = (1 << 2); // three coordinates
+ public final static int SND_ENT = (1 << 3); // a short 0-2: channel, 3-12: entity
+ public final static int SND_OFFSET = (1 << 4); // a byte, msec offset from frame start
+
+ public final static float DEFAULT_SOUND_PACKET_VOLUME = 1.0f;
+ public final static float DEFAULT_SOUND_PACKET_ATTENUATION = 1.0f;
+
+ // --------
+ // client.h
+ public static final int MAX_PARSE_ENTITIES = 1024;
+ public static final int ca_uninitialized = 0;
+ public static final int ca_disconnected = 1;
+ public static final int ca_connecting = 2;
+ public static final int ca_connected = 3;
+ public static final int ca_active = 4;
+ public static final int MAX_ALIAS_NAME = 32;
+ public static final int MAX_NUM_ARGVS = 50;
+ public static final int MAX_MSGLEN = 1400;
+ // ---------
+ // console.h
+ public static final int NUM_CON_TIMES = 4;
+ public static final int CON_TEXTSIZE = 32768;
+ public final static int BSPVERSION = 38;
+ // upper design bounds
+ // leaffaces, leafbrushes, planes, and verts are still bounded by
+ // 16 bit short limits
+ public final static int MAX_MAP_MODELS = 1024;
+
+ // --------
+ // qfiles.h
+ public final static int MAX_MAP_BRUSHES = 8192;
+ public final static int MAX_MAP_ENTSTRING = 0x40000;
+ public final static int MAX_MAP_TEXINFO = 8192;
+ public final static int MAX_MAP_AREAS = 256;
+ public final static int MAX_MAP_AREAPORTALS = 1024;
+ public final static int MAX_MAP_PLANES = 65536;
+ public final static int MAX_MAP_NODES = 65536;
+ public final static int MAX_MAP_BRUSHSIDES = 65536;
+ public final static int MAX_MAP_LEAFS = 65536;
+ public final static int MAX_MAP_LEAFBRUSHES = 65536;
+ public final static int MAX_MAP_SURFEDGES = 256000;
+ public final static int MAX_MAP_VISIBILITY = 0x100000;
+ // 0-2 are axial planes
+ public final static int PLANE_X = 0;
+ public final static int PLANE_Y = 1;
+ public final static int PLANE_Z = 2;
+ public final static int PLANE_ANYZ = 5;
+ public final static int LUMP_ENTITIES = 0;
+ public final static int LUMP_PLANES = 1;
+ public final static int LUMP_VERTEXES = 2;
+ public final static int LUMP_VISIBILITY = 3;
+ public final static int LUMP_NODES = 4;
+ public final static int LUMP_TEXINFO = 5;
+ public final static int LUMP_FACES = 6;
+ public final static int LUMP_LIGHTING = 7;
+ public final static int LUMP_LEAFS = 8;
+ public final static int LUMP_LEAFFACES = 9;
+ public final static int LUMP_LEAFBRUSHES = 10;
+ public final static int LUMP_EDGES = 11;
+ public final static int LUMP_SURFEDGES = 12;
+ public final static int LUMP_MODELS = 13;
+ public final static int LUMP_BRUSHES = 14;
+ public final static int LUMP_BRUSHSIDES = 15;
+ public final static int LUMP_AREAS = 17;
+ public final static int LUMP_AREAPORTALS = 18;
+ public final static int HEADER_LUMPS = 19;
+ public final static int ALIAS_VERSION = 8;
+ public static final String GAMEVERSION = "baseq2";
+ public static final int API_VERSION = 3; // ref_library (refexport_t)
+ public final static int DVIS_PVS = 0;
+ public final static int DVIS_PHS = 1;
+ // ----------------
+ // client/keydest_t
+ public static final int key_game = 0;
+ public static final int key_console = 1;
+ public static final int key_message = 2;
+ public static final int key_menu = 3;
+ // ---------------
+ // server/server.h
+ public static final int cs_free = 0; // can be reused for a new connection
+ public static final int cs_zombie = 1; // client has been disconnected, but don't reuse
+ // connection for a couple seconds
+ public static final int cs_connected = 2; // has been assigned to a client_t, but not in game yet
+ public static final int cs_spawned = 3;
+ public static final int MAX_CHALLENGES = 1024;
+ public static final int ss_dead = 0; // no map loaded
+ public static final int ss_loading = 1; // spawning level edicts
+ public static final int ss_game = 2; // actively running
+ public static final int ss_cinematic = 3;
+ public static final int ss_demo = 4;
+ public static final int ss_pic = 5;
+ public final static int SV_OUTPUTBUF_LENGTH = (MAX_MSGLEN - 16);
+ public final static int RD_CLIENT = 1;
+ public final static int RD_PACKET = 2;
+ public final static int RATE_MESSAGES = 10;
+ public final static int LATENCY_COUNTS = 16;
+ public static final int MAXCMDLINE = 256;
+ public static final int MAX_MASTERS = 8;
+ //server/sv_world.h
+ public static final int AREA_DEPTH = 4;
+ public static final int AREA_NODES = 32;
+ public static final int EXEC_NOW = 0;
+ public static final int EXEC_INSERT = 1;
+ public static final int EXEC_APPEND = 2;
+ //client/qmenu.h
+ public final static int MAXMENUITEMS = 64;
+ public final static int MTYPE_SLIDER = 0;
+ public final static int MTYPE_LIST = 1;
+ public final static int MTYPE_ACTION = 2;
+ public final static int MTYPE_SPINCONTROL = 3;
+ public final static int MTYPE_SEPARATOR = 4;
+ public final static int MTYPE_FIELD = 5;
+ public final static int K_ENTER = 13;
+ public final static int K_ESCAPE = 27;
+
+ // normal keys should be passed as lowercased ascii
+ public final static int K_UPARROW = 128;
+ public final static int K_DOWNARROW = 129;
+ public final static int K_LEFTARROW = 130;
+ public final static int K_RIGHTARROW = 131;
+ public final static int QMF_LEFT_JUSTIFY = 0x00000001;
+ public final static int QMF_GRAYED = 0x00000002;
+ public final static int QMF_NUMBERSONLY = 0x00000004;
+ public final static int RCOLUMN_OFFSET = 16;
+ public final static int LCOLUMN_OFFSET = -16;
+ public final static int MAX_PLAYERMODELS = 1024;
+ public final static int MAX_LOCAL_SERVERS = 8;
+ public final static String NO_SERVER_STRING = "<no server>";
+ public final static int NUM_ADDRESSBOOK_ENTRIES = 9;
+ public final static int STEPSIZE = 18;
+ public static final float MOVE_STOP_EPSILON = 0.1f;
+ public final static float MIN_STEP_NORMAL = 0.7f; // can't step up onto very steep slopes
+ // datentyp konstanten
+ // groesse in bytes
+ public final static boolean LITTLE_ENDIAN = (ByteOrder.nativeOrder() == ByteOrder.LITTLE_ENDIAN);
+ public final static int SIZE_OF_SHORT = 2;
+ public final static int SIZE_OF_INT = 4;
+ public final static int SIZE_OF_FLOAT = 4;
+ public static final int CMD_BACKUP = 64; // allow a lot of command backups for very fast systems
+
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (C) 2011 Ethan "flibitijibibo" Lee
+ *
+ * This file is part of flibitEFX.
+ *
+ * flibitEFX is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * flibitEFX 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 Lesser General Public License for more details.
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with flibitEFX. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package lwjake2.EFX;
+
+import org.lwjgl.openal.EFX10;
+
+/**
+ * @author Ethan "flibitijibibo" Lee
+ */
+
+public abstract class EFXEffect {
+
+ private final int slotIndex;
+ private final int effectIndex;
+
+ /**
+ * Constructor creates an EFX effect slot containing an effect.
+ *
+ * @param efxEffect The EFX10 effect type (i.e. EFX10.AL_EFFECT_REVERB)
+ */
+ public EFXEffect(int efxEffect) {
+ slotIndex = EFX10.alGenAuxiliaryEffectSlots();
+ effectIndex = EFX10.alGenEffects();
+ // TODO: Check to see if efxEffect is a valid AL_EFFECT_TYPE.
+ EFX10.alEffecti(effectIndex, EFX10.AL_EFFECT_TYPE, efxEffect);
+ EFX10.alAuxiliaryEffectSloti(slotIndex, EFX10.AL_EFFECTSLOT_EFFECT, effectIndex);
+ }
+
+ /**
+ * Unloads the EFX effect and effect slot.
+ */
+ public void killEffect() {
+ EFX10.alDeleteEffects(effectIndex);
+ EFX10.alDeleteAuxiliaryEffectSlots(slotIndex);
+ }
+
+ /**
+ * Returns the index of the EFX effect slot.
+ *
+ * @return The index of the EFX effect slot
+ */
+ public int getIndex() {
+ return slotIndex;
+ }
+
+ /**
+ * Applies an effect property to this EFX effect.
+ *
+ * @param passedProperty The EFX10 effect property
+ * @param passedValue The EFX10 effect value
+ */
+ protected void addEffectf(int passedProperty, float passedValue) {
+ EFX10.alEffectf(effectIndex, passedProperty, passedValue);
+ }
+
+ /**
+ * Applies an effect property to this EFX effect.
+ *
+ * @param passedValue The EFX10 effect value
+ */
+ protected void addEffecti(int passedValue) {
+ EFX10.alEffecti(effectIndex, EFX10.AL_RING_MODULATOR_WAVEFORM, passedValue);
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (C) 2011 Ethan "flibitijibibo" Lee
+ *
+ * This file is part of flibitEFX.
+ *
+ * flibitEFX is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * flibitEFX 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 Lesser General Public License for more details.
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with flibitEFX. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package lwjake2.EFX;
+
+import org.lwjgl.openal.EFX10;
+
+/**
+ * @author Ethan "flibitijibibo" Lee
+ */
+
+public class EFXEffectEcho extends EFXEffect {
+
+ /**
+ * Constructor creates a generic echo effect.
+ */
+ public EFXEffectEcho() {
+ super(EFX10.AL_EFFECT_ECHO);
+ }
+
+ /**
+ * Sets the amount of high frequency damping to apply to the echo effect.
+ *
+ * @param passedValue The new damping value to apply to the echo effect
+ */
+ public void setDamping(float passedValue) {
+ if (passedValue < EFX10.AL_ECHO_MIN_DAMPING)
+ addEffectf(EFX10.AL_ECHO_DAMPING, EFX10.AL_ECHO_MIN_DAMPING);
+ else if (passedValue > EFX10.AL_ECHO_MAX_DAMPING)
+ addEffectf(EFX10.AL_ECHO_DAMPING, EFX10.AL_ECHO_MAX_DAMPING);
+ else
+ addEffectf(EFX10.AL_ECHO_DAMPING, passedValue);
+ }
+
+ /**
+ * Sets the echo effect's delay time.
+ *
+ * @param passedValue The echo effect's new delay time
+ */
+ public void setDelay(float passedValue) {
+ if (passedValue < EFX10.AL_ECHO_MIN_DELAY)
+ addEffectf(EFX10.AL_ECHO_DELAY, EFX10.AL_ECHO_MIN_DELAY);
+ else if (passedValue > EFX10.AL_ECHO_MAX_DELAY)
+ addEffectf(EFX10.AL_ECHO_DELAY, EFX10.AL_ECHO_MAX_DELAY);
+ else
+ addEffectf(EFX10.AL_ECHO_DELAY, passedValue);
+ }
+
+ /**
+ * Sets the amount of feedback to echo back.
+ *
+ * @param passedValue The new feedback volume of the echo effect
+ */
+ public void setFeedback(float passedValue) {
+ if (passedValue < EFX10.AL_ECHO_MIN_FEEDBACK)
+ addEffectf(EFX10.AL_ECHO_FEEDBACK, EFX10.AL_ECHO_MIN_FEEDBACK);
+ else if (passedValue > EFX10.AL_ECHO_MAX_FEEDBACK)
+ addEffectf(EFX10.AL_ECHO_FEEDBACK, EFX10.AL_ECHO_MAX_FEEDBACK);
+ else
+ addEffectf(EFX10.AL_ECHO_FEEDBACK, passedValue);
+ }
+
+ /**
+ * Sets the delay between each echo "tap".
+ *
+ * @param passedValue The new delay time between each echo "tap"
+ */
+ public void setLRDelay(float passedValue) {
+ if (passedValue < EFX10.AL_ECHO_MIN_LRDELAY)
+ addEffectf(EFX10.AL_ECHO_LRDELAY, EFX10.AL_ECHO_MIN_LRDELAY);
+ else if (passedValue > EFX10.AL_ECHO_MAX_LRDELAY)
+ addEffectf(EFX10.AL_ECHO_LRDELAY, EFX10.AL_ECHO_MAX_LRDELAY);
+ else
+ addEffectf(EFX10.AL_ECHO_LRDELAY, passedValue);
+ }
+
+ /**
+ * Sets the amount of hard panning allowed for each echo.
+ *
+ * @param passedValue The new level of flexibility for the echo effect's panning
+ */
+ public void setSpread(float passedValue) {
+ if (passedValue < EFX10.AL_ECHO_MIN_SPREAD)
+ addEffectf(EFX10.AL_ECHO_SPREAD, EFX10.AL_ECHO_MIN_SPREAD);
+ else if (passedValue > EFX10.AL_ECHO_MAX_SPREAD)
+ addEffectf(EFX10.AL_ECHO_SPREAD, EFX10.AL_ECHO_MAX_SPREAD);
+ else
+ addEffectf(EFX10.AL_ECHO_SPREAD, passedValue);
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (C) 2011 Ethan "flibitijibibo" Lee
+ *
+ * This file is part of flibitEFX.
+ *
+ * flibitEFX is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * flibitEFX 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 Lesser General Public License for more details.
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with flibitEFX. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package lwjake2.EFX;
+
+import org.lwjgl.openal.EFX10;
+
+/**
+ * @author Ethan "flibitijibibo" Lee
+ */
+
+public class EFXEffectReverb extends EFXEffect {
+
+ /**
+ * Constructor creates a generic reverb effect.
+ */
+ public EFXEffectReverb() {
+ super(EFX10.AL_EFFECT_REVERB);
+ }
+
+ // TODO: Add methods for all AL_EFFECT_REVERB properties.
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (C) 2011 Ethan "flibitijibibo" Lee
+ *
+ * This file is part of flibitEFX.
+ *
+ * flibitEFX is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * flibitEFX 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 Lesser General Public License for more details.
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with flibitEFX. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package lwjake2.EFX;
+
+import org.lwjgl.openal.EFX10;
+
+/**
+ * @author Ethan "flibitijibibo" Lee
+ */
+
+public class EFXEffectRingModulator extends EFXEffect {
+
+ public static final int SINUSOID = EFX10.AL_RING_MODULATOR_SINUSOID;
+ public static final int SAWTOOTH = EFX10.AL_RING_MODULATOR_SAWTOOTH;
+ public static final int SQUARE = EFX10.AL_RING_MODULATOR_SQUARE;
+
+ /**
+ * Constructor creates a generic ring modulator effect.
+ */
+ public EFXEffectRingModulator() {
+ super(EFX10.AL_EFFECT_RING_MODULATOR);
+ }
+
+ /**
+ * Sets the frequency of the ring modulator.
+ *
+ * @param passedValue The new frequency of the ring modulator
+ */
+ public void setFrequency(float passedValue) {
+ if (passedValue > EFX10.AL_RING_MODULATOR_MAX_FREQUENCY)
+ addEffectf(EFX10.AL_RING_MODULATOR_FREQUENCY, EFX10.AL_RING_MODULATOR_MAX_FREQUENCY);
+ else if (passedValue < EFX10.AL_RING_MODULATOR_MIN_FREQUENCY)
+ addEffectf(EFX10.AL_RING_MODULATOR_FREQUENCY, EFX10.AL_RING_MODULATOR_MIN_FREQUENCY);
+ else
+ addEffectf(EFX10.AL_RING_MODULATOR_FREQUENCY, passedValue);
+ }
+
+ /**
+ * Sets the high-pass cutoff for the ring modulator.
+ *
+ * @param passedValue The new cutoff value for the ring modulator high-pass filter
+ */
+ public void setHighPassCutoff(float passedValue) {
+ if (passedValue > EFX10.AL_RING_MODULATOR_MAX_HIGHPASS_CUTOFF)
+ addEffectf(EFX10.AL_RING_MODULATOR_HIGHPASS_CUTOFF, EFX10.AL_RING_MODULATOR_MAX_HIGHPASS_CUTOFF);
+ else if (passedValue < EFX10.AL_RING_MODULATOR_MIN_HIGHPASS_CUTOFF)
+ addEffectf(EFX10.AL_RING_MODULATOR_HIGHPASS_CUTOFF, EFX10.AL_RING_MODULATOR_MIN_HIGHPASS_CUTOFF);
+ else
+ addEffectf(EFX10.AL_RING_MODULATOR_HIGHPASS_CUTOFF, passedValue);
+ }
+
+ /**
+ * Sets the waveform of the ring modulator.
+ *
+ * @param waveform Should be EFXEffectRingModulator.SINUSOID, SAWTOOTH or SQUARE
+ */
+ public void setWaveform(int waveform) {
+ if (waveform != SINUSOID && waveform != SAWTOOTH && waveform != SQUARE)
+ return;
+ addEffecti(waveform);
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (C) 2011 Ethan "flibitijibibo" Lee
+ *
+ * This file is part of flibitEFX.
+ *
+ * flibitEFX is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * flibitEFX 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 Lesser General Public License for more details.
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with flibitEFX. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package lwjake2.EFX;
+
+import org.lwjgl.openal.EFX10;
+
+/**
+ * @author Ethan "flibitijibibo" Lee
+ */
+
+public abstract class EFXFilter {
+
+ private final int filterIndex;
+
+ /**
+ * Constructor creates an EFX effect slot containing a filter.
+ *
+ * @param efxFilter The EFX10 filter type (i.e. EFX10.AL_FILTER_HIGHPASS)
+ */
+ public EFXFilter(int efxFilter) {
+ filterIndex = EFX10.alGenFilters();
+ // TODO: Check to see if efxFilter is a valid AL_FILTER_TYPE.
+ EFX10.alFilteri(filterIndex, EFX10.AL_FILTER_TYPE, efxFilter);
+ }
+
+ /**
+ * Unloads the EFX filter.
+ */
+ public void killFilter() {
+ EFX10.alDeleteFilters(filterIndex);
+ }
+
+ /**
+ * Returns the index of the EFX filter.
+ *
+ * @return The index of the EFX filter
+ */
+ public int getIndex() {
+ return filterIndex;
+ }
+
+ /**
+ * Applies a filter property to this EFX filter.
+ *
+ * @param passedProperty The EFX10 filter property
+ * @param passedValue The EFX10 filter value
+ */
+ protected void addFilter(int passedProperty, float passedValue) {
+ EFX10.alFilterf(filterIndex, passedProperty, passedValue);
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (C) 2011 Ethan "flibitijibibo" Lee
+ *
+ * This file is part of flibitEFX.
+ *
+ * flibitEFX is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * flibitEFX 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 Lesser General Public License for more details.
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with flibitEFX. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package lwjake2.EFX;
+
+import org.lwjgl.openal.EFX10;
+
+/**
+ * @author Ethan "flibitijibibo" Lee
+ */
+
+public class EFXFilterLowPass extends EFXFilter {
+
+ /**
+ * Constructor creates a generic low-pass filter.
+ */
+ public EFXFilterLowPass() {
+ super(EFX10.AL_FILTER_LOWPASS);
+ }
+
+ /**
+ * Sets the gain of the low-pass filter.
+ *
+ * @param passedValue The new value of the low-pass filter gain
+ */
+ public void setGain(float passedValue) {
+ addFilter(EFX10.AL_LOWPASS_GAIN, passedValue);
+ }
+
+ /**
+ * Sets the gain of the low-pass filter's high frequencies.
+ *
+ * @param passedValue The new value of the low-pass filter's HF gain
+ */
+ public void setGainHF(float passedValue) {
+ addFilter(EFX10.AL_LOWPASS_GAINHF, passedValue);
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2;
+
+import lwjake2.client.*;
+import lwjake2.game.CvarT;
+import lwjake2.game.cmdalias_t;
+import lwjake2.game.entity_state_t;
+import lwjake2.qcommon.NetadrT;
+import lwjake2.qcommon.sizebuf_t;
+import lwjake2.render.DummyRenderer;
+
+import java.io.RandomAccessFile;
+import java.util.Random;
+
+/**
+ * Globals ist the collection of global variables and constants.
+ * It is more elegant to use these vars by inheritance to separate
+ * it with eclipse refactoring later.
+ * <p>
+ * As consequence you don't have to touch that much code this time.
+ */
+public class Globals extends Defines {
+
+ public static final String __DATE__ = "2003";
+
+ public static final float VERSION = 3.21f;
+
+ public static final String BASEDIRNAME = "baseq2";
+ // client/anorms.h
+ public static final float bytedirs[][] = { /**
+ */
+ {-0.525731f, 0.000000f, 0.850651f}, {
+ -0.442863f, 0.238856f, 0.864188f}, {
+ -0.295242f, 0.000000f, 0.955423f}, {
+ -0.309017f, 0.500000f, 0.809017f}, {
+ -0.162460f, 0.262866f, 0.951056f}, {
+ 0.000000f, 0.000000f, 1.000000f}, {
+ 0.000000f, 0.850651f, 0.525731f}, {
+ -0.147621f, 0.716567f, 0.681718f}, {
+ 0.147621f, 0.716567f, 0.681718f}, {
+ 0.000000f, 0.525731f, 0.850651f}, {
+ 0.309017f, 0.500000f, 0.809017f}, {
+ 0.525731f, 0.000000f, 0.850651f}, {
+ 0.295242f, 0.000000f, 0.955423f}, {
+ 0.442863f, 0.238856f, 0.864188f}, {
+ 0.162460f, 0.262866f, 0.951056f}, {
+ -0.681718f, 0.147621f, 0.716567f}, {
+ -0.809017f, 0.309017f, 0.500000f}, {
+ -0.587785f, 0.425325f, 0.688191f}, {
+ -0.850651f, 0.525731f, 0.000000f}, {
+ -0.864188f, 0.442863f, 0.238856f}, {
+ -0.716567f, 0.681718f, 0.147621f}, {
+ -0.688191f, 0.587785f, 0.425325f}, {
+ -0.500000f, 0.809017f, 0.309017f}, {
+ -0.238856f, 0.864188f, 0.442863f}, {
+ -0.425325f, 0.688191f, 0.587785f}, {
+ -0.716567f, 0.681718f, -0.147621f}, {
+ -0.500000f, 0.809017f, -0.309017f}, {
+ -0.525731f, 0.850651f, 0.000000f}, {
+ 0.000000f, 0.850651f, -0.525731f}, {
+ -0.238856f, 0.864188f, -0.442863f}, {
+ 0.000000f, 0.955423f, -0.295242f}, {
+ -0.262866f, 0.951056f, -0.162460f}, {
+ 0.000000f, 1.000000f, 0.000000f}, {
+ 0.000000f, 0.955423f, 0.295242f}, {
+ -0.262866f, 0.951056f, 0.162460f}, {
+ 0.238856f, 0.864188f, 0.442863f}, {
+ 0.262866f, 0.951056f, 0.162460f}, {
+ 0.500000f, 0.809017f, 0.309017f}, {
+ 0.238856f, 0.864188f, -0.442863f}, {
+ 0.262866f, 0.951056f, -0.162460f}, {
+ 0.500000f, 0.809017f, -0.309017f}, {
+ 0.850651f, 0.525731f, 0.000000f}, {
+ 0.716567f, 0.681718f, 0.147621f}, {
+ 0.716567f, 0.681718f, -0.147621f}, {
+ 0.525731f, 0.850651f, 0.000000f}, {
+ 0.425325f, 0.688191f, 0.587785f}, {
+ 0.864188f, 0.442863f, 0.238856f}, {
+ 0.688191f, 0.587785f, 0.425325f}, {
+ 0.809017f, 0.309017f, 0.500000f}, {
+ 0.681718f, 0.147621f, 0.716567f}, {
+ 0.587785f, 0.425325f, 0.688191f}, {
+ 0.955423f, 0.295242f, 0.000000f}, {
+ 1.000000f, 0.000000f, 0.000000f}, {
+ 0.951056f, 0.162460f, 0.262866f}, {
+ 0.850651f, -0.525731f, 0.000000f}, {
+ 0.955423f, -0.295242f, 0.000000f}, {
+ 0.864188f, -0.442863f, 0.238856f}, {
+ 0.951056f, -0.162460f, 0.262866f}, {
+ 0.809017f, -0.309017f, 0.500000f}, {
+ 0.681718f, -0.147621f, 0.716567f}, {
+ 0.850651f, 0.000000f, 0.525731f}, {
+ 0.864188f, 0.442863f, -0.238856f}, {
+ 0.809017f, 0.309017f, -0.500000f}, {
+ 0.951056f, 0.162460f, -0.262866f}, {
+ 0.525731f, 0.000000f, -0.850651f}, {
+ 0.681718f, 0.147621f, -0.716567f}, {
+ 0.681718f, -0.147621f, -0.716567f}, {
+ 0.850651f, 0.000000f, -0.525731f}, {
+ 0.809017f, -0.309017f, -0.500000f}, {
+ 0.864188f, -0.442863f, -0.238856f}, {
+ 0.951056f, -0.162460f, -0.262866f}, {
+ 0.147621f, 0.716567f, -0.681718f}, {
+ 0.309017f, 0.500000f, -0.809017f}, {
+ 0.425325f, 0.688191f, -0.587785f}, {
+ 0.442863f, 0.238856f, -0.864188f}, {
+ 0.587785f, 0.425325f, -0.688191f}, {
+ 0.688191f, 0.587785f, -0.425325f}, {
+ -0.147621f, 0.716567f, -0.681718f}, {
+ -0.309017f, 0.500000f, -0.809017f}, {
+ 0.000000f, 0.525731f, -0.850651f}, {
+ -0.525731f, 0.000000f, -0.850651f}, {
+ -0.442863f, 0.238856f, -0.864188f}, {
+ -0.295242f, 0.000000f, -0.955423f}, {
+ -0.162460f, 0.262866f, -0.951056f}, {
+ 0.000000f, 0.000000f, -1.000000f}, {
+ 0.295242f, 0.000000f, -0.955423f}, {
+ 0.162460f, 0.262866f, -0.951056f}, {
+ -0.442863f, -0.238856f, -0.864188f}, {
+ -0.309017f, -0.500000f, -0.809017f}, {
+ -0.162460f, -0.262866f, -0.951056f}, {
+ 0.000000f, -0.850651f, -0.525731f}, {
+ -0.147621f, -0.716567f, -0.681718f}, {
+ 0.147621f, -0.716567f, -0.681718f}, {
+ 0.000000f, -0.525731f, -0.850651f}, {
+ 0.309017f, -0.500000f, -0.809017f}, {
+ 0.442863f, -0.238856f, -0.864188f}, {
+ 0.162460f, -0.262866f, -0.951056f}, {
+ 0.238856f, -0.864188f, -0.442863f}, {
+ 0.500000f, -0.809017f, -0.309017f}, {
+ 0.425325f, -0.688191f, -0.587785f}, {
+ 0.716567f, -0.681718f, -0.147621f}, {
+ 0.688191f, -0.587785f, -0.425325f}, {
+ 0.587785f, -0.425325f, -0.688191f}, {
+ 0.000000f, -0.955423f, -0.295242f}, {
+ 0.000000f, -1.000000f, 0.000000f}, {
+ 0.262866f, -0.951056f, -0.162460f}, {
+ 0.000000f, -0.850651f, 0.525731f}, {
+ 0.000000f, -0.955423f, 0.295242f}, {
+ 0.238856f, -0.864188f, 0.442863f}, {
+ 0.262866f, -0.951056f, 0.162460f}, {
+ 0.500000f, -0.809017f, 0.309017f}, {
+ 0.716567f, -0.681718f, 0.147621f}, {
+ 0.525731f, -0.850651f, 0.000000f}, {
+ -0.238856f, -0.864188f, -0.442863f}, {
+ -0.500000f, -0.809017f, -0.309017f}, {
+ -0.262866f, -0.951056f, -0.162460f}, {
+ -0.850651f, -0.525731f, 0.000000f}, {
+ -0.716567f, -0.681718f, -0.147621f}, {
+ -0.716567f, -0.681718f, 0.147621f}, {
+ -0.525731f, -0.850651f, 0.000000f}, {
+ -0.500000f, -0.809017f, 0.309017f}, {
+ -0.238856f, -0.864188f, 0.442863f}, {
+ -0.262866f, -0.951056f, 0.162460f}, {
+ -0.864188f, -0.442863f, 0.238856f}, {
+ -0.809017f, -0.309017f, 0.500000f}, {
+ -0.688191f, -0.587785f, 0.425325f}, {
+ -0.681718f, -0.147621f, 0.716567f}, {
+ -0.442863f, -0.238856f, 0.864188f}, {
+ -0.587785f, -0.425325f, 0.688191f}, {
+ -0.309017f, -0.500000f, 0.809017f}, {
+ -0.147621f, -0.716567f, 0.681718f}, {
+ -0.425325f, -0.688191f, 0.587785f}, {
+ -0.162460f, -0.262866f, 0.951056f}, {
+ 0.442863f, -0.238856f, 0.864188f}, {
+ 0.162460f, -0.262866f, 0.951056f}, {
+ 0.309017f, -0.500000f, 0.809017f}, {
+ 0.147621f, -0.716567f, 0.681718f}, {
+ 0.000000f, -0.525731f, 0.850651f}, {
+ 0.425325f, -0.688191f, 0.587785f}, {
+ 0.587785f, -0.425325f, 0.688191f}, {
+ 0.688191f, -0.587785f, 0.425325f}, {
+ -0.955423f, 0.295242f, 0.000000f}, {
+ -0.951056f, 0.162460f, 0.262866f}, {
+ -1.000000f, 0.000000f, 0.000000f}, {
+ -0.850651f, 0.000000f, 0.525731f}, {
+ -0.955423f, -0.295242f, 0.000000f}, {
+ -0.951056f, -0.162460f, 0.262866f}, {
+ -0.864188f, 0.442863f, -0.238856f}, {
+ -0.951056f, 0.162460f, -0.262866f}, {
+ -0.809017f, 0.309017f, -0.500000f}, {
+ -0.864188f, -0.442863f, -0.238856f}, {
+ -0.951056f, -0.162460f, -0.262866f}, {
+ -0.809017f, -0.309017f, -0.500000f}, {
+ -0.681718f, 0.147621f, -0.716567f}, {
+ -0.681718f, -0.147621f, -0.716567f}, {
+ -0.850651f, 0.000000f, -0.525731f}, {
+ -0.688191f, 0.587785f, -0.425325f}, {
+ -0.587785f, 0.425325f, -0.688191f}, {
+ -0.425325f, 0.688191f, -0.587785f}, {
+ -0.425325f, -0.688191f, -0.587785f}, {
+ -0.587785f, -0.425325f, -0.688191f}, {
+ -0.688191f, -0.587785f, -0.425325f}
+ };
+ public static final console_t con = new console_t();
+ public static final sizebuf_t net_message = new sizebuf_t();
+ public static final sizebuf_t cmd_text = new sizebuf_t();
+ public static final byte[] defer_text_buf = new byte[8192];
+ //=============================================================================
+ public static final byte[] cmd_text_buf = new byte[8192];
+ public static final byte[] net_message_buffer = new byte[MAX_MSGLEN];
+ public static final ClientStaticT clientStaticT = new ClientStaticT();
+ public static final centity_t[] cl_entities = new centity_t[Defines.MAX_EDICTS];
+ public static final entity_state_t[] cl_parse_entities = new entity_state_t[Defines.MAX_PARSE_ENTITIES];
+ public static final viddef_t viddef = new viddef_t();
+ public static final String[] keybindings = new String[256];
+ public static final boolean[] keydown = new boolean[256];
+ public static final byte[][] key_lines = new byte[32][];
+ public static final vrect_t scr_vrect = new vrect_t();
+ public static final int chat_bufferlen = 0;
+ public static final NetadrT net_from = new NetadrT();
+ public static final float[] vec3_origin = {0.0f, 0.0f, 0.0f};
+ public static final int vidref_val = VIDREF_GL;
+ public static final Random rnd = new Random();
+ /*
+ * global variables
+ */
+ public static int curtime = 0;
+ public static boolean cmd_wait;
+ public static int alias_count;
+ public static int c_traces;
+ public static int c_brush_traces;
+ public static int c_pointcontents;
+ public static int server_state;
+ public static CvarT cl_add_blend;
+ public static CvarT cl_add_entities;
+ public static CvarT cl_add_lights;
+ public static CvarT cl_add_particles;
+ public static CvarT cl_anglespeedkey;
+ public static CvarT cl_autoskins;
+ public static CvarT cl_footsteps;
+ public static CvarT cl_forwardspeed;
+ public static CvarT cl_gun;
+ public static CvarT cl_maxfps;
+ public static CvarT cl_noskins;
+ public static CvarT cl_pitchspeed;
+ public static CvarT cl_predict;
+ public static CvarT cl_run;
+ public static CvarT cl_sidespeed;
+ public static CvarT cl_stereo;
+ public static CvarT cl_stereo_separation;
+ public static CvarT cl_timedemo = new CvarT();
+ public static CvarT cl_timeout;
+ public static CvarT cl_upspeed;
+ public static CvarT cl_yawspeed;
+ public static CvarT dedicated;
+ public static CvarT developer;
+ public static CvarT freelook;
+ public static CvarT lookspring;
+ public static CvarT lookstrafe;
+ public static CvarT nostdout;
+ public static CvarT sensitivity;
+ public static CvarT showtrace;
+ public static CvarT in_mouse;
+ /*
+ =============================================================================
+
+ COMMAND BUFFER
+
+ =============================================================================
+ */
+ public static CvarT in_joystick;
+ public static cmdalias_t cmd_alias;
+ public static int time_before_game;
+ public static int time_after_game;
+ public static int time_before_ref;
+ public static int time_after_ref;
+ public static CvarT m_pitch;
+ public static CvarT m_yaw;
+ public static CvarT m_forward;
+ public static CvarT m_side;
+ public static CvarT cl_lightlevel;
+ //
+ // userinfo
+ //
+ public static CvarT info_password;
+ public static CvarT info_spectator;
+ public static CvarT name;
+ public static CvarT skin;
+ public static CvarT rate;
+ public static CvarT fov;
+ public static CvarT msg;
+ public static CvarT hand;
+ public static CvarT gender;
+ public static CvarT gender_auto;
+ public static CvarT cl_vwep;
+ public static ClientStateT clientStateT = new ClientStateT();
+ public static CvarT rcon_client_password;
+ public static CvarT rcon_address;
+ public static CvarT cl_shownet;
+ public static CvarT cl_showmiss;
+ public static CvarT cl_showclamp;
+ public static CvarT cl_paused;
+ public static boolean userinfo_modified = false;
+ public static CvarT cvar_vars;
+ public static CvarT con_notifytime;
+ // Renderer interface used by VideoDriver, SCR, ...
+ public static refexport_t re = new DummyRenderer();
+ public static boolean chat_team = false;
+ public static String chat_buffer = "";
+ public static int key_linepos;
+ public static int edit_line;
+ public static CvarT crosshair;
+ public static int sys_frame_time;
+ // logfile
+ public static RandomAccessFile logfile = null;
+ public static CvarT m_filter;
+
+ static {
+ for (int i = 0; i < cl_entities.length; i++) {
+ cl_entities[i] = new centity_t();
+ }
+ }
+
+ static {
+ for (int i = 0; i < cl_parse_entities.length; i++) {
+ cl_parse_entities[i] = new entity_state_t(null);
+ }
+ }
+
+ static {
+ for (int i = 0; i < key_lines.length; i++)
+ key_lines[i] = new byte[Defines.MAXCMDLINE];
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2;
+
+import lwjake2.qcommon.Com;
+import lwjake2.qcommon.Cvar;
+import lwjake2.qcommon.QCommon;
+import lwjake2.sys.Timer;
+
+/**
+ * Jake2 is the main class of Quake2 for Java.
+ */
+public final class LWJake2 {
+
+ /**
+ * main is used to start the game. Quake2 for Java supports the following
+ * command line arguments:
+ *
+ * @param args
+ */
+ public static void main(String[] args) {
+
+ boolean dedicated = isDedicated(args);
+
+ // TODO: check if dedicated is set in config file
+
+ Globals.dedicated = Cvar.get("dedicated", "0", QCommon.CVAR_NOSET);
+
+ if (dedicated)
+ Globals.dedicated.value = 1.0f;
+
+ QCommon.init(argsToCArgs(args));
+
+ Globals.nostdout = Cvar.get("nostdout", "0", 0);
+
+ mainGameLoop();
+ }
+
+ private static void mainGameLoop() {
+ int oldTimeMs = Timer.getCurrentTimeMillis();
+ int currentTimeMs;
+ int timeSinceLastFrameMs;
+
+ while (true) {
+ // find time spending rendering last frame
+ currentTimeMs = Timer.getCurrentTimeMillis();
+ timeSinceLastFrameMs = currentTimeMs - oldTimeMs;
+
+ if (timeSinceLastFrameMs > 0)
+ QCommon.doFrame(timeSinceLastFrameMs);
+
+ oldTimeMs = currentTimeMs;
+ }
+ }
+
+ /**
+ * in C the first arg is the filename
+ */
+ private static String[] argsToCArgs(String[] args) {
+ int argc = (args == null) ? 1 : args.length + 1;
+ String[] c_args = new String[argc];
+ c_args[0] = "LWJake2";
+ if (argc > 1) {
+ System.arraycopy(args, 0, c_args, 1, argc - 1);
+ }
+ return c_args;
+ }
+
+ private static boolean isDedicated(String[] args) {
+ boolean dedicated = false;
+
+ // check if we are in dedicated mode to hide the java dialog.
+ for (int n = 0; n < args.length; n++) {
+ if (args[n].equals("+set")) {
+ if (n++ >= args.length)
+ break;
+
+ if (!args[n].equals("dedicated"))
+ continue;
+
+ if (n++ >= args.length)
+ break;
+
+ if (args[n].equals("1") || args[n].equals("\"1\"")) {
+ Com.Printf("Starting in dedicated mode.\n");
+ dedicated = true;
+ }
+ }
+ }
+ return dedicated;
+ }
+}
\ No newline at end of file
--- /dev/null
+package lwjake2;
+
+import eu.svjatoslav.inspector.java.structure.ClassGraph;
+
+public class VisualizeCode {
+ public static void main(String[] args) {
+ final ClassGraph graph = new ClassGraph();
+
+ graph.addProject(".");
+
+ graph.setKeepDotFile(true);
+
+ graph.hideOrphanedClasses();
+
+ graph.generateGraph("Quake source");
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.client;
+
+import lwjake2.Defines;
+import lwjake2.Globals;
+import lwjake2.game.Move;
+import lwjake2.game.entity_state_t;
+import lwjake2.game.player_state_t;
+import lwjake2.qcommon.Com;
+import lwjake2.qcommon.FS;
+import lwjake2.qcommon.MSG;
+import lwjake2.util.Math3D;
+
+/**
+ * CL_ents
+ */
+// cl_ents.c -- entity parsing and management
+/*
+ * =========================================================================
+ *
+ * FRAME PARSING
+ *
+ * =========================================================================
+ */
+public class CL_ents {
+
+ // stack variable
+ public static final entity_t ent = new entity_t();
+ // stack variable
+ static final int[] bitcounts = new int[32]; /// just for protocol profiling
+ static final int[] bfg_lightramp = {300, 400, 600, 300, 150, 75};
+ // call by reference
+ private static final int[] iw = {0};
+
+ /*
+ * ================= CL_ParseEntityBits
+ *
+ * Returns the entity number and the header bits =================
+ */
+ public static int ParseEntityBits(int bits[]) {
+ int b, total;
+ int i;
+ int number;
+
+ total = MSG.ReadByte(Globals.net_message);
+ if ((total & Defines.U_MOREBITS1) != 0) {
+
+ b = MSG.ReadByte(Globals.net_message);
+ total |= b << 8;
+ }
+ if ((total & Defines.U_MOREBITS2) != 0) {
+
+ b = MSG.ReadByte(Globals.net_message);
+ total |= b << 16;
+ }
+ if ((total & Defines.U_MOREBITS3) != 0) {
+
+ b = MSG.ReadByte(Globals.net_message);
+ total |= b << 24;
+ }
+
+ // count the bits for net profiling
+ for (i = 0; i < 32; i++)
+ if ((total & (1 << i)) != 0)
+ bitcounts[i]++;
+
+ if ((total & Defines.U_NUMBER16) != 0)
+ number = MSG.ReadShort(Globals.net_message);
+ else
+ number = MSG.ReadByte(Globals.net_message);
+
+ bits[0] = total;
+
+ return number;
+ }
+
+ /*
+ * ================== CL_ParseDelta
+ *
+ * Can go from either a baseline or a previous packet_entity
+ * ==================
+ */
+ public static void ParseDelta(entity_state_t from, entity_state_t to, int number, int bits) {
+ // set everything to the state we are delta'ing from
+ to.set(from);
+
+ Math3D.vectorCopy(from.origin, to.old_origin);
+ to.number = number;
+
+ if ((bits & Defines.U_MODEL) != 0)
+ to.modelindex = MSG.ReadByte(Globals.net_message);
+ if ((bits & Defines.U_MODEL2) != 0)
+ to.modelindex2 = MSG.ReadByte(Globals.net_message);
+ if ((bits & Defines.U_MODEL3) != 0)
+ to.modelindex3 = MSG.ReadByte(Globals.net_message);
+ if ((bits & Defines.U_MODEL4) != 0)
+ to.modelindex4 = MSG.ReadByte(Globals.net_message);
+
+ if ((bits & Defines.U_FRAME8) != 0)
+ to.frame = MSG.ReadByte(Globals.net_message);
+ if ((bits & Defines.U_FRAME16) != 0)
+ to.frame = MSG.ReadShort(Globals.net_message);
+
+ if ((bits & Defines.U_SKIN8) != 0 && (bits & Defines.U_SKIN16) != 0) //used
+ // for
+ // laser
+ // colors
+ to.skinnum = MSG.ReadLong(Globals.net_message);
+ else if ((bits & Defines.U_SKIN8) != 0)
+ to.skinnum = MSG.ReadByte(Globals.net_message);
+ else if ((bits & Defines.U_SKIN16) != 0)
+ to.skinnum = MSG.ReadShort(Globals.net_message);
+
+ if ((bits & (Defines.U_EFFECTS8 | Defines.U_EFFECTS16)) == (Defines.U_EFFECTS8 | Defines.U_EFFECTS16))
+ to.effects = MSG.ReadLong(Globals.net_message);
+ else if ((bits & Defines.U_EFFECTS8) != 0)
+ to.effects = MSG.ReadByte(Globals.net_message);
+ else if ((bits & Defines.U_EFFECTS16) != 0)
+ to.effects = MSG.ReadShort(Globals.net_message);
+
+ if ((bits & (Defines.U_RENDERFX8 | Defines.U_RENDERFX16)) == (Defines.U_RENDERFX8 | Defines.U_RENDERFX16))
+ to.renderfx = MSG.ReadLong(Globals.net_message);
+ else if ((bits & Defines.U_RENDERFX8) != 0)
+ to.renderfx = MSG.ReadByte(Globals.net_message);
+ else if ((bits & Defines.U_RENDERFX16) != 0)
+ to.renderfx = MSG.ReadShort(Globals.net_message);
+
+ if ((bits & Defines.U_ORIGIN1) != 0)
+ to.origin[0] = MSG.ReadCoord(Globals.net_message);
+ if ((bits & Defines.U_ORIGIN2) != 0)
+ to.origin[1] = MSG.ReadCoord(Globals.net_message);
+ if ((bits & Defines.U_ORIGIN3) != 0)
+ to.origin[2] = MSG.ReadCoord(Globals.net_message);
+
+ if ((bits & Defines.U_ANGLE1) != 0)
+ to.angles[0] = MSG.ReadAngle(Globals.net_message);
+ if ((bits & Defines.U_ANGLE2) != 0)
+ to.angles[1] = MSG.ReadAngle(Globals.net_message);
+ if ((bits & Defines.U_ANGLE3) != 0)
+ to.angles[2] = MSG.ReadAngle(Globals.net_message);
+
+ if ((bits & Defines.U_OLDORIGIN) != 0)
+ MSG.ReadPos(Globals.net_message, to.old_origin);
+
+ if ((bits & Defines.U_SOUND) != 0)
+ to.sound = MSG.ReadByte(Globals.net_message);
+
+ if ((bits & Defines.U_EVENT) != 0)
+ to.event = MSG.ReadByte(Globals.net_message);
+ else
+ to.event = 0;
+
+ if ((bits & Defines.U_SOLID) != 0)
+ to.solid = MSG.ReadShort(Globals.net_message);
+ }
+
+ /*
+ * ================== CL_DeltaEntity
+ *
+ * Parses deltas from the given base and adds the resulting entity to the
+ * current frame ==================
+ */
+ public static void DeltaEntity(frame_t frame, int newnum, entity_state_t old, int bits) {
+ centity_t ent;
+ entity_state_t state;
+
+ ent = Globals.cl_entities[newnum];
+
+ state = Globals.cl_parse_entities[Globals.clientStateT.parse_entities & (Defines.MAX_PARSE_ENTITIES - 1)];
+ Globals.clientStateT.parse_entities++;
+ frame.num_entities++;
+
+ ParseDelta(old, state, newnum, bits);
+
+ // some data changes will force no lerping
+ if (state.modelindex != ent.current.modelindex || state.modelindex2 != ent.current.modelindex2
+ || state.modelindex3 != ent.current.modelindex3 || state.modelindex4 != ent.current.modelindex4
+ || Math.abs(state.origin[0] - ent.current.origin[0]) > 512 || Math.abs(state.origin[1] - ent.current.origin[1]) > 512
+ || Math.abs(state.origin[2] - ent.current.origin[2]) > 512 || state.event == Defines.EV_PLAYER_TELEPORT
+ || state.event == Defines.EV_OTHER_TELEPORT) {
+ ent.serverframe = -99;
+ }
+
+ if (ent.serverframe != Globals.clientStateT.frame.serverframe - 1) { // wasn't in
+ // last
+ // update, so
+ // initialize
+ // some
+ // things
+ ent.trailcount = 1024; // for diminishing rocket / grenade trails
+ // duplicate the current state so lerping doesn't hurt anything
+ ent.prev.set(state);
+ if (state.event == Defines.EV_OTHER_TELEPORT) {
+ Math3D.vectorCopy(state.origin, ent.prev.origin);
+ Math3D.vectorCopy(state.origin, ent.lerp_origin);
+ } else {
+ Math3D.vectorCopy(state.old_origin, ent.prev.origin);
+ Math3D.vectorCopy(state.old_origin, ent.lerp_origin);
+ }
+ } else { // shuffle the last state to previous
+ // Copy !
+ ent.prev.set(ent.current);
+ }
+
+ ent.serverframe = Globals.clientStateT.frame.serverframe;
+ // Copy !
+ ent.current.set(state);
+ }
+
+ /*
+ * ================== CL_ParsePacketEntities
+ *
+ * An svc_packetentities has just been parsed, deal with the rest of the
+ * data stream. ==================
+ */
+ public static void ParsePacketEntities(frame_t oldframe, frame_t newframe) {
+ int newnum;
+ int bits = 0;
+
+ entity_state_t oldstate = null;
+ int oldnum;
+
+ newframe.parse_entities = Globals.clientStateT.parse_entities;
+ newframe.num_entities = 0;
+
+ // delta from the entities present in oldframe
+ int oldindex = 0;
+ if (oldframe == null)
+ oldnum = 99999;
+ else {
+ // oldindex == 0. hoz
+ // if (oldindex >= oldframe.num_entities)
+ // oldnum = 99999;
+ // else {
+ oldstate = Globals.cl_parse_entities[(oldframe.parse_entities + oldindex) & (Defines.MAX_PARSE_ENTITIES - 1)];
+ oldnum = oldstate.number;
+ // }
+ }
+
+ while (true) {
+ //int iw[] = { bits };
+ iw[0] = bits;
+ newnum = ParseEntityBits(iw);
+ bits = iw[0];
+
+ if (newnum >= Defines.MAX_EDICTS)
+ Com.Error(Defines.ERR_DROP, "CL_ParsePacketEntities: bad number:" + newnum);
+
+ if (Globals.net_message.readcount > Globals.net_message.cursize)
+ Com.Error(Defines.ERR_DROP, "CL_ParsePacketEntities: end of message");
+
+ if (0 == newnum)
+ break;
+
+ while (oldnum < newnum) { // one or more entities from the old
+ // packet are unchanged
+ if (Globals.cl_shownet.value == 3)
+ Com.Printf(" unchanged: " + oldnum + "\n");
+ DeltaEntity(newframe, oldnum, oldstate, 0);
+
+ oldindex++;
+
+ if (oldindex >= oldframe.num_entities)
+ oldnum = 99999;
+ else {
+ oldstate = Globals.cl_parse_entities[(oldframe.parse_entities + oldindex) & (Defines.MAX_PARSE_ENTITIES - 1)];
+ oldnum = oldstate.number;
+ }
+ }
+
+ if ((bits & Defines.U_REMOVE) != 0) { // the entity present in
+ // oldframe is not in the
+ // current frame
+ if (Globals.cl_shownet.value == 3)
+ Com.Printf(" remove: " + newnum + "\n");
+ if (oldnum != newnum)
+ Com.Printf("U_REMOVE: oldnum != newnum\n");
+
+ oldindex++;
+
+ if (oldindex >= oldframe.num_entities)
+ oldnum = 99999;
+ else {
+ oldstate = Globals.cl_parse_entities[(oldframe.parse_entities + oldindex) & (Defines.MAX_PARSE_ENTITIES - 1)];
+ oldnum = oldstate.number;
+ }
+ continue;
+ }
+
+ if (oldnum == newnum) { // delta from previous state
+ if (Globals.cl_shownet.value == 3)
+ Com.Printf(" delta: " + newnum + "\n");
+ DeltaEntity(newframe, newnum, oldstate, bits);
+
+ oldindex++;
+
+ if (oldindex >= oldframe.num_entities)
+ oldnum = 99999;
+ else {
+ oldstate = Globals.cl_parse_entities[(oldframe.parse_entities + oldindex) & (Defines.MAX_PARSE_ENTITIES - 1)];
+ oldnum = oldstate.number;
+ }
+ continue;
+ }
+
+ if (oldnum > newnum) { // delta from baseline
+ if (Globals.cl_shownet.value == 3)
+ Com.Printf(" baseline: " + newnum + "\n");
+ DeltaEntity(newframe, newnum, Globals.cl_entities[newnum].baseline, bits);
+ }
+
+ }
+
+ // any remaining entities in the old frame are copied over
+ while (oldnum != 99999) { // one or more entities from the old packet
+ // are unchanged
+ if (Globals.cl_shownet.value == 3)
+ Com.Printf(" unchanged: " + oldnum + "\n");
+ DeltaEntity(newframe, oldnum, oldstate, 0);
+
+ oldindex++;
+
+ if (oldindex >= oldframe.num_entities)
+ oldnum = 99999;
+ else {
+ oldstate = Globals.cl_parse_entities[(oldframe.parse_entities + oldindex) & (Defines.MAX_PARSE_ENTITIES - 1)];
+ oldnum = oldstate.number;
+ }
+ }
+ }
+
+ /*
+ * =================== CL_ParsePlayerstate ===================
+ */
+ public static void ParsePlayerstate(frame_t oldframe, frame_t newframe) {
+ int flags;
+ player_state_t state;
+ int i;
+ int statbits;
+
+ state = newframe.playerstate;
+
+ // clear to old value before delta parsing
+ if (oldframe != null)
+ state.set(oldframe.playerstate);
+ else
+ //memset (state, 0, sizeof(*state));
+ state.clear();
+
+ flags = MSG.ReadShort(Globals.net_message);
+
+ //
+ // parse the pmove_state_t
+ //
+ if ((flags & Defines.PS_M_TYPE) != 0)
+ state.pmove.pm_type = MSG.ReadByte(Globals.net_message);
+
+ if ((flags & Defines.PS_M_ORIGIN) != 0) {
+ state.pmove.origin[0] = MSG.ReadShort(Globals.net_message);
+ state.pmove.origin[1] = MSG.ReadShort(Globals.net_message);
+ state.pmove.origin[2] = MSG.ReadShort(Globals.net_message);
+ }
+
+ if ((flags & Defines.PS_M_VELOCITY) != 0) {
+ state.pmove.velocity[0] = MSG.ReadShort(Globals.net_message);
+ state.pmove.velocity[1] = MSG.ReadShort(Globals.net_message);
+ state.pmove.velocity[2] = MSG.ReadShort(Globals.net_message);
+ }
+
+ if ((flags & Defines.PS_M_TIME) != 0) {
+ state.pmove.pm_time = (byte) MSG.ReadByte(Globals.net_message);
+ }
+
+ if ((flags & Defines.PS_M_FLAGS) != 0)
+ state.pmove.pm_flags = (byte) MSG.ReadByte(Globals.net_message);
+
+ if ((flags & Defines.PS_M_GRAVITY) != 0)
+ state.pmove.gravity = MSG.ReadShort(Globals.net_message);
+
+ if ((flags & Defines.PS_M_DELTA_ANGLES) != 0) {
+ state.pmove.delta_angles[0] = MSG.ReadShort(Globals.net_message);
+ state.pmove.delta_angles[1] = MSG.ReadShort(Globals.net_message);
+ state.pmove.delta_angles[2] = MSG.ReadShort(Globals.net_message);
+ }
+
+ if (Globals.clientStateT.attractloop)
+ state.pmove.pm_type = Defines.PM_FREEZE; // demo playback
+
+ //
+ // parse the rest of the player_state_t
+ //
+ if ((flags & Defines.PS_VIEWOFFSET) != 0) {
+ state.viewoffset[0] = MSG.ReadChar(Globals.net_message) * 0.25f;
+ state.viewoffset[1] = MSG.ReadChar(Globals.net_message) * 0.25f;
+ state.viewoffset[2] = MSG.ReadChar(Globals.net_message) * 0.25f;
+ }
+
+ if ((flags & Defines.PS_VIEWANGLES) != 0) {
+ state.viewangles[0] = MSG.ReadAngle16(Globals.net_message);
+ state.viewangles[1] = MSG.ReadAngle16(Globals.net_message);
+ state.viewangles[2] = MSG.ReadAngle16(Globals.net_message);
+ }
+
+ if ((flags & Defines.PS_KICKANGLES) != 0) {
+
+ state.kick_angles[0] = MSG.ReadChar(Globals.net_message) * 0.25f;
+ state.kick_angles[1] = MSG.ReadChar(Globals.net_message) * 0.25f;
+ state.kick_angles[2] = MSG.ReadChar(Globals.net_message) * 0.25f;
+
+ }
+
+ if ((flags & Defines.PS_BLEND) != 0) {
+ state.blend[0] = MSG.ReadByte(Globals.net_message) / 255.0f;
+ state.blend[1] = MSG.ReadByte(Globals.net_message) / 255.0f;
+ state.blend[2] = MSG.ReadByte(Globals.net_message) / 255.0f;
+ state.blend[3] = MSG.ReadByte(Globals.net_message) / 255.0f;
+ }
+
+ if ((flags & Defines.PS_FOV) != 0)
+ state.fov = MSG.ReadByte(Globals.net_message);
+
+ if ((flags & Defines.PS_RDFLAGS) != 0)
+ state.rdflags = MSG.ReadByte(Globals.net_message);
+
+ // parse stats
+ statbits = MSG.ReadLong(Globals.net_message);
+ for (i = 0; i < Defines.MAX_STATS; i++)
+ if ((statbits & (1 << i)) != 0)
+ state.stats[i] = MSG.ReadShort(Globals.net_message);
+ }
+
+ /*
+ * ==========================================================================
+ *
+ * INTERPOLATE BETWEEN FRAMES TO GET RENDERING PARMS
+ *
+ * ==========================================================================
+ */
+
+ /*
+ * ================== CL_FireEntityEvents
+ *
+ * ==================
+ */
+ public static void FireEntityEvents(frame_t frame) {
+ entity_state_t s1;
+ int pnum, num;
+
+ for (pnum = 0; pnum < frame.num_entities; pnum++) {
+ num = (frame.parse_entities + pnum) & (Defines.MAX_PARSE_ENTITIES - 1);
+ s1 = Globals.cl_parse_entities[num];
+ if (s1.event != 0)
+ CL_fx.EntityEvent(s1);
+
+ // EF_TELEPORTER acts like an event, but is not cleared each frame
+ if ((s1.effects & Defines.EF_TELEPORTER) != 0)
+ CL_fx.TeleporterParticles(s1);
+ }
+ }
+
+ /*
+ * ================ CL_ParseFrame ================
+ */
+ public static void ParseFrame() {
+ int cmd;
+ int len;
+ frame_t old;
+
+ //memset( cl.frame, 0, sizeof(cl.frame));
+ Globals.clientStateT.frame.reset();
+
+ Globals.clientStateT.frame.serverframe = MSG.ReadLong(Globals.net_message);
+ Globals.clientStateT.frame.deltaframe = MSG.ReadLong(Globals.net_message);
+ Globals.clientStateT.frame.servertime = Globals.clientStateT.frame.serverframe * 100;
+
+ // BIG HACK to let old demos continue to work
+ if (Globals.clientStaticT.serverProtocol != 26)
+ Globals.clientStateT.surpressCount = MSG.ReadByte(Globals.net_message);
+
+ if (Globals.cl_shownet.value == 3)
+ Com.Printf(" frame:" + Globals.clientStateT.frame.serverframe + " delta:" + Globals.clientStateT.frame.deltaframe + "\n");
+
+ // If the frame is delta compressed from data that we
+ // no longer have available, we must suck up the rest of
+ // the frame, but not use it, then ask for a non-compressed
+ // message
+ if (Globals.clientStateT.frame.deltaframe <= 0) {
+ Globals.clientStateT.frame.valid = true; // uncompressed frame
+ old = null;
+ Globals.clientStaticT.demowaiting = false; // we can start recording now
+ } else {
+ old = Globals.clientStateT.frames[Globals.clientStateT.frame.deltaframe & Defines.UPDATE_MASK];
+ if (!old.valid) { // should never happen
+ Com.Printf("Delta from invalid frame (not supposed to happen!).\n");
+ }
+ if (old.serverframe != Globals.clientStateT.frame.deltaframe) { // The frame
+ // that the
+ // server did
+ // the delta
+ // from
+ // is too old, so we can't reconstruct it properly.
+ Com.Printf("Delta frame too old.\n");
+ } else if (Globals.clientStateT.parse_entities - old.parse_entities > Defines.MAX_PARSE_ENTITIES - 128) {
+ Com.Printf("Delta parse_entities too old.\n");
+ } else
+ Globals.clientStateT.frame.valid = true; // valid delta parse
+ }
+
+ // clamp time
+ if (Globals.clientStateT.time > Globals.clientStateT.frame.servertime)
+ Globals.clientStateT.time = Globals.clientStateT.frame.servertime;
+ else if (Globals.clientStateT.time < Globals.clientStateT.frame.servertime - 100)
+ Globals.clientStateT.time = Globals.clientStateT.frame.servertime - 100;
+
+ // read areabits
+ len = MSG.ReadByte(Globals.net_message);
+ MSG.ReadData(Globals.net_message, Globals.clientStateT.frame.areabits, len);
+
+ // read playerinfo
+ cmd = MSG.ReadByte(Globals.net_message);
+ CL_parse.SHOWNET(CL_parse.svc_strings[cmd]);
+ if (cmd != Defines.svc_playerinfo)
+ Com.Error(Defines.ERR_DROP, "CL_ParseFrame: not playerinfo");
+ ParsePlayerstate(old, Globals.clientStateT.frame);
+
+ // read packet entities
+ cmd = MSG.ReadByte(Globals.net_message);
+ CL_parse.SHOWNET(CL_parse.svc_strings[cmd]);
+ if (cmd != Defines.svc_packetentities)
+ Com.Error(Defines.ERR_DROP, "CL_ParseFrame: not packetentities");
+
+ ParsePacketEntities(old, Globals.clientStateT.frame);
+
+ // save the frame off in the backup array for later delta comparisons
+ Globals.clientStateT.frames[Globals.clientStateT.frame.serverframe & Defines.UPDATE_MASK].set(Globals.clientStateT.frame);
+
+ if (Globals.clientStateT.frame.valid) {
+ // getting a valid frame message ends the connection process
+ if (Globals.clientStaticT.state != Defines.ca_active) {
+ Globals.clientStaticT.state = Defines.ca_active;
+ Globals.clientStateT.force_refdef = true;
+
+ Globals.clientStateT.predicted_origin[0] = Globals.clientStateT.frame.playerstate.pmove.origin[0] * 0.125f;
+ Globals.clientStateT.predicted_origin[1] = Globals.clientStateT.frame.playerstate.pmove.origin[1] * 0.125f;
+ Globals.clientStateT.predicted_origin[2] = Globals.clientStateT.frame.playerstate.pmove.origin[2] * 0.125f;
+
+ Math3D.vectorCopy(Globals.clientStateT.frame.playerstate.viewangles, Globals.clientStateT.predicted_angles);
+ if (Globals.clientStaticT.disable_servercount != Globals.clientStateT.servercount && Globals.clientStateT.refresh_prepped)
+ SCR.EndLoadingPlaque(); // get rid of loading plaque
+ }
+ Globals.clientStateT.sound_prepped = true; // can start mixing ambient sounds
+
+ // fire entity events
+ FireEntityEvents(Globals.clientStateT.frame);
+ CL_pred.CheckPredictionError();
+ }
+ }
+
+ /*
+ * ===============
+ * CL_AddPacketEntities
+ * ===============
+ */
+ static void AddPacketEntities(frame_t frame) {
+ entity_state_t s1;
+ float autorotate;
+ int i;
+ int pnum;
+ centity_t cent;
+ int autoanim;
+ ClientInfo clientInfo;
+ int effects, renderfx;
+
+ // bonus items rotate at a fixed rate
+ autorotate = Math3D.angleMod(Globals.clientStateT.time / 10);
+
+ // brush models can auto animate their frames
+ autoanim = 2 * Globals.clientStateT.time / 1000;
+
+ //memset( ent, 0, sizeof(ent));
+ ent.clear();
+
+ for (pnum = 0; pnum < frame.num_entities; pnum++) {
+ s1 = Globals.cl_parse_entities[(frame.parse_entities + pnum) & (Defines.MAX_PARSE_ENTITIES - 1)];
+
+ cent = Globals.cl_entities[s1.number];
+
+ effects = s1.effects;
+ renderfx = s1.renderfx;
+
+ // set frame
+ if ((effects & Defines.EF_ANIM01) != 0)
+ ent.frame = autoanim & 1;
+ else if ((effects & Defines.EF_ANIM23) != 0)
+ ent.frame = 2 + (autoanim & 1);
+ else if ((effects & Defines.EF_ANIM_ALL) != 0)
+ ent.frame = autoanim;
+ else if ((effects & Defines.EF_ANIM_ALLFAST) != 0)
+ ent.frame = Globals.clientStateT.time / 100;
+ else
+ ent.frame = s1.frame;
+
+ // quad and pent can do different things on client
+ if ((effects & Defines.EF_PENT) != 0) {
+ effects &= ~Defines.EF_PENT;
+ effects |= Defines.EF_COLOR_SHELL;
+ renderfx |= Defines.RF_SHELL_RED;
+ }
+
+ if ((effects & Defines.EF_QUAD) != 0) {
+ effects &= ~Defines.EF_QUAD;
+ effects |= Defines.EF_COLOR_SHELL;
+ renderfx |= Defines.RF_SHELL_BLUE;
+ }
+ // ======
+ // PMM
+ if ((effects & Defines.EF_DOUBLE) != 0) {
+ effects &= ~Defines.EF_DOUBLE;
+ effects |= Defines.EF_COLOR_SHELL;
+ renderfx |= Defines.RF_SHELL_DOUBLE;
+ }
+
+ if ((effects & Defines.EF_HALF_DAMAGE) != 0) {
+ effects &= ~Defines.EF_HALF_DAMAGE;
+ effects |= Defines.EF_COLOR_SHELL;
+ renderfx |= Defines.RF_SHELL_HALF_DAM;
+ }
+ // pmm
+ // ======
+ ent.oldframe = cent.prev.frame;
+ ent.backlerp = 1.0f - Globals.clientStateT.lerpfrac;
+
+ // interpolate origin
+ for (i = 0; i < 3; i++) {
+ ent.origin[i] = ent.oldorigin[i] = cent.prev.origin[i] + Globals.clientStateT.lerpfrac
+ * (cent.current.origin[i] - cent.prev.origin[i]);
+ }
+
+ // create a new entity
+
+ // tweak the color of beams
+ // set skin
+ if (s1.modelindex == 255) { // use custom player skin
+ ent.skinnum = 0;
+ clientInfo = Globals.clientStateT.clientinfo[s1.skinnum & 0xff];
+ ent.skin = clientInfo.skin;
+ ent.model = clientInfo.model;
+ if (null == ent.skin || null == ent.model) {
+ ent.skin = Globals.clientStateT.baseclientinfo.skin;
+ ent.model = Globals.clientStateT.baseclientinfo.model;
+ }
+
+ // ============
+ // PGM
+ if ((renderfx & Defines.RF_USE_DISGUISE) != 0) {
+ if (ent.skin.name.startsWith("players/male")) {
+ ent.skin = Globals.re.RegisterSkin("players/male/disguise.pcx");
+ ent.model = Globals.re.RegisterModel("players/male/tris.md2");
+ } else if (ent.skin.name.startsWith("players/female")) {
+ ent.skin = Globals.re.RegisterSkin("players/female/disguise.pcx");
+ ent.model = Globals.re.RegisterModel("players/female/tris.md2");
+ } else if (ent.skin.name.startsWith("players/cyborg")) {
+ ent.skin = Globals.re.RegisterSkin("players/cyborg/disguise.pcx");
+ ent.model = Globals.re.RegisterModel("players/cyborg/tris.md2");
+ }
+ }
+ // PGM
+ // ============
+ } else {
+ ent.skinnum = s1.skinnum;
+ ent.skin = null;
+ ent.model = Globals.clientStateT.model_draw[s1.modelindex];
+ }
+
+ // only used for black hole model right now, FIXME: do better
+ if (renderfx == Defines.RF_TRANSLUCENT)
+ ent.alpha = 0.70f;
+
+ // render effects (fullbright, translucent, etc)
+ if ((effects & Defines.EF_COLOR_SHELL) != 0)
+ ent.flags = 0; // renderfx go on color shell entity
+ else
+ ent.flags = renderfx;
+
+ // calculate angles
+ if ((effects & Defines.EF_ROTATE) != 0) { // some bonus items
+ // auto-rotate
+ ent.angles[0] = 0;
+ ent.angles[1] = autorotate;
+ ent.angles[2] = 0;
+ }
+ // RAFAEL
+ else if ((effects & Defines.EF_SPINNINGLIGHTS) != 0) {
+ ent.angles[0] = 0;
+ ent.angles[1] = Math3D.angleMod(Globals.clientStateT.time / 2) + s1.angles[1];
+ ent.angles[2] = 180;
+ {
+ float[] forward = {0, 0, 0};
+ float[] start = {0, 0, 0};
+
+ Math3D.angleVectors(ent.angles, forward, null, null);
+ Math3D.vectorMA(ent.origin, 64, forward, start);
+ V.AddLight(start, 100, 1, 0, 0);
+ }
+ } else { // interpolate angles
+ float a1, a2;
+
+ for (i = 0; i < 3; i++) {
+ a1 = cent.current.angles[i];
+ a2 = cent.prev.angles[i];
+ ent.angles[i] = Math3D.lerpAngle(a2, a1, Globals.clientStateT.lerpfrac);
+ }
+ }
+
+ if (s1.number == Globals.clientStateT.playernum + 1) {
+ ent.flags |= Defines.RF_VIEWERMODEL; // only draw from mirrors
+ // FIXME: still pass to refresh
+
+ if ((effects & Defines.EF_FLAG1) != 0)
+ V.AddLight(ent.origin, 225, 1.0f, 0.1f, 0.1f);
+ else if ((effects & Defines.EF_FLAG2) != 0)
+ V.AddLight(ent.origin, 225, 0.1f, 0.1f, 1.0f);
+ else if ((effects & Defines.EF_TAGTRAIL) != 0) //PGM
+ V.AddLight(ent.origin, 225, 1.0f, 1.0f, 0.0f); //PGM
+ else if ((effects & Defines.EF_TRACKERTRAIL) != 0) //PGM
+ V.AddLight(ent.origin, 225, -1.0f, -1.0f, -1.0f); //PGM
+
+ continue;
+ }
+
+ // if set to invisible, skip
+ if (s1.modelindex == 0)
+ continue;
+
+ if ((effects & Defines.EF_BFG) != 0) {
+ ent.flags |= Defines.RF_TRANSLUCENT;
+ ent.alpha = 0.30f;
+ }
+
+ // RAFAEL
+ if ((effects & Defines.EF_PLASMA) != 0) {
+ ent.flags |= Defines.RF_TRANSLUCENT;
+ ent.alpha = 0.6f;
+ }
+
+ if ((effects & Defines.EF_SPHERETRANS) != 0) {
+ ent.flags |= Defines.RF_TRANSLUCENT;
+ // PMM - *sigh* yet more EF overloading
+ if ((effects & Defines.EF_TRACKERTRAIL) != 0)
+ ent.alpha = 0.6f;
+ else
+ ent.alpha = 0.3f;
+ }
+ // pmm
+
+ // add to refresh list
+ V.AddEntity(ent);
+
+ // color shells generate a seperate entity for the main model
+ if ((effects & Defines.EF_COLOR_SHELL) != 0) {
+ /*
+ * PMM - at this point, all of the shells have been handled if
+ * we're in the rogue pack, set up the custom mixing, otherwise
+ * just keep going if(Developer_searchpath(2) == 2) { all of the
+ * solo colors are fine. we need to catch any of the
+ * combinations that look bad (double & half) and turn them into
+ * the appropriate color, and make double/quad something special
+ *
+ */
+ if ((renderfx & Defines.RF_SHELL_HALF_DAM) != 0) {
+ if (FS.Developer_searchpath(2) == 2) {
+ // ditch the half damage shell if any of red, blue, or
+ // double are on
+ if ((renderfx & (Defines.RF_SHELL_RED | Defines.RF_SHELL_BLUE | Defines.RF_SHELL_DOUBLE)) != 0)
+ renderfx &= ~Defines.RF_SHELL_HALF_DAM;
+ }
+ }
+
+ if ((renderfx & Defines.RF_SHELL_DOUBLE) != 0) {
+ if (FS.Developer_searchpath(2) == 2) {
+ // lose the yellow shell if we have a red, blue, or
+ // green shell
+ if ((renderfx & (Defines.RF_SHELL_RED | Defines.RF_SHELL_BLUE | Defines.RF_SHELL_GREEN)) != 0)
+ renderfx &= ~Defines.RF_SHELL_DOUBLE;
+ // if we have a red shell, turn it to purple by adding
+ // blue
+ if ((renderfx & Defines.RF_SHELL_RED) != 0)
+ renderfx |= Defines.RF_SHELL_BLUE;
+ // if we have a blue shell (and not a red shell), turn
+ // it to cyan by adding green
+ else if ((renderfx & Defines.RF_SHELL_BLUE) != 0)
+ // go to green if it's on already, otherwise do cyan
+ // (flash green)
+ if ((renderfx & Defines.RF_SHELL_GREEN) != 0)
+ renderfx &= ~Defines.RF_SHELL_BLUE;
+ else
+ renderfx |= Defines.RF_SHELL_GREEN;
+ }
+ }
+ // }
+ // pmm
+ ent.flags = renderfx | Defines.RF_TRANSLUCENT;
+ ent.alpha = 0.30f;
+ V.AddEntity(ent);
+ }
+
+ ent.skin = null; // never use a custom skin on others
+ ent.skinnum = 0;
+ ent.flags = 0;
+ ent.alpha = 0;
+
+ // duplicate for linked models
+ if (s1.modelindex2 != 0) {
+ ent.model = Globals.clientStateT.model_draw[s1.modelindex2];
+
+ // PMM - check for the defender sphere shell .. make it
+ // translucent
+ // replaces the previous version which used the high bit on
+ // modelindex2 to determine transparency
+ if (Globals.clientStateT.configstrings[Defines.CS_MODELS + (s1.modelindex2)].equalsIgnoreCase("models/items/shell/tris.md2")) {
+ ent.alpha = 0.32f;
+ ent.flags = Defines.RF_TRANSLUCENT;
+ }
+ // pmm
+
+ V.AddEntity(ent);
+
+ //PGM - make sure these get reset.
+ ent.flags = 0;
+ ent.alpha = 0;
+ //PGM
+ }
+ if (s1.modelindex3 != 0) {
+ ent.model = Globals.clientStateT.model_draw[s1.modelindex3];
+ V.AddEntity(ent);
+ }
+ if (s1.modelindex4 != 0) {
+ ent.model = Globals.clientStateT.model_draw[s1.modelindex4];
+ V.AddEntity(ent);
+ }
+
+ if ((effects & Defines.EF_POWERSCREEN) != 0) {
+ ent.model = CL_tent.cl_mod_powerscreen;
+ ent.oldframe = 0;
+ ent.frame = 0;
+ ent.flags |= (Defines.RF_TRANSLUCENT | Defines.RF_SHELL_GREEN);
+ ent.alpha = 0.30f;
+ V.AddEntity(ent);
+ }
+
+ // add automatic particle trails
+ if ((effects & ~Defines.EF_ROTATE) != 0) {
+ if ((effects & Defines.EF_ROCKET) != 0) {
+ CL_fx.RocketTrail(cent.lerp_origin, ent.origin, cent);
+ V.AddLight(ent.origin, 200, 1, 1, 0);
+ }
+ // PGM - Do not reorder EF_BLASTER and EF_HYPERBLASTER.
+ // EF_BLASTER | EF_TRACKER is a special case for EF_BLASTER2...
+ // Cheese!
+ else if ((effects & Defines.EF_BLASTER) != 0) {
+ // CL_BlasterTrail (cent.lerp_origin, ent.origin);
+ // PGM
+ if ((effects & Defines.EF_TRACKER) != 0) // lame...
+ // problematic?
+ {
+ CL_newfx.BlasterTrail2(cent.lerp_origin, ent.origin);
+ V.AddLight(ent.origin, 200, 0, 1, 0);
+ } else {
+ CL_fx.BlasterTrail(cent.lerp_origin, ent.origin);
+ V.AddLight(ent.origin, 200, 1, 1, 0);
+ }
+ // PGM
+ } else if ((effects & Defines.EF_HYPERBLASTER) != 0) {
+ if ((effects & Defines.EF_TRACKER) != 0) // PGM overloaded
+ // for blaster2.
+ V.AddLight(ent.origin, 200, 0, 1, 0); // PGM
+ else
+ // PGM
+ V.AddLight(ent.origin, 200, 1, 1, 0);
+ } else if ((effects & Defines.EF_GIB) != 0) {
+ CL_fx.DiminishingTrail(cent.lerp_origin, ent.origin, cent, effects);
+ } else if ((effects & Defines.EF_GRENADE) != 0) {
+ CL_fx.DiminishingTrail(cent.lerp_origin, ent.origin, cent, effects);
+ } else if ((effects & Defines.EF_FLIES) != 0) {
+ CL_fx.FlyEffect(cent, ent.origin);
+ } else if ((effects & Defines.EF_BFG) != 0) {
+
+ if ((effects & Defines.EF_ANIM_ALLFAST) != 0) {
+ CL_fx.BfgParticles();
+ i = 200;
+ } else {
+ i = bfg_lightramp[s1.frame];
+ }
+ V.AddLight(ent.origin, i, 0, 1, 0);
+ }
+ // RAFAEL
+ else if ((effects & Defines.EF_TRAP) != 0) {
+ ent.origin[2] += 32;
+ CL_fx.TrapParticles();
+ i = (Globals.rnd.nextInt(100)) + 100;
+ V.AddLight(ent.origin, i, 1, 0.8f, 0.1f);
+ } else if ((effects & Defines.EF_FLAG1) != 0) {
+ CL_fx.FlagTrail(cent.lerp_origin, ent.origin, 242);
+ V.AddLight(ent.origin, 225, 1, 0.1f, 0.1f);
+ } else if ((effects & Defines.EF_FLAG2) != 0) {
+ CL_fx.FlagTrail(cent.lerp_origin, ent.origin, 115);
+ V.AddLight(ent.origin, 225, 0.1f, 0.1f, 1);
+ }
+ // ======
+ // ROGUE
+ else if ((effects & Defines.EF_TAGTRAIL) != 0) {
+ CL_newfx.TagTrail(cent.lerp_origin, ent.origin);
+ V.AddLight(ent.origin, 225, 1.0f, 1.0f, 0.0f);
+ } else if ((effects & Defines.EF_TRACKERTRAIL) != 0) {
+ if ((effects & Defines.EF_TRACKER) != 0) {
+ float intensity;
+
+ intensity = (float) (50 + (500 * (Math.sin(Globals.clientStateT.time / 500.0) + 1.0)));
+ // FIXME - check out this effect in rendition
+ if (Globals.vidref_val == Defines.VIDREF_GL)
+ V.AddLight(ent.origin, intensity, -1.0f, -1.0f, -1.0f);
+ else
+ V.AddLight(ent.origin, -1.0f * intensity, 1.0f, 1.0f, 1.0f);
+ } else {
+ CL_newfx.Tracker_Shell(cent.lerp_origin);
+ V.AddLight(ent.origin, 155, -1.0f, -1.0f, -1.0f);
+ }
+ } else if ((effects & Defines.EF_TRACKER) != 0) {
+ CL_newfx.TrackerTrail(cent.lerp_origin, ent.origin);
+ // FIXME - check out this effect in rendition
+ if (Globals.vidref_val == Defines.VIDREF_GL)
+ V.AddLight(ent.origin, 200, -1, -1, -1);
+ else
+ V.AddLight(ent.origin, -200, 1, 1, 1);
+ }
+ // ROGUE
+ // ======
+ // RAFAEL
+ else if ((effects & Defines.EF_GREENGIB) != 0) {
+ CL_fx.DiminishingTrail(cent.lerp_origin, ent.origin, cent, effects);
+ }
+ // RAFAEL
+ else if ((effects & Defines.EF_IONRIPPER) != 0) {
+ CL_fx.IonripperTrail(cent.lerp_origin, ent.origin);
+ V.AddLight(ent.origin, 100, 1, 0.5f, 0.5f);
+ }
+ // RAFAEL
+ else if ((effects & Defines.EF_BLUEHYPERBLASTER) != 0) {
+ V.AddLight(ent.origin, 200, 0, 0, 1);
+ }
+ // RAFAEL
+ else if ((effects & Defines.EF_PLASMA) != 0) {
+ if ((effects & Defines.EF_ANIM_ALLFAST) != 0) {
+ CL_fx.BlasterTrail(cent.lerp_origin, ent.origin);
+ }
+ V.AddLight(ent.origin, 130, 1, 0.5f, 0.5f);
+ }
+ }
+
+ Math3D.vectorCopy(ent.origin, cent.lerp_origin);
+ }
+ }
+
+ /*
+ * =============== CL_CalcViewValues
+ *
+ * Sets cl.refdef view values ===============
+ */
+ static void CalcViewValues() {
+ int i;
+ float lerp, backlerp;
+ frame_t oldframe;
+ player_state_t ps, ops;
+
+ // find the previous frame to interpolate from
+ ps = Globals.clientStateT.frame.playerstate;
+
+ i = (Globals.clientStateT.frame.serverframe - 1) & Defines.UPDATE_MASK;
+ oldframe = Globals.clientStateT.frames[i];
+
+ if (oldframe.serverframe != Globals.clientStateT.frame.serverframe - 1 || !oldframe.valid)
+ oldframe = Globals.clientStateT.frame; // previous frame was dropped or
+ // involid
+ ops = oldframe.playerstate;
+
+ // see if the player entity was teleported this frame
+ if (Math.abs(ops.pmove.origin[0] - ps.pmove.origin[0]) > 256 * 8
+ || Math.abs(ops.pmove.origin[1] - ps.pmove.origin[1]) > 256 * 8
+ || Math.abs(ops.pmove.origin[2] - ps.pmove.origin[2]) > 256 * 8)
+ ops = ps; // don't interpolate
+
+ lerp = Globals.clientStateT.lerpfrac;
+
+ // calculate the origin
+ if ((Globals.cl_predict.value != 0) && 0 == (Globals.clientStateT.frame.playerstate.pmove.pm_flags & Move.PMF_NO_PREDICTION)) { // use
+ // predicted
+ // values
+ int delta;
+
+ backlerp = 1.0f - lerp;
+ for (i = 0; i < 3; i++) {
+ Globals.clientStateT.refdef.vieworg[i] = Globals.clientStateT.predicted_origin[i] + ops.viewoffset[i] + Globals.clientStateT.lerpfrac
+ * (ps.viewoffset[i] - ops.viewoffset[i]) - backlerp * Globals.clientStateT.prediction_error[i];
+ }
+
+ // smooth out stair climbing
+ delta = Globals.clientStaticT.realtime - Globals.clientStateT.predicted_step_time;
+ if (delta < 100)
+ Globals.clientStateT.refdef.vieworg[2] -= Globals.clientStateT.predicted_step * (100 - delta) * 0.01;
+ } else { // just use interpolated values
+ for (i = 0; i < 3; i++)
+ Globals.clientStateT.refdef.vieworg[i] = ops.pmove.origin[i] * 0.125f + ops.viewoffset[i] + lerp
+ * (ps.pmove.origin[i] * 0.125f + ps.viewoffset[i] - (ops.pmove.origin[i] * 0.125f + ops.viewoffset[i]));
+ }
+
+ // if not running a demo or on a locked frame, add the local angle
+ // movement
+ if (Globals.clientStateT.frame.playerstate.pmove.pm_type < Defines.PM_DEAD) { // use
+ // predicted
+ // values
+ for (i = 0; i < 3; i++)
+ Globals.clientStateT.refdef.viewangles[i] = Globals.clientStateT.predicted_angles[i];
+ } else { // just use interpolated values
+ for (i = 0; i < 3; i++)
+ Globals.clientStateT.refdef.viewangles[i] = Math3D.lerpAngle(ops.viewangles[i], ps.viewangles[i], lerp);
+ }
+
+ for (i = 0; i < 3; i++)
+ Globals.clientStateT.refdef.viewangles[i] += Math3D.lerpAngle(ops.kick_angles[i], ps.kick_angles[i], lerp);
+
+ Math3D.angleVectors(Globals.clientStateT.refdef.viewangles, Globals.clientStateT.v_forward, Globals.clientStateT.v_right, Globals.clientStateT.v_up);
+
+ // interpolate field of view
+ Globals.clientStateT.refdef.fov_x = ops.fov + lerp * (ps.fov - ops.fov);
+
+ // don't interpolate blend color
+ for (i = 0; i < 4; i++)
+ Globals.clientStateT.refdef.blend[i] = ps.blend[i];
+
+ }
+
+ /*
+ * =============== CL_AddEntities
+ *
+ * Emits all entities, particles, and lights to the refresh ===============
+ */
+ static void AddEntities() {
+ if (Globals.clientStaticT.state != Defines.ca_active)
+ return;
+
+ if (Globals.clientStateT.time > Globals.clientStateT.frame.servertime) {
+ if (Globals.cl_showclamp.value != 0)
+ Com.Printf("high clamp " + (Globals.clientStateT.time - Globals.clientStateT.frame.servertime) + "\n");
+ Globals.clientStateT.time = Globals.clientStateT.frame.servertime;
+ Globals.clientStateT.lerpfrac = 1.0f;
+ } else if (Globals.clientStateT.time < Globals.clientStateT.frame.servertime - 100) {
+ if (Globals.cl_showclamp.value != 0)
+ Com.Printf("low clamp " + (Globals.clientStateT.frame.servertime - 100 - Globals.clientStateT.time) + "\n");
+ Globals.clientStateT.time = Globals.clientStateT.frame.servertime - 100;
+ Globals.clientStateT.lerpfrac = 0;
+ } else
+ Globals.clientStateT.lerpfrac = 1.0f - (Globals.clientStateT.frame.servertime - Globals.clientStateT.time) * 0.01f;
+
+ if (Globals.cl_timedemo.value != 0)
+ Globals.clientStateT.lerpfrac = 1.0f;
+
+ /*
+ * is ok.. CL_AddPacketEntities (cl.frame); CL_AddTEnts ();
+ * CL_AddParticles (); CL_AddDLights (); CL_AddLightStyles ();
+ */
+
+ CalcViewValues();
+ // PMM - moved this here so the heat beam has the right values for the
+ // vieworg, and can lock the beam to the gun
+ AddPacketEntities(Globals.clientStateT.frame);
+
+ CL_tent.AddTEnts();
+ CL_fx.AddParticles();
+ CL_fx.AddDLights();
+ CL_fx.AddLightStyles();
+ }
+
+ /*
+ * =============== CL_GetEntitySoundOrigin
+ *
+ * Called to get the sound spatialization origin ===============
+ */
+ public static void GetEntitySoundOrigin(int ent, float[] org) {
+ centity_t old;
+
+ if (ent < 0 || ent >= Defines.MAX_EDICTS)
+ Com.Error(Defines.ERR_DROP, "CL_GetEntitySoundOrigin: bad ent");
+ old = Globals.cl_entities[ent];
+ Math3D.vectorCopy(old.lerp_origin, org);
+
+ // FIXME: bmodel issues...
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.client;
+
+import lwjake2.Defines;
+import lwjake2.Globals;
+import lwjake2.game.entity_state_t;
+import lwjake2.qcommon.Com;
+import lwjake2.sound.S;
+import lwjake2.util.Lib;
+import lwjake2.util.Math3D;
+
+/**
+ * Client Graphics Effects.
+ */
+public class CL_fx {
+
+ static final float INSTANT_PARTICLE = -10000.0f;
+ static final int PARTICLE_GRAVITY = 40;
+ static final cparticle_t[] particles = new cparticle_t[Defines.MAX_PARTICLES];
+ static final float[][] avelocities = new float[Defines.NUMVERTEXNORMALS][3];
+ static final clightstyle_t[] cl_lightstyle = new clightstyle_t[Defines.MAX_LIGHTSTYLES];
+ static final cdlight_t[] cl_dlights = new cdlight_t[Defines.MAX_DLIGHTS];
+ // stack variable
+ private static final float[] fv = {0, 0, 0};
+ private static final float[] rv = {0, 0, 0};
+ // stack variable
+ private static final float[] origin = {0, 0, 0};
+ private static final float[] forward = {0, 0, 0};
+ private static final float[] right = {0, 0, 0};
+ // stack variable
+ private static final float[] move = {0, 0, 0};
+ /*
+ * ==============================================================
+ *
+ * LIGHT STYLE MANAGEMENT
+ *
+ * ==============================================================
+ */
+ private static final float[] vec = {0, 0, 0};
+ // stack variable
+ private static final float[] v = {0, 0, 0};
+ /*
+ * ==============================================================
+ *
+ * DLIGHT MANAGEMENT
+ *
+ * ==============================================================
+ */
+ // stack variable
+ // move, vec
+ private static final float[] start = {0, 0, 0};
+ private static final float[] end = {0, 0, 0};
+ // stack variable
+ private static final float[] dir = {0, 0, 0};
+ // stack variable
+ private static final float[] org = {0, 0, 0};
+ private static final int BEAMLENGTH = 16;
+ /*
+ * =============== CL_BigTeleportParticles ===============
+ */
+ private static final int[] colortable = {2 * 8, 13 * 8, 21 * 8, 18 * 8};
+ static int cl_numparticles = Defines.MAX_PARTICLES;
+ static int lastofs;
+ static cparticle_t active_particles, free_particles;
+
+ static {
+ for (int i = 0; i < particles.length; i++)
+ particles[i] = new cparticle_t();
+ }
+
+ static {
+ for (int i = 0; i < cl_lightstyle.length; i++) {
+ cl_lightstyle[i] = new clightstyle_t();
+ }
+ }
+
+ static {
+ for (int i = 0; i < cl_dlights.length; i++)
+ cl_dlights[i] = new cdlight_t();
+ }
+
+ /*
+ * ================ CL_ClearDlights ================
+ */
+ static void ClearDlights() {
+ // memset (cl_dlights, 0, sizeof(cl_dlights));
+ for (cdlight_t cl_dlight : cl_dlights) {
+ cl_dlight.clear();
+ }
+ }
+
+ /*
+ * ================ CL_ClearLightStyles ================
+ */
+ static void ClearLightStyles() {
+ //memset (cl_lightstyle, 0, sizeof(cl_lightstyle));
+ for (clightstyle_t aCl_lightstyle : cl_lightstyle) aCl_lightstyle.clear();
+ lastofs = -1;
+ }
+
+ /*
+ * ================ CL_RunLightStyles ================
+ */
+ static void RunLightStyles() {
+ clightstyle_t ls;
+
+ int ofs = Globals.clientStateT.time / 100;
+ if (ofs == lastofs)
+ return;
+ lastofs = ofs;
+
+ for (clightstyle_t aCl_lightstyle : cl_lightstyle) {
+ ls = aCl_lightstyle;
+ if (ls.length == 0) {
+ ls.value[0] = ls.value[1] = ls.value[2] = 1.0f;
+ continue;
+ }
+ if (ls.length == 1)
+ ls.value[0] = ls.value[1] = ls.value[2] = ls.map[0];
+ else
+ ls.value[0] = ls.value[1] = ls.value[2] = ls.map[ofs % ls.length];
+ }
+ }
+
+ static void SetLightstyle(int i) {
+ String s;
+ int j, k;
+
+ s = Globals.clientStateT.configstrings[i + Defines.CS_LIGHTS];
+
+ j = s.length();
+ if (j >= Defines.MAX_QPATH)
+ Com.Error(Defines.ERR_DROP, "svc_lightstyle length=" + j);
+
+ cl_lightstyle[i].length = j;
+
+ for (k = 0; k < j; k++)
+ cl_lightstyle[i].map[k] = (float) (s.charAt(k) - 'a') / (float) ('m' - 'a');
+ }
+
+ /*
+ * ================ CL_AddLightStyles ================
+ */
+ static void AddLightStyles() {
+ clightstyle_t ls;
+
+ for (int i = 0; i < cl_lightstyle.length; i++) {
+ ls = cl_lightstyle[i];
+ V.AddLightStyle(i, ls.value[0], ls.value[1], ls.value[2]);
+ }
+ }
+
+ /*
+ * =============== CL_AllocDlight
+ *
+ * ===============
+ */
+ static cdlight_t AllocDlight(int key) {
+ int i;
+ cdlight_t dl;
+
+ // first look for an exact key match
+ if (key != 0) {
+ for (i = 0; i < Defines.MAX_DLIGHTS; i++) {
+ dl = cl_dlights[i];
+ if (dl.key == key) {
+ //memset (dl, 0, sizeof(*dl));
+ dl.clear();
+ dl.key = key;
+ return dl;
+ }
+ }
+ }
+
+ // then look for anything else
+ for (i = 0; i < Defines.MAX_DLIGHTS; i++) {
+ dl = cl_dlights[i];
+ if (dl.die < Globals.clientStateT.time) {
+ //memset (dl, 0, sizeof(*dl));
+ dl.clear();
+ dl.key = key;
+ return dl;
+ }
+ }
+
+ //dl = &cl_dlights[0];
+ //memset (dl, 0, sizeof(*dl));
+ dl = cl_dlights[0];
+ dl.clear();
+ dl.key = key;
+ return dl;
+ }
+
+ /*
+ * ===============
+ * CL_RunDLights
+ * ===============
+ */
+ static void RunDLights() {
+ cdlight_t dl;
+
+ for (int i = 0; i < Defines.MAX_DLIGHTS; i++) {
+ dl = cl_dlights[i];
+ if (dl.radius == 0.0f)
+ continue;
+
+ if (dl.die < Globals.clientStateT.time) {
+ dl.radius = 0.0f;
+ return;
+ }
+ }
+ }
+
+ /*
+ * =============== CL_AddDLights
+ *
+ * ===============
+ */
+ static void AddDLights() {
+ cdlight_t dl;
+
+ // =====
+ // PGM
+ if (Globals.vidref_val == Defines.VIDREF_GL) {
+ for (int i = 0; i < Defines.MAX_DLIGHTS; i++) {
+ dl = cl_dlights[i];
+ if (dl.radius == 0.0f)
+ continue;
+ V.AddLight(dl.origin, dl.radius, dl.color[0], dl.color[1], dl.color[2]);
+ }
+ } else {
+ for (int i = 0; i < Defines.MAX_DLIGHTS; i++) {
+ dl = cl_dlights[i];
+ if (dl.radius == 0.0f)
+ continue;
+
+ // negative light in software. only black allowed
+ if ((dl.color[0] < 0) || (dl.color[1] < 0) || (dl.color[2] < 0)) {
+ dl.radius = -(dl.radius);
+ dl.color[0] = 1;
+ dl.color[1] = 1;
+ dl.color[2] = 1;
+ }
+ V.AddLight(dl.origin, dl.radius, dl.color[0], dl.color[1], dl.color[2]);
+ }
+ }
+ // PGM
+ // =====
+ }
+
+ /*
+ * =============== CL_ClearParticles ===============
+ */
+ static void ClearParticles() {
+ free_particles = particles[0];
+ active_particles = null;
+
+ for (int i = 0; i < particles.length - 1; i++)
+ particles[i].next = particles[i + 1];
+ particles[particles.length - 1].next = null;
+ }
+
+ /*
+ * =============== CL_ParticleEffect
+ *
+ * Wall impact puffs ===============
+ */
+ static void ParticleEffect(int color, int count) {
+ int j;
+ cparticle_t p;
+ float d;
+
+ for (int i = 0; i < count; i++) {
+ if (free_particles == null)
+ return;
+ p = free_particles;
+ free_particles = p.next;
+ p.next = active_particles;
+ active_particles = p;
+
+ p.time = Globals.clientStateT.time;
+ p.color = color + (Lib.rand() & 7);
+
+ d = Lib.rand() & 31;
+ for (j = 0; j < 3; j++) {
+ p.org[j] = CL_tent.pos[j] + ((Lib.rand() & 7) - 4) + d * CL_tent.dir[j];
+ p.vel[j] = Lib.crand() * 20;
+ }
+
+ p.accel[0] = p.accel[1] = 0;
+ p.accel[2] = -PARTICLE_GRAVITY;
+ p.alpha = 1.0f;
+
+ p.alphavel = -1.0f / (0.5f + Globals.rnd.nextFloat() * 0.3f);
+ }
+ }
+
+ /*
+ * =============== CL_ParticleEffect2 ===============
+ */
+ static void ParticleEffect2(int color, int count) {
+ int j;
+ cparticle_t p;
+ float d;
+
+ for (int i = 0; i < count; i++) {
+ if (free_particles == null)
+ return;
+ p = free_particles;
+ free_particles = p.next;
+ p.next = active_particles;
+ active_particles = p;
+
+ p.time = Globals.clientStateT.time;
+ p.color = color;
+
+ d = Lib.rand() & 7;
+ for (j = 0; j < 3; j++) {
+ p.org[j] = CL_tent.pos[j] + ((Lib.rand() & 7) - 4) + d * CL_tent.dir[j];
+ p.vel[j] = Lib.crand() * 20;
+ }
+
+ p.accel[0] = p.accel[1] = 0;
+ p.accel[2] = -PARTICLE_GRAVITY;
+ p.alpha = 1.0f;
+
+ p.alphavel = -1.0f / (0.5f + Globals.rnd.nextFloat() * 0.3f);
+ }
+ }
+
+ // RAFAEL
+ /*
+ * =============== CL_ParticleEffect3 ===============
+ */
+ static void ParticleEffect3(int color, int count) {
+ int j;
+ cparticle_t p;
+ float d;
+
+ for (int i = 0; i < count; i++) {
+ if (free_particles == null)
+ return;
+ p = free_particles;
+ free_particles = p.next;
+ p.next = active_particles;
+ active_particles = p;
+
+ p.time = Globals.clientStateT.time;
+ p.color = color;
+
+ d = Lib.rand() & 7;
+ for (j = 0; j < 3; j++) {
+ p.org[j] = CL_tent.pos[j] + ((Lib.rand() & 7) - 4) + d * CL_tent.dir[j];
+ p.vel[j] = Lib.crand() * 20;
+ }
+
+ p.accel[0] = p.accel[1] = 0;
+ p.accel[2] = PARTICLE_GRAVITY;
+ p.alpha = 1.0f;
+
+ p.alphavel = -1.0f / (0.5f + Globals.rnd.nextFloat() * 0.3f);
+ }
+ }
+
+ /*
+ * =============== CL_TeleporterParticles ===============
+ */
+ static void TeleporterParticles(entity_state_t ent) {
+ int j;
+ cparticle_t p;
+
+ for (int i = 0; i < 8; i++) {
+ if (free_particles == null)
+ return;
+ p = free_particles;
+ free_particles = p.next;
+ p.next = active_particles;
+ active_particles = p;
+
+ p.time = Globals.clientStateT.time;
+ p.color = 0xdb;
+
+ for (j = 0; j < 2; j++) {
+ p.org[j] = ent.origin[j] - 16 + (Lib.rand() & 31);
+ p.vel[j] = Lib.crand() * 14;
+ }
+
+ p.org[2] = ent.origin[2] - 8 + (Lib.rand() & 7);
+ p.vel[2] = 80 + (Lib.rand() & 7);
+
+ p.accel[0] = p.accel[1] = 0;
+ p.accel[2] = -PARTICLE_GRAVITY;
+ p.alpha = 1.0f;
+
+ p.alphavel = -0.5f;
+ }
+ }
+
+ /*
+ * =============== CL_LogoutEffect
+ *
+ * ===============
+ */
+ static void LogoutEffect(float[] org, int type) {
+ int j;
+ cparticle_t p;
+
+ for (int i = 0; i < 500; i++) {
+ if (free_particles == null)
+ return;
+ p = free_particles;
+ free_particles = p.next;
+ p.next = active_particles;
+ active_particles = p;
+
+ p.time = Globals.clientStateT.time;
+
+ if (type == Defines.MZ_LOGIN)
+ p.color = 0xd0 + (Lib.rand() & 7); // green
+ else if (type == Defines.MZ_LOGOUT)
+ p.color = 0x40 + (Lib.rand() & 7); // red
+ else
+ p.color = 0xe0 + (Lib.rand() & 7); // yellow
+
+ p.org[0] = org[0] - 16 + Globals.rnd.nextFloat() * 32;
+ p.org[1] = org[1] - 16 + Globals.rnd.nextFloat() * 32;
+ p.org[2] = org[2] - 24 + Globals.rnd.nextFloat() * 56;
+
+ for (j = 0; j < 3; j++)
+ p.vel[j] = Lib.crand() * 20;
+
+ p.accel[0] = p.accel[1] = 0;
+ p.accel[2] = -PARTICLE_GRAVITY;
+ p.alpha = 1.0f;
+
+ p.alphavel = -1.0f / (1.0f + Globals.rnd.nextFloat() * 0.3f);
+ }
+ }
+
+ /*
+ * =============== CL_ItemRespawnParticles
+ *
+ * ===============
+ */
+ static void ItemRespawnParticles(float[] org) {
+ int j;
+ cparticle_t p;
+
+ for (int i = 0; i < 64; i++) {
+ if (free_particles == null)
+ return;
+ p = free_particles;
+ free_particles = p.next;
+ p.next = active_particles;
+ active_particles = p;
+
+ p.time = Globals.clientStateT.time;
+
+ p.color = 0xd4 + (Lib.rand() & 3); // green
+
+ p.org[0] = org[0] + Lib.crand() * 8;
+ p.org[1] = org[1] + Lib.crand() * 8;
+ p.org[2] = org[2] + Lib.crand() * 8;
+
+ for (j = 0; j < 3; j++)
+ p.vel[j] = Lib.crand() * 8;
+
+ p.accel[0] = p.accel[1] = 0;
+ p.accel[2] = -PARTICLE_GRAVITY * 0.2f;
+ p.alpha = 1.0f;
+
+ p.alphavel = -1.0f / (1.0f + Globals.rnd.nextFloat() * 0.3f);
+ }
+ }
+
+ /*
+ * =============== CL_ExplosionParticles ===============
+ */
+ static void ExplosionParticles() {
+ int j;
+ cparticle_t p;
+
+ for (int i = 0; i < 256; i++) {
+ if (free_particles == null)
+ return;
+ p = free_particles;
+ free_particles = p.next;
+ p.next = active_particles;
+ active_particles = p;
+
+ p.time = Globals.clientStateT.time;
+ p.color = 0xe0 + (Lib.rand() & 7);
+
+ for (j = 0; j < 3; j++) {
+ p.org[j] = CL_tent.pos[j] + ((Lib.rand() % 32) - 16);
+ p.vel[j] = (Lib.rand() % 384) - 192;
+ }
+
+ p.accel[0] = p.accel[1] = 0.0f;
+ p.accel[2] = -PARTICLE_GRAVITY;
+ p.alpha = 1.0f;
+
+ p.alphavel = -0.8f / (0.5f + Globals.rnd.nextFloat() * 0.3f);
+ }
+ }
+
+ static void BigTeleportParticles() {
+ cparticle_t p;
+ float angle, dist;
+
+ for (int i = 0; i < 4096; i++) {
+ if (free_particles == null)
+ return;
+ p = free_particles;
+ free_particles = p.next;
+ p.next = active_particles;
+ active_particles = p;
+
+ p.time = Globals.clientStateT.time;
+
+ p.color = colortable[Lib.rand() & 3];
+
+ angle = (float) (Math.PI * 2 * (Lib.rand() & 1023) / 1023.0);
+ dist = Lib.rand() & 31;
+ p.org[0] = (float) (CL_tent.pos[0] + Math.cos(angle) * dist);
+ p.vel[0] = (float) (Math.cos(angle) * (70 + (Lib.rand() & 63)));
+ p.accel[0] = (float) (-Math.cos(angle) * 100);
+
+ p.org[1] = (float) (CL_tent.pos[1] + Math.sin(angle) * dist);
+ p.vel[1] = (float) (Math.sin(angle) * (70 + (Lib.rand() & 63)));
+ p.accel[1] = (float) (-Math.sin(angle) * 100);
+
+ p.org[2] = CL_tent.pos[2] + 8 + (Lib.rand() % 90);
+ p.vel[2] = -100 + (Lib.rand() & 31);
+ p.accel[2] = PARTICLE_GRAVITY * 4;
+ p.alpha = 1.0f;
+
+ p.alphavel = -0.3f / (0.5f + Globals.rnd.nextFloat() * 0.3f);
+ }
+ }
+
+ /*
+ * =============== CL_BlasterParticles
+ *
+ * Wall impact puffs ===============
+ */
+ static void BlasterParticles() {
+ int j;
+ cparticle_t p;
+ float d;
+
+ int count = 40;
+ for (int i = 0; i < count; i++) {
+ if (free_particles == null)
+ return;
+ p = free_particles;
+ free_particles = p.next;
+ p.next = active_particles;
+ active_particles = p;
+
+ p.time = Globals.clientStateT.time;
+ p.color = 0xe0 + (Lib.rand() & 7);
+
+ d = Lib.rand() & 15;
+ for (j = 0; j < 3; j++) {
+ p.org[j] = CL_tent.pos[j] + ((Lib.rand() & 7) - 4) + d * CL_tent.dir[j];
+ p.vel[j] = CL_tent.dir[j] * 30 + Lib.crand() * 40;
+ }
+
+ p.accel[0] = p.accel[1] = 0;
+ p.accel[2] = -PARTICLE_GRAVITY;
+ p.alpha = 1.0f;
+
+ p.alphavel = -1.0f / (0.5f + Globals.rnd.nextFloat() * 0.3f);
+ }
+ }
+
+ /*
+ * =============== CL_BlasterTrail
+ *
+ * ===============
+ */
+ static void BlasterTrail(float[] start, float[] end) {
+ float len;
+ int j;
+ cparticle_t p;
+ int dec;
+
+ Math3D.vectorCopy(start, move);
+ Math3D.vectorSubtract(end, start, vec);
+ len = Math3D.vectorNormalize(vec);
+
+ dec = 5;
+ Math3D.vectorScale(vec, 5, vec);
+
+ // FIXME: this is a really silly way to have a loop
+ while (len > 0) {
+ len -= dec;
+
+ if (free_particles == null)
+ return;
+ p = free_particles;
+ free_particles = p.next;
+ p.next = active_particles;
+ active_particles = p;
+ Math3D.vectorClear(p.accel);
+
+ p.time = Globals.clientStateT.time;
+
+ p.alpha = 1.0f;
+ p.alphavel = -1.0f / (0.3f + Globals.rnd.nextFloat() * 0.2f);
+ p.color = 0xe0;
+ for (j = 0; j < 3; j++) {
+ p.org[j] = move[j] + Lib.crand();
+ p.vel[j] = Lib.crand() * 5;
+ p.accel[j] = 0;
+ }
+
+ Math3D.vectorAdd(move, vec, move);
+ }
+ }
+
+ // stack variable
+ // move, vec
+ /*
+ * ===============
+ * CL_FlagTrail
+ * ===============
+ */
+ static void FlagTrail(float[] start, float[] end, float color) {
+ float len;
+ int j;
+ cparticle_t p;
+ int dec;
+
+ Math3D.vectorCopy(start, move);
+ Math3D.vectorSubtract(end, start, vec);
+ len = Math3D.vectorNormalize(vec);
+
+ dec = 5;
+ Math3D.vectorScale(vec, 5, vec);
+
+ while (len > 0) {
+ len -= dec;
+
+ if (free_particles == null)
+ return;
+ p = free_particles;
+ free_particles = p.next;
+ p.next = active_particles;
+ active_particles = p;
+ Math3D.vectorClear(p.accel);
+
+ p.time = Globals.clientStateT.time;
+
+ p.alpha = 1.0f;
+ p.alphavel = -1.0f / (0.8f + Globals.rnd.nextFloat() * 0.2f);
+ p.color = color;
+ for (j = 0; j < 3; j++) {
+ p.org[j] = move[j] + Lib.crand() * 16;
+ p.vel[j] = Lib.crand() * 5;
+ p.accel[j] = 0;
+ }
+
+ Math3D.vectorAdd(move, vec, move);
+ }
+ }
+
+ // stack variable
+ // move, vec
+ /*
+ * =============== CL_DiminishingTrail
+ *
+ * ===============
+ */
+ static void DiminishingTrail(float[] start, float[] end, centity_t old, int flags) {
+ cparticle_t p;
+ float orgscale;
+ float velscale;
+
+ Math3D.vectorCopy(start, move);
+ Math3D.vectorSubtract(end, start, vec);
+ float len = Math3D.vectorNormalize(vec);
+
+ float dec = 0.5f;
+ Math3D.vectorScale(vec, dec, vec);
+
+ if (old.trailcount > 900) {
+ orgscale = 4;
+ velscale = 15;
+ } else if (old.trailcount > 800) {
+ orgscale = 2;
+ velscale = 10;
+ } else {
+ orgscale = 1;
+ velscale = 5;
+ }
+
+ while (len > 0) {
+ len -= dec;
+
+ if (free_particles == null)
+ return;
+
+ // drop less particles as it flies
+ if ((Lib.rand() & 1023) < old.trailcount) {
+ p = free_particles;
+ free_particles = p.next;
+ p.next = active_particles;
+ active_particles = p;
+ Math3D.vectorClear(p.accel);
+
+ p.time = Globals.clientStateT.time;
+
+ if ((flags & Defines.EF_GIB) != 0) {
+ p.alpha = 1.0f;
+ p.alphavel = -1.0f / (1.0f + Globals.rnd.nextFloat() * 0.4f);
+ p.color = 0xe8 + (Lib.rand() & 7);
+ for (int j = 0; j < 3; j++) {
+ p.org[j] = move[j] + Lib.crand() * orgscale;
+ p.vel[j] = Lib.crand() * velscale;
+ p.accel[j] = 0;
+ }
+ p.vel[2] -= PARTICLE_GRAVITY;
+ } else if ((flags & Defines.EF_GREENGIB) != 0) {
+ p.alpha = 1.0f;
+ p.alphavel = -1.0f / (1.0f + Globals.rnd.nextFloat() * 0.4f);
+ p.color = 0xdb + (Lib.rand() & 7);
+ for (int j = 0; j < 3; j++) {
+ p.org[j] = move[j] + Lib.crand() * orgscale;
+ p.vel[j] = Lib.crand() * velscale;
+ p.accel[j] = 0;
+ }
+ p.vel[2] -= PARTICLE_GRAVITY;
+ } else {
+ p.alpha = 1.0f;
+ p.alphavel = -1.0f / (1.0f + Globals.rnd.nextFloat() * 0.2f);
+ p.color = 4 + (Lib.rand() & 7);
+ for (int j = 0; j < 3; j++) {
+ p.org[j] = move[j] + Lib.crand() * orgscale;
+ p.vel[j] = Lib.crand() * velscale;
+ }
+ p.accel[2] = 20;
+ }
+ }
+
+ old.trailcount -= 5;
+ if (old.trailcount < 100)
+ old.trailcount = 100;
+ Math3D.vectorAdd(move, vec, move);
+ }
+ }
+
+ // stack variable
+ // move, vec
+ /*
+ * =============== CL_RocketTrail
+ *
+ * ===============
+ */
+ static void RocketTrail(float[] start, float[] end, centity_t old) {
+ float len;
+ int j;
+ cparticle_t p;
+ float dec;
+
+ // smoke
+ DiminishingTrail(start, end, old, Defines.EF_ROCKET);
+
+ // fire
+ Math3D.vectorCopy(start, move);
+ Math3D.vectorSubtract(end, start, vec);
+ len = Math3D.vectorNormalize(vec);
+
+ dec = 1;
+ Math3D.vectorScale(vec, dec, vec);
+
+ while (len > 0) {
+ len -= dec;
+
+ if (free_particles == null)
+ return;
+
+ if ((Lib.rand() & 7) == 0) {
+ p = free_particles;
+ free_particles = p.next;
+ p.next = active_particles;
+ active_particles = p;
+
+ Math3D.vectorClear(p.accel);
+ p.time = Globals.clientStateT.time;
+
+ p.alpha = 1.0f;
+ p.alphavel = -1.0f / (1.0f + Globals.rnd.nextFloat() * 0.2f);
+ p.color = 0xdc + (Lib.rand() & 3);
+ for (j = 0; j < 3; j++) {
+ p.org[j] = move[j] + Lib.crand() * 5;
+ p.vel[j] = Lib.crand() * 20;
+ }
+ p.accel[2] = -PARTICLE_GRAVITY;
+ }
+ Math3D.vectorAdd(move, vec, move);
+ }
+ }
+
+ // stack variable
+ // move, vec
+ /*
+ * =============== CL_RailTrail
+ *
+ * ===============
+ */
+ static void RailTrail() {
+ float len;
+ int j;
+ cparticle_t p;
+ float dec;
+ float[] right = new float[3];
+ float[] up = new float[3];
+ int i;
+ float d, c, s;
+ float[] dir = new float[3];
+ byte clr = 0x74;
+
+ Math3D.vectorCopy(CL_tent.pos, move);
+ Math3D.vectorSubtract(CL_tent.pos2, CL_tent.pos, vec);
+ len = Math3D.vectorNormalize(vec);
+
+ Math3D.makeNormalVectors(vec, right, up);
+
+ for (i = 0; i < len; i++) {
+ if (free_particles == null)
+ return;
+
+ p = free_particles;
+ free_particles = p.next;
+ p.next = active_particles;
+ active_particles = p;
+
+ p.time = Globals.clientStateT.time;
+ Math3D.vectorClear(p.accel);
+
+ d = i * 0.1f;
+ c = (float) Math.cos(d);
+ s = (float) Math.sin(d);
+
+ Math3D.vectorScale(right, c, dir);
+ Math3D.vectorMA(dir, s, up, dir);
+
+ p.alpha = 1.0f;
+ p.alphavel = -1.0f / (1.0f + Globals.rnd.nextFloat() * 0.2f);
+ p.color = clr + (Lib.rand() & 7);
+ for (j = 0; j < 3; j++) {
+ p.org[j] = move[j] + dir[j] * 3;
+ p.vel[j] = dir[j] * 6;
+ }
+
+ Math3D.vectorAdd(move, vec, move);
+ }
+
+ dec = 0.75f;
+ Math3D.vectorScale(vec, dec, vec);
+ Math3D.vectorCopy(CL_tent.pos, move);
+
+ while (len > 0) {
+ len -= dec;
+
+ if (free_particles == null)
+ return;
+ p = free_particles;
+ free_particles = p.next;
+ p.next = active_particles;
+ active_particles = p;
+
+ p.time = Globals.clientStateT.time;
+ Math3D.vectorClear(p.accel);
+
+ p.alpha = 1.0f;
+ p.alphavel = -1.0f / (0.6f + Globals.rnd.nextFloat() * 0.2f);
+ p.color = Lib.rand() & 15;
+
+ for (j = 0; j < 3; j++) {
+ p.org[j] = move[j] + Lib.crand() * 3;
+ p.vel[j] = Lib.crand() * 3;
+ p.accel[j] = 0;
+ }
+
+ Math3D.vectorAdd(move, vec, move);
+ }
+ }
+
+ // stack variable
+ // move, vec
+ /*
+ * =============== CL_IonripperTrail ===============
+ */
+ static void IonripperTrail(float[] start, float[] ent) {
+ float len;
+ int j;
+ cparticle_t p;
+ int dec;
+ int left = 0;
+
+ Math3D.vectorCopy(start, move);
+ Math3D.vectorSubtract(ent, start, vec);
+ len = Math3D.vectorNormalize(vec);
+
+ dec = 5;
+ Math3D.vectorScale(vec, 5, vec);
+
+ while (len > 0) {
+ len -= dec;
+
+ if (free_particles == null)
+ return;
+ p = free_particles;
+ free_particles = p.next;
+ p.next = active_particles;
+ active_particles = p;
+ Math3D.vectorClear(p.accel);
+
+ p.time = Globals.clientStateT.time;
+ p.alpha = 0.5f;
+ p.alphavel = -1.0f / (0.3f + Globals.rnd.nextFloat() * 0.2f);
+ p.color = 0xe4 + (Lib.rand() & 3);
+
+ for (j = 0; j < 3; j++) {
+ p.org[j] = move[j];
+ p.accel[j] = 0;
+ }
+ if (left != 0) {
+ left = 0;
+ p.vel[0] = 10;
+ } else {
+ left = 1;
+ p.vel[0] = -10;
+ }
+
+ p.vel[1] = 0;
+ p.vel[2] = 0;
+
+ Math3D.vectorAdd(move, vec, move);
+ }
+ }
+
+ // stack variable
+ // move, vec
+ /*
+ * =============== CL_BubbleTrail
+ *
+ * ===============
+ */
+ static void BubbleTrail() {
+ float len;
+ int i, j;
+ cparticle_t p;
+ float dec;
+
+ Math3D.vectorCopy(CL_tent.pos, move);
+ Math3D.vectorSubtract(CL_tent.pos2, CL_tent.pos, vec);
+ len = Math3D.vectorNormalize(vec);
+
+ dec = 32;
+ Math3D.vectorScale(vec, dec, vec);
+
+ for (i = 0; i < len; i += dec) {
+ if (free_particles == null)
+ return;
+
+ p = free_particles;
+ free_particles = p.next;
+ p.next = active_particles;
+ active_particles = p;
+
+ Math3D.vectorClear(p.accel);
+ p.time = Globals.clientStateT.time;
+
+ p.alpha = 1.0f;
+ p.alphavel = -1.0f / (1.0f + Globals.rnd.nextFloat() * 0.2f);
+ p.color = 4 + (Lib.rand() & 7);
+ for (j = 0; j < 3; j++) {
+ p.org[j] = move[j] + Lib.crand() * 2;
+ p.vel[j] = Lib.crand() * 5;
+ }
+ p.vel[2] += 6;
+
+ Math3D.vectorAdd(move, vec, move);
+ }
+ }
+
+ // stack variable
+ // forward
+ /*
+ * =============== CL_FlyParticles ===============
+ */
+ static void FlyParticles(float[] origin, int count) {
+ int i;
+ cparticle_t p;
+ float angle;
+ float sp, sy, cp, cy;
+ float dist = 64;
+ float ltime;
+
+ if (count > Defines.NUMVERTEXNORMALS)
+ count = Defines.NUMVERTEXNORMALS;
+
+ if (avelocities[0][0] == 0.0f) {
+ for (i = 0; i < Defines.NUMVERTEXNORMALS; i++) {
+ avelocities[i][0] = (Lib.rand() & 255) * 0.01f;
+ avelocities[i][1] = (Lib.rand() & 255) * 0.01f;
+ avelocities[i][2] = (Lib.rand() & 255) * 0.01f;
+ }
+ }
+
+ ltime = Globals.clientStateT.time / 1000.0f;
+ for (i = 0; i < count; i += 2) {
+ angle = ltime * avelocities[i][0];
+ sy = (float) Math.sin(angle);
+ cy = (float) Math.cos(angle);
+ angle = ltime * avelocities[i][1];
+ sp = (float) Math.sin(angle);
+ cp = (float) Math.cos(angle);
+ angle = ltime * avelocities[i][2];
+
+ forward[0] = cp * cy;
+ forward[1] = cp * sy;
+ forward[2] = -sp;
+
+ if (free_particles == null)
+ return;
+ p = free_particles;
+ free_particles = p.next;
+ p.next = active_particles;
+ active_particles = p;
+
+ p.time = Globals.clientStateT.time;
+
+ dist = (float) Math.sin(ltime + i) * 64;
+ p.org[0] = origin[0] + Globals.bytedirs[i][0] * dist + forward[0] * BEAMLENGTH;
+ p.org[1] = origin[1] + Globals.bytedirs[i][1] * dist + forward[1] * BEAMLENGTH;
+ p.org[2] = origin[2] + Globals.bytedirs[i][2] * dist + forward[2] * BEAMLENGTH;
+
+ Math3D.vectorClear(p.vel);
+ Math3D.vectorClear(p.accel);
+
+ p.color = 0;
+ //p.colorvel = 0;
+
+ p.alpha = 1;
+ p.alphavel = -100;
+ }
+ }
+
+ static void FlyEffect(centity_t ent, float[] origin) {
+ int n;
+ int count;
+ int starttime;
+
+ if (ent.fly_stoptime < Globals.clientStateT.time) {
+ starttime = Globals.clientStateT.time;
+ ent.fly_stoptime = Globals.clientStateT.time + 60000;
+ } else {
+ starttime = ent.fly_stoptime - 60000;
+ }
+
+ n = Globals.clientStateT.time - starttime;
+ if (n < 20000)
+ count = (int) ((n * 162) / 20000.0);
+ else {
+ n = ent.fly_stoptime - Globals.clientStateT.time;
+ if (n < 20000)
+ count = (int) ((n * 162) / 20000.0);
+ else
+ count = 162;
+ }
+
+ FlyParticles(origin, count);
+ }
+
+ // forward
+ /*
+ * =============== CL_BfgParticles ===============
+ */
+ //#define BEAMLENGTH 16
+ static void BfgParticles() {
+ int i;
+ cparticle_t p;
+ float angle;
+ float sp, sy, cp, cy;
+ float dist = 64;
+ float ltime;
+
+ if (avelocities[0][0] == 0.0f) {
+ for (i = 0; i < Defines.NUMVERTEXNORMALS; i++) {
+ avelocities[i][0] = (Lib.rand() & 255) * 0.01f;
+ avelocities[i][1] = (Lib.rand() & 255) * 0.01f;
+ avelocities[i][2] = (Lib.rand() & 255) * 0.01f;
+ }
+ }
+
+ ltime = Globals.clientStateT.time / 1000.0f;
+ for (i = 0; i < Defines.NUMVERTEXNORMALS; i++) {
+ angle = ltime * avelocities[i][0];
+ sy = (float) Math.sin(angle);
+ cy = (float) Math.cos(angle);
+ angle = ltime * avelocities[i][1];
+ sp = (float) Math.sin(angle);
+ cp = (float) Math.cos(angle);
+ angle = ltime * avelocities[i][2];
+
+ forward[0] = cp * cy;
+ forward[1] = cp * sy;
+ forward[2] = -sp;
+
+ if (free_particles == null)
+ return;
+ p = free_particles;
+ free_particles = p.next;
+ p.next = active_particles;
+ active_particles = p;
+
+ p.time = Globals.clientStateT.time;
+
+ dist = (float) (Math.sin(ltime + i) * 64);
+ p.org[0] = CL_ents.ent.origin[0] + Globals.bytedirs[i][0] * dist + forward[0] * BEAMLENGTH;
+ p.org[1] = CL_ents.ent.origin[1] + Globals.bytedirs[i][1] * dist + forward[1] * BEAMLENGTH;
+ p.org[2] = CL_ents.ent.origin[2] + Globals.bytedirs[i][2] * dist + forward[2] * BEAMLENGTH;
+
+ Math3D.vectorClear(p.vel);
+ Math3D.vectorClear(p.accel);
+
+ Math3D.vectorSubtract(p.org, CL_ents.ent.origin, v);
+ dist = Math3D.vectorLength(v) / 90.0f;
+ p.color = (float) Math.floor(0xd0 + dist * 7);
+ //p.colorvel = 0;
+
+ p.alpha = 1.0f - dist;
+ p.alphavel = -100;
+ }
+ }
+
+ /*
+ * =============== CL_TrapParticles ===============
+ */
+ // RAFAEL
+ static void TrapParticles() {
+ float len;
+ int j;
+ cparticle_t p;
+ int dec;
+
+ CL_ents.ent.origin[2] -= 14;
+ Math3D.vectorCopy(CL_ents.ent.origin, start);
+ Math3D.vectorCopy(CL_ents.ent.origin, end);
+ end[2] += 64;
+
+ Math3D.vectorCopy(start, move);
+ Math3D.vectorSubtract(end, start, vec);
+ len = Math3D.vectorNormalize(vec);
+
+ dec = 5;
+ Math3D.vectorScale(vec, 5, vec);
+
+ // FIXME: this is a really silly way to have a loop
+ while (len > 0) {
+ len -= dec;
+
+ if (free_particles == null)
+ return;
+ p = free_particles;
+ free_particles = p.next;
+ p.next = active_particles;
+ active_particles = p;
+ Math3D.vectorClear(p.accel);
+
+ p.time = Globals.clientStateT.time;
+
+ p.alpha = 1.0f;
+ p.alphavel = -1.0f / (0.3f + Globals.rnd.nextFloat() * 0.2f);
+ p.color = 0xe0;
+ for (j = 0; j < 3; j++) {
+ p.org[j] = move[j] + Lib.crand();
+ p.vel[j] = Lib.crand() * 15;
+ p.accel[j] = 0;
+ }
+ p.accel[2] = PARTICLE_GRAVITY;
+
+ Math3D.vectorAdd(move, vec, move);
+ }
+
+ int i, k;
+ //cparticle_t p;
+ float vel;
+ float[] dir = new float[3];
+ float[] org = new float[3];
+
+ CL_ents.ent.origin[2] += 14;
+ Math3D.vectorCopy(CL_ents.ent.origin, org);
+
+ for (i = -2; i <= 2; i += 4)
+ for (j = -2; j <= 2; j += 4)
+ for (k = -2; k <= 4; k += 4) {
+ if (free_particles == null)
+ return;
+ p = free_particles;
+ free_particles = p.next;
+ p.next = active_particles;
+ active_particles = p;
+
+ p.time = Globals.clientStateT.time;
+ p.color = 0xe0 + (Lib.rand() & 3);
+
+ p.alpha = 1.0f;
+ p.alphavel = -1.0f / (0.3f + (Lib.rand() & 7) * 0.02f);
+
+ p.org[0] = org[0] + i + ((Lib.rand() & 23) * Lib.crand());
+ p.org[1] = org[1] + j + ((Lib.rand() & 23) * Lib.crand());
+ p.org[2] = org[2] + k + ((Lib.rand() & 23) * Lib.crand());
+
+ dir[0] = j * 8;
+ dir[1] = i * 8;
+ dir[2] = k * 8;
+
+ Math3D.vectorNormalize(dir);
+ vel = 50 + Lib.rand() & 63;
+ Math3D.vectorScale(dir, vel, p.vel);
+
+ p.accel[0] = p.accel[1] = 0;
+ p.accel[2] = -PARTICLE_GRAVITY;
+ }
+
+ }
+
+ /*
+ * =============== CL_BFGExplosionParticles ===============
+ */
+ // FIXME combined with CL_ExplosionParticles
+ static void BFGExplosionParticles() {
+ int j;
+ cparticle_t p;
+
+ for (int i = 0; i < 256; i++) {
+ if (free_particles == null)
+ return;
+ p = free_particles;
+ free_particles = p.next;
+ p.next = active_particles;
+ active_particles = p;
+
+ p.time = Globals.clientStateT.time;
+ p.color = 0xd0 + (Lib.rand() & 7);
+
+ for (j = 0; j < 3; j++) {
+ p.org[j] = CL_tent.pos[j] + ((Lib.rand() % 32) - 16);
+ p.vel[j] = (Lib.rand() % 384) - 192;
+ }
+
+ p.accel[0] = p.accel[1] = 0;
+ p.accel[2] = -PARTICLE_GRAVITY;
+ p.alpha = 1.0f;
+
+ p.alphavel = -0.8f / (0.5f + Globals.rnd.nextFloat() * 0.3f);
+ }
+ }
+
+ /*
+ * =============== CL_TeleportParticles
+ *
+ * ===============
+ */
+ static void TeleportParticles() {
+ cparticle_t p;
+ float vel;
+
+ for (int i = -16; i <= 16; i += 4)
+ for (int j = -16; j <= 16; j += 4)
+ for (int k = -16; k <= 32; k += 4) {
+ if (free_particles == null)
+ return;
+ p = free_particles;
+ free_particles = p.next;
+ p.next = active_particles;
+ active_particles = p;
+
+ p.time = Globals.clientStateT.time;
+ p.color = 7 + (Lib.rand() & 7);
+
+ p.alpha = 1.0f;
+ p.alphavel = -1.0f / (0.3f + (Lib.rand() & 7) * 0.02f);
+
+ p.org[0] = CL_tent.pos[0] + i + (Lib.rand() & 3);
+ p.org[1] = CL_tent.pos[1] + j + (Lib.rand() & 3);
+ p.org[2] = CL_tent.pos[2] + k + (Lib.rand() & 3);
+
+ dir[0] = j * 8;
+ dir[1] = i * 8;
+ dir[2] = k * 8;
+
+ Math3D.vectorNormalize(dir);
+ vel = 50 + (Lib.rand() & 63);
+ Math3D.vectorScale(dir, vel, p.vel);
+
+ p.accel[0] = p.accel[1] = 0;
+ p.accel[2] = -PARTICLE_GRAVITY;
+ }
+ }
+
+ /*
+ * =============== CL_AddParticles ===============
+ */
+ static void AddParticles() {
+ cparticle_t p, next;
+ float alpha;
+ float time = 0.0f;
+ float time2;
+ int color;
+ cparticle_t active, tail;
+
+ active = null;
+ tail = null;
+
+ for (p = active_particles; p != null; p = next) {
+ next = p.next;
+
+ // PMM - added INSTANT_PARTICLE handling for heat beam
+ if (p.alphavel != INSTANT_PARTICLE) {
+ time = (Globals.clientStateT.time - p.time) * 0.001f;
+ alpha = p.alpha + time * p.alphavel;
+ if (alpha <= 0) { // faded out
+ p.next = free_particles;
+ free_particles = p;
+ continue;
+ }
+ } else {
+ alpha = p.alpha;
+ }
+
+ p.next = null;
+ if (tail == null)
+ active = tail = p;
+ else {
+ tail.next = p;
+ tail = p;
+ }
+
+ if (alpha > 1.0)
+ alpha = 1;
+ color = (int) p.color;
+
+ time2 = time * time;
+
+ org[0] = p.org[0] + p.vel[0] * time + p.accel[0] * time2;
+ org[1] = p.org[1] + p.vel[1] * time + p.accel[1] * time2;
+ org[2] = p.org[2] + p.vel[2] * time + p.accel[2] * time2;
+
+ V.AddParticle(org, color, alpha);
+ // PMM
+ if (p.alphavel == INSTANT_PARTICLE) {
+ p.alphavel = 0.0f;
+ p.alpha = 0.0f;
+ }
+ }
+
+ active_particles = active;
+ }
+
+ /*
+ * ==============================================================
+ *
+ * PARTICLE MANAGEMENT
+ *
+ * ==============================================================
+ */
+
+ /*
+ * ============== CL_EntityEvent
+ *
+ * An entity has just been parsed that has an event value
+ *
+ * the female events are there for backwards compatability ==============
+ */
+ static void EntityEvent(entity_state_t ent) {
+ switch (ent.event) {
+ case Defines.EV_FOOTSTEP:
+ if (Globals.cl_footsteps.value != 0.0f)
+ S.StartSound(null, ent.number, Defines.CHAN_BODY, CL_tent.cl_sfx_footsteps[Lib.rand() & 3], 1, Defines.ATTN_NORM, 0);
+ break;
+ case Defines.EV_FALLSHORT:
+ S.StartSound(null, ent.number, Defines.CHAN_AUTO, S.RegisterSound("player/land1.wav"), 1, Defines.ATTN_NORM, 0);
+ break;
+ case Defines.EV_FALL:
+ S.StartSound(null, ent.number, Defines.CHAN_AUTO, S.RegisterSound("*fall2.wav"), 1, Defines.ATTN_NORM, 0);
+ break;
+ case Defines.EV_FALLFAR:
+ S.StartSound(null, ent.number, Defines.CHAN_AUTO, S.RegisterSound("*fall1.wav"), 1, Defines.ATTN_NORM, 0);
+ break;
+ }
+ }
+
+ /*
+ * ============== CL_ClearEffects
+ *
+ * ==============
+ */
+ static void ClearEffects() {
+ ClearParticles();
+ ClearDlights();
+ ClearLightStyles();
+ }
+
+ static class cdlight_t {
+ final float[] color = {0, 0, 0};
+ final float[] origin = {0, 0, 0};
+ int key; // so entities can reuse same entry
+ float radius;
+
+ float die; // stop lighting after this time
+
+ float minlight; // don't add when contributing less
+
+ void clear() {
+ radius = minlight = color[0] = color[1] = color[2] = 0;
+ }
+ }
+
+ static class clightstyle_t {
+ final float[] value = new float[3];
+ final float[] map = new float[Defines.MAX_QPATH];
+ int length;
+
+ void clear() {
+ value[0] = value[1] = value[2] = length = 0;
+ for (int i = 0; i < map.length; i++)
+ map[i] = 0.0f;
+ }
+ }
+
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.client;
+
+import lwjake2.Defines;
+import lwjake2.Globals;
+import lwjake2.game.Cmd;
+import lwjake2.game.CvarT;
+import lwjake2.game.usercmd_t;
+import lwjake2.qcommon.*;
+import lwjake2.sys.UserInputHandler;
+import lwjake2.util.Lib;
+import lwjake2.util.Math3D;
+
+/**
+ * CL_input
+ */
+public class CL_input {
+
+ /*
+ * ===============================================================================
+ *
+ * KEY BUTTONS
+ *
+ * Continuous button event tracking is complicated by the fact that two
+ * different input sources (say, mouse button 1 and the control key) can
+ * both press the same button, but the button should only be released when
+ * both of the pressing key have been released.
+ *
+ * When a key event issues a button command (+forward, +attack, etc), it
+ * appends its key number as a parameter to the command so it can be matched
+ * up with the release.
+ *
+ * state bit 0 is the current state of the key state bit 1 is edge triggered
+ * on the up to down transition state bit 2 is edge triggered on the down to
+ * up transition
+ *
+ *
+ * Key_Event (int key, qboolean down, unsigned time);
+ *
+ * +mlook src time
+ *
+ * ===============================================================================
+ */
+ public static final kbutton_t in_strafe = new kbutton_t();
+ static final kbutton_t in_klook = new kbutton_t();
+ static final kbutton_t in_left = new kbutton_t();
+ static final kbutton_t in_right = new kbutton_t();
+ static final kbutton_t in_forward = new kbutton_t();
+ static final kbutton_t in_back = new kbutton_t();
+ static final kbutton_t in_lookup = new kbutton_t();
+ static final kbutton_t in_lookdown = new kbutton_t();
+ static final kbutton_t in_moveleft = new kbutton_t();
+ static final kbutton_t in_moveright = new kbutton_t();
+ static final kbutton_t in_speed = new kbutton_t();
+ static final kbutton_t in_use = new kbutton_t();
+ static final kbutton_t in_attack = new kbutton_t();
+ static final kbutton_t in_up = new kbutton_t();
+ static final kbutton_t in_down = new kbutton_t();
+ private static final sizebuf_t buf = new sizebuf_t();
+ private static final byte[] data = new byte[128];
+ private static final usercmd_t nullcmd = new usercmd_t();
+ static long frame_msec;
+ static long old_sys_frame_time;
+ static CvarT cl_nodelta;
+ static int in_impulse;
+
+ static void KeyDown(kbutton_t b) {
+ int k;
+ String c;
+
+ c = Cmd.Argv(1);
+ if (c.length() > 0)
+ k = Lib.atoi(c);
+ else
+ k = -1; // typed manually at the console for continuous down
+
+ if (k == b.down[0] || k == b.down[1])
+ return; // repeating key
+
+ if (b.down[0] == 0)
+ b.down[0] = k;
+ else if (b.down[1] == 0)
+ b.down[1] = k;
+ else {
+ Com.Printf("Three keys down for a button!\n");
+ return;
+ }
+
+ if ((b.state & 1) != 0)
+ return; // still down
+
+ // save timestamp
+ c = Cmd.Argv(2);
+ b.downtime = Lib.atoi(c);
+ if (b.downtime == 0)
+ b.downtime = Globals.sys_frame_time - 100;
+
+ b.state |= 3; // down + impulse down
+ }
+
+ static void KeyUp(kbutton_t b) {
+ int k;
+ String c;
+ int uptime;
+
+ c = Cmd.Argv(1);
+ if (c.length() > 0)
+ k = Lib.atoi(c);
+ else {
+ // typed manually at the console, assume for unsticking, so clear
+ // all
+ b.down[0] = b.down[1] = 0;
+ b.state = 4; // impulse up
+ return;
+ }
+
+ if (b.down[0] == k)
+ b.down[0] = 0;
+ else if (b.down[1] == k)
+ b.down[1] = 0;
+ else
+ return; // key up without coresponding down (menu pass through)
+ if (b.down[0] != 0 || b.down[1] != 0)
+ return; // some other key is still holding it down
+
+ if ((b.state & 1) == 0)
+ return; // still up (this should not happen)
+
+ // save timestamp
+ c = Cmd.Argv(2);
+ uptime = Lib.atoi(c);
+ if (uptime != 0)
+ b.msec += uptime - b.downtime;
+ else
+ b.msec += 10;
+
+ b.state &= ~1; // now up
+ b.state |= 4; // impulse up
+ }
+
+ static void IN_KLookDown() {
+ KeyDown(in_klook);
+ }
+
+ static void IN_KLookUp() {
+ KeyUp(in_klook);
+ }
+
+ static void IN_UpDown() {
+ KeyDown(in_up);
+ }
+
+ static void IN_UpUp() {
+ KeyUp(in_up);
+ }
+
+ static void IN_DownDown() {
+ KeyDown(in_down);
+ }
+
+ static void IN_DownUp() {
+ KeyUp(in_down);
+ }
+
+ static void IN_LeftDown() {
+ KeyDown(in_left);
+ }
+
+ static void IN_LeftUp() {
+ KeyUp(in_left);
+ }
+
+ static void IN_RightDown() {
+ KeyDown(in_right);
+ }
+
+ static void IN_RightUp() {
+ KeyUp(in_right);
+ }
+
+ static void IN_ForwardDown() {
+ KeyDown(in_forward);
+ }
+
+ static void IN_ForwardUp() {
+ KeyUp(in_forward);
+ }
+
+ static void IN_BackDown() {
+ KeyDown(in_back);
+ }
+
+ static void IN_BackUp() {
+ KeyUp(in_back);
+ }
+
+ static void IN_LookupDown() {
+ KeyDown(in_lookup);
+ }
+
+ static void IN_LookupUp() {
+ KeyUp(in_lookup);
+ }
+
+ static void IN_LookdownDown() {
+ KeyDown(in_lookdown);
+ }
+
+ static void IN_LookdownUp() {
+ KeyUp(in_lookdown);
+ }
+
+ static void IN_MoveleftDown() {
+ KeyDown(in_moveleft);
+ }
+
+ static void IN_MoveleftUp() {
+ KeyUp(in_moveleft);
+ }
+
+ static void IN_MoverightDown() {
+ KeyDown(in_moveright);
+ }
+
+ static void IN_MoverightUp() {
+ KeyUp(in_moveright);
+ }
+
+ static void IN_SpeedDown() {
+ KeyDown(in_speed);
+ }
+
+ static void IN_SpeedUp() {
+ KeyUp(in_speed);
+ }
+
+ static void IN_StrafeDown() {
+ KeyDown(in_strafe);
+ }
+
+ static void IN_StrafeUp() {
+ KeyUp(in_strafe);
+ }
+
+ static void IN_AttackDown() {
+ KeyDown(in_attack);
+ }
+
+ static void IN_AttackUp() {
+ KeyUp(in_attack);
+ }
+
+ static void IN_UseDown() {
+ KeyDown(in_use);
+ }
+
+ // ==========================================================================
+
+ static void IN_UseUp() {
+ KeyUp(in_use);
+ }
+
+ static void IN_Impulse() {
+ in_impulse = Lib.atoi(Cmd.Argv(1));
+ }
+
+ /*
+ * =============== CL_KeyState
+ *
+ * Returns the fraction of the frame that the key was down ===============
+ */
+ static float KeyState(kbutton_t key) {
+ float val;
+ long msec;
+
+ key.state &= 1; // clear impulses
+
+ msec = key.msec;
+ key.msec = 0;
+
+ if (key.state != 0) {
+ // still down
+ msec += Globals.sys_frame_time - key.downtime;
+ key.downtime = Globals.sys_frame_time;
+ }
+
+ val = (float) msec / frame_msec;
+ if (val < 0)
+ val = 0;
+ if (val > 1)
+ val = 1;
+
+ return val;
+ }
+
+ /*
+ * ================ CL_AdjustAngles
+ *
+ * Moves the local angle positions ================
+ */
+ static void AdjustAngles() {
+ float speed;
+ float up, down;
+
+ if ((in_speed.state & 1) != 0)
+ speed = Globals.clientStaticT.frametime * Globals.cl_anglespeedkey.value;
+ else
+ speed = Globals.clientStaticT.frametime;
+
+ if ((in_strafe.state & 1) == 0) {
+ Globals.clientStateT.viewangles[Defines.YAW] -= speed * Globals.cl_yawspeed.value * KeyState(in_right);
+ Globals.clientStateT.viewangles[Defines.YAW] += speed * Globals.cl_yawspeed.value * KeyState(in_left);
+ }
+ if ((in_klook.state & 1) != 0) {
+ Globals.clientStateT.viewangles[Defines.PITCH] -= speed * Globals.cl_pitchspeed.value * KeyState(in_forward);
+ Globals.clientStateT.viewangles[Defines.PITCH] += speed * Globals.cl_pitchspeed.value * KeyState(in_back);
+ }
+
+ up = KeyState(in_lookup);
+ down = KeyState(in_lookdown);
+
+ Globals.clientStateT.viewangles[Defines.PITCH] -= speed * Globals.cl_pitchspeed.value * up;
+ Globals.clientStateT.viewangles[Defines.PITCH] += speed * Globals.cl_pitchspeed.value * down;
+ }
+
+ /*
+ * ================ CL_BaseMove
+ *
+ * Send the intended movement message to the server ================
+ */
+ static void BaseMove(usercmd_t cmd) {
+ AdjustAngles();
+
+ //memset (cmd, 0, sizeof(*cmd));
+ cmd.clear();
+
+ Math3D.vectorCopy(Globals.clientStateT.viewangles, cmd.angles);
+ if ((in_strafe.state & 1) != 0) {
+ cmd.sidemove += Globals.cl_sidespeed.value * KeyState(in_right);
+ cmd.sidemove -= Globals.cl_sidespeed.value * KeyState(in_left);
+ }
+
+ cmd.sidemove += Globals.cl_sidespeed.value * KeyState(in_moveright);
+ cmd.sidemove -= Globals.cl_sidespeed.value * KeyState(in_moveleft);
+
+ cmd.upmove += Globals.cl_upspeed.value * KeyState(in_up);
+ cmd.upmove -= Globals.cl_upspeed.value * KeyState(in_down);
+
+ if ((in_klook.state & 1) == 0) {
+ cmd.forwardmove += Globals.cl_forwardspeed.value * KeyState(in_forward);
+ cmd.forwardmove -= Globals.cl_forwardspeed.value * KeyState(in_back);
+ }
+
+ //
+ // adjust for speed key / running
+ //
+ if (((in_speed.state & 1) ^ (int) (Globals.cl_run.value)) != 0) {
+ cmd.forwardmove *= 2;
+ cmd.sidemove *= 2;
+ cmd.upmove *= 2;
+ }
+
+ }
+
+ static void ClampPitch() {
+
+ float pitch;
+
+ pitch = Math3D.short2Angle(Globals.clientStateT.frame.playerstate.pmove.delta_angles[Defines.PITCH]);
+ if (pitch > 180)
+ pitch -= 360;
+
+ if (Globals.clientStateT.viewangles[Defines.PITCH] + pitch < -360)
+ Globals.clientStateT.viewangles[Defines.PITCH] += 360; // wrapped
+ if (Globals.clientStateT.viewangles[Defines.PITCH] + pitch > 360)
+ Globals.clientStateT.viewangles[Defines.PITCH] -= 360; // wrapped
+
+ if (Globals.clientStateT.viewangles[Defines.PITCH] + pitch > 89)
+ Globals.clientStateT.viewangles[Defines.PITCH] = 89 - pitch;
+ if (Globals.clientStateT.viewangles[Defines.PITCH] + pitch < -89)
+ Globals.clientStateT.viewangles[Defines.PITCH] = -89 - pitch;
+ }
+
+ /*
+ * ============== CL_FinishMove ==============
+ */
+ static void FinishMove(usercmd_t cmd) {
+ int ms;
+ int i;
+
+ //
+ // figure button bits
+ //
+ if ((in_attack.state & 3) != 0)
+ cmd.buttons |= Defines.BUTTON_ATTACK;
+ in_attack.state &= ~2;
+
+ if ((in_use.state & 3) != 0)
+ cmd.buttons |= Defines.BUTTON_USE;
+ in_use.state &= ~2;
+
+ if (Key.anykeydown != 0 && Globals.clientStaticT.key_dest == Defines.key_game)
+ cmd.buttons |= Defines.BUTTON_ANY;
+
+ // send milliseconds of time to apply the move
+ ms = (int) (Globals.clientStaticT.frametime * 1000);
+ if (ms > 250)
+ ms = 100; // time was unreasonable
+ cmd.msec = (byte) ms;
+
+ ClampPitch();
+ for (i = 0; i < 3; i++)
+ cmd.angles[i] = (short) Math3D.angle2Short(Globals.clientStateT.viewangles[i]);
+
+ cmd.impulse = (byte) in_impulse;
+ in_impulse = 0;
+
+ // send the ambient light level at the player's current position
+ cmd.lightlevel = (byte) Globals.cl_lightlevel.value;
+ }
+
+ /*
+ * ================= CL_CreateCmd =================
+ */
+ static void CreateCmd(usercmd_t cmd) {
+ //usercmd_t cmd = new usercmd_t();
+
+ frame_msec = Globals.sys_frame_time - old_sys_frame_time;
+ if (frame_msec < 1)
+ frame_msec = 1;
+ if (frame_msec > 200)
+ frame_msec = 200;
+
+ // get basic movement from keyboard
+ BaseMove(cmd);
+
+ // allow mice or other external controllers to add to the move
+ UserInputHandler.Move(cmd);
+
+ FinishMove(cmd);
+
+ old_sys_frame_time = Globals.sys_frame_time;
+
+ //return cmd;
+ }
+
+ /*
+ * ============ CL_InitInput ============
+ */
+ static void InitInput() {
+ Cmd.AddCommand("centerview", new xcommand_t() {
+ public void execute() {
+ UserInputHandler.CenterView();
+ }
+ });
+
+ Cmd.AddCommand("+moveup", new xcommand_t() {
+ public void execute() {
+ IN_UpDown();
+ }
+ });
+ Cmd.AddCommand("-moveup", new xcommand_t() {
+ public void execute() {
+ IN_UpUp();
+ }
+ });
+ Cmd.AddCommand("+movedown", new xcommand_t() {
+ public void execute() {
+ IN_DownDown();
+ }
+ });
+ Cmd.AddCommand("-movedown", new xcommand_t() {
+ public void execute() {
+ IN_DownUp();
+ }
+ });
+ Cmd.AddCommand("+left", new xcommand_t() {
+ public void execute() {
+ IN_LeftDown();
+ }
+ });
+ Cmd.AddCommand("-left", new xcommand_t() {
+ public void execute() {
+ IN_LeftUp();
+ }
+ });
+ Cmd.AddCommand("+right", new xcommand_t() {
+ public void execute() {
+ IN_RightDown();
+ }
+ });
+ Cmd.AddCommand("-right", new xcommand_t() {
+ public void execute() {
+ IN_RightUp();
+ }
+ });
+ Cmd.AddCommand("+forward", new xcommand_t() {
+ public void execute() {
+ IN_ForwardDown();
+ }
+ });
+ Cmd.AddCommand("-forward", new xcommand_t() {
+ public void execute() {
+ IN_ForwardUp();
+ }
+ });
+ Cmd.AddCommand("+back", new xcommand_t() {
+ public void execute() {
+ IN_BackDown();
+ }
+ });
+ Cmd.AddCommand("-back", new xcommand_t() {
+ public void execute() {
+ IN_BackUp();
+ }
+ });
+ Cmd.AddCommand("+lookup", new xcommand_t() {
+ public void execute() {
+ IN_LookupDown();
+ }
+ });
+ Cmd.AddCommand("-lookup", new xcommand_t() {
+ public void execute() {
+ IN_LookupUp();
+ }
+ });
+ Cmd.AddCommand("+lookdown", new xcommand_t() {
+ public void execute() {
+ IN_LookdownDown();
+ }
+ });
+ Cmd.AddCommand("-lookdown", new xcommand_t() {
+ public void execute() {
+ IN_LookdownUp();
+ }
+ });
+ Cmd.AddCommand("+strafe", new xcommand_t() {
+ public void execute() {
+ IN_StrafeDown();
+ }
+ });
+ Cmd.AddCommand("-strafe", new xcommand_t() {
+ public void execute() {
+ IN_StrafeUp();
+ }
+ });
+ Cmd.AddCommand("+moveleft", new xcommand_t() {
+ public void execute() {
+ IN_MoveleftDown();
+ }
+ });
+ Cmd.AddCommand("-moveleft", new xcommand_t() {
+ public void execute() {
+ IN_MoveleftUp();
+ }
+ });
+ Cmd.AddCommand("+moveright", new xcommand_t() {
+ public void execute() {
+ IN_MoverightDown();
+ }
+ });
+ Cmd.AddCommand("-moveright", new xcommand_t() {
+ public void execute() {
+ IN_MoverightUp();
+ }
+ });
+ Cmd.AddCommand("+speed", new xcommand_t() {
+ public void execute() {
+ IN_SpeedDown();
+ }
+ });
+ Cmd.AddCommand("-speed", new xcommand_t() {
+ public void execute() {
+ IN_SpeedUp();
+ }
+ });
+ Cmd.AddCommand("+attack", new xcommand_t() {
+ public void execute() {
+ IN_AttackDown();
+ }
+ });
+ Cmd.AddCommand("-attack", new xcommand_t() {
+ public void execute() {
+ IN_AttackUp();
+ }
+ });
+ Cmd.AddCommand("+use", new xcommand_t() {
+ public void execute() {
+ IN_UseDown();
+ }
+ });
+ Cmd.AddCommand("-use", new xcommand_t() {
+ public void execute() {
+ IN_UseUp();
+ }
+ });
+ Cmd.AddCommand("impulse", new xcommand_t() {
+ public void execute() {
+ IN_Impulse();
+ }
+ });
+ Cmd.AddCommand("+klook", new xcommand_t() {
+ public void execute() {
+ IN_KLookDown();
+ }
+ });
+ Cmd.AddCommand("-klook", new xcommand_t() {
+ public void execute() {
+ IN_KLookUp();
+ }
+ });
+
+ cl_nodelta = Cvar.get("cl_nodelta", "0", 0);
+ }
+
+ /*
+ * ================= CL_SendCmd =================
+ */
+ static void SendCmd() {
+ int i;
+ usercmd_t cmd, oldcmd;
+ int checksumIndex;
+
+ // build a command even if not connected
+
+ // save this command off for prediction
+ i = Globals.clientStaticT.netchan.outgoing_sequence & (Defines.CMD_BACKUP - 1);
+ cmd = Globals.clientStateT.cmds[i];
+ Globals.clientStateT.cmd_time[i] = Globals.clientStaticT.realtime; // for netgraph
+ // ping calculation
+
+ // fill the cmd
+ CreateCmd(cmd);
+
+ Globals.clientStateT.cmd.set(cmd);
+
+ if (Globals.clientStaticT.state == Defines.ca_disconnected || Globals.clientStaticT.state == Defines.ca_connecting)
+ return;
+
+ if (Globals.clientStaticT.state == Defines.ca_connected) {
+ if (Globals.clientStaticT.netchan.message.cursize != 0 || Globals.curtime - Globals.clientStaticT.netchan.last_sent > 1000)
+ Netchan.Transmit(Globals.clientStaticT.netchan, 0, new byte[0]);
+ return;
+ }
+
+ // send a userinfo update if needed
+ if (Globals.userinfo_modified) {
+ Client.FixUpGender();
+ Globals.userinfo_modified = false;
+ MSG.WriteByte(Globals.clientStaticT.netchan.message, Defines.clc_userinfo);
+ MSG.WriteString(Globals.clientStaticT.netchan.message, Cvar.userinfo());
+ }
+
+ SZ.Init(buf, data, data.length);
+
+ if (cmd.buttons != 0 && Globals.clientStateT.cinematictime > 0 && !Globals.clientStateT.attractloop
+ && Globals.clientStaticT.realtime - Globals.clientStateT.cinematictime > 1000) { // skip
+ // the
+ // rest
+ // of
+ // the
+ // cinematic
+ SCR.FinishCinematic();
+ }
+
+ // begin a client move command
+ MSG.WriteByte(buf, Defines.clc_move);
+
+ // save the position for a checksum byte
+ checksumIndex = buf.cursize;
+ MSG.WriteByte(buf, 0);
+
+ // let the server know what the last frame we
+ // got was, so the next message can be delta compressed
+ if (cl_nodelta.value != 0.0f || !Globals.clientStateT.frame.valid || Globals.clientStaticT.demowaiting)
+ MSG.WriteLong(buf, -1); // no compression
+ else
+ MSG.WriteLong(buf, Globals.clientStateT.frame.serverframe);
+
+ // send this and the previous cmds in the message, so
+ // if the last packet was dropped, it can be recovered
+ i = (Globals.clientStaticT.netchan.outgoing_sequence - 2) & (Defines.CMD_BACKUP - 1);
+ cmd = Globals.clientStateT.cmds[i];
+ //memset (nullcmd, 0, sizeof(nullcmd));
+ nullcmd.clear();
+
+ MSG.WriteDeltaUsercmd(buf, nullcmd, cmd);
+ oldcmd = cmd;
+
+ i = (Globals.clientStaticT.netchan.outgoing_sequence - 1) & (Defines.CMD_BACKUP - 1);
+ cmd = Globals.clientStateT.cmds[i];
+
+ MSG.WriteDeltaUsercmd(buf, oldcmd, cmd);
+ oldcmd = cmd;
+
+ i = (Globals.clientStaticT.netchan.outgoing_sequence) & (Defines.CMD_BACKUP - 1);
+ cmd = Globals.clientStateT.cmds[i];
+
+ MSG.WriteDeltaUsercmd(buf, oldcmd, cmd);
+
+ // calculate a checksum over the move commands
+ buf.data[checksumIndex] = Com.BlockSequenceCRCByte(buf.data, checksumIndex + 1, buf.cursize - checksumIndex - 1,
+ Globals.clientStaticT.netchan.outgoing_sequence);
+
+ //
+ // deliver the message
+ //
+ Netchan.Transmit(Globals.clientStaticT.netchan, buf.cursize, buf.data);
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.client;
+
+import lwjake2.Defines;
+import lwjake2.Globals;
+import lwjake2.qcommon.Com;
+import lwjake2.qcommon.MSG;
+import lwjake2.util.Lib;
+import lwjake2.util.Vargs;
+
+/**
+ * CL_inv
+ */
+public class CL_inv {
+
+ /*
+ * ================ CL_DrawInventory ================
+ */
+ static final int DISPLAY_ITEMS = 17;
+
+ /*
+ * ================ CL_ParseInventory ================
+ */
+ static void ParseInventory() {
+ int i;
+
+ for (i = 0; i < Defines.MAX_ITEMS; i++)
+ Globals.clientStateT.inventory[i] = MSG.ReadShort(Globals.net_message);
+ }
+
+ /*
+ * ================ Inv_DrawString ================
+ */
+ static void Inv_DrawString(int x, int y, String string) {
+ for (int i = 0; i < string.length(); i++) {
+ Globals.re.DrawChar(x, y, string.charAt(i));
+ x += 8;
+ }
+ }
+
+ static String getHighBitString(String s) {
+ byte[] b = Lib.stringToBytes(s);
+ for (int i = 0; i < b.length; i++) {
+ b[i] = (byte) (b[i] | 128);
+ }
+ return Lib.bytesToString(b);
+ }
+
+ static void DrawInventory() {
+ int i, j;
+ int num, selected_num, item;
+ int[] index = new int[Defines.MAX_ITEMS];
+ String string;
+ int x, y;
+ String binding;
+ String bind;
+ int selected;
+ int top;
+
+ selected = Globals.clientStateT.frame.playerstate.stats[Defines.STAT_SELECTED_ITEM];
+
+ num = 0;
+ selected_num = 0;
+ for (i = 0; i < Defines.MAX_ITEMS; i++) {
+ if (i == selected)
+ selected_num = num;
+ if (Globals.clientStateT.inventory[i] != 0) {
+ index[num] = i;
+ num++;
+ }
+ }
+
+ // determine scroll point
+ top = selected_num - DISPLAY_ITEMS / 2;
+ if (num - top < DISPLAY_ITEMS)
+ top = num - DISPLAY_ITEMS;
+ if (top < 0)
+ top = 0;
+
+ x = (Globals.viddef.width - 256) / 2;
+ y = (Globals.viddef.height - 240) / 2;
+
+ // repaint everything next frame
+ SCR.DirtyScreen();
+
+ Globals.re.DrawPic(x, y + 8, "inventory");
+
+ y += 24;
+ x += 24;
+ Inv_DrawString(x, y, "hotkey ### item");
+ Inv_DrawString(x, y + 8, "------ --- ----");
+ y += 16;
+ for (i = top; i < num && i < top + DISPLAY_ITEMS; i++) {
+ item = index[i];
+ // search for a binding
+ //Com_sprintf (binding, sizeof(binding), "use %s",
+ // cl.configstrings[CS_ITEMS+item]);
+ binding = "use " + Globals.clientStateT.configstrings[Defines.CS_ITEMS + item];
+ bind = "";
+ for (j = 0; j < 256; j++)
+ if (Globals.keybindings[j] != null && Globals.keybindings[j].equals(binding)) {
+ bind = Key.KeynumToString(j);
+ break;
+ }
+
+ string = Com.sprintf("%6s %3i %s", new Vargs(3).add(bind).add(Globals.clientStateT.inventory[item]).add(
+ Globals.clientStateT.configstrings[Defines.CS_ITEMS + item]));
+ if (item != selected)
+ string = getHighBitString(string);
+ else // draw a blinky cursor by the selected item
+ {
+ if ((Globals.clientStaticT.realtime * 10 & 1) != 0)
+ Globals.re.DrawChar(x - 8, y, 15);
+ }
+ Inv_DrawString(x, y, string);
+ y += 8;
+ }
+
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.client;
+
+import lwjake2.Defines;
+import lwjake2.Globals;
+import lwjake2.util.Lib;
+import lwjake2.util.Math3D;
+
+/**
+ * CL_newfx
+ */
+public class CL_newfx {
+
+ // stack variable
+ private static final float[] move = {0, 0, 0};
+ private static final float[] vec = {0, 0, 0};
+ private static final float[] right = {0, 0, 0};
+ private static final float[] up = {0, 0, 0};
+ // stack variable
+ // move, vec, right, up
+ private static final float[] dir = {0, 0, 0};
+ private static final float[] end = {0, 0, 0};
+ // stack variable
+ private static final float[] r = {0, 0, 0};
+ private static final float[] u = {0, 0, 0};
+ // stack variable
+ // move, vec, right, up
+ private static final float[] forward = {0, 0, 0};
+ private static final float[] angle_dir = {0, 0, 0};
+ private static final int[] wb_colortable = {2 * 8, 13 * 8, 21 * 8, 18 * 8};
+ private static final int[] nb_colortable = {110, 112, 114, 116};
+ private static final int[] ws_colortable = {2 * 8, 13 * 8, 21 * 8, 18 * 8};
+
+ static void Flashlight(int ent) {
+ CL_fx.cdlight_t dl;
+
+ dl = CL_fx.AllocDlight(ent);
+ Math3D.vectorCopy(CL_tent.pos, dl.origin);
+ dl.radius = 400;
+ dl.minlight = 250;
+ dl.die = Globals.clientStateT.time + 100;
+ dl.color[0] = 1;
+ dl.color[1] = 1;
+ dl.color[2] = 1;
+ }
+
+ /*
+ * ====== CL_ColorFlash - flash of light ======
+ */
+ static void ColorFlash(float r,
+ float g, float b) {
+ CL_fx.cdlight_t dl;
+
+ if ((Globals.vidref_val == Defines.VIDREF_SOFT)
+ && ((r < 0) || (g < 0) || (b < 0))) {
+ r = -r;
+ g = -g;
+ b = -b;
+ }
+
+ dl = CL_fx.AllocDlight(0);
+ Math3D.vectorCopy(CL_tent.pos, dl.origin);
+ dl.radius = 150;
+ dl.minlight = 250;
+ dl.die = Globals.clientStateT.time + 100;
+ dl.color[0] = r;
+ dl.color[1] = g;
+ dl.color[2] = b;
+ }
+
+ /*
+ * ====== CL_DebugTrail ======
+ */
+ static void DebugTrail() {
+ float len;
+ // int j;
+ cparticle_t p;
+ float dec;
+ // int i;
+ // float d, c, s;
+ // float[] dir;
+
+ Math3D.vectorCopy(CL_tent.pos, move);
+ Math3D.vectorSubtract(CL_tent.pos2, CL_tent.pos, vec);
+ len = Math3D.vectorNormalize(vec);
+
+ Math3D.makeNormalVectors(vec, right, up);
+
+ // VectorScale(vec, RT2_SKIP, vec);
+
+ // dec = 1.0;
+ // dec = 0.75;
+ dec = 3;
+ Math3D.vectorScale(vec, dec, vec);
+ Math3D.vectorCopy(CL_tent.pos, move);
+
+ while (len > 0) {
+ len -= dec;
+
+ if (CL_fx.free_particles == null)
+ return;
+ p = CL_fx.free_particles;
+ CL_fx.free_particles = p.next;
+ p.next = CL_fx.active_particles;
+ CL_fx.active_particles = p;
+
+ p.time = Globals.clientStateT.time;
+ Math3D.vectorClear(p.accel);
+ Math3D.vectorClear(p.vel);
+ p.alpha = 1.0f;
+ p.alphavel = -0.1f;
+ // p.alphavel = 0;
+ p.color = 0x74 + (Lib.rand() & 7);
+ Math3D.vectorCopy(move, p.org);
+ /*
+ * for (j=0 ; j <3 ; j++) { p.org[j] = move[j] + crand()*2; p.vel[j] =
+ * crand()*3; p.accel[j] = 0; }
+ */
+ Math3D.vectorAdd(move, vec, move);
+ }
+
+ }
+
+ // stack variable
+ // move, vec
+ static void ForceWall(int color) {
+ float len;
+ int j;
+ cparticle_t p;
+
+ Math3D.vectorCopy(CL_tent.pos, move);
+ Math3D.vectorSubtract(CL_tent.pos2, CL_tent.pos, vec);
+ len = Math3D.vectorNormalize(vec);
+
+ Math3D.vectorScale(vec, 4, vec);
+
+ // FIXME: this is a really silly way to have a loop
+ while (len > 0) {
+ len -= 4;
+
+ if (CL_fx.free_particles == null)
+ return;
+
+ if (Globals.rnd.nextFloat() > 0.3) {
+ p = CL_fx.free_particles;
+ CL_fx.free_particles = p.next;
+ p.next = CL_fx.active_particles;
+ CL_fx.active_particles = p;
+ Math3D.vectorClear(p.accel);
+
+ p.time = Globals.clientStateT.time;
+
+ p.alpha = 1.0f;
+ p.alphavel = -1.0f / (3.0f + Globals.rnd.nextFloat() * 0.5f);
+ p.color = color;
+ for (j = 0; j < 3; j++) {
+ p.org[j] = move[j] + Lib.crand() * 3;
+ p.accel[j] = 0;
+ }
+ p.vel[0] = 0;
+ p.vel[1] = 0;
+ p.vel[2] = -40 - (Lib.crand() * 10);
+ }
+
+ Math3D.vectorAdd(move, vec, move);
+ }
+ }
+
+ // stack variable
+ // move, vec
+ /*
+ * =============== CL_BubbleTrail2 (lets you control the # of bubbles by
+ * setting the distance between the spawns)
+ *
+ * ===============
+ */
+ static void BubbleTrail2(int dist) {
+ float len;
+ int i, j;
+ cparticle_t p;
+ float dec;
+
+ Math3D.vectorCopy(CL_tent.pos, move);
+ Math3D.vectorSubtract(CL_tent.pos2, CL_tent.pos, vec);
+ len = Math3D.vectorNormalize(vec);
+
+ dec = dist;
+ Math3D.vectorScale(vec, dec, vec);
+
+ for (i = 0; i < len; i += dec) {
+ if (CL_fx.free_particles == null)
+ return;
+
+ p = CL_fx.free_particles;
+ CL_fx.free_particles = p.next;
+ p.next = CL_fx.active_particles;
+ CL_fx.active_particles = p;
+
+ Math3D.vectorClear(p.accel);
+ p.time = Globals.clientStateT.time;
+
+ p.alpha = 1.0f;
+ p.alphavel = -1.0f / (1 + Globals.rnd.nextFloat() * 0.1f);
+ p.color = 4 + (Lib.rand() & 7);
+ for (j = 0; j < 3; j++) {
+ p.org[j] = move[j] + Lib.crand() * 2;
+ p.vel[j] = Lib.crand() * 10;
+ }
+ p.org[2] -= 4;
+ // p.vel[2] += 6;
+ p.vel[2] += 20;
+
+ Math3D.vectorAdd(move, vec, move);
+ }
+ }
+
+ static void Heatbeam() {
+ float len;
+ int j;
+ cparticle_t p;
+ int i;
+ float c, s;
+ float ltime;
+ float step = 32.0f, rstep;
+ float start_pt;
+ float rot;
+ float variance;
+
+ Math3D.vectorMA(CL_tent.org, 4096, CL_tent.dist, end);
+
+ Math3D.vectorCopy(CL_tent.org, move);
+ Math3D.vectorSubtract(end, CL_tent.org, vec);
+ len = Math3D.vectorNormalize(vec);
+
+ // FIXME - pmm - these might end up using old values?
+ // MakeNormalVectors (vec, right, up);
+ Math3D.vectorCopy(Globals.clientStateT.v_right, right);
+ Math3D.vectorCopy(Globals.clientStateT.v_up, up);
+ if (Globals.vidref_val == Defines.VIDREF_GL) { // GL mode
+ Math3D.vectorMA(move, -0.5f, right, move);
+ Math3D.vectorMA(move, -0.5f, up, move);
+ }
+ // otherwise assume SOFT
+
+ ltime = (float) Globals.clientStateT.time / 1000.0f;
+ start_pt = ltime * 96.0f % step;
+ Math3D.vectorMA(move, start_pt, vec, move);
+
+ Math3D.vectorScale(vec, step, vec);
+
+ // Com_Printf ("%f\n", ltime);
+ rstep = (float) (Math.PI / 10.0);
+ float M_PI2 = (float) (Math.PI * 2.0);
+ for (i = (int) start_pt; i < len; i += step) {
+ if (i > step * 5) // don't bother after the 5th ring
+ break;
+
+ for (rot = 0; rot < M_PI2; rot += rstep) {
+
+ if (CL_fx.free_particles == null)
+ return;
+
+ p = CL_fx.free_particles;
+ CL_fx.free_particles = p.next;
+ p.next = CL_fx.active_particles;
+ CL_fx.active_particles = p;
+
+ p.time = Globals.clientStateT.time;
+ Math3D.vectorClear(p.accel);
+ // rot+= fmod(ltime, 12.0)*M_PI;
+ // c = cos(rot)/2.0;
+ // s = sin(rot)/2.0;
+ // variance = 0.4 + ((float)rand()/(float)RAND_MAX) *0.2;
+ variance = 0.5f;
+ c = (float) (Math.cos(rot) * variance);
+ s = (float) (Math.sin(rot) * variance);
+
+ // trim it so it looks like it's starting at the origin
+ if (i < 10) {
+ Math3D.vectorScale(right, c * (i / 10.0f), dir);
+ Math3D.vectorMA(dir, s * (i / 10.0f), up, dir);
+ } else {
+ Math3D.vectorScale(right, c, dir);
+ Math3D.vectorMA(dir, s, up, dir);
+ }
+
+ p.alpha = 0.5f;
+ // p.alphavel = -1.0 / (1+frand()*0.2);
+ p.alphavel = -1000.0f;
+ // p.color = 0x74 + (rand()&7);
+ p.color = 223 - (Lib.rand() & 7);
+ for (j = 0; j < 3; j++) {
+ p.org[j] = move[j] + dir[j] * 3;
+ // p.vel[j] = dir[j]*6;
+ p.vel[j] = 0;
+ }
+ }
+ Math3D.vectorAdd(move, vec, move);
+ }
+ }
+
+ /*
+ * =============== CL_ParticleSteamEffect
+ *
+ * Puffs with velocity along direction, with some randomness thrown in
+ * ===============
+ */
+ static void ParticleSteamEffect(int color,
+ int count, int magnitude) {
+ int i, j;
+ cparticle_t p;
+ float d;
+
+ // vectoangles2 (dir, angle_dir);
+ // AngleVectors (angle_dir, f, r, u);
+
+ Math3D.makeNormalVectors(CL_tent.dir, r, u);
+
+ for (i = 0; i < count; i++) {
+ if (CL_fx.free_particles == null)
+ return;
+ p = CL_fx.free_particles;
+ CL_fx.free_particles = p.next;
+ p.next = CL_fx.active_particles;
+ CL_fx.active_particles = p;
+
+ p.time = Globals.clientStateT.time;
+ p.color = color + (Lib.rand() & 7);
+
+ for (j = 0; j < 3; j++) {
+ p.org[j] = CL_tent.pos[j] + magnitude * 0.1f * Lib.crand();
+ // p.vel[j] = dir[j]*magnitude;
+ }
+ Math3D.vectorScale(CL_tent.dir, magnitude, p.vel);
+ d = Lib.crand() * magnitude / 3;
+ Math3D.vectorMA(p.vel, d, r, p.vel);
+ d = Lib.crand() * magnitude / 3;
+ Math3D.vectorMA(p.vel, d, u, p.vel);
+
+ p.accel[0] = p.accel[1] = 0;
+ p.accel[2] = -CL_fx.PARTICLE_GRAVITY / 2;
+ p.alpha = 1.0f;
+
+ p.alphavel = -1.0f / (0.5f + Globals.rnd.nextFloat() * 0.3f);
+ }
+ }
+
+ // stack variable
+ // r, u, dir
+ static void ParticleSteamEffect2(cl_sustain_t self)
+ // float[] org, float[] dir, int color, int count, int magnitude)
+ {
+ int i, j;
+ cparticle_t p;
+ float d;
+
+ // vectoangles2 (dir, angle_dir);
+ // AngleVectors (angle_dir, f, r, u);
+
+ Math3D.vectorCopy(self.dir, dir);
+ Math3D.makeNormalVectors(dir, r, u);
+
+ for (i = 0; i < self.count; i++) {
+ if (CL_fx.free_particles == null)
+ return;
+ p = CL_fx.free_particles;
+ CL_fx.free_particles = p.next;
+ p.next = CL_fx.active_particles;
+ CL_fx.active_particles = p;
+
+ p.time = Globals.clientStateT.time;
+ p.color = self.color + (Lib.rand() & 7);
+
+ for (j = 0; j < 3; j++) {
+ p.org[j] = self.org[j] + self.magnitude * 0.1f * Lib.crand();
+ // p.vel[j] = dir[j]*magnitude;
+ }
+ Math3D.vectorScale(dir, self.magnitude, p.vel);
+ d = Lib.crand() * self.magnitude / 3;
+ Math3D.vectorMA(p.vel, d, r, p.vel);
+ d = Lib.crand() * self.magnitude / 3;
+ Math3D.vectorMA(p.vel, d, u, p.vel);
+
+ p.accel[0] = p.accel[1] = 0;
+ p.accel[2] = -CL_fx.PARTICLE_GRAVITY / 2;
+ p.alpha = 1.0f;
+
+ p.alphavel = -1.0f / (0.5f + Globals.rnd.nextFloat() * 0.3f);
+ }
+ self.nextthink += self.thinkinterval;
+ }
+
+ /*
+ * =============== CL_TrackerTrail ===============
+ */
+ static void TrackerTrail(float[] start, float[] end) {
+ float len;
+ cparticle_t p;
+ int dec;
+ float dist;
+
+ Math3D.vectorCopy(start, move);
+ Math3D.vectorSubtract(end, start, vec);
+ len = Math3D.vectorNormalize(vec);
+
+ Math3D.vectorCopy(vec, forward);
+ Math3D.vecToAngles(forward, angle_dir);
+ Math3D.angleVectors(angle_dir, forward, right, up);
+
+ dec = 3;
+ Math3D.vectorScale(vec, 3, vec);
+
+ // FIXME: this is a really silly way to have a loop
+ while (len > 0) {
+ len -= dec;
+
+ if (CL_fx.free_particles == null)
+ return;
+ p = CL_fx.free_particles;
+ CL_fx.free_particles = p.next;
+ p.next = CL_fx.active_particles;
+ CL_fx.active_particles = p;
+ Math3D.vectorClear(p.accel);
+
+ p.time = Globals.clientStateT.time;
+
+ p.alpha = 1.0f;
+ p.alphavel = -2.0f;
+ p.color = 0;
+ dist = Math3D.dotProduct(move, forward);
+ Math3D.vectorMA(move, (float) (8 * Math.cos(dist)), up, p.org);
+ for (int j = 0; j < 3; j++) {
+ p.vel[j] = 0;
+ p.accel[j] = 0;
+ }
+ p.vel[2] = 5;
+
+ Math3D.vectorAdd(move, vec, move);
+ }
+ }
+
+ // stack variable
+ // dir
+ static void Tracker_Shell(float[] origin) {
+ cparticle_t p;
+
+ for (int i = 0; i < 300; i++) {
+ if (CL_fx.free_particles == null)
+ return;
+ p = CL_fx.free_particles;
+ CL_fx.free_particles = p.next;
+ p.next = CL_fx.active_particles;
+ CL_fx.active_particles = p;
+ Math3D.vectorClear(p.accel);
+
+ p.time = Globals.clientStateT.time;
+
+ p.alpha = 1.0f;
+ p.alphavel = CL_fx.INSTANT_PARTICLE;
+ p.color = 0;
+
+ dir[0] = Lib.crand();
+ dir[1] = Lib.crand();
+ dir[2] = Lib.crand();
+ Math3D.vectorNormalize(dir);
+
+ Math3D.vectorMA(origin, 40, dir, p.org);
+ }
+ }
+
+ // stack variable
+ // dir
+ static void MonsterPlasma_Shell(float[] origin) {
+ cparticle_t p;
+
+ for (int i = 0; i < 40; i++) {
+ if (CL_fx.free_particles == null)
+ return;
+ p = CL_fx.free_particles;
+ CL_fx.free_particles = p.next;
+ p.next = CL_fx.active_particles;
+ CL_fx.active_particles = p;
+ Math3D.vectorClear(p.accel);
+
+ p.time = Globals.clientStateT.time;
+
+ p.alpha = 1.0f;
+ p.alphavel = CL_fx.INSTANT_PARTICLE;
+ p.color = 0xe0;
+
+ dir[0] = Lib.crand();
+ dir[1] = Lib.crand();
+ dir[2] = Lib.crand();
+ Math3D.vectorNormalize(dir);
+
+ Math3D.vectorMA(origin, 10, dir, p.org);
+ // VectorMA(origin, 10*(((rand () & 0x7fff) / ((float)0x7fff))),
+ // dir, p.org);
+ }
+ }
+
+ // stack variable
+ // dir
+ static void Widowbeamout(cl_sustain_t self) {
+ int i;
+ cparticle_t p;
+
+ float ratio;
+
+ ratio = 1.0f - (((float) self.endtime - (float) Globals.clientStateT.time) / 2100.0f);
+
+ for (i = 0; i < 300; i++) {
+ if (CL_fx.free_particles == null)
+ return;
+ p = CL_fx.free_particles;
+ CL_fx.free_particles = p.next;
+ p.next = CL_fx.active_particles;
+ CL_fx.active_particles = p;
+ Math3D.vectorClear(p.accel);
+
+ p.time = Globals.clientStateT.time;
+
+ p.alpha = 1.0f;
+ p.alphavel = CL_fx.INSTANT_PARTICLE;
+ p.color = wb_colortable[Lib.rand() & 3];
+
+ dir[0] = Lib.crand();
+ dir[1] = Lib.crand();
+ dir[2] = Lib.crand();
+ Math3D.vectorNormalize(dir);
+
+ Math3D.vectorMA(self.org, (45.0f * ratio), dir, p.org);
+ // VectorMA(origin, 10*(((rand () & 0x7fff) / ((float)0x7fff))),
+ // dir, p.org);
+ }
+ }
+
+ // stack variable
+ // dir
+ static void Nukeblast(cl_sustain_t self) {
+ int i;
+ cparticle_t p;
+
+ float ratio;
+
+ ratio = 1.0f - (((float) self.endtime - (float) Globals.clientStateT.time) / 1000.0f);
+
+ for (i = 0; i < 700; i++) {
+ if (CL_fx.free_particles == null)
+ return;
+ p = CL_fx.free_particles;
+ CL_fx.free_particles = p.next;
+ p.next = CL_fx.active_particles;
+ CL_fx.active_particles = p;
+ Math3D.vectorClear(p.accel);
+
+ p.time = Globals.clientStateT.time;
+
+ p.alpha = 1.0f;
+ p.alphavel = CL_fx.INSTANT_PARTICLE;
+ p.color = nb_colortable[Lib.rand() & 3];
+
+ dir[0] = Lib.crand();
+ dir[1] = Lib.crand();
+ dir[2] = Lib.crand();
+ Math3D.vectorNormalize(dir);
+
+ Math3D.vectorMA(self.org, (200.0f * ratio), dir, p.org);
+ // VectorMA(origin, 10*(((rand () & 0x7fff) / ((float)0x7fff))),
+ // dir, p.org);
+ }
+ }
+
+ // stack variable
+ // dir
+ static void WidowSplash() {
+ int i;
+ cparticle_t p;
+
+ for (i = 0; i < 256; i++) {
+ if (CL_fx.free_particles == null)
+ return;
+ p = CL_fx.free_particles;
+ CL_fx.free_particles = p.next;
+ p.next = CL_fx.active_particles;
+ CL_fx.active_particles = p;
+
+ p.time = Globals.clientStateT.time;
+ p.color = ws_colortable[Lib.rand() & 3];
+
+ dir[0] = Lib.crand();
+ dir[1] = Lib.crand();
+ dir[2] = Lib.crand();
+ Math3D.vectorNormalize(dir);
+ Math3D.vectorMA(CL_tent.pos, 45.0f, dir, p.org);
+ Math3D.vectorMA(Globals.vec3_origin, 40.0f, dir, p.vel);
+
+ p.accel[0] = p.accel[1] = 0;
+ p.alpha = 1.0f;
+
+ p.alphavel = -0.8f / (0.5f + Globals.rnd.nextFloat() * 0.3f);
+ }
+
+ }
+
+ // stack variable
+ // move, vec
+ /*
+ * ===============
+ * CL_TagTrail
+ * ===============
+ */
+ static void TagTrail(float[] start, float[] end) {
+ float len;
+ int j;
+ cparticle_t p;
+ int dec;
+
+ Math3D.vectorCopy(start, move);
+ Math3D.vectorSubtract(end, start, vec);
+ len = Math3D.vectorNormalize(vec);
+
+ dec = 5;
+ Math3D.vectorScale(vec, 5, vec);
+
+ while (len >= 0) {
+ len -= dec;
+
+ if (CL_fx.free_particles == null)
+ return;
+ p = CL_fx.free_particles;
+ CL_fx.free_particles = p.next;
+ p.next = CL_fx.active_particles;
+ CL_fx.active_particles = p;
+ Math3D.vectorClear(p.accel);
+
+ p.time = Globals.clientStateT.time;
+
+ p.alpha = 1.0f;
+ p.alphavel = -1.0f / (0.8f + Globals.rnd.nextFloat() * 0.2f);
+ p.color = (float) 220;
+ for (j = 0; j < 3; j++) {
+ p.org[j] = move[j] + Lib.crand() * 16;
+ p.vel[j] = Lib.crand() * 5;
+ p.accel[j] = 0;
+ }
+
+ Math3D.vectorAdd(move, vec, move);
+ }
+ }
+
+ /*
+ * =============== CL_ColorExplosionParticles ===============
+ */
+ static void ColorExplosionParticles() {
+ int i, j;
+ cparticle_t p;
+
+ for (i = 0; i < 128; i++) {
+ if (CL_fx.free_particles == null)
+ return;
+ p = CL_fx.free_particles;
+ CL_fx.free_particles = p.next;
+ p.next = CL_fx.active_particles;
+ CL_fx.active_particles = p;
+
+ p.time = Globals.clientStateT.time;
+ p.color = (0);
+
+ for (j = 0; j < 3; j++) {
+ p.org[j] = CL_tent.pos[j] + ((Lib.rand() % 32) - 16);
+ p.vel[j] = (Lib.rand() % 256) - 128;
+ }
+
+ p.accel[0] = p.accel[1] = 0;
+ p.accel[2] = -CL_fx.PARTICLE_GRAVITY;
+ p.alpha = 1.0f;
+
+ p.alphavel = -0.4f / (0.6f + Globals.rnd.nextFloat() * 0.2f);
+ }
+ }
+
+ // stack variable
+ // r, u
+ /*
+ * =============== CL_ParticleSmokeEffect - like the steam effect, but
+ * unaffected by gravity ===============
+ */
+ static void ParticleSmokeEffect() {
+ int i, j;
+ cparticle_t p;
+ float d;
+
+ Math3D.makeNormalVectors(CL_tent.dir, r, u);
+
+ for (i = 0; i < 20; i++) {
+ if (CL_fx.free_particles == null)
+ return;
+ p = CL_fx.free_particles;
+ CL_fx.free_particles = p.next;
+ p.next = CL_fx.active_particles;
+ CL_fx.active_particles = p;
+
+ p.time = Globals.clientStateT.time;
+ p.color = (Lib.rand() & 7);
+
+ for (j = 0; j < 3; j++) {
+ p.org[j] = CL_tent.pos[j] + 20 * 0.1f * Lib.crand();
+ // p.vel[j] = dir[j]*magnitude;
+ }
+ Math3D.vectorScale(CL_tent.dir, 20, p.vel);
+ d = Lib.crand() * 20 / 3;
+ Math3D.vectorMA(p.vel, d, r, p.vel);
+ d = Lib.crand() * 20 / 3;
+ Math3D.vectorMA(p.vel, d, u, p.vel);
+
+ p.accel[0] = p.accel[1] = p.accel[2] = 0;
+ p.alpha = 1.0f;
+
+ p.alphavel = -1.0f / (0.5f + Globals.rnd.nextFloat() * 0.3f);
+ }
+ }
+
+ /*
+ * =============== CL_BlasterParticles2
+ *
+ * Wall impact puffs (Green) ===============
+ */
+ static void BlasterParticles2(long color) {
+ int i, j;
+ cparticle_t p;
+ float d;
+ int count;
+
+ count = 40;
+ for (i = 0; i < count; i++) {
+ if (CL_fx.free_particles == null)
+ return;
+ p = CL_fx.free_particles;
+ CL_fx.free_particles = p.next;
+ p.next = CL_fx.active_particles;
+ CL_fx.active_particles = p;
+
+ p.time = Globals.clientStateT.time;
+ p.color = color + (Lib.rand() & 7);
+
+ d = Lib.rand() & 15;
+ for (j = 0; j < 3; j++) {
+ p.org[j] = CL_tent.pos[j] + ((Lib.rand() & 7) - 4) + d * CL_tent.dir[j];
+ p.vel[j] = CL_tent.dir[j] * 30 + Lib.crand() * 40;
+ }
+
+ p.accel[0] = p.accel[1] = 0;
+ p.accel[2] = -CL_fx.PARTICLE_GRAVITY;
+ p.alpha = 1.0f;
+
+ p.alphavel = -1.0f / (0.5f + Globals.rnd.nextFloat() * 0.3f);
+ }
+ }
+
+ // stack variable
+ // move, vec
+ /*
+ * =============== CL_BlasterTrail2
+ *
+ * Green! ===============
+ */
+ static void BlasterTrail2(float[] start, float[] end) {
+ float len;
+ int j;
+ cparticle_t p;
+ int dec;
+
+ Math3D.vectorCopy(start, move);
+ Math3D.vectorSubtract(end, start, vec);
+ len = Math3D.vectorNormalize(vec);
+
+ dec = 5;
+ Math3D.vectorScale(vec, 5, vec);
+
+ // FIXME: this is a really silly way to have a loop
+ while (len > 0) {
+ len -= dec;
+
+ if (CL_fx.free_particles == null)
+ return;
+ p = CL_fx.free_particles;
+ CL_fx.free_particles = p.next;
+ p.next = CL_fx.active_particles;
+ CL_fx.active_particles = p;
+ Math3D.vectorClear(p.accel);
+
+ p.time = Globals.clientStateT.time;
+
+ p.alpha = 1.0f;
+ p.alphavel = -1.0f / (0.3f + Globals.rnd.nextFloat() * 0.2f);
+ p.color = 0xd0;
+ for (j = 0; j < 3; j++) {
+ p.org[j] = move[j] + Lib.crand();
+ p.vel[j] = Lib.crand() * 5;
+ p.accel[j] = 0;
+ }
+
+ Math3D.vectorAdd(move, vec, move);
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.client;
+
+import lwjake2.Defines;
+import lwjake2.Globals;
+import lwjake2.game.Cmd;
+import lwjake2.game.entity_state_t;
+import lwjake2.qcommon.*;
+import lwjake2.sound.S;
+import lwjake2.sys.Sys;
+import lwjake2.util.Lib;
+
+import java.io.IOException;
+import java.io.RandomAccessFile;
+
+/**
+ * CL_parse
+ */
+public class CL_parse {
+
+ //// cl_parse.c -- parse a message received from the server
+
+ // =============================================================================
+ public static final String[] svc_strings = {"svc_bad", "svc_muzzleflash",
+ "svc_muzzlflash2", "svc_temp_entity", "svc_layout",
+ "svc_inventory", "svc_nop", "svc_disconnect", "svc_reconnect",
+ "svc_sound", "svc_print", "svc_stufftext", "svc_serverdata",
+ "svc_configstring", "svc_spawnbaseline", "svc_centerprint",
+ "svc_download", "svc_playerinfo", "svc_packetentities",
+ "svc_deltapacketentities", "svc_frame"};
+ /*
+ * =============== CL_Download_f
+ *
+ * Request a download from the server ===============
+ */
+ public static final xcommand_t Download_f = new xcommand_t() {
+ public void execute() {
+ String filename;
+
+ if (Cmd.Argc() != 2) {
+ Com.Printf("Usage: download <filename>\n");
+ return;
+ }
+
+ filename = Cmd.Argv(1);
+
+ if (filename.contains("..")) {
+ Com.Printf("Refusing to download a path with ..\n");
+ return;
+ }
+
+ if (FS.LoadFile(filename) != null) { // it exists, no need to
+ // download
+ Com.Printf("File already exists.\n");
+ return;
+ }
+
+ Globals.clientStaticT.downloadname = filename;
+ Com.Printf("Downloading " + Globals.clientStaticT.downloadname + "\n");
+
+ // download to a temp name, and only rename
+ // to the real name when done, so if interrupted
+ // a runt file wont be left
+ Globals.clientStaticT.downloadtempname = Com
+ .StripExtension(Globals.clientStaticT.downloadname);
+ Globals.clientStaticT.downloadtempname += ".tmp";
+
+ MSG.WriteByte(Globals.clientStaticT.netchan.message, Defines.clc_stringcmd);
+ MSG.WriteString(Globals.clientStaticT.netchan.message, "download "
+ + Globals.clientStaticT.downloadname);
+
+ Globals.clientStaticT.downloadnumber++;
+ }
+ };
+ private static final float[] pos_v = {0, 0, 0};
+
+ public static String DownloadFileName(String fn) {
+ return FS.Gamedir() + "/" + fn;
+ }
+
+ /*
+ * =============== CL_CheckOrDownloadFile
+ *
+ * Returns true if the file exists, otherwise it attempts to start a
+ * download from the server. ===============
+ */
+ public static boolean CheckOrDownloadFile(String filename) {
+ RandomAccessFile fp;
+ String name;
+
+ if (filename.contains("..")) {
+ Com.Printf("Refusing to download a path with ..\n");
+ return true;
+ }
+
+ if (FS.FileLength(filename) > 0) { // it exists, no need to download
+ return true;
+ }
+
+ Globals.clientStaticT.downloadname = filename;
+
+ // download to a temp name, and only rename
+ // to the real name when done, so if interrupted
+ // a runt file wont be left
+ Globals.clientStaticT.downloadtempname = Com
+ .StripExtension(Globals.clientStaticT.downloadname);
+ Globals.clientStaticT.downloadtempname += ".tmp";
+
+ // ZOID
+ // check to see if we already have a tmp for this file, if so, try to
+ // resume
+ // open the file if not opened yet
+ name = DownloadFileName(Globals.clientStaticT.downloadtempname);
+
+ fp = Lib.fopen(name, "r+b");
+
+ if (fp != null) {
+
+ // it exists
+ long len = 0;
+
+ try {
+ len = fp.length();
+ } catch (IOException ignored) {
+ }
+
+
+ Globals.clientStaticT.download = fp;
+
+ // give the server an offset to start the download
+ Com.Printf("Resuming " + Globals.clientStaticT.downloadname + "\n");
+ MSG.WriteByte(Globals.clientStaticT.netchan.message, Defines.clc_stringcmd);
+ MSG.WriteString(Globals.clientStaticT.netchan.message, "download "
+ + Globals.clientStaticT.downloadname + " " + len);
+ } else {
+ Com.Printf("Downloading " + Globals.clientStaticT.downloadname + "\n");
+ MSG.WriteByte(Globals.clientStaticT.netchan.message, Defines.clc_stringcmd);
+ MSG.WriteString(Globals.clientStaticT.netchan.message, "download "
+ + Globals.clientStaticT.downloadname);
+ }
+
+ Globals.clientStaticT.downloadnumber++;
+
+ return false;
+ }
+
+ /*
+ * ====================== CL_RegisterSounds ======================
+ */
+ public static void RegisterSounds() {
+ S.BeginRegistration();
+ CL_tent.RegisterTEntSounds();
+ for (int i = 1; i < Defines.MAX_SOUNDS; i++) {
+ if (Globals.clientStateT.configstrings[Defines.CS_SOUNDS + i] == null
+ || Globals.clientStateT.configstrings[Defines.CS_SOUNDS + i]
+ .equals("")
+ || Globals.clientStateT.configstrings[Defines.CS_SOUNDS + i]
+ .equals("\0"))
+ break;
+ Globals.clientStateT.sound_precache[i] = S
+ .RegisterSound(Globals.clientStateT.configstrings[Defines.CS_SOUNDS
+ + i]);
+ Sys.SendKeyEvents(); // pump message loop
+ }
+ S.EndRegistration();
+ }
+
+ /*
+ * =====================================================================
+ *
+ * SERVER CONNECTING MESSAGES
+ *
+ * =====================================================================
+ */
+
+ /*
+ * ===================== CL_ParseDownload
+ *
+ * A download message has been received from the server
+ * =====================
+ */
+ public static void ParseDownload() {
+
+ // read the data
+ int size = MSG.ReadShort(Globals.net_message);
+ int percent = MSG.ReadByte(Globals.net_message);
+ if (size == -1) {
+ Com.Printf("Server does not have this file.\n");
+ if (Globals.clientStaticT.download != null) {
+ // if here, we tried to resume a file but the server said no
+ try {
+ Globals.clientStaticT.download.close();
+ } catch (IOException ignored) {
+ }
+ Globals.clientStaticT.download = null;
+ }
+ Client.RequestNextDownload();
+ return;
+ }
+
+ // open the file if not opened yet
+ if (Globals.clientStaticT.download == null) {
+ String name = DownloadFileName(Globals.clientStaticT.downloadtempname).toLowerCase();
+
+ FS.CreatePath(name);
+
+ Globals.clientStaticT.download = Lib.fopen(name, "rw");
+ if (Globals.clientStaticT.download == null) {
+ Globals.net_message.readcount += size;
+ Com.Printf("Failed to open " + Globals.clientStaticT.downloadtempname
+ + "\n");
+ Client.RequestNextDownload();
+ return;
+ }
+ }
+
+ //fwrite(net_message.data[net_message.readcount], 1, size,
+ // cls.download);
+ try {
+ Globals.clientStaticT.download.write(Globals.net_message.data,
+ Globals.net_message.readcount, size);
+ } catch (Exception ignored) {
+ }
+ Globals.net_message.readcount += size;
+
+ if (percent != 100) {
+ // request next block
+ // change display routines by zoid
+ Globals.clientStaticT.downloadpercent = percent;
+ MSG.WriteByte(Globals.clientStaticT.netchan.message, Defines.clc_stringcmd);
+ SZ.Print(Globals.clientStaticT.netchan.message, "nextdl");
+ } else {
+ String oldn, newn;
+ //char oldn[MAX_OSPATH];
+ //char newn[MAX_OSPATH];
+
+ // Com.Printf ("100%%\n");
+
+ try {
+ Globals.clientStaticT.download.close();
+ } catch (IOException ignored) {
+ }
+
+ // rename the temp file to it's final name
+ oldn = DownloadFileName(Globals.clientStaticT.downloadtempname);
+ newn = DownloadFileName(Globals.clientStaticT.downloadname);
+ int r = Lib.rename(oldn, newn);
+ if (r != 0)
+ Com.Printf("failed to rename.\n");
+
+ Globals.clientStaticT.download = null;
+ Globals.clientStaticT.downloadpercent = 0;
+
+ // get another file if needed
+
+ Client.RequestNextDownload();
+ }
+ }
+
+ /*
+ * ================== CL_ParseServerData ==================
+ */
+ //checked once, was ok.
+ public static void ParseServerData() {
+
+ String str;
+ int i;
+
+ Com.DPrintf("ParseServerData():Serverdata packet received.\n");
+ //
+ // wipe the ClientStateT struct
+ //
+ Client.ClearState();
+ Globals.clientStaticT.state = Defines.ca_connected;
+
+ // parse protocol version number
+ i = MSG.ReadLong(Globals.net_message);
+ Globals.clientStaticT.serverProtocol = i;
+
+ // BIG HACK to let demos from release work with the 3.0x patch!!!
+ if (Globals.server_state != 0) {
+ } else if (i != Defines.PROTOCOL_VERSION)
+ Com.Error(Defines.ERR_DROP, "Server returned version " + i
+ + ", not " + Defines.PROTOCOL_VERSION);
+
+ Globals.clientStateT.servercount = MSG.ReadLong(Globals.net_message);
+ Globals.clientStateT.attractloop = MSG.ReadByte(Globals.net_message) != 0;
+
+ // game directory
+ str = MSG.ReadString(Globals.net_message);
+ Globals.clientStateT.gamedir = str;
+ Com.dprintln("gamedir=" + str);
+
+ // set gamedir
+ if (str.length() > 0
+ && (FS.fs_gamedirvar.string == null
+ || FS.fs_gamedirvar.string.length() == 0 || FS.fs_gamedirvar.string
+ .equals(str))
+ || (str.length() == 0 && (FS.fs_gamedirvar.string != null || FS.fs_gamedirvar.string
+ .length() == 0)))
+ Cvar.set("game", str);
+
+ // parse player entity number
+ Globals.clientStateT.playernum = MSG.ReadShort(Globals.net_message);
+ Com.dprintln("numplayers=" + Globals.clientStateT.playernum);
+ // get the full level name
+ str = MSG.ReadString(Globals.net_message);
+ Com.dprintln("levelname=" + str);
+
+ if (Globals.clientStateT.playernum == -1) { // playing a cinematic or showing a
+ // pic, not a level
+ SCR.PlayCinematic(str);
+ } else {
+ // seperate the printfs so the server message can have a color
+ // Com.Printf(
+ // "\n\n\35\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\37\n\n");
+ // Com.Printf('\02' + str + "\n");
+ Com.Printf("Levelname:" + str + "\n");
+ // need to prep refresh at next oportunity
+ Globals.clientStateT.refresh_prepped = false;
+ }
+ }
+
+ /*
+ * ================== CL_ParseBaseline ==================
+ */
+ public static void ParseBaseline() {
+ entity_state_t es;
+ int newnum;
+
+ entity_state_t nullstate = new entity_state_t(null);
+ //memset(nullstate, 0, sizeof(nullstate));
+ int bits[] = {0};
+ newnum = CL_ents.ParseEntityBits(bits);
+ es = Globals.cl_entities[newnum].baseline;
+ CL_ents.ParseDelta(nullstate, es, newnum, bits[0]);
+ }
+
+ /*
+ * ================ CL_LoadClientinfo
+ *
+ * ================
+ */
+ public static void LoadClientinfo(ClientInfo ci, String s) {
+ int i;
+ int t;
+
+ String model_name, skin_name, model_filename, skin_filename, weapon_filename;
+
+ ci.cinfo = s;
+ //ci.cinfo[sizeof(ci.cinfo) - 1] = 0;
+
+ // isolate the player's name
+ ci.name = s;
+ //ci.name[sizeof(ci.name) - 1] = 0;
+
+ t = s.indexOf('\\');
+ //t = strstr(s, "\\");
+
+ if (t != -1) {
+ ci.name = s.substring(0, t);
+ s = s.substring(t + 1, s.length());
+ //s = t + 1;
+ }
+
+ if (Globals.cl_noskins.value != 0 || s.length() == 0) {
+
+ model_filename = ("players/male/tris.md2");
+ weapon_filename = ("players/male/weapon.md2");
+ skin_filename = ("players/male/grunt.pcx");
+ ci.iconname = ("/players/male/grunt_i.pcx");
+
+ ci.model = Globals.re.RegisterModel(model_filename);
+ ci.skin = Globals.re.RegisterSkin(skin_filename);
+ ci.icon = Globals.re.RegisterPic(ci.iconname);
+
+ } else {
+ // isolate the model name
+
+ int pos = s.indexOf('/');
+
+ if (pos == -1)
+ pos = s.indexOf('/');
+ if (pos == -1) {
+ pos = 0;
+ Com.Error(Defines.ERR_FATAL, "Invalid model name:" + s);
+ }
+
+ model_name = s.substring(0, pos);
+
+ // isolate the skin name
+ skin_name = s.substring(pos + 1, s.length());
+
+ // model file
+ model_filename = "players/" + model_name + "/tris.md2";
+ ci.model = Globals.re.RegisterModel(model_filename);
+
+ if (ci.model == null) {
+ model_name = "male";
+ model_filename = "players/male/tris.md2";
+ ci.model = Globals.re.RegisterModel(model_filename);
+ }
+
+ // skin file
+ skin_filename = "players/" + model_name + "/" + skin_name + ".pcx";
+ ci.skin = Globals.re.RegisterSkin(skin_filename);
+
+ // if we don't have the skin and the model wasn't male,
+ // see if the male has it (this is for CTF's skins)
+ if (ci.skin == null && !model_name.equalsIgnoreCase("male")) {
+ // change model to male
+ model_name = "male";
+ model_filename = "players/male/tris.md2";
+ ci.model = Globals.re.RegisterModel(model_filename);
+
+ // see if the skin exists for the male model
+ skin_filename = "players/" + model_name + "/" + skin_name
+ + ".pcx";
+ ci.skin = Globals.re.RegisterSkin(skin_filename);
+ }
+
+ // if we still don't have a skin, it means that the male model
+ // didn't have
+ // it, so default to grunt
+ if (ci.skin == null) {
+ // see if the skin exists for the male model
+ skin_filename = "players/" + model_name + "/grunt.pcx";
+ ci.skin = Globals.re.RegisterSkin(skin_filename);
+ }
+
+
+ // icon file
+ ci.iconname = "/players/" + model_name + "/" + skin_name + "_i.pcx";
+ ci.icon = Globals.re.RegisterPic(ci.iconname);
+ }
+
+ // must have loaded all data types to be valud
+ if (ci.skin == null || ci.icon == null || ci.model == null) {
+ ci.skin = null;
+ ci.icon = null;
+ ci.model = null;
+ }
+ }
+
+ /*
+ * ================ CL_ParseClientinfo
+ *
+ * Load the skin, icon, and model for a client ================
+ */
+ public static void ParseClientinfo(int player) {
+ String s;
+ ClientInfo ci;
+
+ s = Globals.clientStateT.configstrings[player + Defines.CS_PLAYERSKINS];
+
+ ci = Globals.clientStateT.clientinfo[player];
+
+ LoadClientinfo(ci, s);
+ }
+
+ /*
+ * =====================================================================
+ *
+ * ACTION MESSAGES
+ *
+ * =====================================================================
+ */
+
+ /*
+ * ================ CL_ParseConfigString ================
+ */
+ public static void ParseConfigString() {
+ int i;
+ String s;
+ String olds;
+
+ i = MSG.ReadShort(Globals.net_message);
+
+ if (i < 0 || i >= Defines.MAX_CONFIGSTRINGS)
+ Com.Error(Defines.ERR_DROP, "configstring > MAX_CONFIGSTRINGS");
+
+ s = MSG.ReadString(Globals.net_message);
+
+ olds = Globals.clientStateT.configstrings[i];
+ Globals.clientStateT.configstrings[i] = s;
+
+ //Com.dprintln("ParseConfigString(): configstring[" + i + "]=<"+s+">");
+
+ // do something apropriate
+
+ if (i >= Defines.CS_LIGHTS
+ && i < Defines.CS_LIGHTS + Defines.MAX_LIGHTSTYLES) {
+
+ CL_fx.SetLightstyle(i - Defines.CS_LIGHTS);
+
+ } else if (i >= Defines.CS_MODELS && i < Defines.CS_MODELS + Defines.MAX_MODELS) {
+ if (Globals.clientStateT.refresh_prepped) {
+ Globals.clientStateT.model_draw[i - Defines.CS_MODELS] = Globals.re
+ .RegisterModel(Globals.clientStateT.configstrings[i]);
+ if (Globals.clientStateT.configstrings[i].startsWith("*"))
+ Globals.clientStateT.model_clip[i - Defines.CS_MODELS] = CM
+ .InlineModel(Globals.clientStateT.configstrings[i]);
+ else
+ Globals.clientStateT.model_clip[i - Defines.CS_MODELS] = null;
+ }
+ } else if (i >= Defines.CS_SOUNDS
+ && i < Defines.CS_SOUNDS + Defines.MAX_MODELS) {
+ if (Globals.clientStateT.refresh_prepped)
+ Globals.clientStateT.sound_precache[i - Defines.CS_SOUNDS] = S
+ .RegisterSound(Globals.clientStateT.configstrings[i]);
+ } else if (i >= Defines.CS_IMAGES
+ && i < Defines.CS_IMAGES + Defines.MAX_MODELS) {
+ if (Globals.clientStateT.refresh_prepped)
+ Globals.clientStateT.image_precache[i - Defines.CS_IMAGES] = Globals.re
+ .RegisterPic(Globals.clientStateT.configstrings[i]);
+ } else if (i >= Defines.CS_PLAYERSKINS
+ && i < Defines.CS_PLAYERSKINS + Defines.MAX_CLIENTS) {
+ if (Globals.clientStateT.refresh_prepped && !olds.equals(s))
+ ParseClientinfo(i - Defines.CS_PLAYERSKINS);
+ }
+ }
+
+ /*
+ * ================== CL_ParseStartSoundPacket ==================
+ */
+ public static void ParseStartSoundPacket() {
+ float pos[];
+ int channel, ent;
+ int sound_num;
+ float volume;
+ float attenuation;
+ int flags;
+ float ofs;
+
+ flags = MSG.ReadByte(Globals.net_message);
+ sound_num = MSG.ReadByte(Globals.net_message);
+
+ if ((flags & Defines.SND_VOLUME) != 0)
+ volume = MSG.ReadByte(Globals.net_message) / 255.0f;
+ else
+ volume = Defines.DEFAULT_SOUND_PACKET_VOLUME;
+
+ if ((flags & Defines.SND_ATTENUATION) != 0)
+ attenuation = MSG.ReadByte(Globals.net_message) / 64.0f;
+ else
+ attenuation = Defines.DEFAULT_SOUND_PACKET_ATTENUATION;
+
+ if ((flags & Defines.SND_OFFSET) != 0)
+ ofs = MSG.ReadByte(Globals.net_message) / 1000.0f;
+ else
+ ofs = 0;
+
+ if ((flags & Defines.SND_ENT) != 0) { // entity reletive
+ channel = MSG.ReadShort(Globals.net_message);
+ ent = channel >> 3;
+ if (ent > Defines.MAX_EDICTS)
+ Com.Error(Defines.ERR_DROP, "CL_ParseStartSoundPacket: ent = "
+ + ent);
+
+ channel &= 7;
+ } else {
+ ent = 0;
+ channel = 0;
+ }
+
+ if ((flags & Defines.SND_POS) != 0) { // positioned in space
+ MSG.ReadPos(Globals.net_message, pos_v);
+ // is ok. sound driver copies
+ pos = pos_v;
+ } else
+ // use entity number
+ pos = null;
+
+ if (null == Globals.clientStateT.sound_precache[sound_num])
+ return;
+
+ S.StartSound(pos, ent, channel, Globals.clientStateT.sound_precache[sound_num],
+ volume, attenuation, ofs);
+ }
+
+ public static void SHOWNET(String s) {
+ if (Globals.cl_shownet.value >= 2)
+ Com.Printf(Globals.net_message.readcount - 1 + ":" + s + "\n");
+ }
+
+ /*
+ * ===================== CL_ParseServerMessage =====================
+ */
+ public static void ParseServerMessage() {
+ int cmd;
+ String s;
+ int i;
+
+ //
+ // if recording demos, copy the message out
+ //
+ //if (cl_shownet.value == 1)
+ //Com.Printf(net_message.cursize + " ");
+ //else if (cl_shownet.value >= 2)
+ //Com.Printf("------------------\n");
+
+ //
+ // parse the message
+ //
+ while (true) {
+ if (Globals.net_message.readcount > Globals.net_message.cursize) {
+ Com.Error(Defines.ERR_FATAL,
+ "CL_ParseServerMessage: Bad server message:");
+ break;
+ }
+
+ cmd = MSG.ReadByte(Globals.net_message);
+
+ if (cmd == -1) {
+ SHOWNET("END OF MESSAGE");
+ break;
+ }
+
+ if (Globals.cl_shownet.value >= 2) {
+ if (null == svc_strings[cmd])
+ Com.Printf(Globals.net_message.readcount - 1 + ":BAD CMD "
+ + cmd + "\n");
+ else
+ SHOWNET(svc_strings[cmd]);
+ }
+
+ // other commands
+ switch (cmd) {
+ default:
+ Com.Error(Defines.ERR_DROP,
+ "CL_ParseServerMessage: Illegible server message\n");
+ break;
+
+ case Defines.svc_nop:
+ // Com.Printf ("svc_nop\n");
+ break;
+
+ case Defines.svc_disconnect:
+ Com.Error(Defines.ERR_DISCONNECT, "Server disconnected\n");
+ break;
+
+ case Defines.svc_reconnect:
+ Com.Printf("Server disconnected, reconnecting\n");
+ if (Globals.clientStaticT.download != null) {
+ //ZOID, close download
+ try {
+ Globals.clientStaticT.download.close();
+ } catch (IOException ignored) {
+ }
+ Globals.clientStaticT.download = null;
+ }
+ Globals.clientStaticT.state = Defines.ca_connecting;
+ Globals.clientStaticT.connect_time = -99999; // CL_CheckForResend() will
+ // fire immediately
+ break;
+
+ case Defines.svc_print:
+ i = MSG.ReadByte(Globals.net_message);
+ if (i == Defines.PRINT_CHAT) {
+ S.StartLocalSound("misc/talk.wav");
+ Globals.con.ormask = 128;
+ }
+ Com.Printf(MSG.ReadString(Globals.net_message));
+ Globals.con.ormask = 0;
+ break;
+
+ case Defines.svc_centerprint:
+ SCR.CenterPrint(MSG.ReadString(Globals.net_message));
+ break;
+
+ case Defines.svc_stufftext:
+ s = MSG.ReadString(Globals.net_message);
+ Com.DPrintf("stufftext: " + s + "\n");
+ CommandBuffer.AddText(s);
+ break;
+
+ case Defines.svc_serverdata:
+ CommandBuffer.execute(); // make sure any stuffed commands are done
+ ParseServerData();
+ break;
+
+ case Defines.svc_configstring:
+ ParseConfigString();
+ break;
+
+ case Defines.svc_sound:
+ ParseStartSoundPacket();
+ break;
+
+ case Defines.svc_spawnbaseline:
+ ParseBaseline();
+ break;
+
+ case Defines.svc_temp_entity:
+ CL_tent.ParseTEnt();
+ break;
+
+ case Defines.svc_download:
+ ParseDownload();
+ break;
+
+ case Defines.svc_frame:
+ CL_ents.ParseFrame();
+ break;
+
+ case Defines.svc_inventory:
+ CL_inv.ParseInventory();
+ break;
+
+ case Defines.svc_layout:
+ s = MSG.ReadString(Globals.net_message);
+ Globals.clientStateT.layout = s;
+ break;
+
+ case Defines.svc_playerinfo:
+ case Defines.svc_packetentities:
+ case Defines.svc_deltapacketentities:
+ Com.Error(Defines.ERR_DROP, "Out of place frame data");
+ break;
+ }
+ }
+
+ CL_view.AddNetgraph();
+
+ //
+ // we don't know if it is ok to save a demo message until
+ // after we have parsed the frame
+ //
+ if (Globals.clientStaticT.demorecording && !Globals.clientStaticT.demowaiting)
+ Client.WriteDemoMessage();
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.client;
+
+import lwjake2.Defines;
+import lwjake2.Globals;
+import lwjake2.game.*;
+import lwjake2.qcommon.CM;
+import lwjake2.qcommon.Com;
+import lwjake2.qcommon.PMove;
+import lwjake2.util.Math3D;
+
+/**
+ * CL_pred
+ */
+public class CL_pred {
+
+ public static final EDict DUMMY_ENT = new EDict(-1);
+
+ /*
+ * =================== CL_CheckPredictionError ===================
+ */
+ static void CheckPredictionError() {
+ int frame;
+ int[] delta = new int[3];
+ int i;
+ int len;
+
+ if (Globals.cl_predict.value == 0.0f
+ || (Globals.clientStateT.frame.playerstate.pmove.pm_flags & Move.PMF_NO_PREDICTION) != 0)
+ return;
+
+ // calculate the last usercmd_t we sent that the server has processed
+ frame = Globals.clientStaticT.netchan.incoming_acknowledged;
+ frame &= (Defines.CMD_BACKUP - 1);
+
+ // compare what the server returned with what we had predicted it to be
+ Math3D.vectorSubtract(Globals.clientStateT.frame.playerstate.pmove.origin,
+ Globals.clientStateT.predicted_origins[frame], delta);
+
+ // save the prediction error for interpolation
+ len = Math.abs(delta[0]) + Math.abs(delta[1]) + Math.abs(delta[2]);
+ if (len > 640) // 80 world units
+ { // a teleport or something
+ Math3D.vectorClear(Globals.clientStateT.prediction_error);
+ } else {
+ if (Globals.cl_showmiss.value != 0.0f
+ && (delta[0] != 0 || delta[1] != 0 || delta[2] != 0))
+ Com.Printf("prediction miss on " + Globals.clientStateT.frame.serverframe
+ + ": " + (delta[0] + delta[1] + delta[2]) + "\n");
+
+ Math3D.vectorCopy(Globals.clientStateT.frame.playerstate.pmove.origin,
+ Globals.clientStateT.predicted_origins[frame]);
+
+ // save for error itnerpolation
+ for (i = 0; i < 3; i++)
+ Globals.clientStateT.prediction_error[i] = delta[i] * 0.125f;
+ }
+ }
+
+ /*
+ * ================ CL_PMTrace ================
+ */
+
+ static void ClipMoveToEntities(float[] start, float[] mins, float[] maxs,
+ float[] end, trace_t tr) {
+ int i, x, zd, zu;
+ trace_t trace;
+ int headnode;
+ float[] angles;
+ entity_state_t ent;
+ int num;
+ cmodel_t cmodel;
+ float[] bmins = new float[3];
+ float[] bmaxs = new float[3];
+
+ for (i = 0; i < Globals.clientStateT.frame.num_entities; i++) {
+ num = (Globals.clientStateT.frame.parse_entities + i)
+ & (Defines.MAX_PARSE_ENTITIES - 1);
+ ent = Globals.cl_parse_entities[num];
+
+ if (ent.solid == 0)
+ continue;
+
+ if (ent.number == Globals.clientStateT.playernum + 1)
+ continue;
+
+ if (ent.solid == 31) { // special value for bmodel
+ cmodel = Globals.clientStateT.model_clip[ent.modelindex];
+ if (cmodel == null)
+ continue;
+ headnode = cmodel.headnode;
+ angles = ent.angles;
+ } else { // encoded bbox
+ x = 8 * (ent.solid & 31);
+ zd = 8 * ((ent.solid >>> 5) & 31);
+ zu = 8 * ((ent.solid >>> 10) & 63) - 32;
+
+ bmins[0] = bmins[1] = -x;
+ bmaxs[0] = bmaxs[1] = x;
+ bmins[2] = -zd;
+ bmaxs[2] = zu;
+
+ headnode = CM.HeadnodeForBox(bmins, bmaxs);
+ angles = Globals.vec3_origin; // boxes don't rotate
+ }
+
+ if (tr.allsolid)
+ return;
+
+ trace = CM.TransformedBoxTrace(start, end, mins, maxs, headnode,
+ Defines.MASK_PLAYERSOLID, ent.origin, angles);
+
+ if (trace.allsolid || trace.startsolid
+ || trace.fraction < tr.fraction) {
+ trace.ent = ent.surrounding_ent;
+ if (tr.startsolid) {
+ tr.set(trace); // rst: solved the Z U P P E L - P R O B L E
+ // M
+ tr.startsolid = true;
+ } else
+ tr.set(trace); // rst: solved the Z U P P E L - P R O B L E
+ // M
+ }
+ }
+ }
+
+ static trace_t PMTrace(float[] start, float[] mins, float[] maxs,
+ float[] end) {
+ trace_t t;
+
+ // check against world
+ t = CM.boxTrace(start, end, mins, maxs, 0, Defines.MASK_PLAYERSOLID);
+
+ if (t.fraction < 1.0f) {
+ t.ent = DUMMY_ENT;
+ }
+
+ // check all other solid models
+ ClipMoveToEntities(start, mins, maxs, end, t);
+
+ return t;
+ }
+
+ /*
+ * ================= PMpointcontents
+ *
+ * Returns the content identificator of the point. =================
+ */
+ static int PMpointcontents(float[] point) {
+ int i;
+ entity_state_t ent;
+ int num;
+ cmodel_t cmodel;
+ int contents;
+
+ contents = CM.PointContents(point, 0);
+
+ for (i = 0; i < Globals.clientStateT.frame.num_entities; i++) {
+ num = (Globals.clientStateT.frame.parse_entities + i)
+ & (Defines.MAX_PARSE_ENTITIES - 1);
+ ent = Globals.cl_parse_entities[num];
+
+ if (ent.solid != 31) // special value for bmodel
+ continue;
+
+ cmodel = Globals.clientStateT.model_clip[ent.modelindex];
+ if (cmodel == null)
+ continue;
+
+ contents |= CM.TransformedPointContents(point, cmodel.headnode,
+ ent.origin, ent.angles);
+ }
+ return contents;
+ }
+
+ /*
+ * ================= CL_PredictMovement
+ *
+ * Sets cl.predicted_origin and cl.predicted_angles =================
+ */
+ static void predictMovement() {
+
+ if (Globals.clientStaticT.state != Defines.ca_active)
+ return;
+
+ if (Globals.cl_paused.value != 0.0f)
+ return;
+
+ if (Globals.cl_predict.value == 0.0f
+ || (Globals.clientStateT.frame.playerstate.pmove.pm_flags & Move.PMF_NO_PREDICTION) != 0) {
+ // just set angles
+ for (int i = 0; i < 3; i++) {
+ Globals.clientStateT.predicted_angles[i] = Globals.clientStateT.viewangles[i]
+ + Math3D
+ .short2Angle(Globals.clientStateT.frame.playerstate.pmove.delta_angles[i]);
+ }
+ return;
+ }
+
+ int ack = Globals.clientStaticT.netchan.incoming_acknowledged;
+ int current = Globals.clientStaticT.netchan.outgoing_sequence;
+
+ // if we are too far out of date, just freeze
+ if (current - ack >= Defines.CMD_BACKUP) {
+ if (Globals.cl_showmiss.value != 0.0f)
+ Com.Printf("exceeded CMD_BACKUP\n");
+ return;
+ }
+
+ // copy current state to pmove
+ //memset (pm, 0, sizeof(pm));
+ Move pm = new Move();
+
+ pm.trace = new Move.TraceAdapter() {
+ public trace_t trace(float[] start, float[] mins, float[] maxs,
+ float[] end) {
+ return PMTrace(start, mins, maxs, end);
+ }
+ };
+ pm.pointcontents = new Move.PointContentsAdapter() {
+ public int pointcontents(float[] point) {
+ return PMpointcontents(point);
+ }
+ };
+
+ try {
+ PMove.pm_airaccelerate = Float
+ .parseFloat(Globals.clientStateT.configstrings[Defines.CS_AIRACCEL]);
+ } catch (Exception e) {
+ PMove.pm_airaccelerate = 0;
+ }
+
+ // bugfix (rst) yeah !!!!!!!! found the solution to the B E W E G U N G
+ // S P R O B L E M.
+ pm.s.set(Globals.clientStateT.frame.playerstate.pmove);
+
+ // SCR_DebugGraph (current - ack - 1, 0);
+ int frame = 0;
+
+ // run frames
+ usercmd_t cmd;
+ while (++ack < current) {
+ frame = ack & (Defines.CMD_BACKUP - 1);
+ cmd = Globals.clientStateT.cmds[frame];
+
+ pm.cmd.set(cmd);
+
+ PMove.Pmove(pm);
+
+ // save for debug checking
+ Math3D.vectorCopy(pm.s.origin, Globals.clientStateT.predicted_origins[frame]);
+ }
+
+ int oldframe = (ack - 2) & (Defines.CMD_BACKUP - 1);
+ int oldz = Globals.clientStateT.predicted_origins[oldframe][2];
+ int step = pm.s.origin[2] - oldz;
+ if (step > 63 && step < 160
+ && (pm.s.pm_flags & Move.PMF_ON_GROUND) != 0) {
+ Globals.clientStateT.predicted_step = step * 0.125f;
+ Globals.clientStateT.predicted_step_time = (int) (Globals.clientStaticT.realtime - Globals.clientStaticT.frametime * 500);
+ }
+
+ // copy results out for rendering
+ Globals.clientStateT.predicted_origin[0] = pm.s.origin[0] * 0.125f;
+ Globals.clientStateT.predicted_origin[1] = pm.s.origin[1] * 0.125f;
+ Globals.clientStateT.predicted_origin[2] = pm.s.origin[2] * 0.125f;
+
+ Math3D.vectorCopy(pm.viewangles, Globals.clientStateT.predicted_angles);
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.client;
+
+import lwjake2.Defines;
+import lwjake2.Globals;
+import lwjake2.game.player_state_t;
+import lwjake2.qcommon.Com;
+import lwjake2.qcommon.MSG;
+import lwjake2.render.Model;
+import lwjake2.sound.S;
+import lwjake2.sound.sfx_t;
+import lwjake2.util.Lib;
+import lwjake2.util.Math3D;
+
+/**
+ * CL_tent
+ */
+public class CL_tent {
+
+ // stack variable
+ public static final float[] pos = new float[3];
+ public static final float[] dir = new float[3];
+ // stack variable
+ // pos, dir
+ public static final float[] pos2 = {0, 0, 0};
+ // stack variable
+ public static final float[] dist = new float[3];
+ public static final float[] org = new float[3];
+ static final int MAX_EXPLOSIONS = 32;
+ static final int MAX_BEAMS = 32;
+ static final int MAX_LASERS = 32;
+ // ROGUE
+ static final int MAX_SUSTAINS = 32;
+ static final int ex_free = 0;
+ static final int ex_explosion = 1;
+ static final int ex_misc = 2;
+ static final int ex_flash = 3;
+ static final int ex_mflash = 4;
+ static final int ex_poly = 5;
+ static final int ex_poly2 = 6;
+ static final explosion_t[] cl_explosions = new explosion_t[MAX_EXPLOSIONS];
+ // ROGUE
+ static final beam_t[] cl_beams = new beam_t[MAX_BEAMS];
+ // PMM - added this for player-linked beams. Currently only used by the
+ // plasma beam
+ static final beam_t[] cl_playerbeams = new beam_t[MAX_BEAMS];
+ static final laser_t[] cl_lasers = new laser_t[MAX_LASERS];
+ static final cl_sustain_t[] cl_sustains = new cl_sustain_t[MAX_SUSTAINS];
+ static final sfx_t[] cl_sfx_footsteps = new sfx_t[4];
+ /*
+ * ================= CL_ParseTEnt =================
+ */
+ static final int[] splash_color = {0x00, 0xe0, 0xb0, 0x50, 0xd0, 0xe0, 0xe8};
+ // stack variable
+ private static final float[] start = new float[3];
+ private static final float[] end = new float[3];
+ // stack variable
+ // dist, org
+ private static final entity_t ent = new entity_t();
+ private static final float[] f = new float[3];
+ // all are references;
+ private static final float[] u = new float[3];
+ private static final float[] r = new float[3];
+ static Model cl_mod_explode;
+ static Model cl_mod_smoke;
+ static Model cl_mod_flash;
+ static Model cl_mod_parasite_segment;
+ static Model cl_mod_grapple_cable;
+ static Model cl_mod_parasite_tip;
+ static Model cl_mod_explo4;
+ static Model cl_mod_bfg_explo;
+ static Model cl_mod_powerscreen;
+ // RAFAEL
+ static Model cl_mod_plasmaexplo;
+ // ROGUE
+ static sfx_t cl_sfx_lightning;
+ static sfx_t cl_sfx_disrexp;
+ static Model cl_mod_lightning;
+ static Model cl_mod_heatbeam;
+ static Model cl_mod_monster_heatbeam;
+ static Model cl_mod_explo4_big;
+
+ static {
+ for (int i = 0; i < cl_explosions.length; i++)
+ cl_explosions[i] = new explosion_t();
+ }
+
+ static {
+ for (int i = 0; i < cl_beams.length; i++)
+ cl_beams[i] = new beam_t();
+ for (int i = 0; i < cl_playerbeams.length; i++)
+ cl_playerbeams[i] = new beam_t();
+ }
+
+ static {
+ for (int i = 0; i < cl_lasers.length; i++)
+ cl_lasers[i] = new laser_t();
+ }
+
+ // rogue
+
+ static {
+ for (int i = 0; i < cl_sustains.length; i++)
+ cl_sustains[i] = new cl_sustain_t();
+ }
+
+ // ROGUE
+ /*
+ * ================= CL_RegisterTEntSounds =================
+ */
+ static void RegisterTEntSounds() {
+ int i;
+ String name;
+
+ // RAFAEL
+ // cl_sfx_plasexp = S.RegisterSound ("weapons/plasexpl.wav");
+ S.RegisterSound("player/land1.wav");
+
+ S.RegisterSound("player/fall2.wav");
+ S.RegisterSound("player/fall1.wav");
+
+ for (i = 0; i < 4; i++) {
+ //Com_sprintf (name, sizeof(name), "player/step%i.wav", i+1);
+ name = "player/step" + (i + 1) + ".wav";
+ cl_sfx_footsteps[i] = S.RegisterSound(name);
+ }
+
+ }
+
+ /*
+ * ================= CL_RegisterTEntModels =================
+ */
+ static void RegisterTEntModels() {
+ }
+
+ /*
+ * ================= CL_ClearTEnts =================
+ */
+ static void ClearTEnts() {
+ // memset (cl_beams, 0, sizeof(cl_beams));
+ for (beam_t cl_beam : cl_beams) cl_beam.clear();
+ // memset (cl_explosions, 0, sizeof(cl_explosions));
+ for (explosion_t cl_explosion : cl_explosions) cl_explosion.clear();
+ // memset (cl_lasers, 0, sizeof(cl_lasers));
+ for (laser_t cl_laser : cl_lasers) cl_laser.clear();
+ //
+ // ROGUE
+ // memset (cl_playerbeams, 0, sizeof(cl_playerbeams));
+ for (beam_t cl_playerbeam : cl_playerbeams) cl_playerbeam.clear();
+ // memset (cl_sustains, 0, sizeof(cl_sustains));
+ for (cl_sustain_t cl_sustain : cl_sustains) cl_sustain.clear();
+ // ROGUE
+ }
+
+ /*
+ * ================= CL_AllocExplosion =================
+ */
+ static explosion_t AllocExplosion() {
+ int i;
+ int time;
+ int index;
+
+ for (i = 0; i < MAX_EXPLOSIONS; i++) {
+ if (cl_explosions[i].type == ex_free) {
+ //memset (&cl_explosions[i], 0, sizeof (cl_explosions[i]));
+ cl_explosions[i].clear();
+ return cl_explosions[i];
+ }
+ }
+ // find the oldest explosion
+ time = Globals.clientStateT.time;
+ index = 0;
+
+ for (i = 0; i < MAX_EXPLOSIONS; i++)
+ if (cl_explosions[i].start < time) {
+ time = (int) cl_explosions[i].start;
+ index = i;
+ }
+ //memset (&cl_explosions[index], 0, sizeof (cl_explosions[index]));
+ cl_explosions[index].clear();
+ return cl_explosions[index];
+ }
+
+ /*
+ * ================= CL_SmokeAndFlash =================
+ */
+ static void SmokeAndFlash() {
+ explosion_t ex;
+
+ ex = AllocExplosion();
+ Math3D.vectorCopy(CL_tent.pos, ex.ent.origin);
+ ex.type = ex_misc;
+ ex.frames = 4;
+ ex.ent.flags = Defines.RF_TRANSLUCENT;
+ ex.start = Globals.clientStateT.frame.servertime - 100;
+ ex.ent.model = cl_mod_smoke;
+
+ ex = AllocExplosion();
+ Math3D.vectorCopy(CL_tent.pos, ex.ent.origin);
+ ex.type = ex_flash;
+ ex.ent.flags = Defines.RF_FULLBRIGHT;
+ ex.frames = 2;
+ ex.start = Globals.clientStateT.frame.servertime - 100;
+ ex.ent.model = cl_mod_flash;
+ }
+
+ /*
+ * =================
+ * CL_ParseBeam
+ * =================
+ */
+ static int ParseBeam(Model model) {
+ int ent;
+ float[] start = new float[3];
+ float[] end = new float[3];
+ beam_t[] b;
+ int i;
+
+ ent = MSG.ReadShort(Globals.net_message);
+
+ MSG.ReadPos(Globals.net_message, start);
+ MSG.ReadPos(Globals.net_message, end);
+
+ // override any beam with the same entity
+ b = cl_beams;
+ for (i = 0; i < MAX_BEAMS; i++)
+ if (b[i].entity == ent) {
+ b[i].entity = ent;
+ b[i].model = model;
+ b[i].endtime = Globals.clientStateT.time + 200;
+ Math3D.vectorCopy(start, b[i].start);
+ Math3D.vectorCopy(end, b[i].end);
+ Math3D.vectorClear(b[i].offset);
+ return ent;
+ }
+
+ // find a free beam
+ b = cl_beams;
+ for (i = 0; i < MAX_BEAMS; i++) {
+ if (b[i].model == null || b[i].endtime < Globals.clientStateT.time) {
+ b[i].entity = ent;
+ b[i].model = model;
+ b[i].endtime = Globals.clientStateT.time + 200;
+ Math3D.vectorCopy(start, b[i].start);
+ Math3D.vectorCopy(end, b[i].end);
+ Math3D.vectorClear(b[i].offset);
+ return ent;
+ }
+ }
+ Com.Printf("beam list overflow!\n");
+ return ent;
+ }
+
+ /*
+ * ================= CL_ParseBeam2 =================
+ */
+ static int ParseBeam2(Model model) {
+ int ent;
+ float[] start = new float[3];
+ float[] end = new float[3];
+ float[] offset = new float[3];
+ beam_t[] b;
+ int i;
+
+ ent = MSG.ReadShort(Globals.net_message);
+
+ MSG.ReadPos(Globals.net_message, start);
+ MSG.ReadPos(Globals.net_message, end);
+ MSG.ReadPos(Globals.net_message, offset);
+
+ // Com_Printf ("end- %f %f %f\n", end[0], end[1], end[2]);
+
+ // override any beam with the same entity
+ b = cl_beams;
+ for (i = 0; i < MAX_BEAMS; i++)
+ if (b[i].entity == ent) {
+ b[i].entity = ent;
+ b[i].model = model;
+ b[i].endtime = Globals.clientStateT.time + 200;
+ Math3D.vectorCopy(start, b[i].start);
+ Math3D.vectorCopy(end, b[i].end);
+ Math3D.vectorCopy(offset, b[i].offset);
+ return ent;
+ }
+
+ // find a free beam
+ b = cl_beams;
+ for (i = 0; i < MAX_BEAMS; i++) {
+ if (b[i].model == null || b[i].endtime < Globals.clientStateT.time) {
+ b[i].entity = ent;
+ b[i].model = model;
+ b[i].endtime = Globals.clientStateT.time + 200;
+ Math3D.vectorCopy(start, b[i].start);
+ Math3D.vectorCopy(end, b[i].end);
+ Math3D.vectorCopy(offset, b[i].offset);
+ return ent;
+ }
+ }
+ Com.Printf("beam list overflow!\n");
+ return ent;
+ }
+
+ // ROGUE
+ /*
+ * ================= CL_ParsePlayerBeam - adds to the cl_playerbeam array
+ * instead of the cl_beams array =================
+ */
+ static int ParsePlayerBeam(Model model) {
+ int ent;
+ float[] start = new float[3];
+ float[] end = new float[3];
+ float[] offset = new float[3];
+ beam_t[] b;
+ int i;
+
+ ent = MSG.ReadShort(Globals.net_message);
+
+ MSG.ReadPos(Globals.net_message, start);
+ MSG.ReadPos(Globals.net_message, end);
+ // PMM - network optimization
+ if (model == cl_mod_heatbeam)
+ Math3D.vectorSet(offset, 2, 7, -3);
+ else if (model == cl_mod_monster_heatbeam) {
+ model = cl_mod_heatbeam;
+ Math3D.vectorSet(offset, 0, 0, 0);
+ } else
+ MSG.ReadPos(Globals.net_message, offset);
+
+ // Com_Printf ("end- %f %f %f\n", end[0], end[1], end[2]);
+
+ // override any beam with the same entity
+ // PMM - For player beams, we only want one per player (entity) so..
+ b = cl_playerbeams;
+ for (i = 0; i < MAX_BEAMS; i++) {
+ if (b[i].entity == ent) {
+ b[i].entity = ent;
+ b[i].model = model;
+ b[i].endtime = Globals.clientStateT.time + 200;
+ Math3D.vectorCopy(start, b[i].start);
+ Math3D.vectorCopy(end, b[i].end);
+ Math3D.vectorCopy(offset, b[i].offset);
+ return ent;
+ }
+ }
+
+ // find a free beam
+ b = cl_playerbeams;
+ for (i = 0; i < MAX_BEAMS; i++) {
+ if (b[i].model == null || b[i].endtime < Globals.clientStateT.time) {
+ b[i].entity = ent;
+ b[i].model = model;
+ b[i].endtime = Globals.clientStateT.time + 100; // PMM - this needs to be
+ // 100 to prevent multiple
+ // heatbeams
+ Math3D.vectorCopy(start, b[i].start);
+ Math3D.vectorCopy(end, b[i].end);
+ Math3D.vectorCopy(offset, b[i].offset);
+ return ent;
+ }
+ }
+ Com.Printf("beam list overflow!\n");
+ return ent;
+ }
+
+ // ROGUE
+ // =============
+
+ /*
+ * ================= CL_ParseLightning =================
+ */
+ static int ParseLightning(Model model) {
+ int srcEnt, destEnt;
+ beam_t[] b;
+ int i;
+
+ srcEnt = MSG.ReadShort(Globals.net_message);
+ destEnt = MSG.ReadShort(Globals.net_message);
+
+ MSG.ReadPos(Globals.net_message, start);
+ MSG.ReadPos(Globals.net_message, end);
+
+ // override any beam with the same source AND destination entities
+ b = cl_beams;
+ for (i = 0; i < MAX_BEAMS; i++)
+ if (b[i].entity == srcEnt && b[i].dest_entity == destEnt) {
+ // Com_Printf("%d: OVERRIDE %d . %d\n", cl.time, srcEnt,
+ // destEnt);
+ b[i].entity = srcEnt;
+ b[i].dest_entity = destEnt;
+ b[i].model = model;
+ b[i].endtime = Globals.clientStateT.time + 200;
+ Math3D.vectorCopy(start, b[i].start);
+ Math3D.vectorCopy(end, b[i].end);
+ Math3D.vectorClear(b[i].offset);
+ return srcEnt;
+ }
+
+ // find a free beam
+ b = cl_beams;
+ for (i = 0; i < MAX_BEAMS; i++) {
+ if (b[i].model == null || b[i].endtime < Globals.clientStateT.time) {
+ // Com_Printf("%d: NORMAL %d . %d\n", cl.time, srcEnt, destEnt);
+ b[i].entity = srcEnt;
+ b[i].dest_entity = destEnt;
+ b[i].model = model;
+ b[i].endtime = Globals.clientStateT.time + 200;
+ Math3D.vectorCopy(start, b[i].start);
+ Math3D.vectorCopy(end, b[i].end);
+ Math3D.vectorClear(b[i].offset);
+ return srcEnt;
+ }
+ }
+ Com.Printf("beam list overflow!\n");
+ return srcEnt;
+ }
+
+ // stack variable
+ // start, end
+ /*
+ * ================= CL_ParseLaser =================
+ */
+ static void ParseLaser() {
+
+ MSG.ReadPos(Globals.net_message, start);
+ MSG.ReadPos(Globals.net_message, end);
+
+ }
+
+ // =============
+ // ROGUE
+ static void ParseSteam() {
+ int id, i;
+ int r;
+ int cnt;
+ int color;
+ int magnitude;
+ cl_sustain_t[] s;
+ cl_sustain_t free_sustain;
+
+ id = MSG.ReadShort(Globals.net_message); // an id of -1 is an instant
+ // effect
+ if (id != -1) // sustains
+ {
+ // Com_Printf ("Sustain effect id %d\n", id);
+ free_sustain = null;
+ s = cl_sustains;
+ for (i = 0; i < MAX_SUSTAINS; i++) {
+ if (s[i].id == 0) {
+ free_sustain = s[i];
+ break;
+ }
+ }
+ if (free_sustain != null) {
+ s[i].id = id;
+ s[i].count = MSG.ReadByte(Globals.net_message);
+ MSG.ReadPos(Globals.net_message, s[i].org);
+ MSG.ReadDir(Globals.net_message, s[i].dir);
+ r = MSG.ReadByte(Globals.net_message);
+ s[i].color = r & 0xff;
+ s[i].magnitude = MSG.ReadShort(Globals.net_message);
+ s[i].endtime = Globals.clientStateT.time
+ + MSG.ReadLong(Globals.net_message);
+ s[i].think = new cl_sustain_t.ThinkAdapter() {
+ void think(cl_sustain_t self) {
+ CL_newfx.ParticleSteamEffect2(self);
+ }
+ };
+ s[i].thinkinterval = 100;
+ s[i].nextthink = Globals.clientStateT.time;
+ } else {
+ // Com_Printf ("No free sustains!\n");
+ // FIXME - read the stuff anyway
+ cnt = MSG.ReadByte(Globals.net_message);
+ MSG.ReadPos(Globals.net_message, pos);
+ MSG.ReadDir(Globals.net_message, dir);
+ r = MSG.ReadByte(Globals.net_message);
+ magnitude = MSG.ReadShort(Globals.net_message);
+ magnitude = MSG.ReadLong(Globals.net_message); // really
+ // interval
+ }
+ } else // instant
+ {
+ cnt = MSG.ReadByte(Globals.net_message);
+ MSG.ReadPos(Globals.net_message, pos);
+ MSG.ReadDir(Globals.net_message, dir);
+ r = MSG.ReadByte(Globals.net_message);
+ magnitude = MSG.ReadShort(Globals.net_message);
+ color = r & 0xff;
+ CL_newfx.ParticleSteamEffect(color, cnt, magnitude);
+ // S_StartSound (pos, 0, 0, cl_sfx_lashit, 1, ATTN_NORM, 0);
+ }
+ }
+
+ // stack variable
+ // pos
+ static void ParseWidow() {
+ int id, i;
+ cl_sustain_t[] s;
+ cl_sustain_t free_sustain;
+
+ id = MSG.ReadShort(Globals.net_message);
+
+ free_sustain = null;
+ s = cl_sustains;
+ for (i = 0; i < MAX_SUSTAINS; i++) {
+ if (s[i].id == 0) {
+ free_sustain = s[i];
+ break;
+ }
+ }
+ if (free_sustain != null) {
+ s[i].id = id;
+ MSG.ReadPos(Globals.net_message, s[i].org);
+ s[i].endtime = Globals.clientStateT.time + 2100;
+ s[i].think = new cl_sustain_t.ThinkAdapter() {
+ void think(cl_sustain_t self) {
+ CL_newfx.Widowbeamout(self);
+ }
+ };
+ s[i].thinkinterval = 1;
+ s[i].nextthink = Globals.clientStateT.time;
+ } else // no free sustains
+ {
+ // FIXME - read the stuff anyway
+ MSG.ReadPos(Globals.net_message, pos);
+ }
+ }
+
+ // stack variable
+ // pos
+ static void ParseNuke() {
+ int i;
+ cl_sustain_t[] s;
+ cl_sustain_t free_sustain;
+
+ free_sustain = null;
+ s = cl_sustains;
+ for (i = 0; i < MAX_SUSTAINS; i++) {
+ if (s[i].id == 0) {
+ free_sustain = s[i];
+ break;
+ }
+ }
+ if (free_sustain != null) {
+ s[i].id = 21000;
+ MSG.ReadPos(Globals.net_message, s[i].org);
+ s[i].endtime = Globals.clientStateT.time + 1000;
+ s[i].think = new cl_sustain_t.ThinkAdapter() {
+ void think(cl_sustain_t self) {
+ CL_newfx.Nukeblast(self);
+ }
+ };
+ s[i].thinkinterval = 1;
+ s[i].nextthink = Globals.clientStateT.time;
+ } else // no free sustains
+ {
+ // FIXME - read the stuff anyway
+ MSG.ReadPos(Globals.net_message, pos);
+ }
+ }
+
+ //extern CvarT *hand;
+
+ static void ParseTEnt() {
+ int type;
+ explosion_t ex;
+ int cnt;
+ int color;
+ int r;
+ int ent;
+ int magnitude;
+
+ type = MSG.ReadByte(Globals.net_message);
+
+ switch (type) {
+ case Defines.TE_BLOOD: // bullet hitting flesh
+ MSG.ReadPos(Globals.net_message, pos);
+ MSG.ReadDir(Globals.net_message, dir);
+ CL_fx.ParticleEffect(0xe8, 60);
+ break;
+
+ case Defines.TE_GUNSHOT: // bullet hitting wall
+ case Defines.TE_SPARKS:
+ case Defines.TE_BULLET_SPARKS:
+ MSG.ReadPos(Globals.net_message, pos);
+ MSG.ReadDir(Globals.net_message, dir);
+ if (type == Defines.TE_GUNSHOT)
+ CL_fx.ParticleEffect(0, 40);
+ else
+ CL_fx.ParticleEffect(0xe0, 6);
+
+ if (type != Defines.TE_SPARKS) {
+ SmokeAndFlash();
+
+ }
+
+ break;
+
+ case Defines.TE_SCREEN_SPARKS:
+ case Defines.TE_SHIELD_SPARKS:
+ MSG.ReadPos(Globals.net_message, pos);
+ MSG.ReadDir(Globals.net_message, dir);
+ if (type == Defines.TE_SCREEN_SPARKS)
+ CL_fx.ParticleEffect(0xd0, 40);
+ else
+ CL_fx.ParticleEffect(0xb0, 40);
+
+ break;
+
+ case Defines.TE_SHOTGUN: // bullet hitting wall
+ MSG.ReadPos(Globals.net_message, pos);
+ MSG.ReadDir(Globals.net_message, dir);
+ CL_fx.ParticleEffect(0, 20);
+ SmokeAndFlash();
+ break;
+
+ case Defines.TE_SPLASH: // bullet hitting water
+ cnt = MSG.ReadByte(Globals.net_message);
+ MSG.ReadPos(Globals.net_message, pos);
+ MSG.ReadDir(Globals.net_message, dir);
+ r = MSG.ReadByte(Globals.net_message);
+ if (r > 6)
+ color = 0x00;
+ else
+ color = splash_color[r];
+ CL_fx.ParticleEffect(color, cnt);
+
+ break;
+
+ case Defines.TE_LASER_SPARKS:
+ cnt = MSG.ReadByte(Globals.net_message);
+ MSG.ReadPos(Globals.net_message, pos);
+ MSG.ReadDir(Globals.net_message, dir);
+ color = MSG.ReadByte(Globals.net_message);
+ CL_fx.ParticleEffect2(color, cnt);
+ break;
+
+ // RAFAEL
+ case Defines.TE_BLUEHYPERBLASTER:
+ MSG.ReadPos(Globals.net_message, pos);
+ MSG.ReadPos(Globals.net_message, dir);
+ CL_fx.BlasterParticles();
+ break;
+
+ case Defines.TE_BLASTER: // blaster hitting wall
+ MSG.ReadPos(Globals.net_message, pos);
+ MSG.ReadDir(Globals.net_message, dir);
+ CL_fx.BlasterParticles();
+
+ ex = AllocExplosion();
+ Math3D.vectorCopy(pos, ex.ent.origin);
+ ex.ent.angles[0] = (float) (Math.acos(dir[2]) / Math.PI * 180);
+ // PMM - fixed to correct for pitch of 0
+ if (dir[0] != 0.0f)
+ ex.ent.angles[1] = (float) (Math.atan2(dir[1], dir[0])
+ / Math.PI * 180);
+ else if (dir[1] > 0)
+ ex.ent.angles[1] = 90;
+ else if (dir[1] < 0)
+ ex.ent.angles[1] = 270;
+ else
+ ex.ent.angles[1] = 0;
+
+ ex.type = ex_misc;
+ ex.ent.flags = Defines.RF_FULLBRIGHT | Defines.RF_TRANSLUCENT;
+ ex.start = Globals.clientStateT.frame.servertime - 100;
+ ex.light = 150;
+ ex.lightcolor[0] = 1;
+ ex.lightcolor[1] = 1;
+ ex.ent.model = cl_mod_explode;
+ ex.frames = 4;
+ break;
+
+ case Defines.TE_RAILTRAIL: // railgun effect
+ MSG.ReadPos(Globals.net_message, pos);
+ MSG.ReadPos(Globals.net_message, pos2);
+ CL_fx.RailTrail();
+ break;
+
+ case Defines.TE_EXPLOSION2:
+ case Defines.TE_GRENADE_EXPLOSION:
+ case Defines.TE_GRENADE_EXPLOSION_WATER:
+ MSG.ReadPos(Globals.net_message, pos);
+
+ ex = AllocExplosion();
+ Math3D.vectorCopy(pos, ex.ent.origin);
+ ex.type = ex_poly;
+ ex.ent.flags = Defines.RF_FULLBRIGHT;
+ ex.start = Globals.clientStateT.frame.servertime - 100;
+ ex.light = 350;
+ ex.lightcolor[0] = 1.0f;
+ ex.lightcolor[1] = 0.5f;
+ ex.lightcolor[2] = 0.5f;
+ ex.ent.model = cl_mod_explo4;
+ ex.frames = 19;
+ ex.baseframe = 30;
+ ex.ent.angles[1] = Lib.rand() % 360;
+ CL_fx.ExplosionParticles();
+ break;
+
+ // RAFAEL
+ case Defines.TE_PLASMA_EXPLOSION:
+ MSG.ReadPos(Globals.net_message, pos);
+ ex = AllocExplosion();
+ Math3D.vectorCopy(pos, ex.ent.origin);
+ ex.type = ex_poly;
+ ex.ent.flags = Defines.RF_FULLBRIGHT;
+ ex.start = Globals.clientStateT.frame.servertime - 100;
+ ex.light = 350;
+ ex.lightcolor[0] = 1.0f;
+ ex.lightcolor[1] = 0.5f;
+ ex.lightcolor[2] = 0.5f;
+ ex.ent.angles[1] = Lib.rand() % 360;
+ ex.ent.model = cl_mod_explo4;
+ if (Globals.rnd.nextFloat() < 0.5)
+ ex.baseframe = 15;
+ ex.frames = 15;
+ CL_fx.ExplosionParticles();
+ break;
+
+ case Defines.TE_EXPLOSION1:
+ case Defines.TE_EXPLOSION1_BIG: // PMM
+ case Defines.TE_ROCKET_EXPLOSION:
+ case Defines.TE_ROCKET_EXPLOSION_WATER:
+ case Defines.TE_EXPLOSION1_NP: // PMM
+ MSG.ReadPos(Globals.net_message, pos);
+
+ ex = AllocExplosion();
+ Math3D.vectorCopy(pos, ex.ent.origin);
+ ex.type = ex_poly;
+ ex.ent.flags = Defines.RF_FULLBRIGHT;
+ ex.start = Globals.clientStateT.frame.servertime - 100;
+ ex.light = 350;
+ ex.lightcolor[0] = 1.0f;
+ ex.lightcolor[1] = 0.5f;
+ ex.lightcolor[2] = 0.5f;
+ ex.ent.angles[1] = Lib.rand() % 360;
+ if (type != Defines.TE_EXPLOSION1_BIG) // PMM
+ ex.ent.model = cl_mod_explo4; // PMM
+ else
+ ex.ent.model = cl_mod_explo4_big;
+ if (Globals.rnd.nextFloat() < 0.5)
+ ex.baseframe = 15;
+ ex.frames = 15;
+ if ((type != Defines.TE_EXPLOSION1_BIG)
+ && (type != Defines.TE_EXPLOSION1_NP)) // PMM
+ CL_fx.ExplosionParticles(); // PMM
+ break;
+
+ case Defines.TE_BFG_EXPLOSION:
+ MSG.ReadPos(Globals.net_message, pos);
+ ex = AllocExplosion();
+ Math3D.vectorCopy(pos, ex.ent.origin);
+ ex.type = ex_poly;
+ ex.ent.flags = Defines.RF_FULLBRIGHT;
+ ex.start = Globals.clientStateT.frame.servertime - 100;
+ ex.light = 350;
+ ex.lightcolor[0] = 0.0f;
+ ex.lightcolor[1] = 1.0f;
+ ex.lightcolor[2] = 0.0f;
+ ex.ent.model = cl_mod_bfg_explo;
+ ex.ent.flags |= Defines.RF_TRANSLUCENT;
+ ex.ent.alpha = 0.30f;
+ ex.frames = 4;
+ break;
+
+ case Defines.TE_BFG_BIGEXPLOSION:
+ MSG.ReadPos(Globals.net_message, pos);
+ CL_fx.BFGExplosionParticles();
+ break;
+
+ case Defines.TE_BFG_LASER:
+ ParseLaser();
+ break;
+
+ case Defines.TE_BUBBLETRAIL:
+ MSG.ReadPos(Globals.net_message, pos);
+ MSG.ReadPos(Globals.net_message, pos2);
+ CL_fx.BubbleTrail();
+ break;
+
+ case Defines.TE_PARASITE_ATTACK:
+ case Defines.TE_MEDIC_CABLE_ATTACK:
+ ent = ParseBeam(cl_mod_parasite_segment);
+ break;
+
+ case Defines.TE_BOSSTPORT: // boss teleporting to station
+ MSG.ReadPos(Globals.net_message, pos);
+ CL_fx.BigTeleportParticles();
+ S.StartSound(pos, 0, 0, S.RegisterSound("misc/bigtele.wav"), 1,
+ Defines.ATTN_NONE, 0);
+ break;
+
+ case Defines.TE_GRAPPLE_CABLE:
+ ent = ParseBeam2(cl_mod_grapple_cable);
+ break;
+
+ // RAFAEL
+ case Defines.TE_WELDING_SPARKS:
+ cnt = MSG.ReadByte(Globals.net_message);
+ MSG.ReadPos(Globals.net_message, pos);
+ MSG.ReadDir(Globals.net_message, dir);
+ color = MSG.ReadByte(Globals.net_message);
+ CL_fx.ParticleEffect2(color, cnt);
+
+ ex = AllocExplosion();
+ Math3D.vectorCopy(pos, ex.ent.origin);
+ ex.type = ex_flash;
+ // note to self
+ // we need a better no draw flag
+ break;
+
+ case Defines.TE_GREENBLOOD:
+ MSG.ReadPos(Globals.net_message, pos);
+ MSG.ReadDir(Globals.net_message, dir);
+ CL_fx.ParticleEffect2(0xdf, 30);
+ break;
+
+ // RAFAEL
+ case Defines.TE_TUNNEL_SPARKS:
+ cnt = MSG.ReadByte(Globals.net_message);
+ MSG.ReadPos(Globals.net_message, pos);
+ MSG.ReadDir(Globals.net_message, dir);
+ color = MSG.ReadByte(Globals.net_message);
+ CL_fx.ParticleEffect3(color, cnt);
+ break;
+
+ // =============
+ // PGM
+ // PMM -following code integrated for flechette (different color)
+ case Defines.TE_BLASTER2: // green blaster hitting wall
+ case Defines.TE_FLECHETTE: // flechette
+ MSG.ReadPos(Globals.net_message, pos);
+ MSG.ReadDir(Globals.net_message, dir);
+
+ // PMM
+ if (type == Defines.TE_BLASTER2)
+ CL_newfx.BlasterParticles2(0xd0);
+ else
+ CL_newfx.BlasterParticles2(0x6f); // 75
+
+ ex = AllocExplosion();
+ Math3D.vectorCopy(pos, ex.ent.origin);
+ ex.ent.angles[0] = (float) (Math.acos(dir[2]) / Math.PI * 180);
+ // PMM - fixed to correct for pitch of 0
+ if (dir[0] != 0.0f)
+ ex.ent.angles[1] = (float) (Math.atan2(dir[1], dir[0])
+ / Math.PI * 180);
+ else if (dir[1] > 0)
+ ex.ent.angles[1] = 90;
+ else if (dir[1] < 0)
+ ex.ent.angles[1] = 270;
+ else
+ ex.ent.angles[1] = 0;
+
+ ex.type = ex_misc;
+ ex.ent.flags = Defines.RF_FULLBRIGHT | Defines.RF_TRANSLUCENT;
+
+ // PMM
+ if (type == Defines.TE_BLASTER2)
+ ex.ent.skinnum = 1;
+ else
+ // flechette
+ ex.ent.skinnum = 2;
+
+ ex.start = Globals.clientStateT.frame.servertime - 100;
+ ex.light = 150;
+ // PMM
+ if (type == Defines.TE_BLASTER2)
+ ex.lightcolor[1] = 1;
+ else // flechette
+ {
+ ex.lightcolor[0] = 0.19f;
+ ex.lightcolor[1] = 0.41f;
+ ex.lightcolor[2] = 0.75f;
+ }
+ ex.ent.model = cl_mod_explode;
+ ex.frames = 4;
+ break;
+
+
+ case Defines.TE_DEBUGTRAIL:
+ MSG.ReadPos(Globals.net_message, pos);
+ MSG.ReadPos(Globals.net_message, pos2);
+ CL_newfx.DebugTrail();
+ break;
+
+ case Defines.TE_PLAIN_EXPLOSION:
+ MSG.ReadPos(Globals.net_message, pos);
+
+ ex = AllocExplosion();
+ Math3D.vectorCopy(pos, ex.ent.origin);
+ ex.type = ex_poly;
+ ex.ent.flags = Defines.RF_FULLBRIGHT;
+ ex.start = Globals.clientStateT.frame.servertime - 100;
+ ex.light = 350;
+ ex.lightcolor[0] = 1.0f;
+ ex.lightcolor[1] = 0.5f;
+ ex.lightcolor[2] = 0.5f;
+ ex.ent.angles[1] = Lib.rand() % 360;
+ ex.ent.model = cl_mod_explo4;
+ if (Globals.rnd.nextFloat() < 0.5)
+ ex.baseframe = 15;
+ ex.frames = 15;
+ break;
+
+ case Defines.TE_FLASHLIGHT:
+ MSG.ReadPos(Globals.net_message, pos);
+ ent = MSG.ReadShort(Globals.net_message);
+ CL_newfx.Flashlight(ent);
+ break;
+
+ case Defines.TE_FORCEWALL:
+ MSG.ReadPos(Globals.net_message, pos);
+ MSG.ReadPos(Globals.net_message, pos2);
+ color = MSG.ReadByte(Globals.net_message);
+ CL_newfx.ForceWall(color);
+ break;
+
+ case Defines.TE_HEATBEAM:
+ ent = ParsePlayerBeam(cl_mod_heatbeam);
+ break;
+
+ case Defines.TE_MONSTER_HEATBEAM:
+ ent = ParsePlayerBeam(cl_mod_monster_heatbeam);
+ break;
+
+ case Defines.TE_HEATBEAM_SPARKS:
+ // cnt = MSG.ReadByte (net_message);
+ cnt = 50;
+ MSG.ReadPos(Globals.net_message, pos);
+ MSG.ReadDir(Globals.net_message, dir);
+ // r = MSG.ReadByte (net_message);
+ // magnitude = MSG.ReadShort (net_message);
+ r = 8;
+ magnitude = 60;
+ color = r & 0xff;
+ CL_newfx.ParticleSteamEffect(color, cnt, magnitude);
+ break;
+
+ case Defines.TE_HEATBEAM_STEAM:
+ // cnt = MSG.ReadByte (net_message);
+ cnt = 20;
+ MSG.ReadPos(Globals.net_message, pos);
+ MSG.ReadDir(Globals.net_message, dir);
+ // r = MSG.ReadByte (net_message);
+ // magnitude = MSG.ReadShort (net_message);
+ // color = r & 0xff;
+ color = 0xe0;
+ magnitude = 60;
+ CL_newfx.ParticleSteamEffect(color, cnt, magnitude);
+ break;
+
+ case Defines.TE_STEAM:
+ ParseSteam();
+ break;
+
+ case Defines.TE_BUBBLETRAIL2:
+ // cnt = MSG.ReadByte (net_message);
+ cnt = 8;
+ MSG.ReadPos(Globals.net_message, pos);
+ MSG.ReadPos(Globals.net_message, pos2);
+ CL_newfx.BubbleTrail2(cnt);
+ break;
+
+ case Defines.TE_MOREBLOOD:
+ MSG.ReadPos(Globals.net_message, pos);
+ MSG.ReadDir(Globals.net_message, dir);
+ CL_fx.ParticleEffect(0xe8, 250);
+ break;
+
+ case Defines.TE_CHAINFIST_SMOKE:
+ dir[0] = 0;
+ dir[1] = 0;
+ dir[2] = 1;
+ MSG.ReadPos(Globals.net_message, pos);
+ CL_newfx.ParticleSmokeEffect();
+ break;
+
+ case Defines.TE_ELECTRIC_SPARKS:
+ MSG.ReadPos(Globals.net_message, pos);
+ MSG.ReadDir(Globals.net_message, dir);
+ // CL_ParticleEffect (pos, dir, 109, 40);
+ CL_fx.ParticleEffect(0x75, 40);
+ break;
+
+ case Defines.TE_TRACKER_EXPLOSION:
+ MSG.ReadPos(Globals.net_message, pos);
+ CL_newfx.ColorFlash(-1, -1, -1);
+ CL_newfx.ColorExplosionParticles();
+ S.StartSound(pos, 0, 0, cl_sfx_disrexp, 1, Defines.ATTN_NORM, 0);
+ break;
+
+ case Defines.TE_TELEPORT_EFFECT:
+ case Defines.TE_DBALL_GOAL:
+ MSG.ReadPos(Globals.net_message, pos);
+ CL_fx.TeleportParticles();
+ break;
+
+ case Defines.TE_WIDOWBEAMOUT:
+ ParseWidow();
+ break;
+
+ case Defines.TE_NUKEBLAST:
+ ParseNuke();
+ break;
+
+ case Defines.TE_WIDOWSPLASH:
+ MSG.ReadPos(Globals.net_message, pos);
+ CL_newfx.WidowSplash();
+ break;
+ // PGM
+ // ==============
+
+ default:
+ Com.Error(Defines.ERR_DROP, "CL_ParseTEnt: bad type");
+ }
+ }
+
+ /*
+ * ================= CL_AddBeams =================
+ */
+ static void AddBeams() {
+ int i, j;
+ beam_t[] b;
+ float d;
+ float yaw, pitch;
+ float forward;
+ float len, steps;
+ float model_length;
+
+ // update beams
+ b = cl_beams;
+ for (i = 0; i < MAX_BEAMS; i++) {
+ if (b[i].model == null || b[i].endtime < Globals.clientStateT.time)
+ continue;
+
+ // if coming from the player, update the start position
+ if (b[i].entity == Globals.clientStateT.playernum + 1) // entity 0 is the
+ // world
+ {
+ Math3D.vectorCopy(Globals.clientStateT.refdef.vieworg, b[i].start);
+ b[i].start[2] -= 22; // adjust for view height
+ }
+ Math3D.vectorAdd(b[i].start, b[i].offset, org);
+
+ // calculate pitch and yaw
+ Math3D.vectorSubtract(b[i].end, org, dist);
+
+ if (dist[1] == 0 && dist[0] == 0) {
+ yaw = 0;
+ if (dist[2] > 0)
+ pitch = 90;
+ else
+ pitch = 270;
+ } else {
+ // PMM - fixed to correct for pitch of 0
+ if (dist[0] != 0.0f)
+ yaw = (float) (Math.atan2(dist[1], dist[0]) * 180 / Math.PI);
+ else if (dist[1] > 0)
+ yaw = 90;
+ else
+ yaw = 270;
+ if (yaw < 0)
+ yaw += 360;
+
+ forward = (float) Math.sqrt(dist[0] * dist[0] + dist[1]
+ * dist[1]);
+ pitch = (float) (Math.atan2(dist[2], forward) * -180.0 / Math.PI);
+ if (pitch < 0)
+ pitch += 360.0;
+ }
+
+ // add new entities for the beams
+ d = Math3D.vectorNormalize(dist);
+
+ //memset (&ent, 0, sizeof(ent));
+ ent.clear();
+ if (b[i].model == cl_mod_lightning) {
+ model_length = 35.0f;
+ d -= 20.0; // correction so it doesn't end in middle of tesla
+ } else {
+ model_length = 30.0f;
+ }
+ steps = (float) Math.ceil(d / model_length);
+ len = (d - model_length) / (steps - 1);
+
+ // PMM - special case for lightning model .. if the real length is
+ // shorter than the model,
+ // flip it around & draw it from the end to the start. This prevents
+ // the model from going
+ // through the tesla mine (instead it goes through the target)
+ if ((b[i].model == cl_mod_lightning) && (d <= model_length)) {
+ // Com_Printf ("special case\n");
+ Math3D.vectorCopy(b[i].end, ent.origin);
+ // offset to push beam outside of tesla model (negative because
+ // dist is from end to start
+ // for this beam)
+ // for (j=0 ; j<3 ; j++)
+ // ent.origin[j] -= dist[j]*10.0;
+ ent.model = b[i].model;
+ ent.flags = Defines.RF_FULLBRIGHT;
+ ent.angles[0] = pitch;
+ ent.angles[1] = yaw;
+ ent.angles[2] = Lib.rand() % 360;
+ V.AddEntity(ent);
+ return;
+ }
+ while (d > 0) {
+ Math3D.vectorCopy(org, ent.origin);
+ ent.model = b[i].model;
+ if (b[i].model == cl_mod_lightning) {
+ ent.flags = Defines.RF_FULLBRIGHT;
+ ent.angles[0] = -pitch;
+ ent.angles[1] = yaw + 180.0f;
+ ent.angles[2] = Lib.rand() % 360;
+ } else {
+ ent.angles[0] = pitch;
+ ent.angles[1] = yaw;
+ ent.angles[2] = Lib.rand() % 360;
+ }
+
+ // Com_Printf("B: %d . %d\n", b[i].entity, b[i].dest_entity);
+ V.AddEntity(ent);
+
+ for (j = 0; j < 3; j++)
+ org[j] += dist[j] * len;
+ d -= model_length;
+ }
+ }
+ }
+
+ /*
+ * ================= ROGUE - draw player locked beams CL_AddPlayerBeams
+ * =================
+ */
+ static void AddPlayerBeams() {
+ float d;
+ //entity_t ent = new entity_t();
+ float yaw, pitch;
+ float forward;
+ float len, steps;
+ int framenum = 0;
+ float model_length;
+
+ float hand_multiplier;
+ frame_t oldframe;
+ player_state_t ps, ops;
+
+ // PMM
+ if (Globals.hand != null) {
+ if (Globals.hand.value == 2)
+ hand_multiplier = 0;
+ else if (Globals.hand.value == 1)
+ hand_multiplier = -1;
+ else
+ hand_multiplier = 1;
+ } else {
+ hand_multiplier = 1;
+ }
+ // PMM
+
+ // update beams
+ beam_t[] b = cl_playerbeams;
+ for (int i = 0; i < MAX_BEAMS; i++) {
+
+ if (b[i].model == null || b[i].endtime < Globals.clientStateT.time)
+ continue;
+
+ if (cl_mod_heatbeam != null && (b[i].model == cl_mod_heatbeam)) {
+
+ // if coming from the player, update the start position
+ if (b[i].entity == Globals.clientStateT.playernum + 1) // entity 0 is the
+ // world
+ {
+ // set up gun position
+ // code straight out of CL_AddViewWeapon
+ ps = Globals.clientStateT.frame.playerstate;
+ int j = (Globals.clientStateT.frame.serverframe - 1)
+ & Defines.UPDATE_MASK;
+ oldframe = Globals.clientStateT.frames[j];
+
+ if (oldframe.serverframe != Globals.clientStateT.frame.serverframe - 1
+ || !oldframe.valid)
+ oldframe = Globals.clientStateT.frame; // previous frame was
+ // dropped or involid
+
+ ops = oldframe.playerstate;
+
+ Math3D.vectorMA(b[i].start,
+ (hand_multiplier * b[i].offset[0]),
+ Globals.clientStateT.v_right, org);
+ Math3D.vectorMA(org, b[i].offset[1], Globals.clientStateT.v_forward,
+ org);
+ Math3D.vectorMA(org, b[i].offset[2], Globals.clientStateT.v_up, org);
+ if ((Globals.hand != null) && (Globals.hand.value == 2)) {
+ Math3D.vectorMA(org, -1, Globals.clientStateT.v_up, org);
+ }
+ // FIXME - take these out when final
+ Math3D.vectorCopy(Globals.clientStateT.v_right, r);
+ Math3D.vectorCopy(Globals.clientStateT.v_forward, f);
+ Math3D.vectorCopy(Globals.clientStateT.v_up, u);
+
+ } else
+ Math3D.vectorCopy(b[i].start, org);
+ } else {
+ // if coming from the player, update the start position
+ if (b[i].entity == Globals.clientStateT.playernum + 1) // entity 0 is the
+ // world
+ {
+ Math3D.vectorCopy(Globals.clientStateT.refdef.vieworg, b[i].start);
+ b[i].start[2] -= 22; // adjust for view height
+ }
+ Math3D.vectorAdd(b[i].start, b[i].offset, org);
+ }
+
+ // calculate pitch and yaw
+ Math3D.vectorSubtract(b[i].end, org, dist);
+
+ // PMM
+ if (cl_mod_heatbeam != null && (b[i].model == cl_mod_heatbeam)
+ && (b[i].entity == Globals.clientStateT.playernum + 1)) {
+
+ len = Math3D.vectorLength(dist);
+ Math3D.vectorScale(f, len, dist);
+ Math3D.vectorMA(dist, (hand_multiplier * b[i].offset[0]), r,
+ dist);
+ Math3D.vectorMA(dist, b[i].offset[1], f, dist);
+ Math3D.vectorMA(dist, b[i].offset[2], u, dist);
+ if ((Globals.hand != null) && (Globals.hand.value == 2)) {
+ Math3D.vectorMA(org, -1, Globals.clientStateT.v_up, org);
+ }
+ }
+ // PMM
+
+ if (dist[1] == 0 && dist[0] == 0) {
+ yaw = 0;
+ if (dist[2] > 0)
+ pitch = 90;
+ else
+ pitch = 270;
+ } else {
+ // PMM - fixed to correct for pitch of 0
+ if (dist[0] != 0.0f)
+ yaw = (float) (Math.atan2(dist[1], dist[0]) * 180 / Math.PI);
+ else if (dist[1] > 0)
+ yaw = 90;
+ else
+ yaw = 270;
+ if (yaw < 0)
+ yaw += 360;
+
+ forward = (float) Math.sqrt(dist[0] * dist[0] + dist[1]
+ * dist[1]);
+ pitch = (float) (Math.atan2(dist[2], forward) * -180.0 / Math.PI);
+ if (pitch < 0)
+ pitch += 360.0;
+ }
+
+ if (cl_mod_heatbeam != null && (b[i].model == cl_mod_heatbeam)) {
+ if (b[i].entity != Globals.clientStateT.playernum + 1) {
+ framenum = 2;
+ // Com_Printf ("Third person\n");
+ ent.angles[0] = -pitch;
+ ent.angles[1] = yaw + 180.0f;
+ ent.angles[2] = 0;
+ // Com_Printf ("%f %f - %f %f %f\n", -pitch, yaw+180.0,
+ // b[i].offset[0], b[i].offset[1], b[i].offset[2]);
+ Math3D.angleVectors(ent.angles, f, r, u);
+
+ // if it's a non-origin offset, it's a player, so use the
+ // hardcoded player offset
+ if (!Math3D.vectorEquals(b[i].offset, Globals.vec3_origin)) {
+ Math3D.vectorMA(org, -(b[i].offset[0]) + 1, r, org);
+ Math3D.vectorMA(org, -(b[i].offset[1]), f, org);
+ Math3D.vectorMA(org, -(b[i].offset[2]) - 10, u, org);
+ } else {
+ // if it's a monster, do the particle effect
+ CL_newfx.MonsterPlasma_Shell(b[i].start);
+ }
+ } else {
+ framenum = 1;
+ }
+ }
+
+ // if it's the heatbeam, draw the particle effect
+ if ((cl_mod_heatbeam != null && (b[i].model == cl_mod_heatbeam) && (b[i].entity == Globals.clientStateT.playernum + 1))) {
+ CL_newfx.Heatbeam();
+ }
+
+ // add new entities for the beams
+ d = Math3D.vectorNormalize(dist);
+
+ //memset (&ent, 0, sizeof(ent));
+ ent.clear();
+
+ if (b[i].model == cl_mod_heatbeam) {
+ model_length = 32.0f;
+ } else if (b[i].model == cl_mod_lightning) {
+ model_length = 35.0f;
+ d -= 20.0; // correction so it doesn't end in middle of tesla
+ } else {
+ model_length = 30.0f;
+ }
+ steps = (float) Math.ceil(d / model_length);
+ len = (d - model_length) / (steps - 1);
+
+ // PMM - special case for lightning model .. if the real length is
+ // shorter than the model,
+ // flip it around & draw it from the end to the start. This prevents
+ // the model from going
+ // through the tesla mine (instead it goes through the target)
+ if ((b[i].model == cl_mod_lightning) && (d <= model_length)) {
+ // Com_Printf ("special case\n");
+ Math3D.vectorCopy(b[i].end, ent.origin);
+ // offset to push beam outside of tesla model (negative because
+ // dist is from end to start
+ // for this beam)
+ // for (j=0 ; j<3 ; j++)
+ // ent.origin[j] -= dist[j]*10.0;
+ ent.model = b[i].model;
+ ent.flags = Defines.RF_FULLBRIGHT;
+ ent.angles[0] = pitch;
+ ent.angles[1] = yaw;
+ ent.angles[2] = Lib.rand() % 360;
+ V.AddEntity(ent);
+ return;
+ }
+ while (d > 0) {
+ Math3D.vectorCopy(org, ent.origin);
+ ent.model = b[i].model;
+ if (cl_mod_heatbeam != null && (b[i].model == cl_mod_heatbeam)) {
+ // ent.flags = RF_FULLBRIGHT|RF_TRANSLUCENT;
+ // ent.alpha = 0.3;
+ ent.flags = Defines.RF_FULLBRIGHT;
+ ent.angles[0] = -pitch;
+ ent.angles[1] = yaw + 180.0f;
+ ent.angles[2] = (Globals.clientStateT.time) % 360;
+ // ent.angles[2] = rand()%360;
+ ent.frame = framenum;
+ } else if (b[i].model == cl_mod_lightning) {
+ ent.flags = Defines.RF_FULLBRIGHT;
+ ent.angles[0] = -pitch;
+ ent.angles[1] = yaw + 180.0f;
+ ent.angles[2] = Lib.rand() % 360;
+ } else {
+ ent.angles[0] = pitch;
+ ent.angles[1] = yaw;
+ ent.angles[2] = Lib.rand() % 360;
+ }
+
+ // Com_Printf("B: %d . %d\n", b[i].entity, b[i].dest_entity);
+ V.AddEntity(ent);
+
+ for (int j = 0; j < 3; j++)
+ org[j] += dist[j] * len;
+ d -= model_length;
+ }
+ }
+ }
+
+ /*
+ * ================= CL_AddExplosions =================
+ */
+ static void AddExplosions() {
+ entity_t ent;
+ int i;
+ explosion_t[] ex;
+ float frac;
+ int f;
+
+ //memset (&ent, 0, sizeof(ent)); Pointer!
+ ent = null;
+ ex = cl_explosions;
+ for (i = 0; i < MAX_EXPLOSIONS; i++) {
+ if (ex[i].type == ex_free)
+ continue;
+ frac = (Globals.clientStateT.time - ex[i].start) / 100.0f;
+ f = (int) Math.floor(frac);
+
+ ent = ex[i].ent;
+
+ switch (ex[i].type) {
+ case ex_mflash:
+ if (f >= ex[i].frames - 1)
+ ex[i].type = ex_free;
+ break;
+ case ex_misc:
+ if (f >= ex[i].frames - 1) {
+ ex[i].type = ex_free;
+ break;
+ }
+ ent.alpha = 1.0f - frac / (ex[i].frames - 1);
+ break;
+ case ex_flash:
+ if (f >= 1) {
+ ex[i].type = ex_free;
+ break;
+ }
+ ent.alpha = 1.0f;
+ break;
+ case ex_poly:
+ if (f >= ex[i].frames - 1) {
+ ex[i].type = ex_free;
+ break;
+ }
+
+ ent.alpha = (16.0f - (float) f) / 16.0f;
+
+ if (f < 10) {
+ ent.skinnum = (f >> 1);
+ if (ent.skinnum < 0)
+ ent.skinnum = 0;
+ } else {
+ ent.flags |= Defines.RF_TRANSLUCENT;
+ if (f < 13)
+ ent.skinnum = 5;
+ else
+ ent.skinnum = 6;
+ }
+ break;
+ case ex_poly2:
+ if (f >= ex[i].frames - 1) {
+ ex[i].type = ex_free;
+ break;
+ }
+
+ ent.alpha = (5.0f - (float) f) / 5.0f;
+ ent.skinnum = 0;
+ ent.flags |= Defines.RF_TRANSLUCENT;
+ break;
+ }
+
+ if (ex[i].type == ex_free)
+ continue;
+ if (ex[i].light != 0.0f) {
+ V.AddLight(ent.origin, ex[i].light * ent.alpha,
+ ex[i].lightcolor[0], ex[i].lightcolor[1],
+ ex[i].lightcolor[2]);
+ }
+
+ Math3D.vectorCopy(ent.origin, ent.oldorigin);
+
+ if (f < 0)
+ f = 0;
+ ent.frame = ex[i].baseframe + f + 1;
+ ent.oldframe = ex[i].baseframe + f;
+ ent.backlerp = 1.0f - Globals.clientStateT.lerpfrac;
+
+ V.AddEntity(ent);
+ }
+ }
+
+ /*
+ * ================= CL_AddLasers =================
+ */
+ static void AddLasers() {
+ laser_t[] l;
+ int i;
+
+ l = cl_lasers;
+ for (i = 0; i < MAX_LASERS; i++) {
+ if (l[i].endtime >= Globals.clientStateT.time)
+ V.AddEntity(l[i].ent);
+ }
+ }
+
+ /* PMM - CL_Sustains */
+ static void ProcessSustain() {
+ cl_sustain_t[] s;
+ int i;
+
+ s = cl_sustains;
+ for (i = 0; i < MAX_SUSTAINS; i++) {
+ if (s[i].id != 0)
+ if ((s[i].endtime >= Globals.clientStateT.time)
+ && (Globals.clientStateT.time >= s[i].nextthink)) {
+ s[i].think.think(s[i]);
+ } else if (s[i].endtime < Globals.clientStateT.time)
+ s[i].id = 0;
+ }
+ }
+
+ /*
+ * ================= CL_AddTEnts =================
+ */
+ static void AddTEnts() {
+ AddBeams();
+ // PMM - draw plasma beams
+ AddPlayerBeams();
+ AddExplosions();
+ AddLasers();
+ // PMM - set up sustain
+ ProcessSustain();
+ }
+
+ static class explosion_t {
+ final entity_t ent = new entity_t();
+ final float[] lightcolor = new float[3];
+ int type;
+ int frames;
+ float light;
+ float start;
+
+ int baseframe;
+
+ void clear() {
+ lightcolor[0] = lightcolor[1] = lightcolor[2] = light = start = type = frames = baseframe = 0;
+ ent.clear();
+ }
+ }
+
+ static class beam_t {
+ final float[] offset = new float[3];
+ final float[] start = new float[3];
+ final float[] end = new float[3];
+ int entity;
+ int dest_entity;
+ Model model;
+ int endtime;
+
+ void clear() {
+ offset[0] = offset[1] = offset[2] = start[0] = start[1] = start[2] = end[0] = end[1] = end[2] = entity = dest_entity = endtime = 0;
+ model = null;
+ }
+ }
+
+ static class laser_t {
+ final entity_t ent = new entity_t();
+
+ int endtime;
+
+ void clear() {
+ endtime = 0;
+ ent.clear();
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.client;
+
+import lwjake2.Defines;
+import lwjake2.Globals;
+import lwjake2.qcommon.CM;
+import lwjake2.qcommon.Com;
+import lwjake2.sys.Sys;
+
+import java.util.StringTokenizer;
+
+public class CL_view {
+
+
+ /*
+ * =================
+ *
+ * CL_PrepRefresh
+ *
+ * Call before entering a new level, or after changing dlls
+ * =================
+ */
+ static void PrepRefresh() {
+ String mapname;
+ int i;
+ String name;
+ float rotate;
+ float[] axis = new float[3];
+
+ if ((i = Globals.clientStateT.configstrings[Defines.CS_MODELS + 1].length()) == 0)
+ return; // no map loaded
+
+ SCR.AddDirtyPoint(0, 0);
+ SCR.AddDirtyPoint(Globals.viddef.width - 1, Globals.viddef.height - 1);
+
+ // let the render dll load the map
+ mapname = Globals.clientStateT.configstrings[Defines.CS_MODELS + 1].substring(5,
+ i - 4); // skip "maps/"
+ // cut off ".bsp"
+
+ // register models, pics, and skins
+ Com.Printf("Map: " + mapname + "\r");
+ SCR.UpdateScreen();
+ Globals.re.BeginRegistration(mapname);
+ Com.Printf(" \r");
+
+ // precache status bar pics
+ Com.Printf("pics\r");
+ SCR.UpdateScreen();
+ SCR.TouchPics();
+ Com.Printf(" \r");
+
+ CL_tent.RegisterTEntModels();
+
+ for (i = 1; i < Defines.MAX_MODELS
+ && Globals.clientStateT.configstrings[Defines.CS_MODELS + i].length() != 0; i++) {
+ name = Globals.clientStateT.configstrings[Defines.CS_MODELS + i];
+ if (name.length() > 37)
+ name = name.substring(0, 36);
+
+ if (name.charAt(0) != '*')
+ Com.Printf(name + "\r");
+
+ SCR.UpdateScreen();
+ Sys.SendKeyEvents(); // pump message loop
+ Globals.clientStateT.model_draw[i] = Globals.re
+ .RegisterModel(Globals.clientStateT.configstrings[Defines.CS_MODELS
+ + i]);
+ if (name.charAt(0) == '*')
+ Globals.clientStateT.model_clip[i] = CM
+ .InlineModel(Globals.clientStateT.configstrings[Defines.CS_MODELS
+ + i]);
+ else
+ Globals.clientStateT.model_clip[i] = null;
+ if (name.charAt(0) != '*')
+ Com.Printf(" \r");
+ }
+
+ Com.Printf("images\r");
+ SCR.UpdateScreen();
+ for (i = 1; i < Defines.MAX_IMAGES
+ && Globals.clientStateT.configstrings[Defines.CS_IMAGES + i].length() > 0; i++) {
+ Globals.clientStateT.image_precache[i] = Globals.re
+ .RegisterPic(Globals.clientStateT.configstrings[Defines.CS_IMAGES + i]);
+ Sys.SendKeyEvents(); // pump message loop
+ }
+
+ Com.Printf(" \r");
+ for (i = 0; i < Defines.MAX_CLIENTS; i++) {
+ if (Globals.clientStateT.configstrings[Defines.CS_PLAYERSKINS + i].length() == 0)
+ continue;
+ Com.Printf("client " + i + '\r');
+ SCR.UpdateScreen();
+ Sys.SendKeyEvents(); // pump message loop
+ CL_parse.ParseClientinfo(i);
+ Com.Printf(" \r");
+ }
+
+ CL_parse.LoadClientinfo(Globals.clientStateT.baseclientinfo,
+ "unnamed\\male/grunt");
+
+ // set sky textures and speed
+ Com.Printf("sky\r");
+ SCR.UpdateScreen();
+ rotate = Float
+ .parseFloat(Globals.clientStateT.configstrings[Defines.CS_SKYROTATE]);
+ StringTokenizer st = new StringTokenizer(
+ Globals.clientStateT.configstrings[Defines.CS_SKYAXIS]);
+ axis[0] = Float.parseFloat(st.nextToken());
+ axis[1] = Float.parseFloat(st.nextToken());
+ axis[2] = Float.parseFloat(st.nextToken());
+ Globals.re.SetSky(Globals.clientStateT.configstrings[Defines.CS_SKY], rotate,
+ axis);
+ Com.Printf(" \r");
+
+ // the renderer can now free unneeded stuff
+ Globals.re.EndRegistration();
+
+ // clear any lines of console text
+ Console.ClearNotify();
+
+ SCR.UpdateScreen();
+ Globals.clientStateT.refresh_prepped = true;
+ Globals.clientStateT.force_refdef = true; // make sure we have a valid refdef
+ }
+
+ public static void AddNetgraph() {
+ int i;
+ int in;
+ int ping;
+
+ // if using the debuggraph for something else, don't
+ // add the net lines
+ if (SCR.scr_debuggraph.value == 0.0f || SCR.scr_timegraph.value == 0.0f)
+ return;
+
+ for (i = 0; i < Globals.clientStaticT.netchan.dropped; i++)
+ SCR.DebugGraph(30, 0x40);
+
+ for (i = 0; i < Globals.clientStateT.surpressCount; i++)
+ SCR.DebugGraph(30, 0xdf);
+
+ // see what the latency was on this packet
+ in = Globals.clientStaticT.netchan.incoming_acknowledged
+ & (Defines.CMD_BACKUP - 1);
+ ping = Globals.clientStaticT.realtime - Globals.clientStateT.cmd_time[in];
+ ping /= 30;
+ if (ping > 30)
+ ping = 30;
+ SCR.DebugGraph(ping, 0xd0);
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.client;
+
+import lwjake2.Defines;
+import lwjake2.Globals;
+import lwjake2.game.*;
+import lwjake2.qcommon.*;
+import lwjake2.server.Server;
+import lwjake2.sound.S;
+import lwjake2.sys.NET;
+import lwjake2.sys.Sys;
+import lwjake2.sys.Timer;
+import lwjake2.sys.UserInputHandler;
+import lwjake2.util.Lib;
+import lwjake2.util.Math3D;
+import lwjake2.util.Vargs;
+
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+/**
+ * CL
+ */
+public final class Client {
+
+ public static final int PLAYER_MULT = 5;
+ // ENV_CNT is map load, ENV_CNT+1 is first env map
+ public static final int ENV_CNT = (Defines.CS_PLAYERSKINS + Defines.MAX_CLIENTS
+ * Client.PLAYER_MULT);
+ public static final int TEXTURE_CNT = (ENV_CNT + 13);
+ public static final String[][] cheatvarsinfo = {{"timescale", "1"},
+ {"timedemo", "0"}, {"r_drawworld", "1"},
+ {"cl_testlights", "0"}, {"r_fullbright", "0"},
+ {"r_drawflat", "0"}, {"paused", "0"}, {"fixedtime", "0"},
+ {"sw_draworder", "0"}, {"gl_lightmap", "0"},
+ {"gl_saturatelighting", "0"}, {null, null}};
+ public static final cheatvar_t[] cheatvars;
+ /**
+ * Stop_f
+ * <p>
+ * Stop recording a demo.
+ */
+ static final xcommand_t Stop_f = new xcommand_t() {
+ public void execute() {
+ try {
+
+ int len;
+
+ if (!Globals.clientStaticT.demorecording) {
+ Com.Printf("Not recording a demo.\n");
+ return;
+ }
+
+ // finish up
+ len = -1;
+ Globals.clientStaticT.demofile.writeInt(EndianHandler.swapInt(len));
+ Globals.clientStaticT.demofile.close();
+ Globals.clientStaticT.demofile = null;
+ Globals.clientStaticT.demorecording = false;
+ Com.Printf("Stopped demo.\n");
+ } catch (IOException ignored) {
+ }
+ }
+ };
+ static final entity_state_t nullstate = new entity_state_t(null);
+ /**
+ * Record_f
+ * <p>
+ * record <demoname>
+ * Begins recording a demo from the current position.
+ */
+ static final xcommand_t Record_f = new xcommand_t() {
+ public void execute() {
+ try {
+ String name;
+ byte buf_data[] = new byte[Defines.MAX_MSGLEN];
+ sizebuf_t buf = new sizebuf_t();
+ int i;
+ entity_state_t ent;
+
+ if (Cmd.Argc() != 2) {
+ Com.Printf("record <demoname>\n");
+ return;
+ }
+
+ if (Globals.clientStaticT.demorecording) {
+ Com.Printf("Already recording.\n");
+ return;
+ }
+
+ if (Globals.clientStaticT.state != Defines.ca_active) {
+ Com.Printf("You must be in a level to record.\n");
+ return;
+ }
+
+ //
+ // open the demo file
+ //
+ name = FS.Gamedir() + "/demos/" + Cmd.Argv(1) + ".dm2";
+
+ Com.Printf("recording to " + name + ".\n");
+ FS.CreatePath(name);
+ Globals.clientStaticT.demofile = new RandomAccessFile(name, "rw");
+ if (Globals.clientStaticT.demofile == null) {
+ Com.Printf("ERROR: couldn't open.\n");
+ return;
+ }
+ Globals.clientStaticT.demorecording = true;
+
+ // don't start saving messages until a non-delta compressed
+ // message is received
+ Globals.clientStaticT.demowaiting = true;
+
+ //
+ // write out messages to hold the startup information
+ //
+ SZ.Init(buf, buf_data, Defines.MAX_MSGLEN);
+
+ // send the serverdata
+ MSG.WriteByte(buf, Defines.svc_serverdata);
+ MSG.WriteInt(buf, Defines.PROTOCOL_VERSION);
+ MSG.WriteInt(buf, 0x10000 + Globals.clientStateT.servercount);
+ MSG.WriteByte(buf, 1); // demos are always attract loops
+ MSG.WriteString(buf, Globals.clientStateT.gamedir);
+ MSG.WriteShort(buf, Globals.clientStateT.playernum);
+
+ MSG.WriteString(buf, Globals.clientStateT.configstrings[Defines.CS_NAME]);
+
+ // configstrings
+ for (i = 0; i < Defines.MAX_CONFIGSTRINGS; i++) {
+ if (Globals.clientStateT.configstrings[i].length() > 0) {
+ if (buf.cursize + Globals.clientStateT.configstrings[i].length()
+ + 32 > buf.maxsize) {
+ // write it out
+ Globals.clientStaticT.demofile.writeInt(EndianHandler.swapInt(buf.cursize));
+ Globals.clientStaticT.demofile
+ .write(buf.data, 0, buf.cursize);
+ buf.cursize = 0;
+ }
+
+ MSG.WriteByte(buf, Defines.svc_configstring);
+ MSG.WriteShort(buf, i);
+ MSG.WriteString(buf, Globals.clientStateT.configstrings[i]);
+ }
+
+ }
+
+ // baselines
+ nullstate.clear();
+ for (i = 0; i < Defines.MAX_EDICTS; i++) {
+ ent = Globals.cl_entities[i].baseline;
+ if (ent.modelindex == 0)
+ continue;
+
+ if (buf.cursize + 64 > buf.maxsize) { // write it out
+ Globals.clientStaticT.demofile.writeInt(EndianHandler.swapInt(buf.cursize));
+ Globals.clientStaticT.demofile.write(buf.data, 0, buf.cursize);
+ buf.cursize = 0;
+ }
+
+ MSG.WriteByte(buf, Defines.svc_spawnbaseline);
+ MSG.WriteDeltaEntity(nullstate,
+ Globals.cl_entities[i].baseline, buf, true, true);
+ }
+
+ MSG.WriteByte(buf, Defines.svc_stufftext);
+ MSG.WriteString(buf, "precache\n");
+
+ // write it to the demo file
+ Globals.clientStaticT.demofile.writeInt(EndianHandler.swapInt(buf.cursize));
+ Globals.clientStaticT.demofile.write(buf.data, 0, buf.cursize);
+ // the rest of the demo file will be individual frames
+
+ } catch (IOException ignored) {
+ }
+ }
+ };
+ /**
+ * ForwardToServer_f
+ */
+ static final xcommand_t ForwardToServer_f = new xcommand_t() {
+ public void execute() {
+ if (Globals.clientStaticT.state != Defines.ca_connected
+ && Globals.clientStaticT.state != Defines.ca_active) {
+ Com.Printf("Can't \"" + Cmd.Argv(0) + "\", not connected\n");
+ return;
+ }
+
+ // don't forward the first argument
+ if (Cmd.Argc() > 1) {
+ MSG.WriteByte(Globals.clientStaticT.netchan.message,
+ Defines.clc_stringcmd);
+ SZ.Print(Globals.clientStaticT.netchan.message, Cmd.Args());
+ }
+ }
+ };
+ /**
+ * Pause_f
+ */
+ static final xcommand_t Pause_f = new xcommand_t() {
+ public void execute() {
+ // never pause in multiplayer
+
+ if (Cvar.variableValue("maxclients") > 1
+ || Globals.server_state == 0) {
+ Cvar.setValue("paused", 0);
+ return;
+ }
+
+ Cvar.setValue("paused", Globals.cl_paused.value);
+ }
+ };
+ /**
+ * Connect_f
+ */
+ static final xcommand_t Connect_f = new xcommand_t() {
+ public void execute() {
+ String server;
+
+ if (Cmd.Argc() != 2) {
+ Com.Printf("usage: connect <server>\n");
+ return;
+ }
+
+ if (Globals.server_state != 0) {
+ // if running a local server, kill it and reissue
+ Server.SV_Shutdown("Server quit\n", false);
+ } else {
+ Disconnect();
+ }
+
+ server = Cmd.Argv(1);
+
+ NET.Config(true); // allow remote
+
+ Disconnect();
+
+ Globals.clientStaticT.state = Defines.ca_connecting;
+ //strncpy (cls.servername, server, sizeof(cls.servername)-1);
+ Globals.clientStaticT.servername = server;
+ Globals.clientStaticT.connect_time = -99999;
+ // CL_CheckForResend() will fire immediately
+ }
+ };
+ /**
+ * Rcon_f
+ * <p>
+ * Send the rest of the command line over as an unconnected command.
+ */
+ static final xcommand_t Rcon_f = new xcommand_t() {
+ public void execute() {
+
+ if (Globals.rcon_client_password.string.length() == 0) {
+ Com.Printf("You must set 'rcon_password' before\nissuing an rcon command.\n");
+ return;
+ }
+
+ StringBuilder message = new StringBuilder(1024);
+
+ // connection less packet
+ message.append('\u00ff');
+ message.append('\u00ff');
+ message.append('\u00ff');
+ message.append('\u00ff');
+
+ // allow remote
+ NET.Config(true);
+
+ message.append("rcon ");
+ message.append(Globals.rcon_client_password.string);
+ message.append(" ");
+
+ for (int i = 1; i < Cmd.Argc(); i++) {
+ message.append(Cmd.Argv(i));
+ message.append(" ");
+ }
+
+ NetadrT to = new NetadrT();
+
+ if (Globals.clientStaticT.state >= Defines.ca_connected)
+ to = Globals.clientStaticT.netchan.remote_address;
+ else {
+ if (Globals.rcon_address.string.length() == 0) {
+ Com.Printf("You must either be connected,\nor set the 'rcon_address' cvar\nto issue rcon commands\n");
+ return;
+ }
+ NET.StringToAdr(Globals.rcon_address.string, to);
+ if (to.port == 0) to.port = Defines.PORT_SERVER;
+ }
+ message.append('\0');
+ String b = message.toString();
+ NET.SendPacket(Defines.NS_CLIENT, b.length(), Lib.stringToBytes(b), to);
+ }
+ };
+ /**
+ * Changing_f
+ * <p>
+ * Just sent as a hint to the client that they should drop to full console.
+ */
+ static final xcommand_t Changing_f = new xcommand_t() {
+ public void execute() {
+ //ZOID
+ //if we are downloading, we don't change!
+ // This so we don't suddenly stop downloading a map
+
+ if (Globals.clientStaticT.download != null)
+ return;
+
+ SCR.BeginLoadingPlaque();
+ Globals.clientStaticT.state = Defines.ca_connected; // not active anymore, but
+ // not disconnected
+ Com.Printf("\nChanging map...\n");
+ }
+ };
+ /**
+ * Reconnect_f
+ * <p>
+ * The server is changing levels.
+ */
+ static final xcommand_t Reconnect_f = new xcommand_t() {
+ public void execute() {
+ //ZOID
+ //if we are downloading, we don't change! This so we don't suddenly
+ // stop downloading a map
+ if (Globals.clientStaticT.download != null)
+ return;
+
+ S.StopAllSounds();
+ if (Globals.clientStaticT.state == Defines.ca_connected) {
+ Com.Printf("reconnecting...\n");
+ Globals.clientStaticT.state = Defines.ca_connected;
+ MSG.WriteChar(Globals.clientStaticT.netchan.message,
+ Defines.clc_stringcmd);
+ MSG.WriteString(Globals.clientStaticT.netchan.message, "new");
+ return;
+ }
+
+ if (Globals.clientStaticT.servername != null) {
+ if (Globals.clientStaticT.state >= Defines.ca_connected) {
+ Disconnect();
+ Globals.clientStaticT.connect_time = Globals.clientStaticT.realtime - 1500;
+ } else
+ Globals.clientStaticT.connect_time = -99999; // fire immediately
+
+ Globals.clientStaticT.state = Defines.ca_connecting;
+ Com.Printf("reconnecting...\n");
+ }
+ }
+ };
+ /**
+ * PingServers_f
+ */
+ static final xcommand_t PingServers_f = new xcommand_t() {
+ public void execute() {
+ int i;
+ NetadrT adr = new NetadrT();
+ //char name[32];
+ String name;
+ String adrstring;
+ CvarT noudp;
+ CvarT noipx;
+
+ NET.Config(true); // allow remote
+
+ // send a broadcast packet
+ Com.Printf("pinging broadcast...\n");
+
+ noudp = Cvar.get("noudp", "0", Defines.CVAR_NOSET);
+ if (noudp.value == 0.0f) {
+ adr.type = Defines.NA_BROADCAST;
+ adr.port = Defines.PORT_SERVER;
+ //adr.port = BigShort(PORT_SERVER);
+ Netchan.OutOfBandPrint(Defines.NS_CLIENT, adr, "info "
+ + Defines.PROTOCOL_VERSION);
+ }
+
+ // we use no IPX
+ noipx = Cvar.get("noipx", "1", Defines.CVAR_NOSET);
+ if (noipx.value == 0.0f) {
+ adr.type = Defines.NA_BROADCAST_IPX;
+ //adr.port = BigShort(PORT_SERVER);
+ adr.port = Defines.PORT_SERVER;
+ Netchan.OutOfBandPrint(Defines.NS_CLIENT, adr, "info "
+ + Defines.PROTOCOL_VERSION);
+ }
+
+ // send a packet to each address book entry
+ for (i = 0; i < 16; i++) {
+ //Com_sprintf (name, sizeof(name), "adr%i", i);
+ name = "adr" + i;
+ adrstring = Cvar.variableString(name);
+ if (adrstring == null || adrstring.length() == 0)
+ continue;
+
+ Com.Printf("pinging " + adrstring + "...\n");
+ if (!NET.StringToAdr(adrstring, adr)) {
+ Com.Printf("Bad address: " + adrstring + "\n");
+ continue;
+ }
+ if (adr.port == 0)
+ //adr.port = BigShort(PORT_SERVER);
+ adr.port = Defines.PORT_SERVER;
+ Netchan.OutOfBandPrint(Defines.NS_CLIENT, adr, "info "
+ + Defines.PROTOCOL_VERSION);
+ }
+ }
+ };
+ /**
+ * Skins_f
+ * <p>
+ * Load or download any custom player skins and models.
+ */
+ static final xcommand_t Skins_f = new xcommand_t() {
+ public void execute() {
+ int i;
+
+ for (i = 0; i < Defines.MAX_CLIENTS; i++) {
+ if (Globals.clientStateT.configstrings[Defines.CS_PLAYERSKINS + i] == null)
+ continue;
+ Com.Printf("client " + i + ": "
+ + Globals.clientStateT.configstrings[Defines.CS_PLAYERSKINS + i]
+ + "\n");
+ SCR.UpdateScreen();
+ Sys.SendKeyEvents(); // pump message loop
+ CL_parse.ParseClientinfo(i);
+ }
+ }
+ };
+ /**
+ * Userinfo_f
+ */
+ static final xcommand_t Userinfo_f = new xcommand_t() {
+ public void execute() {
+ Com.Printf("User info settings:\n");
+ Info.Print(Cvar.userinfo());
+ }
+ };
+ /**
+ * Snd_Restart_f
+ * <p>
+ * Restart the sound subsystem so it can pick up new parameters and flush
+ * all sounds.
+ */
+ static final xcommand_t Snd_Restart_f = new xcommand_t() {
+ public void execute() {
+ S.Shutdown();
+ S.Init();
+ CL_parse.RegisterSounds();
+ }
+ };
+ static final String[] env_suf = {"rt", "bk", "lf", "ft", "up", "dn"};
+ static int precache_check; // for autodownload of precache items
+ static int precache_spawncount;
+ static int precache_tex;
+ static int precache_model_skin;
+ static byte precache_model[]; // used for skin checking in alias models
+ /**
+ * The server will send this command right before allowing the client into
+ * the server.
+ */
+ static final xcommand_t Precache_f = new xcommand_t() {
+ public void execute() {
+ // Yet another hack to let old demos work the old precache sequence.
+ if (Cmd.Argc() < 2) {
+
+ int iw[] = {0}; // for detecting cheater maps
+
+ CM.CM_LoadMap(Globals.clientStateT.configstrings[Defines.CS_MODELS + 1],
+ true, iw);
+
+ CL_parse.RegisterSounds();
+ CL_view.PrepRefresh();
+ return;
+ }
+
+ Client.precache_check = Defines.CS_MODELS;
+ Client.precache_spawncount = Lib.atoi(Cmd.Argv(1));
+ Client.precache_model = null;
+ Client.precache_model_skin = 0;
+
+ RequestNextDownload();
+ }
+ };
+ static int numcheatvars;
+ /**
+ * Shutdown
+ * <p>
+ * FIXME: this is a callback from Sys_Quit and Com_Error. It would be better
+ * to run quit through here before the final handoff to the sys code.
+ */
+ static boolean isdown = false;
+ /**
+ * Quit_f
+ */
+ static final xcommand_t Quit_f = new xcommand_t() {
+ public void execute() {
+ Disconnect();
+ Com.Quit();
+ }
+ };
+ static final xcommand_t Disconnect_f = new xcommand_t() {
+ public void execute() {
+ Com.Error(Defines.ERR_DROP, "Disconnected from server");
+ }
+ };
+ private static int extratime;
+
+ static {
+ cheatvars = new cheatvar_t[cheatvarsinfo.length];
+ for (int n = 0; n < cheatvarsinfo.length; n++) {
+ cheatvars[n] = new cheatvar_t();
+ cheatvars[n].name = cheatvarsinfo[n][0];
+ cheatvars[n].value = cheatvarsinfo[n][1];
+ }
+ }
+
+ // ============================================================================
+
+ /**
+ * WriteDemoMessage
+ * <p>
+ * Dumps the current net message, prefixed by the length
+ */
+ static void WriteDemoMessage() {
+ int swlen;
+
+ // the first eight bytes are just packet sequencing stuff
+ swlen = Globals.net_message.cursize - 8;
+
+ try {
+ Globals.clientStaticT.demofile.writeInt(EndianHandler.swapInt(swlen));
+ Globals.clientStaticT.demofile.write(Globals.net_message.data, 8, swlen);
+ } catch (IOException ignored) {
+ }
+
+ }
+
+ /**
+ * SendConnectPacket
+ * <p>
+ * We have gotten a challenge from the server, so try and connect.
+ */
+ static void SendConnectPacket() {
+ NetadrT adr = new NetadrT();
+ int port;
+
+ if (!NET.StringToAdr(Globals.clientStaticT.servername, adr)) {
+ Com.Printf("Bad server address\n");
+ Globals.clientStaticT.connect_time = 0;
+ return;
+ }
+ if (adr.port == 0)
+ adr.port = Defines.PORT_SERVER;
+ // adr.port = BigShort(PORT_SERVER);
+
+ port = (int) Cvar.variableValue("qport");
+ Globals.userinfo_modified = false;
+
+ Netchan.OutOfBandPrint(Defines.NS_CLIENT, adr, "connect "
+ + Defines.PROTOCOL_VERSION + " " + port + " "
+ + Globals.clientStaticT.challenge + " \"" + Cvar.userinfo() + "\"\n");
+ }
+
+ /**
+ * CheckForResend
+ * <p>
+ * Resend a connect message if the last one has timed out.
+ */
+ static void CheckForResend() {
+ NetadrT adr = new NetadrT();
+
+ // if the local server is running and we aren't
+ // then connect
+ if (Globals.clientStaticT.state == Defines.ca_disconnected
+ && Globals.server_state != 0) {
+ Globals.clientStaticT.state = Defines.ca_connecting;
+ Globals.clientStaticT.servername = "localhost";
+ // we don't need a challenge on the localhost
+ SendConnectPacket();
+ return;
+ }
+
+ // resend if we haven't gotten a reply yet
+ if (Globals.clientStaticT.state != Defines.ca_connecting)
+ return;
+
+ if (Globals.clientStaticT.realtime - Globals.clientStaticT.connect_time < 3000)
+ return;
+
+ if (!NET.StringToAdr(Globals.clientStaticT.servername, adr)) {
+ Com.Printf("Bad server address\n");
+ Globals.clientStaticT.state = Defines.ca_disconnected;
+ return;
+ }
+ if (adr.port == 0)
+ adr.port = Defines.PORT_SERVER;
+
+ // for retransmit requests
+ Globals.clientStaticT.connect_time = Globals.clientStaticT.realtime;
+
+ Com.Printf("Connecting to " + Globals.clientStaticT.servername + "...\n");
+
+ Netchan.OutOfBandPrint(Defines.NS_CLIENT, adr, "getchallenge\n");
+ }
+
+ /**
+ * ClearState
+ */
+ static void ClearState() {
+ S.StopAllSounds();
+ CL_fx.ClearEffects();
+ CL_tent.ClearTEnts();
+
+ // wipe the entire cl structure
+
+ Globals.clientStateT = new ClientStateT();
+ for (int i = 0; i < Globals.cl_entities.length; i++) {
+ Globals.cl_entities[i] = new centity_t();
+ }
+
+ SZ.Clear(Globals.clientStaticT.netchan.message);
+ }
+
+ /**
+ * Disconnect
+ * <p>
+ * Goes from a connected state to full screen console state Sends a
+ * disconnect message to the server This is also called on Com_Error, so it
+ * shouldn't cause any errors.
+ */
+ static void Disconnect() {
+
+ String fin;
+
+ if (Globals.clientStaticT.state == Defines.ca_disconnected)
+ return;
+
+ if (Globals.cl_timedemo != null && Globals.cl_timedemo.value != 0.0f) {
+ int time;
+
+ time = Timer.getCurrentTimeMillis() - Globals.clientStateT.timedemo_start;
+ if (time > 0)
+ Com.Printf("%i frames, %3.1f seconds: %3.1f fps\n",
+ new Vargs(3).add(Globals.clientStateT.timedemo_frames).add(
+ time / 1000.0).add(
+ Globals.clientStateT.timedemo_frames * 1000.0 / time));
+ }
+
+ Math3D.vectorClear(Globals.clientStateT.refdef.blend);
+
+ Globals.re.CinematicSetPalette(null);
+
+ Menu.ForceMenuOff();
+
+ Globals.clientStaticT.connect_time = 0;
+
+ SCR.StopCinematic();
+
+ if (Globals.clientStaticT.demorecording)
+ Stop_f.execute();
+
+ // send a disconnect message to the server
+ fin = (char) Defines.clc_stringcmd + "disconnect";
+ Netchan.Transmit(Globals.clientStaticT.netchan, fin.length(), Lib.stringToBytes(fin));
+ Netchan.Transmit(Globals.clientStaticT.netchan, fin.length(), Lib.stringToBytes(fin));
+ Netchan.Transmit(Globals.clientStaticT.netchan, fin.length(), Lib.stringToBytes(fin));
+
+ ClearState();
+
+ // stop download
+ if (Globals.clientStaticT.download != null) {
+ Lib.fclose(Globals.clientStaticT.download);
+ Globals.clientStaticT.download = null;
+ }
+
+ Globals.clientStaticT.state = Defines.ca_disconnected;
+ }
+
+ /**
+ * ParseStatusMessage
+ * <p>
+ * Handle a reply from a ping.
+ */
+ static void ParseStatusMessage() {
+ String s;
+
+ s = MSG.ReadString(Globals.net_message);
+
+ Com.Printf(s + "\n");
+ Menu.AddToServerList(s);
+ }
+
+ /**
+ * ConnectionlessPacket
+ * <p>
+ * Responses to broadcasts, etc
+ */
+ static void ConnectionlessPacket() {
+ String s;
+ String c;
+
+ MSG.BeginReading(Globals.net_message);
+ MSG.ReadLong(Globals.net_message); // skip the -1
+
+ s = MSG.ReadStringLine(Globals.net_message);
+
+ Cmd.TokenizeString(s.toCharArray(), false);
+
+ c = Cmd.Argv(0);
+
+ Com.Println(Globals.net_from.toString() + ": " + c);
+
+ // server connection
+ if (c.equals("client_connect")) {
+ if (Globals.clientStaticT.state == Defines.ca_connected) {
+ Com.Printf("Dup connect received. Ignored.\n");
+ return;
+ }
+ Netchan.Setup(Defines.NS_CLIENT, Globals.clientStaticT.netchan,
+ Globals.net_from, Globals.clientStaticT.quakePort);
+ MSG.WriteChar(Globals.clientStaticT.netchan.message, Defines.clc_stringcmd);
+ MSG.WriteString(Globals.clientStaticT.netchan.message, "new");
+ Globals.clientStaticT.state = Defines.ca_connected;
+ return;
+ }
+
+ // server responding to a status broadcast
+ if (c.equals("info")) {
+ ParseStatusMessage();
+ return;
+ }
+
+ // remote command from gui front end
+ if (c.equals("cmd")) {
+ if (!NET.IsLocalAddress(Globals.net_from)) {
+ Com.Printf("command packet from remote host. Ignored.\n");
+ return;
+ }
+ s = MSG.ReadString(Globals.net_message);
+ CommandBuffer.AddText(s);
+ CommandBuffer.AddText("\n");
+ return;
+ }
+ // print command from somewhere
+ if (c.equals("print")) {
+ s = MSG.ReadString(Globals.net_message);
+ if (s.length() > 0)
+ Com.Printf(s);
+ return;
+ }
+
+ // ping from somewhere
+ if (c.equals("ping")) {
+ Netchan.OutOfBandPrint(Defines.NS_CLIENT, Globals.net_from, "ack");
+ return;
+ }
+
+ // challenge from the server we are connecting to
+ if (c.equals("challenge")) {
+ Globals.clientStaticT.challenge = Lib.atoi(Cmd.Argv(1));
+ SendConnectPacket();
+ return;
+ }
+
+ // echo request from server
+ if (c.equals("echo")) {
+ Netchan.OutOfBandPrint(Defines.NS_CLIENT, Globals.net_from, Cmd
+ .Argv(1));
+ return;
+ }
+
+ Com.Printf("Unknown command.\n");
+ }
+
+ /**
+ * readPackets
+ */
+ static void readPackets() {
+ while (NET.GetPacket(Defines.NS_CLIENT, Globals.net_from,
+ Globals.net_message)) {
+
+ //
+ // remote command packet
+ //
+ if (Globals.net_message.data[0] == -1
+ && Globals.net_message.data[1] == -1
+ && Globals.net_message.data[2] == -1
+ && Globals.net_message.data[3] == -1) {
+ // if (*(int *)net_message.data == -1)
+ ConnectionlessPacket();
+ continue;
+ }
+
+ if (Globals.clientStaticT.state == Defines.ca_disconnected
+ || Globals.clientStaticT.state == Defines.ca_connecting)
+ continue; // dump it if not connected
+
+ if (Globals.net_message.cursize < 8) {
+ Com.Printf(NET.AdrToString(Globals.net_from)
+ + ": Runt packet\n");
+ continue;
+ }
+
+ //
+ // packet from server
+ //
+ if (!NET.CompareAdr(Globals.net_from,
+ Globals.clientStaticT.netchan.remote_address)) {
+ Com.DPrintf(NET.AdrToString(Globals.net_from)
+ + ":sequenced packet without connection\n");
+ continue;
+ }
+ if (!Netchan.Process(Globals.clientStaticT.netchan, Globals.net_message))
+ continue; // wasn't accepted for some reason
+ CL_parse.ParseServerMessage();
+ }
+
+ //
+ // check timeout
+ //
+ if (Globals.clientStaticT.state >= Defines.ca_connected
+ && Globals.clientStaticT.realtime - Globals.clientStaticT.netchan.last_received > Globals.cl_timeout.value * 1000) {
+ if (++Globals.clientStateT.timeoutcount > 5) // timeoutcount saves debugger
+ {
+ Com.Printf("\nServer connection timed out.\n");
+ Disconnect();
+ }
+ } else
+ Globals.clientStateT.timeoutcount = 0;
+ }
+
+ /**
+ * FixUpGender_f
+ */
+ static void FixUpGender() {
+
+ String sk;
+
+ if (Globals.gender_auto.value != 0.0f) {
+
+ if (Globals.gender.modified) {
+ // was set directly, don't override the user
+ Globals.gender.modified = false;
+ return;
+ }
+
+ sk = Globals.skin.string;
+ if (sk.startsWith("male") || sk.startsWith("cyborg"))
+ Cvar.set("gender", "male");
+ else if (sk.startsWith("female") || sk.startsWith("crackhor"))
+ Cvar.set("gender", "female");
+ else
+ Cvar.set("gender", "none");
+ Globals.gender.modified = false;
+ }
+ }
+
+ // =============================================================================
+
+ public static void RequestNextDownload() {
+ int map_checksum = 0; // for detecting cheater maps
+ //char fn[MAX_OSPATH];
+ String fn;
+
+ qfiles.dmdl_t pheader;
+
+ if (Globals.clientStaticT.state != Defines.ca_connected)
+ return;
+
+ if (Server.allow_download.value == 0 && Client.precache_check < ENV_CNT)
+ Client.precache_check = ENV_CNT;
+
+ // ZOID
+ if (Client.precache_check == Defines.CS_MODELS) { // confirm map
+ Client.precache_check = Defines.CS_MODELS + 2; // 0 isn't used
+ if (Server.allow_download_maps.value != 0)
+ if (!CL_parse
+ .CheckOrDownloadFile(Globals.clientStateT.configstrings[Defines.CS_MODELS + 1]))
+ return; // started a download
+ }
+ if (Client.precache_check >= Defines.CS_MODELS
+ && Client.precache_check < Defines.CS_MODELS + Defines.MAX_MODELS) {
+ if (Server.allow_download_models.value != 0) {
+ while (Client.precache_check < Defines.CS_MODELS
+ + Defines.MAX_MODELS
+ && Globals.clientStateT.configstrings[Client.precache_check].length() > 0) {
+ if (Globals.clientStateT.configstrings[Client.precache_check].charAt(0) == '*'
+ || Globals.clientStateT.configstrings[Client.precache_check]
+ .charAt(0) == '#') {
+ Client.precache_check++;
+ continue;
+ }
+ if (Client.precache_model_skin == 0) {
+ if (!CL_parse
+ .CheckOrDownloadFile(Globals.clientStateT.configstrings[Client.precache_check])) {
+ Client.precache_model_skin = 1;
+ return; // started a download
+ }
+ Client.precache_model_skin = 1;
+ }
+
+ // checking for skins in the model
+ if (Client.precache_model == null) {
+
+ Client.precache_model = FS
+ .LoadFile(Globals.clientStateT.configstrings[Client.precache_check]);
+ if (Client.precache_model == null) {
+ Client.precache_model_skin = 0;
+ Client.precache_check++;
+ continue; // couldn't load it
+ }
+ ByteBuffer bb = ByteBuffer.wrap(Client.precache_model);
+ bb.order(ByteOrder.LITTLE_ENDIAN);
+
+ int header = bb.getInt();
+
+ if (header != qfiles.IDALIASHEADER) {
+ // not an alias model
+ FS.FreeFile();
+ Client.precache_model = null;
+ Client.precache_model_skin = 0;
+ Client.precache_check++;
+ continue;
+ }
+ pheader = new qfiles.dmdl_t(ByteBuffer.wrap(
+ Client.precache_model).order(
+ ByteOrder.LITTLE_ENDIAN));
+ if (pheader.version != Defines.ALIAS_VERSION) {
+ Client.precache_check++;
+ Client.precache_model_skin = 0;
+ continue; // couldn't load it
+ }
+ }
+
+ pheader = new qfiles.dmdl_t(ByteBuffer.wrap(
+ Client.precache_model).order(ByteOrder.LITTLE_ENDIAN));
+
+ int num_skins = pheader.num_skins;
+
+ while (Client.precache_model_skin - 1 < num_skins) {
+ //Com.Printf("critical code section because of endian
+ // mess!\n");
+
+ String name = Lib.CtoJava(Client.precache_model,
+ pheader.ofs_skins
+ + (Client.precache_model_skin - 1)
+ * Defines.MAX_SKINNAME,
+ Defines.MAX_SKINNAME * num_skins);
+
+ if (!CL_parse.CheckOrDownloadFile(name)) {
+ Client.precache_model_skin++;
+ return; // started a download
+ }
+ Client.precache_model_skin++;
+ }
+ if (Client.precache_model != null) {
+ FS.FreeFile();
+ Client.precache_model = null;
+ }
+ Client.precache_model_skin = 0;
+ Client.precache_check++;
+ }
+ }
+ Client.precache_check = Defines.CS_SOUNDS;
+ }
+ if (Client.precache_check >= Defines.CS_SOUNDS
+ && Client.precache_check < Defines.CS_SOUNDS + Defines.MAX_SOUNDS) {
+ if (Server.allow_download_sounds.value != 0) {
+ if (Client.precache_check == Defines.CS_SOUNDS)
+ Client.precache_check++; // zero is blank
+ while (Client.precache_check < Defines.CS_SOUNDS
+ + Defines.MAX_SOUNDS
+ && Globals.clientStateT.configstrings[Client.precache_check].length() > 0) {
+ if (Globals.clientStateT.configstrings[Client.precache_check].charAt(0) == '*') {
+ Client.precache_check++;
+ continue;
+ }
+ fn = "sound/"
+ + Globals.clientStateT.configstrings[Client.precache_check++];
+ if (!CL_parse.CheckOrDownloadFile(fn))
+ return; // started a download
+ }
+ }
+ Client.precache_check = Defines.CS_IMAGES;
+ }
+ if (Client.precache_check >= Defines.CS_IMAGES
+ && Client.precache_check < Defines.CS_IMAGES + Defines.MAX_IMAGES) {
+ if (Client.precache_check == Defines.CS_IMAGES)
+ Client.precache_check++; // zero is blank
+
+ while (Client.precache_check < Defines.CS_IMAGES + Defines.MAX_IMAGES
+ && Globals.clientStateT.configstrings[Client.precache_check].length() > 0) {
+ fn = "pics/" + Globals.clientStateT.configstrings[Client.precache_check++]
+ + ".pcx";
+ if (!CL_parse.CheckOrDownloadFile(fn))
+ return; // started a download
+ }
+ Client.precache_check = Defines.CS_PLAYERSKINS;
+ }
+ // skins are special, since a player has three things to download:
+ // model, weapon model and skin
+ // so precache_check is now *3
+ if (Client.precache_check >= Defines.CS_PLAYERSKINS
+ && Client.precache_check < Defines.CS_PLAYERSKINS
+ + Defines.MAX_CLIENTS * Client.PLAYER_MULT) {
+ // precache phase completed
+ Client.precache_check = ENV_CNT;
+ }
+
+ if (Client.precache_check == ENV_CNT) {
+ Client.precache_check = ENV_CNT + 1;
+
+ int iw[] = {map_checksum};
+
+ CM.CM_LoadMap(Globals.clientStateT.configstrings[Defines.CS_MODELS + 1],
+ true, iw);
+ map_checksum = iw[0];
+
+ if ((map_checksum ^ Lib
+ .atoi(Globals.clientStateT.configstrings[Defines.CS_MAPCHECKSUM])) != 0) {
+ Com
+ .Error(
+ Defines.ERR_DROP,
+ "Local map version differs from server: "
+ + map_checksum
+ + " != '"
+ + Globals.clientStateT.configstrings[Defines.CS_MAPCHECKSUM]
+ + "'\n");
+ return;
+ }
+ }
+
+ if (Client.precache_check > ENV_CNT && Client.precache_check < TEXTURE_CNT) {
+ if (Server.allow_download.value != 0
+ && Server.allow_download_maps.value != 0) {
+ while (Client.precache_check < TEXTURE_CNT) {
+ int n = Client.precache_check++ - ENV_CNT - 1;
+
+ if ((n & 1) != 0)
+ fn = "env/" + Globals.clientStateT.configstrings[Defines.CS_SKY]
+ + env_suf[n / 2] + ".pcx";
+ else
+ fn = "env/" + Globals.clientStateT.configstrings[Defines.CS_SKY]
+ + env_suf[n / 2] + ".tga";
+ if (!CL_parse.CheckOrDownloadFile(fn))
+ return; // started a download
+ }
+ }
+ Client.precache_check = TEXTURE_CNT;
+ }
+
+ if (Client.precache_check == TEXTURE_CNT) {
+ Client.precache_check = TEXTURE_CNT + 1;
+ Client.precache_tex = 0;
+ }
+
+ // confirm existance of textures, download any that don't exist
+ if (Client.precache_check == TEXTURE_CNT + 1) {
+ // from qcommon/cmodel.c
+ // extern int numtexinfo;
+ // extern mapsurface_t map_surfaces[];
+
+ if (Server.allow_download.value != 0
+ && Server.allow_download_maps.value != 0) {
+ while (Client.precache_tex < CM.numtexinfo) {
+ //char fn[MAX_OSPATH];
+
+ fn = "textures/" + CM.map_surfaces[Client.precache_tex++].rname
+ + ".wal";
+ if (!CL_parse.CheckOrDownloadFile(fn))
+ return; // started a download
+ }
+ }
+ Client.precache_check = TEXTURE_CNT + 999;
+ }
+
+ // ZOID
+ CL_parse.RegisterSounds();
+ CL_view.PrepRefresh();
+
+ MSG.WriteByte(Globals.clientStaticT.netchan.message, Defines.clc_stringcmd);
+ MSG.WriteString(Globals.clientStaticT.netchan.message, "begin "
+ + Client.precache_spawncount + "\n");
+ }
+
+ /**
+ * InitLocal
+ */
+ public static void InitLocal() {
+ Globals.clientStaticT.state = Defines.ca_disconnected;
+ Globals.clientStaticT.realtime = Timer.getCurrentTimeMillis();
+
+ CL_input.InitInput();
+
+ Cvar.get("adr0", "", Defines.CVAR_ARCHIVE);
+ Cvar.get("adr1", "", Defines.CVAR_ARCHIVE);
+ Cvar.get("adr2", "", Defines.CVAR_ARCHIVE);
+ Cvar.get("adr3", "", Defines.CVAR_ARCHIVE);
+ Cvar.get("adr4", "", Defines.CVAR_ARCHIVE);
+ Cvar.get("adr5", "", Defines.CVAR_ARCHIVE);
+ Cvar.get("adr6", "", Defines.CVAR_ARCHIVE);
+ Cvar.get("adr7", "", Defines.CVAR_ARCHIVE);
+ Cvar.get("adr8", "", Defines.CVAR_ARCHIVE);
+
+ //
+ // register our variables
+ //
+ Globals.cl_stereo_separation = Cvar.get("cl_stereo_separation", "0.4",
+ Defines.CVAR_ARCHIVE);
+ Globals.cl_stereo = Cvar.get("cl_stereo", "0", 0);
+
+ Globals.cl_add_blend = Cvar.get("cl_blend", "1", 0);
+ Globals.cl_add_lights = Cvar.get("cl_lights", "1", 0);
+ Globals.cl_add_particles = Cvar.get("cl_particles", "1", 0);
+ Globals.cl_add_entities = Cvar.get("cl_entities", "1", 0);
+ Globals.cl_gun = Cvar.get("cl_gun", "1", 0);
+ Globals.cl_footsteps = Cvar.get("cl_footsteps", "1", 0);
+ Globals.cl_noskins = Cvar.get("cl_noskins", "0", 0);
+ Globals.cl_autoskins = Cvar.get("cl_autoskins", "0", 0);
+ Globals.cl_predict = Cvar.get("cl_predict", "1", 0);
+
+ Globals.cl_maxfps = Cvar.get("cl_maxfps", "90", 0);
+
+ Globals.cl_upspeed = Cvar.get("cl_upspeed", "200", 0);
+ Globals.cl_forwardspeed = Cvar.get("cl_forwardspeed", "200", 0);
+ Globals.cl_sidespeed = Cvar.get("cl_sidespeed", "200", 0);
+ Globals.cl_yawspeed = Cvar.get("cl_yawspeed", "140", 0);
+ Globals.cl_pitchspeed = Cvar.get("cl_pitchspeed", "150", 0);
+ Globals.cl_anglespeedkey = Cvar.get("cl_anglespeedkey", "1.5", 0);
+
+ Globals.cl_run = Cvar.get("cl_run", "0", Defines.CVAR_ARCHIVE);
+ Globals.lookspring = Cvar.get("lookspring", "0", Defines.CVAR_ARCHIVE);
+ Globals.lookstrafe = Cvar.get("lookstrafe", "0", Defines.CVAR_ARCHIVE);
+ Globals.sensitivity = Cvar
+ .get("sensitivity", "3", Defines.CVAR_ARCHIVE);
+
+ Globals.m_pitch = Cvar.get("m_pitch", "0.022", Defines.CVAR_ARCHIVE);
+ Globals.m_yaw = Cvar.get("m_yaw", "0.022", 0);
+ Globals.m_forward = Cvar.get("m_forward", "1", 0);
+ Globals.m_side = Cvar.get("m_side", "1", 0);
+
+ Globals.cl_shownet = Cvar.get("cl_shownet", "0", 0);
+ Globals.cl_showmiss = Cvar.get("cl_showmiss", "0", 0);
+ Globals.cl_showclamp = Cvar.get("showclamp", "0", 0);
+ Globals.cl_timeout = Cvar.get("cl_timeout", "120", 0);
+ Globals.cl_paused = Cvar.get("paused", "0", 0);
+ Globals.cl_timedemo = Cvar.get("timedemo", "0", 0);
+
+ Globals.rcon_client_password = Cvar.get("rcon_password", "", 0);
+ Globals.rcon_address = Cvar.get("rcon_address", "", 0);
+
+ Globals.cl_lightlevel = Cvar.get("r_lightlevel", "0", 0);
+
+ //
+ // userinfo
+ //
+ Globals.info_password = Cvar.get("password", "", Defines.CVAR_USERINFO);
+ Globals.info_spectator = Cvar.get("spectator", "0",
+ Defines.CVAR_USERINFO);
+ Globals.name = Cvar.get("name", "unnamed", Defines.CVAR_USERINFO
+ | Defines.CVAR_ARCHIVE);
+ Globals.skin = Cvar.get("skin", "male/grunt", Defines.CVAR_USERINFO
+ | Defines.CVAR_ARCHIVE);
+ Globals.rate = Cvar.get("rate", "25000", Defines.CVAR_USERINFO
+ | Defines.CVAR_ARCHIVE); // FIXME
+ Globals.msg = Cvar.get("msg", "1", Defines.CVAR_USERINFO
+ | Defines.CVAR_ARCHIVE);
+ Globals.hand = Cvar.get("hand", "0", Defines.CVAR_USERINFO
+ | Defines.CVAR_ARCHIVE);
+ Globals.fov = Cvar.get("fov", "90", Defines.CVAR_USERINFO
+ | Defines.CVAR_ARCHIVE);
+ Globals.gender = Cvar.get("gender", "male", Defines.CVAR_USERINFO
+ | Defines.CVAR_ARCHIVE);
+ Globals.gender_auto = Cvar
+ .get("gender_auto", "1", Defines.CVAR_ARCHIVE);
+ Globals.gender.modified = false; // clear this so we know when user sets
+ // it manually
+
+ Globals.cl_vwep = Cvar.get("cl_vwep", "1", Defines.CVAR_ARCHIVE);
+
+ //
+ // register our commands
+ //
+ Cmd.AddCommand("cmd", ForwardToServer_f);
+ Cmd.AddCommand("pause", Pause_f);
+ Cmd.AddCommand("pingservers", PingServers_f);
+ Cmd.AddCommand("skins", Skins_f);
+
+ Cmd.AddCommand("userinfo", Userinfo_f);
+ Cmd.AddCommand("snd_restart", Snd_Restart_f);
+
+ Cmd.AddCommand("changing", Changing_f);
+ Cmd.AddCommand("disconnect", Disconnect_f);
+ Cmd.AddCommand("record", Record_f);
+ Cmd.AddCommand("stop", Stop_f);
+
+ Cmd.AddCommand("quit", Quit_f);
+
+ Cmd.AddCommand("connect", Connect_f);
+ Cmd.AddCommand("reconnect", Reconnect_f);
+
+ Cmd.AddCommand("rcon", Rcon_f);
+
+ Cmd.AddCommand("precache", Precache_f);
+
+ Cmd.AddCommand("download", CL_parse.Download_f);
+
+ //
+ // forward to server commands
+ //
+ // the only thing this does is allow command completion
+ // to work -- all unknown commands are automatically
+ // forwarded to the server
+ Cmd.AddCommand("wave", null);
+ Cmd.AddCommand("inven", null);
+ Cmd.AddCommand("kill", null);
+ Cmd.AddCommand("use", null);
+ Cmd.AddCommand("drop", null);
+ Cmd.AddCommand("say", null);
+ Cmd.AddCommand("say_team", null);
+ Cmd.AddCommand("info", null);
+ Cmd.AddCommand("prog", null);
+ Cmd.AddCommand("give", null);
+ Cmd.AddCommand("god", null);
+ Cmd.AddCommand("notarget", null);
+ Cmd.AddCommand("noclip", null);
+ Cmd.AddCommand("invuse", null);
+ Cmd.AddCommand("invprev", null);
+ Cmd.AddCommand("invnext", null);
+ Cmd.AddCommand("invdrop", null);
+ Cmd.AddCommand("weapnext", null);
+ Cmd.AddCommand("weapprev", null);
+
+ }
+
+ /**
+ * WriteConfiguration
+ * <p>
+ * Writes key bindings and archived cvars to config.cfg.
+ */
+ public static void WriteConfiguration() {
+ RandomAccessFile f;
+ String path;
+
+// if (Globals.cls.state == Defines.ca_uninitialized)
+// return;
+
+ path = FS.Gamedir() + "/config.cfg";
+ f = Lib.fopen(path, "rw");
+ if (f == null) {
+ Com.Printf("Couldn't write config.cfg.\n");
+ return;
+ }
+ try {
+ f.seek(0);
+ f.setLength(0);
+ } catch (IOException ignored) {
+ }
+ try {
+ f.writeBytes("// generated by quake, do not modify\n");
+ } catch (IOException ignored) {
+ }
+
+ Key.WriteBindings(f);
+ Lib.fclose(f);
+ Cvar.writeVariables(path);
+ }
+
+ /**
+ * FixCvarCheats
+ */
+ public static void FixCvarCheats() {
+ int i;
+ Client.cheatvar_t var;
+
+ if ("1".equals(Globals.clientStateT.configstrings[Defines.CS_MAXCLIENTS])
+ || 0 == Globals.clientStateT.configstrings[Defines.CS_MAXCLIENTS]
+ .length())
+ return; // single player can cheat
+
+ // find all the cvars if we haven't done it yet
+ if (0 == Client.numcheatvars) {
+ while (Client.cheatvars[Client.numcheatvars].name != null) {
+ Client.cheatvars[Client.numcheatvars].var = Cvar.get(
+ Client.cheatvars[Client.numcheatvars].name,
+ Client.cheatvars[Client.numcheatvars].value, 0);
+ Client.numcheatvars++;
+ }
+ }
+
+ // make sure they are all set to the proper values
+ for (i = 0; i < Client.numcheatvars; i++) {
+ var = Client.cheatvars[i];
+ if (!var.var.string.equals(var.value)) {
+ Cvar.set(var.name, var.value);
+ }
+ }
+ }
+
+ /**
+ * sendCommand
+ */
+ public static void sendCommand() {
+ // get new key events
+ Sys.SendKeyEvents();
+
+ // allow mice or other external controllers to add commands
+ UserInputHandler.Commands();
+
+ // process console commands
+ CommandBuffer.execute();
+
+ // fix any cheating cvars
+ FixCvarCheats();
+
+ // send intentions now
+ CL_input.SendCmd();
+
+ // resend a connection request if necessary
+ CheckForResend();
+ }
+
+ // =============================================================
+
+ public static void doFrame(int msec) {
+
+ if (Globals.dedicated.value != 0)
+ return;
+
+ extratime += msec;
+
+ if (Globals.cl_timedemo.value == 0.0f) {
+ if (Globals.clientStaticT.state == Defines.ca_connected && extratime < 100) {
+ return; // don't flood packets out while connecting
+ }
+ if (extratime < 1000 / Globals.cl_maxfps.value) {
+ return; // framerate is too high
+ }
+ }
+
+ // let the mouse activate or deactivate
+ UserInputHandler.doFrame();
+
+ // decide the simulation time
+ Globals.clientStaticT.frametime = extratime / 1000.0f;
+ Globals.clientStateT.time += extratime;
+ Globals.clientStaticT.realtime = Globals.curtime;
+
+ extratime = 0;
+
+ if (Globals.clientStaticT.frametime > (1.0f / 5))
+ Globals.clientStaticT.frametime = (1.0f / 5);
+
+ // if in the debugger last frame, don't timeout
+ if (msec > 5000)
+ Globals.clientStaticT.netchan.last_received = Timer.getCurrentTimeMillis();
+
+ // fetch results from server
+ readPackets();
+
+ // send a new command message to the server
+ sendCommand();
+
+ // predict all unacknowledged movements
+ CL_pred.predictMovement();
+
+ // allow rendering DLL change
+ VideoDriver.CheckChanges();
+ if (!Globals.clientStateT.refresh_prepped
+ && Globals.clientStaticT.state == Defines.ca_active) {
+ CL_view.PrepRefresh();
+ // force GC after level loading
+ // but not on playing a cinematic
+ if (Globals.clientStateT.cinematictime == 0) System.gc();
+ }
+
+ SCR.UpdateScreen();
+
+ // update audio
+ S.Update(Globals.clientStateT.refdef.vieworg, Globals.clientStateT.v_forward,
+ Globals.clientStateT.v_right, Globals.clientStateT.v_up);
+
+ // advance local effects for next frame
+ CL_fx.RunDLights();
+ CL_fx.RunLightStyles();
+ SCR.RunCinematic();
+ SCR.RunConsole();
+
+ Globals.clientStaticT.framecount++;
+ if (Globals.clientStaticT.state != Defines.ca_active
+ || Globals.clientStaticT.key_dest != Defines.key_game) {
+ try {
+ Thread.sleep(20);
+ } catch (InterruptedException ignored) {
+ }
+ }
+ }
+
+ // private static int lasttimecalled;
+
+ /**
+ * Shutdown
+ */
+ public static void Shutdown() {
+
+ if (isdown) {
+ System.out.print("recursive shutdown\n");
+ return;
+ }
+ isdown = true;
+
+ WriteConfiguration();
+
+ S.Shutdown();
+ UserInputHandler.Shutdown();
+ VideoDriver.Shutdown();
+ }
+
+ /**
+ * Initialize client subsystem.
+ */
+ public static void Init() {
+ if (Globals.dedicated.value != 0.0f)
+ return; // nothing running on the client
+
+ // all archived variables will now be loaded
+
+ Console.Init(); //ok
+
+ S.Init(); //empty
+ VideoDriver.Init();
+
+ V.Init();
+
+ Globals.net_message.data = Globals.net_message_buffer;
+ Globals.net_message.maxsize = Globals.net_message_buffer.length;
+
+ Menu.Init();
+
+ SCR.Init();
+ //Globals.cls.disable_screen = 1.0f; // don't draw yet
+
+ InitLocal();
+ UserInputHandler.Init();
+
+ FS.ExecAutoexec();
+ CommandBuffer.execute();
+ }
+
+ /**
+ * Called after an ERR_DROP was thrown.
+ */
+ public static void Drop() {
+ if (Globals.clientStaticT.state == Defines.ca_uninitialized)
+ return;
+ if (Globals.clientStaticT.state == Defines.ca_disconnected)
+ return;
+
+ Disconnect();
+
+ // drop loading plaque unless this is the initial game start
+ if (Globals.clientStaticT.disable_servercount != -1)
+ SCR.EndLoadingPlaque(); // get rid of loading plaque
+ }
+
+ public static class cheatvar_t {
+ String name;
+
+ String value;
+
+ CvarT var;
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.client;
+
+import lwjake2.render.Image;
+import lwjake2.render.Model;
+
+public class ClientInfo {
+ String name = "";
+ String cinfo = "";
+ Image skin; // ptr
+ Image icon; // ptr
+ String iconname = "";
+ Model model; // ptr
+
+// public void reset()
+// {
+// set(new clientinfo_t());
+// }
+
+ public void set(ClientInfo from) {
+ name = from.name;
+ cinfo = from.cinfo;
+ skin = from.skin;
+ icon = from.icon;
+ iconname = from.iconname;
+ model = from.model;
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.client;
+
+import lwjake2.Defines;
+import lwjake2.game.cmodel_t;
+import lwjake2.game.usercmd_t;
+import lwjake2.render.Image;
+import lwjake2.render.Model;
+import lwjake2.sound.sfx_t;
+
+import java.nio.ByteBuffer;
+
+public class ClientStateT {
+
+ public final frame_t frame = new frame_t(); // received from server
+ // the client maintains its own idea of view angles, which are
+ // sent to the server each frame. It is cleared to 0 upon entering each level.
+ // the server sends a delta each frame which is added to the locally
+ // tracked view angles to account for standing on rotating objects,
+ // and teleport direction changes
+ public final float[] viewangles = {0, 0, 0};
+ public final String[] configstrings = new String[Defines.MAX_CONFIGSTRINGS];
+ public final sfx_t[] sound_precache = new sfx_t[Defines.MAX_SOUNDS];
+ final usercmd_t cmd = new usercmd_t();
+ final usercmd_t[] cmds = new usercmd_t[Defines.CMD_BACKUP]; // each mesage will send several old cmds
+ final int[] cmd_time = new int[Defines.CMD_BACKUP]; // time sent, for calculating pings
+ final short[][] predicted_origins = new short[Defines.CMD_BACKUP][3]; // for debug comparing against server
+ final float[] predicted_origin = {0, 0, 0}; // generated by CL_PredictMovement
+ final float[] predicted_angles = {0, 0, 0};
+ final float[] prediction_error = {0, 0, 0};
+ final frame_t[] frames = new frame_t[Defines.UPDATE_BACKUP];
+ final refdef_t refdef = new refdef_t();
+ final float[] v_forward = {0, 0, 0};
+ //
+ // transient data from server
+ //
+ final float[] v_right = {0, 0, 0};
+ final float[] v_up = {0, 0, 0}; // set when refdef.angles is set
+ final int[] inventory = new int[Defines.MAX_ITEMS];
+ final byte[] cinematicpalette = new byte[768];
+ //
+ // locally derived information from server state
+ //
+ final Model[] model_draw = new Model[Defines.MAX_MODELS];
+ final cmodel_t[] model_clip = new cmodel_t[Defines.MAX_MODELS];
+ final Image[] image_precache = new Image[Defines.MAX_IMAGES];
+ final ClientInfo[] clientinfo = new ClientInfo[Defines.MAX_CLIENTS];
+ final ClientInfo baseclientinfo = new ClientInfo();
+ public boolean refresh_prepped; // false if on new level or new ref dll
+ public boolean sound_prepped; // ambient sounds can start
+ public int time; // this is the time value that the client
+ public int playernum;
+ //
+ // the ClientStateT structure is wiped completely at every
+ // server map change
+ //
+ int timeoutcount;
+ int timedemo_frames;
+ int timedemo_start;
+ boolean force_refdef; // vid has changed, so we can't use a paused refdef
+ int parse_entities; // index (not anded off) into cl_parse_entities[]
+ float predicted_step; // for stair up smoothing
+ int predicted_step_time;
+ int surpressCount; // number of messages rate supressed
+ // is rendering at. always <= cls.realtime
+ float lerpfrac; // between oldframe and frame
+ String layout = ""; // general 2D overlay
+ //
+ // non-gameserver infornamtion
+ // FIXME: move this cinematic stuff into the cin_t structure
+ ByteBuffer cinematic_file;
+ int cinematictime; // cls.realtime for first cinematic frame
+ int cinematicframe;
+ boolean cinematicpalette_active;
+ //
+ // server state information
+ //
+ boolean attractloop; // running the attract loop, any key will menu
+ int servercount; // server identification for prespawns
+ String gamedir = "";
+
+ public ClientStateT() {
+ for (int n = 0; n < Defines.CMD_BACKUP; n++)
+ cmds[n] = new usercmd_t();
+ for (int i = 0; i < frames.length; i++) {
+ frames[i] = new frame_t();
+ }
+
+ for (int n = 0; n < Defines.MAX_CONFIGSTRINGS; n++)
+ configstrings[n] = "";
+
+ for (int n = 0; n < Defines.MAX_CLIENTS; n++)
+ clientinfo[n] = new ClientInfo();
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.client;
+
+import lwjake2.qcommon.netchan_t;
+
+import java.io.RandomAccessFile;
+
+public class ClientStaticT {
+
+ // to work around address translating routers
+ public final netchan_t netchan = new netchan_t();
+ // was enum connstate_t
+ public int state;
+ // was enum keydest_t
+ public int key_dest;
+ public int framecount;
+ public int realtime; // always increasing, no clamping, etc
+ public float frametime; // seconds since last frame
+ // screen rendering information
+ public float disable_screen; // showing loading plaque between levels
+ // > cls.disable_servercount, clear disable_screen
+ // or changing rendering dlls
+ // if time gets > 30 seconds ahead, break it
+ public int disable_servercount; // when we receive a frame and cl.servercount
+ // connection information
+ public String servername = ""; // name of server from original connect
+ public float connect_time; // for connection retransmits
+ public int serverProtocol; // in case we are doing some kind of version hack
+ public int challenge; // from the server to use for connecting
+ public RandomAccessFile download; // file transfer from server
+ public String downloadtempname = "";
+ public String downloadname = "";
+ public int downloadnumber;
+ // was enum dltype_t
+ public int downloadtype;
+ public int downloadpercent;
+ // demo recording info must be here, so it isn't cleared on level change
+ public boolean demorecording;
+ public boolean demowaiting; // don't record until a non-delta message is received
+ public RandomAccessFile demofile;
+ int quakePort; // a 16 bit value that allows quake servers
+}
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.client;
+
+import lwjake2.Defines;
+import lwjake2.Globals;
+import lwjake2.game.Cmd;
+import lwjake2.qcommon.*;
+import lwjake2.util.Lib;
+import lwjake2.util.Vargs;
+
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.util.Arrays;
+
+/**
+ * Console
+ */
+public final class Console extends Globals {
+
+ public static final xcommand_t ToggleConsole_f = new xcommand_t() {
+ public void execute() {
+ SCR.EndLoadingPlaque(); // get rid of loading plaque
+
+ if (Globals.clientStateT.attractloop) {
+ CommandBuffer.AddText("killserver\n");
+ return;
+ }
+
+ if (Globals.clientStaticT.state == Defines.ca_disconnected) {
+ // start the demo loop again
+ CommandBuffer.AddText("d1\n");
+ return;
+ }
+
+ Key.ClearTyping();
+ Console.ClearNotify();
+
+ if (Globals.clientStaticT.key_dest == Defines.key_console) {
+ Menu.ForceMenuOff();
+ Cvar.set("paused", "0");
+ } else {
+ Menu.ForceMenuOff();
+ Globals.clientStaticT.key_dest = Defines.key_console;
+
+ if (Cvar.variableValue("maxclients") == 1
+ && Globals.server_state != 0)
+ Cvar.set("paused", "1");
+ }
+ }
+ };
+
+ public static final xcommand_t Clear_f = new xcommand_t() {
+ public void execute() {
+ Arrays.fill(Globals.con.text, (byte) ' ');
+ }
+ };
+ /*
+ * ================ Con_ToggleChat_f ================
+ */
+ static final xcommand_t ToggleChat_f = new xcommand_t() {
+ public void execute() {
+ Key.ClearTyping();
+
+ if (clientStaticT.key_dest == key_console) {
+ if (clientStaticT.state == ca_active) {
+ Menu.ForceMenuOff();
+ clientStaticT.key_dest = key_game;
+ }
+ } else
+ clientStaticT.key_dest = key_console;
+
+ ClearNotify();
+ }
+ };
+ /*
+ * ================ Con_MessageMode_f ================
+ */
+ static final xcommand_t MessageMode_f = new xcommand_t() {
+ public void execute() {
+ chat_team = false;
+ clientStaticT.key_dest = key_message;
+ }
+ };
+ /*
+ * ================ Con_MessageMode2_f ================
+ */
+ static final xcommand_t MessageMode2_f = new xcommand_t() {
+ public void execute() {
+ chat_team = true;
+ clientStaticT.key_dest = key_message;
+ }
+ };
+ /*
+ * ================ Con_Print
+ *
+ * Handles cursor positioning, line wrapping, etc All console printing must
+ * go through this in order to be logged to disk If no console is visible,
+ * the text will appear at the top of the game window ================
+ */
+ private static int cr;
+ public static final xcommand_t Dump_f = new xcommand_t() {
+ public void execute() {
+
+ int l, x;
+ int line;
+ RandomAccessFile f;
+ byte[] buffer = new byte[1024];
+ String name;
+
+ if (Cmd.Argc() != 2) {
+ Com.Printf("usage: condump <filename>\n");
+ return;
+ }
+
+ //Com_sprintf (name, sizeof(name), "%s/%s.txt", FS_Gamedir(),
+ // Cmd_Argv(1));
+ name = FS.Gamedir() + "/" + Cmd.Argv(1) + ".txt";
+
+ Com.Printf("Dumped console text to " + name + ".\n");
+ FS.CreatePath(name);
+ f = Lib.fopen(name, "rw");
+ if (f == null) {
+ Com.Printf("ERROR: couldn't open.\n");
+ return;
+ }
+
+ // skip empty lines
+ for (l = con.current - con.totallines + 1; l <= con.current; l++) {
+ line = (l % con.totallines) * con.linewidth;
+ for (x = 0; x < con.linewidth; x++)
+ if (con.text[line + x] != ' ')
+ break;
+ if (x != con.linewidth)
+ break;
+ }
+
+ // write the remaining lines
+ buffer[con.linewidth] = 0;
+ for (; l <= con.current; l++) {
+ line = (l % con.totallines) * con.linewidth;
+ //strncpy (buffer, line, con.linewidth);
+ System.arraycopy(con.text, line, buffer, 0, con.linewidth);
+ for (x = con.linewidth - 1; x >= 0; x--) {
+ if (buffer[x] == ' ')
+ buffer[x] = 0;
+ else
+ break;
+ }
+ for (x = 0; buffer[x] != 0; x++)
+ buffer[x] &= 0x7f;
+
+ buffer[x] = '\n';
+ // fprintf (f, "%s\n", buffer);
+ try {
+ f.write(buffer, 0, x + 1);
+ } catch (IOException ignored) {
+ }
+ }
+
+ Lib.fclose(f);
+
+ }
+ };
+
+ /**
+ *
+ */
+ public static void Init() {
+ Globals.con.linewidth = -1;
+
+ CheckResize();
+
+ Com.Printf("Console initialized.\n");
+
+ //
+ // register our commands
+ //
+ Globals.con_notifytime = Cvar.get("con_notifytime", "3", 0);
+
+ Cmd.AddCommand("toggleconsole", ToggleConsole_f);
+ Cmd.AddCommand("togglechat", ToggleChat_f);
+ Cmd.AddCommand("messagemode", MessageMode_f);
+ Cmd.AddCommand("messagemode2", MessageMode2_f);
+ Cmd.AddCommand("clear", Clear_f);
+ Cmd.AddCommand("condump", Dump_f);
+ Globals.con.initialized = true;
+ }
+
+ /**
+ * If the line width has changed, reformat the buffer.
+ */
+ public static void CheckResize() {
+
+ int width = (Globals.viddef.width >> 3) - 2;
+ if (width > Defines.MAXCMDLINE) width = Defines.MAXCMDLINE;
+
+ if (width == Globals.con.linewidth)
+ return;
+
+ if (width < 1) { // video hasn't been initialized yet
+ width = 38;
+ Globals.con.linewidth = width;
+ Globals.con.totallines = Defines.CON_TEXTSIZE
+ / Globals.con.linewidth;
+ Arrays.fill(Globals.con.text, (byte) ' ');
+ } else {
+ int oldwidth = Globals.con.linewidth;
+ Globals.con.linewidth = width;
+ int oldtotallines = Globals.con.totallines;
+ Globals.con.totallines = Defines.CON_TEXTSIZE
+ / Globals.con.linewidth;
+ int numlines = oldtotallines;
+
+ if (Globals.con.totallines < numlines)
+ numlines = Globals.con.totallines;
+
+ int numchars = oldwidth;
+
+ if (Globals.con.linewidth < numchars)
+ numchars = Globals.con.linewidth;
+
+ byte[] tbuf = new byte[Defines.CON_TEXTSIZE];
+ System
+ .arraycopy(Globals.con.text, 0, tbuf, 0,
+ Defines.CON_TEXTSIZE);
+ Arrays.fill(Globals.con.text, (byte) ' ');
+
+ for (int i = 0; i < numlines; i++) {
+ System.arraycopy(tbuf, ((Globals.con.current
+ - i + oldtotallines) % oldtotallines) * oldwidth, Globals.con.text, (Globals.con.totallines - 1 - i) * Globals.con.linewidth, numchars);
+ }
+
+ Console.ClearNotify();
+ }
+
+ Globals.con.current = Globals.con.totallines - 1;
+ Globals.con.display = Globals.con.current;
+ }
+
+ public static void ClearNotify() {
+ int i;
+ for (i = 0; i < Defines.NUM_CON_TIMES; i++)
+ Globals.con.times[i] = 0;
+ }
+
+ static void DrawString(int x, int y, String s) {
+ for (int i = 0; i < s.length(); i++) {
+ Globals.re.DrawChar(x, y, s.charAt(i));
+ x += 8;
+ }
+ }
+
+ static void DrawAltString(int x, int y, String s) {
+ for (int i = 0; i < s.length(); i++) {
+ Globals.re.DrawChar(x, y, s.charAt(i) ^ 0x80);
+ x += 8;
+ }
+ }
+
+ /*
+ * =============== Con_Linefeed ===============
+ */
+ static void Linefeed() {
+ Globals.con.x = 0;
+ if (Globals.con.display == Globals.con.current)
+ Globals.con.display++;
+ Globals.con.current++;
+ int i = (Globals.con.current % Globals.con.totallines)
+ * Globals.con.linewidth;
+ int e = i + Globals.con.linewidth;
+ while (i++ < e)
+ Globals.con.text[i] = ' ';
+ }
+
+ public static void Print(String txt) {
+ int y;
+ int c, l;
+ int mask;
+ int txtpos = 0;
+
+ if (!con.initialized)
+ return;
+
+ if (txt.charAt(0) == 1 || txt.charAt(0) == 2) {
+ mask = 128; // go to colored text
+ txtpos++;
+ } else
+ mask = 0;
+
+ while (txtpos < txt.length()) {
+ c = txt.charAt(txtpos);
+ // count word length
+ for (l = 0; l < con.linewidth && l < (txt.length() - txtpos); l++)
+ if (txt.charAt(l + txtpos) <= ' ')
+ break;
+
+ // word wrap
+ if (l != con.linewidth && (con.x + l > con.linewidth))
+ con.x = 0;
+
+ txtpos++;
+
+ if (cr != 0) {
+ con.current--;
+ cr = 0;
+ }
+
+ if (con.x == 0) {
+ Console.Linefeed();
+ // mark time for transparent overlay
+ if (con.current >= 0)
+ con.times[con.current % NUM_CON_TIMES] = clientStaticT.realtime;
+ }
+
+ switch (c) {
+ case '\n':
+ con.x = 0;
+ break;
+
+ case '\r':
+ con.x = 0;
+ cr = 1;
+ break;
+
+ default: // display character and advance
+ y = con.current % con.totallines;
+ con.text[y * con.linewidth + con.x] = (byte) (c | mask | con.ormask);
+ con.x++;
+ if (con.x >= con.linewidth)
+ con.x = 0;
+ break;
+ }
+ }
+ }
+
+ /*
+ * ============== Con_CenteredPrint ==============
+ */
+ static void CenteredPrint(String text) {
+ int l = text.length();
+ l = (con.linewidth - l) / 2;
+ if (l < 0)
+ l = 0;
+
+ StringBuilder sb = new StringBuilder(1024);
+ for (int i = 0; i < l; i++)
+ sb.append(' ');
+ sb.append(text);
+ sb.append('\n');
+
+ sb.setLength(1024);
+
+ Console.Print(sb.toString());
+ }
+
+ /*
+ * ==============================================================================
+ *
+ * DRAWING
+ *
+ * ==============================================================================
+ */
+
+ /*
+ * ================ Con_DrawInput
+ *
+ * The input line scrolls horizontally if typing goes beyond the right edge
+ * ================
+ */
+ static void DrawInput() {
+ int i;
+ byte[] text;
+
+ if (clientStaticT.key_dest == key_menu)
+ return;
+ if (clientStaticT.key_dest != key_console && clientStaticT.state == ca_active)
+ return; // don't draw anything (always draw if not active)
+
+ text = key_lines[edit_line];
+
+ // add the cursor frame
+ text[key_linepos] = (byte) (10 + (clientStaticT.realtime >> 8 & 1));
+
+ // fill out remainder with spaces
+ for (i = key_linepos + 1; i < con.linewidth; i++)
+ text[i] = ' ';
+
+ // prestep if horizontally scrolling
+ //if (key_linepos >= con.linewidth)
+ // start += 1 + key_linepos - con.linewidth;
+
+ // draw it
+ // y = con.vislines-16;
+
+ for (i = 0; i < con.linewidth; i++)
+ re.DrawChar((i + 1) << 3, con.vislines - 22, text[i]);
+
+ // remove cursor
+ key_lines[edit_line][key_linepos] = 0;
+ }
+
+ /*
+ * ================ Con_DrawNotify
+ *
+ * Draws the last few lines of output transparently over the game top
+ * ================
+ */
+ static void DrawNotify() {
+ int x, v;
+ int text;
+ int i;
+ int time;
+ String s;
+ int skip;
+
+ v = 0;
+ for (i = con.current - NUM_CON_TIMES + 1; i <= con.current; i++) {
+ if (i < 0)
+ continue;
+
+ time = (int) con.times[i % NUM_CON_TIMES];
+ if (time == 0)
+ continue;
+
+ time = clientStaticT.realtime - time;
+ if (time > con_notifytime.value * 1000)
+ continue;
+
+ text = (i % con.totallines) * con.linewidth;
+
+ for (x = 0; x < con.linewidth; x++)
+ re.DrawChar((x + 1) << 3, v, con.text[text + x]);
+
+ v += 8;
+ }
+
+ if (clientStaticT.key_dest == key_message) {
+ if (chat_team) {
+ DrawString(8, v, "say_team:");
+ skip = 11;
+ } else {
+ DrawString(8, v, "say:");
+ skip = 5;
+ }
+
+ s = chat_buffer;
+ if (chat_bufferlen > (viddef.width >> 3) - (skip + 1))
+ s = s.substring(chat_bufferlen
+ - ((viddef.width >> 3) - (skip + 1)));
+
+ for (x = 0; x < s.length(); x++) {
+ re.DrawChar((x + skip) << 3, v, s.charAt(x));
+ }
+ re.DrawChar((x + skip) << 3, v,
+ 10 + ((clientStaticT.realtime >> 8) & 1));
+ v += 8;
+ }
+
+ if (v != 0) {
+ SCR.AddDirtyPoint(0, 0);
+ SCR.AddDirtyPoint(viddef.width - 1, v);
+ }
+ }
+
+ /*
+ * ================ Con_DrawConsole
+ *
+ * Draws the console with the solid background ================
+ */
+ static void DrawConsole(float frac) {
+ int i, j, x, y, n;
+ int rows;
+ int text;
+ int row;
+ int lines;
+ String version;
+
+ lines = (int) (viddef.height * frac);
+ if (lines <= 0)
+ return;
+
+ if (lines > viddef.height)
+ lines = viddef.height;
+
+ // draw the background
+ re.DrawStretchPic(0, -viddef.height + lines, viddef.width,
+ viddef.height, "conback");
+ SCR.AddDirtyPoint(0, 0);
+ SCR.AddDirtyPoint(viddef.width - 1, lines - 1);
+
+ version = Com.sprintf("v%4.2f", new Vargs(1).add(VERSION));
+ for (x = 0; x < 5; x++)
+ re.DrawChar(viddef.width - 44 + x * 8, lines - 12, 128 + version
+ .charAt(x));
+
+ // draw the text
+ con.vislines = lines;
+
+ rows = (lines - 22) >> 3; // rows of text to draw
+
+ y = lines - 30;
+
+ // draw from the bottom up
+ if (con.display != con.current) {
+ // draw arrows to show the buffer is backscrolled
+ for (x = 0; x < con.linewidth; x += 4)
+ re.DrawChar((x + 1) << 3, y, '^');
+
+ y -= 8;
+ rows--;
+ }
+
+ row = con.display;
+ for (i = 0; i < rows; i++, y -= 8, row--) {
+ if (row < 0)
+ break;
+ if (con.current - row >= con.totallines)
+ break; // past scrollback wrap point
+
+ int first = (row % con.totallines) * con.linewidth;
+
+ for (x = 0; x < con.linewidth; x++)
+ re.DrawChar((x + 1) << 3, y, con.text[x + first]);
+ }
+
+ //ZOID
+ // draw the download bar
+ // figure out width
+ if (clientStaticT.download != null) {
+ if ((text = clientStaticT.downloadname.lastIndexOf('/')) != 0)
+ text++;
+ else
+ text = 0;
+
+ x = con.linewidth - ((con.linewidth * 7) / 40);
+ y = x - (clientStaticT.downloadname.length() - text) - 8;
+ i = con.linewidth / 3;
+ StringBuilder dlbar = new StringBuilder(512);
+ if (clientStaticT.downloadname.length() - text > i) {
+ y = x - i - 11;
+ int end = text + i - 1;
+ dlbar.append(clientStaticT.downloadname.substring(text, end));
+ dlbar.append("...");
+ } else {
+ dlbar.append(clientStaticT.downloadname.substring(text));
+ }
+ dlbar.append(": ");
+ dlbar.append((char) 0x80);
+
+ // where's the dot go?
+ if (clientStaticT.downloadpercent == 0)
+ n = 0;
+ else
+ n = y * clientStaticT.downloadpercent / 100;
+
+ for (j = 0; j < y; j++) {
+ if (j == n)
+ dlbar.append((char) 0x83);
+ else
+ dlbar.append((char) 0x81);
+ }
+ dlbar.append((char) 0x82);
+ dlbar.append((clientStaticT.downloadpercent < 10) ? " 0" : " ");
+ dlbar.append(clientStaticT.downloadpercent).append('%');
+ // draw it
+ y = con.vislines - 12;
+ for (i = 0; i < dlbar.length(); i++)
+ re.DrawChar((i + 1) << 3, y, dlbar.charAt(i));
+ }
+ //ZOID
+
+ // draw the input prompt, user text, and cursor if desired
+ DrawInput();
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.client;
+
+import lwjake2.Defines;
+import lwjake2.Globals;
+import lwjake2.game.Cmd;
+import lwjake2.qcommon.Com;
+import lwjake2.qcommon.CommandBuffer;
+import lwjake2.qcommon.Cvar;
+import lwjake2.qcommon.xcommand_t;
+import lwjake2.util.Lib;
+
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.util.Vector;
+
+/**
+ * Key
+ */
+public class Key extends Globals {
+ //
+ // these are the key numbers that should be passed to Key_Event
+ //
+ public static final int K_TAB = 9;
+ public static final int K_ENTER = 13;
+ public static final int K_ESCAPE = 27;
+ public static final int K_SPACE = 32;
+
+ // normal keys should be passed as lowercased ascii
+
+ public static final int K_BACKSPACE = 127;
+ public static final int K_UPARROW = 128;
+ public static final int K_DOWNARROW = 129;
+ public static final int K_LEFTARROW = 130;
+ public static final int K_RIGHTARROW = 131;
+
+ public static final int K_ALT = 132;
+ public static final int K_CTRL = 133;
+ public static final int K_SHIFT = 134;
+ public static final int K_F1 = 135;
+ public static final int K_F2 = 136;
+ public static final int K_F3 = 137;
+ public static final int K_F4 = 138;
+ public static final int K_F5 = 139;
+ public static final int K_F6 = 140;
+ public static final int K_F7 = 141;
+ public static final int K_F8 = 142;
+ public static final int K_F9 = 143;
+ public static final int K_F10 = 144;
+ public static final int K_F11 = 145;
+ public static final int K_F12 = 146;
+ public static final int K_INS = 147;
+ public static final int K_DEL = 148;
+ public static final int K_PGDN = 149;
+ public static final int K_PGUP = 150;
+ public static final int K_HOME = 151;
+ public static final int K_END = 152;
+
+ public static final int K_KP_HOME = 160;
+ public static final int K_KP_UPARROW = 161;
+ public static final int K_KP_PGUP = 162;
+ public static final int K_KP_LEFTARROW = 163;
+ public static final int K_KP_5 = 164;
+ public static final int K_KP_RIGHTARROW = 165;
+ public static final int K_KP_END = 166;
+ public static final int K_KP_DOWNARROW = 167;
+ public static final int K_KP_PGDN = 168;
+ public static final int K_KP_ENTER = 169;
+ public static final int K_KP_INS = 170;
+ public static final int K_KP_DEL = 171;
+ public static final int K_KP_SLASH = 172;
+ public static final int K_KP_MINUS = 173;
+ public static final int K_KP_PLUS = 174;
+
+ public static final int K_PAUSE = 255;
+
+ //
+ // mouse buttons generate virtual keys
+ //
+ public static final int K_MOUSE1 = 200;
+ public static final int K_MOUSE2 = 201;
+ public static final int K_MOUSE3 = 202;
+
+ //
+ // joystick buttons
+ //
+ public static final int K_JOY1 = 203;
+ public static final int K_JOY2 = 204;
+ public static final int K_JOY3 = 205;
+ public static final int K_JOY4 = 206;
+
+ public static final int K_MWHEELDOWN = 239;
+ public static final int K_MWHEELUP = 240;
+ static final int[] key_repeats = new int[256];
+ //static int[] keyshift = new int[256];
+ static final boolean[] menubound = new boolean[256];
+ static final boolean[] consolekeys = new boolean[256];
+ static final String[] keynames = new String[256];
+ public static final xcommand_t Bind_f = new xcommand_t() {
+ public void execute() {
+ Key_Bind_f();
+ }
+ };
+ static final xcommand_t Unbind_f = new xcommand_t() {
+ public void execute() {
+ Key_Unbind_f();
+ }
+ };
+ static final xcommand_t Unbindall_f = new xcommand_t() {
+ public void execute() {
+ Key_Unbindall_f();
+ }
+ };
+ static final xcommand_t Bindlist_f = new xcommand_t() {
+ public void execute() {
+ Key_Bindlist_f();
+ }
+ };
+ static int anykeydown = 0;
+ static int key_waiting;
+ static int history_line = 0;
+ static boolean shift_down = false;
+
+ static {
+ keynames[K_TAB] = "TAB";
+ keynames[K_ENTER] = "ENTER";
+ keynames[K_ESCAPE] = "ESCAPE";
+ keynames[K_SPACE] = "SPACE";
+ keynames[K_BACKSPACE] = "BACKSPACE";
+ keynames[K_UPARROW] = "UPARROW";
+ keynames[K_DOWNARROW] = "DOWNARROW";
+ keynames[K_LEFTARROW] = "LEFTARROW";
+ keynames[K_RIGHTARROW] = "RIGHTARROW";
+ keynames[K_ALT] = "ALT";
+ keynames[K_CTRL] = "CTRL";
+ keynames[K_SHIFT] = "SHIFT";
+
+ keynames[K_F1] = "F1";
+ keynames[K_F2] = "F2";
+ keynames[K_F3] = "F3";
+ keynames[K_F4] = "F4";
+ keynames[K_F5] = "F5";
+ keynames[K_F6] = "F6";
+ keynames[K_F7] = "F7";
+ keynames[K_F8] = "F8";
+ keynames[K_F9] = "F9";
+ keynames[K_F10] = "F10";
+ keynames[K_F11] = "F11";
+ keynames[K_F12] = "F12";
+
+ keynames[K_INS] = "INS";
+ keynames[K_DEL] = "DEL";
+ keynames[K_PGDN] = "PGDN";
+ keynames[K_PGUP] = "PGUP";
+ keynames[K_HOME] = "HOME";
+ keynames[K_END] = "END";
+
+ keynames[K_MOUSE1] = "MOUSE1";
+ keynames[K_MOUSE2] = "MOUSE2";
+ keynames[K_MOUSE3] = "MOUSE3";
+
+ // 00092 {"JOY1", K_JOY1},
+ // 00093 {"JOY2", K_JOY2},
+ // 00094 {"JOY3", K_JOY3},
+ // 00095 {"JOY4", K_JOY4},
+
+ keynames[K_KP_HOME] = "KP_HOME";
+ keynames[K_KP_UPARROW] = "KP_UPARROW";
+ keynames[K_KP_PGUP] = "KP_PGUP";
+ keynames[K_KP_LEFTARROW] = "KP_LEFTARROW";
+ keynames[K_KP_5] = "KP_5";
+ keynames[K_KP_RIGHTARROW] = "KP_RIGHTARROW";
+ keynames[K_KP_END] = "KP_END";
+ keynames[K_KP_DOWNARROW] = "KP_DOWNARROW";
+ keynames[K_KP_PGDN] = "KP_PGDN";
+ keynames[K_KP_ENTER] = "KP_ENTER";
+ keynames[K_KP_INS] = "KP_INS";
+ keynames[K_KP_DEL] = "KP_DEL";
+ keynames[K_KP_SLASH] = "KP_SLASH";
+
+ keynames[K_KP_PLUS] = "KP_PLUS";
+ keynames[K_KP_MINUS] = "KP_MINUS";
+
+ keynames[K_MWHEELUP] = "MWHEELUP";
+ keynames[K_MWHEELDOWN] = "MWHEELDOWN";
+
+ keynames[K_PAUSE] = "PAUSE";
+ keynames[';'] = "SEMICOLON"; // because a raw semicolon seperates commands
+
+ keynames[0] = "NULL";
+ }
+
+ /**
+ *
+ */
+ public static void Init() {
+ for (int i = 0; i < 32; i++) {
+ Globals.key_lines[i][0] = ']';
+ Globals.key_lines[i][1] = 0;
+ }
+ Globals.key_linepos = 1;
+
+ //
+ // init ascii characters in console mode
+ //
+ for (int i = 32; i < 128; i++)
+ consolekeys[i] = true;
+ consolekeys[K_ENTER] = true;
+ consolekeys[K_KP_ENTER] = true;
+ consolekeys[K_TAB] = true;
+ consolekeys[K_LEFTARROW] = true;
+ consolekeys[K_KP_LEFTARROW] = true;
+ consolekeys[K_RIGHTARROW] = true;
+ consolekeys[K_KP_RIGHTARROW] = true;
+ consolekeys[K_UPARROW] = true;
+ consolekeys[K_KP_UPARROW] = true;
+ consolekeys[K_DOWNARROW] = true;
+ consolekeys[K_KP_DOWNARROW] = true;
+ consolekeys[K_BACKSPACE] = true;
+ consolekeys[K_HOME] = true;
+ consolekeys[K_KP_HOME] = true;
+ consolekeys[K_END] = true;
+ consolekeys[K_KP_END] = true;
+ consolekeys[K_PGUP] = true;
+ consolekeys[K_KP_PGUP] = true;
+ consolekeys[K_PGDN] = true;
+ consolekeys[K_KP_PGDN] = true;
+ consolekeys[K_SHIFT] = true;
+ consolekeys[K_INS] = true;
+ consolekeys[K_KP_INS] = true;
+ consolekeys[K_KP_DEL] = true;
+ consolekeys[K_KP_SLASH] = true;
+ consolekeys[K_KP_PLUS] = true;
+ consolekeys[K_KP_MINUS] = true;
+ consolekeys[K_KP_5] = true;
+
+ consolekeys['`'] = false;
+ consolekeys['~'] = false;
+
+// for (int i = 0; i < 256; i++)
+// keyshift[i] = i;
+// for (int i = 'a'; i <= 'z'; i++)
+// keyshift[i] = i - 'a' + 'A';
+// keyshift['1'] = '!';
+// keyshift['2'] = '@';
+// keyshift['3'] = '#';
+// keyshift['4'] = '$';
+// keyshift['5'] = '%';
+// keyshift['6'] = '^';
+// keyshift['7'] = '&';
+// keyshift['8'] = '*';
+// keyshift['9'] = '(';
+// keyshift['0'] = ')';
+// keyshift['-'] = '_';
+// keyshift['='] = '+';
+// keyshift[','] = '<';
+// keyshift['.'] = '>';
+// keyshift['/'] = '?';
+// keyshift[';'] = ':';
+// keyshift['\''] = '"';
+// keyshift['['] = '{';
+// keyshift[']'] = '}';
+// keyshift['`'] = '~';
+// keyshift['\\'] = '|';
+
+ menubound[K_ESCAPE] = true;
+ for (int i = 0; i < 12; i++)
+ menubound[K_F1 + i] = true;
+
+ //
+ // register our functions
+ //
+ Cmd.AddCommand("bind", Key.Bind_f);
+ Cmd.AddCommand("unbind", Key.Unbind_f);
+ Cmd.AddCommand("unbindall", Key.Unbindall_f);
+ Cmd.AddCommand("bindlist", Key.Bindlist_f);
+ }
+
+ public static void ClearTyping() {
+ Globals.key_lines[Globals.edit_line][1] = 0; // clear any typing
+ Globals.key_linepos = 1;
+ }
+
+ /**
+ * Called by the system between frames for both key up and key down events.
+ */
+ public static void Event(int key, boolean down, int time) {
+ String kb;
+ String cmd;
+
+ // hack for modal presses
+ if (key_waiting == -1) {
+ if (down)
+ key_waiting = key;
+ return;
+ }
+
+ // update auto-repeat status
+ if (down) {
+ key_repeats[key]++;
+ if (key_repeats[key] > 1
+ && Globals.clientStaticT.key_dest == Defines.key_game
+ && !(Globals.clientStaticT.state == Defines.ca_disconnected))
+ return; // ignore most autorepeats
+
+ if (key >= 200 && Globals.keybindings[key] == null)
+ Com.Printf(Key.KeynumToString(key) + " is unbound, hit F4 to set.\n");
+ } else {
+ key_repeats[key] = 0;
+ }
+
+ if (key == K_SHIFT)
+ shift_down = down;
+
+ // console key is hardcoded, so the user can never unbind it
+ if (key == '`' || key == '~') {
+ if (!down)
+ return;
+
+ Console.ToggleConsole_f.execute();
+ return;
+ }
+
+ // any key during the attract mode will bring up the menu
+ if (Globals.clientStateT.attractloop && Globals.clientStaticT.key_dest != Defines.key_menu && !(key >= K_F1 && key <= K_F12))
+ key = K_ESCAPE;
+
+ // menu key is hardcoded, so the user can never unbind it
+ if (key == K_ESCAPE) {
+ if (!down)
+ return;
+
+ if (Globals.clientStateT.frame.playerstate.stats[Defines.STAT_LAYOUTS] != 0 && Globals.clientStaticT.key_dest == Defines.key_game) {
+ // put away help computer / inventory
+ CommandBuffer.AddText("cmd putaway\n");
+ return;
+ }
+ switch (Globals.clientStaticT.key_dest) {
+ case Defines.key_message:
+ Key.Message(key);
+ break;
+ case Defines.key_menu:
+ Menu.Keydown(key);
+ break;
+ case Defines.key_game:
+ case Defines.key_console:
+ Menu.Menu_Main_f();
+ break;
+ default:
+ Com.Error(Defines.ERR_FATAL, "Bad cls.key_dest");
+ }
+ return;
+ }
+
+ // track if any key is down for BUTTON_ANY
+ Globals.keydown[key] = down;
+ if (down) {
+ if (key_repeats[key] == 1)
+ Key.anykeydown++;
+ } else {
+ Key.anykeydown--;
+ if (Key.anykeydown < 0)
+ Key.anykeydown = 0;
+ }
+
+ //
+ // key up events only generate commands if the game key binding is
+ // a button command (leading + sign). These will occur even in console mode,
+ // to keep the character from continuing an action started before a console
+ // switch. Button commands include the kenum as a parameter, so multiple
+ // downs can be matched with ups
+ //
+ if (!down) {
+ kb = Globals.keybindings[key];
+ if (kb != null && kb.length() > 0 && kb.charAt(0) == '+') {
+ cmd = "-" + kb.substring(1) + " " + key + " " + time + "\n";
+ CommandBuffer.AddText(cmd);
+ }
+// if (keyshift[key] != key) {
+// kb = Globals.keybindings[keyshift[key]];
+// if (kb != null && kb.length()>0 && kb.charAt(0) == '+') {
+// cmd = "-" + kb.substring(1) + " " + key + " " + time + "\n";
+// CommandBuffer.AddText(cmd);
+// }
+// }
+ return;
+ }
+
+ //
+ // if not a consolekey, send to the interpreter no matter what mode is
+ //
+ if ((Globals.clientStaticT.key_dest == Defines.key_menu && menubound[key])
+ || (Globals.clientStaticT.key_dest == Defines.key_console && !consolekeys[key])
+ || (Globals.clientStaticT.key_dest == Defines.key_game && (Globals.clientStaticT.state == Defines.ca_active || !consolekeys[key]))) {
+ kb = Globals.keybindings[key];
+ if (kb != null) {
+ if (kb.length() > 0 && kb.charAt(0) == '+') {
+ // button commands add keynum and time as a parm
+ cmd = kb + " " + key + " " + time + "\n";
+ CommandBuffer.AddText(cmd);
+ } else {
+ CommandBuffer.AddText(kb + "\n");
+ }
+ }
+ return;
+ }
+
+ if (!down)
+ return; // other systems only care about key down events
+
+// if (shift_down)
+// key = keyshift[key];
+
+ switch (Globals.clientStaticT.key_dest) {
+ case Defines.key_message:
+ Key.Message(key);
+ break;
+ case Defines.key_menu:
+ Menu.Keydown(key);
+ break;
+
+ case Defines.key_game:
+ case Defines.key_console:
+ Key.Console(key);
+ break;
+ default:
+ Com.Error(Defines.ERR_FATAL, "Bad cls.key_dest");
+ }
+ }
+
+ /**
+ * Returns a string (either a single ascii char, or a K_* name) for the
+ * given keynum.
+ */
+ public static String KeynumToString(int keynum) {
+ if (keynum < 0 || keynum > 255)
+ return "<KEY NOT FOUND>";
+ if (keynum > 32 && keynum < 127)
+ return Character.toString((char) keynum);
+
+ if (keynames[keynum] != null)
+ return keynames[keynum];
+
+ return "<UNKNOWN KEYNUM>";
+ }
+
+ /**
+ * Returns a key number to be used to index keybindings[] by looking at
+ * the given string. Single ascii characters return themselves, while
+ * the K_* names are matched up.
+ */
+ static int StringToKeynum(String str) {
+
+ if (str == null)
+ return -1;
+
+ if (str.length() == 1)
+ return str.charAt(0);
+
+ for (int i = 0; i < keynames.length; i++) {
+ if (str.equalsIgnoreCase(keynames[i]))
+ return i;
+ }
+
+ return -1;
+ }
+
+ public static void Message(int key) {
+
+ if (key == K_ENTER || key == K_KP_ENTER) {
+ if (Globals.chat_team)
+ CommandBuffer.AddText("say_team \"");
+ else
+ CommandBuffer.AddText("say \"");
+
+ CommandBuffer.AddText(Globals.chat_buffer);
+ CommandBuffer.AddText("\"\n");
+
+ Globals.clientStaticT.key_dest = Defines.key_game;
+ Globals.chat_buffer = "";
+ return;
+ }
+
+ if (key == K_ESCAPE) {
+ Globals.clientStaticT.key_dest = Defines.key_game;
+ Globals.chat_buffer = "";
+ return;
+ }
+
+ if (key < 32 || key > 127)
+ return; // non printable
+
+ if (key == K_BACKSPACE) {
+ if (Globals.chat_buffer.length() > 2) {
+ Globals.chat_buffer = Globals.chat_buffer.substring(0, Globals.chat_buffer.length() - 2);
+ } else
+ Globals.chat_buffer = "";
+ return;
+ }
+
+ if (Globals.chat_buffer.length() > Defines.MAXCMDLINE)
+ return; // all full
+
+ Globals.chat_buffer += (char) key;
+ }
+
+ /**
+ * Interactive line editing and console scrollback.
+ */
+ public static void Console(int key) {
+
+ switch (key) {
+ case K_KP_SLASH:
+ key = '/';
+ break;
+ case K_KP_MINUS:
+ key = '-';
+ break;
+ case K_KP_PLUS:
+ key = '+';
+ break;
+ case K_KP_HOME:
+ key = '7';
+ break;
+ case K_KP_UPARROW:
+ key = '8';
+ break;
+ case K_KP_PGUP:
+ key = '9';
+ break;
+ case K_KP_LEFTARROW:
+ key = '4';
+ break;
+ case K_KP_5:
+ key = '5';
+ break;
+ case K_KP_RIGHTARROW:
+ key = '6';
+ break;
+ case K_KP_END:
+ key = '1';
+ break;
+ case K_KP_DOWNARROW:
+ key = '2';
+ break;
+ case K_KP_PGDN:
+ key = '3';
+ break;
+ case K_KP_INS:
+ key = '0';
+ break;
+ case K_KP_DEL:
+ key = '.';
+ break;
+ }
+
+ if (key == 'l') {
+ if (Globals.keydown[K_CTRL]) {
+ CommandBuffer.AddText("clear\n");
+ return;
+ }
+ }
+
+ if (key == K_ENTER || key == K_KP_ENTER) {
+ // backslash text are commands, else chat
+ if (Globals.key_lines[Globals.edit_line][1] == '\\' || Globals.key_lines[Globals.edit_line][1] == '/')
+ CommandBuffer.AddText(
+ new String(Globals.key_lines[Globals.edit_line], 2, Lib.strlen(Globals.key_lines[Globals.edit_line]) - 2));
+ else
+ CommandBuffer.AddText(
+ new String(Globals.key_lines[Globals.edit_line], 1, Lib.strlen(Globals.key_lines[Globals.edit_line]) - 1));
+
+
+ CommandBuffer.AddText("\n");
+
+ Com.Printf(new String(Globals.key_lines[Globals.edit_line], 0, Lib.strlen(Globals.key_lines[Globals.edit_line])) + "\n");
+ Globals.edit_line = (Globals.edit_line + 1) & 31;
+ history_line = Globals.edit_line;
+
+ Globals.key_lines[Globals.edit_line][0] = ']';
+ Globals.key_linepos = 1;
+ if (Globals.clientStaticT.state == Defines.ca_disconnected)
+ SCR.UpdateScreen(); // force an update, because the command may take some time
+ return;
+ }
+
+ if (key == K_TAB) {
+ // command completion
+ CompleteCommand();
+ return;
+ }
+
+ if ((key == K_BACKSPACE) || (key == K_LEFTARROW) || (key == K_KP_LEFTARROW) || ((key == 'h') && (Globals.keydown[K_CTRL]))) {
+ if (Globals.key_linepos > 1)
+ Globals.key_linepos--;
+ return;
+ }
+
+ if ((key == K_UPARROW) || (key == K_KP_UPARROW) || ((key == 'p') && Globals.keydown[K_CTRL])) {
+ do {
+ history_line = (history_line - 1) & 31;
+ }
+ while (history_line != Globals.edit_line && Globals.key_lines[history_line][1] == 0);
+ if (history_line == Globals.edit_line)
+ history_line = (Globals.edit_line + 1) & 31;
+ //Lib.strcpy(Globals.key_lines[Globals.edit_line], Globals.key_lines[history_line]);
+ System.arraycopy(Globals.key_lines[history_line], 0, Globals.key_lines[Globals.edit_line], 0, Globals.key_lines[Globals.edit_line].length);
+ Globals.key_linepos = Lib.strlen(Globals.key_lines[Globals.edit_line]);
+ return;
+ }
+
+ if ((key == K_DOWNARROW) || (key == K_KP_DOWNARROW) || ((key == 'n') && Globals.keydown[K_CTRL])) {
+ if (history_line == Globals.edit_line)
+ return;
+ do {
+ history_line = (history_line + 1) & 31;
+ }
+ while (history_line != Globals.edit_line && Globals.key_lines[history_line][1] == 0);
+ if (history_line == Globals.edit_line) {
+ Globals.key_lines[Globals.edit_line][0] = ']';
+ Globals.key_linepos = 1;
+ } else {
+ //Lib.strcpy(Globals.key_lines[Globals.edit_line], Globals.key_lines[history_line]);
+ System.arraycopy(Globals.key_lines[history_line], 0, Globals.key_lines[Globals.edit_line], 0, Globals.key_lines[Globals.edit_line].length);
+ Globals.key_linepos = Lib.strlen(Globals.key_lines[Globals.edit_line]);
+ }
+ return;
+ }
+
+ if (key == K_PGUP || key == K_KP_PGUP) {
+ Globals.con.display -= 2;
+ return;
+ }
+
+ if (key == K_PGDN || key == K_KP_PGDN) {
+ Globals.con.display += 2;
+ if (Globals.con.display > Globals.con.current)
+ Globals.con.display = Globals.con.current;
+ return;
+ }
+
+ if (key == K_HOME || key == K_KP_HOME) {
+ Globals.con.display = Globals.con.current - Globals.con.totallines + 10;
+ return;
+ }
+
+ if (key == K_END || key == K_KP_END) {
+ Globals.con.display = Globals.con.current;
+ return;
+ }
+
+ if (key < 32 || key > 127)
+ return; // non printable
+
+ if (Globals.key_linepos < Defines.MAXCMDLINE - 1) {
+ Globals.key_lines[Globals.edit_line][Globals.key_linepos] = (byte) key;
+ Globals.key_linepos++;
+ Globals.key_lines[Globals.edit_line][Globals.key_linepos] = 0;
+ }
+
+ }
+
+ private static void printCompletions(String type, Vector<String> compl) {
+ Com.Printf(type);
+ for (String aCompl : compl) {
+ Com.Printf(aCompl + " ");
+ }
+ Com.Printf("\n");
+ }
+
+ static void CompleteCommand() {
+
+ int start = 1;
+ if (key_lines[edit_line][start] == '\\' || key_lines[edit_line][start] == '/')
+ start++;
+
+ int end = start;
+ while (key_lines[edit_line][end] != 0) end++;
+
+ String s = new String(key_lines[edit_line], start, end - start);
+
+ Vector<String> cmds = Cmd.CompleteCommand(s);
+ Vector<String> vars = Cvar.completeVariable(s);
+
+ int c = cmds.size();
+ int v = vars.size();
+
+ if ((c + v) > 1) {
+ if (c > 0) printCompletions("\nCommands:\n", cmds);
+ if (v > 0) printCompletions("\nVariables:\n", vars);
+ return;
+ } else if (c == 1) {
+ s = cmds.get(0);
+ } else if (v == 1) {
+ s = vars.get(0);
+ } else return;
+
+ key_lines[edit_line][1] = '/';
+ byte[] bytes = Lib.stringToBytes(s);
+ System.arraycopy(bytes, 0, key_lines[edit_line], 2, bytes.length);
+ key_linepos = bytes.length + 2;
+ key_lines[edit_line][key_linepos++] = ' ';
+ key_lines[edit_line][key_linepos] = 0;
+
+ }
+
+ static void Key_Bind_f() {
+ int c = Cmd.Argc();
+
+ if (c < 2) {
+ Com.Printf("bind <key> [command] : attach a command to a key\n");
+ return;
+ }
+ int b = StringToKeynum(Cmd.Argv(1));
+ if (b == -1) {
+ Com.Printf("\"" + Cmd.Argv(1) + "\" isn't a valid key\n");
+ return;
+ }
+
+ if (c == 2) {
+ if (Globals.keybindings[b] != null)
+ Com.Printf("\"" + Cmd.Argv(1) + "\" = \"" + Globals.keybindings[b] + "\"\n");
+ else
+ Com.Printf("\"" + Cmd.Argv(1) + "\" is not bound\n");
+ return;
+ }
+
+ // copy the rest of the command line
+ StringBuilder cmd = new StringBuilder(); // start out with a null string
+ for (int i = 2; i < c; i++) {
+ cmd.append(Cmd.Argv(i));
+ if (i != (c - 1))
+ cmd.append(" ");
+ }
+
+ SetBinding(b, cmd.toString());
+ }
+
+ static void SetBinding(int keynum, String binding) {
+ if (keynum == -1)
+ return;
+
+ // free old bindings
+ Globals.keybindings[keynum] = null;
+
+ Globals.keybindings[keynum] = binding;
+ }
+
+ static void Key_Unbind_f() {
+
+ if (Cmd.Argc() != 2) {
+ Com.Printf("unbind <key> : remove commands from a key\n");
+ return;
+ }
+
+ int b = Key.StringToKeynum(Cmd.Argv(1));
+ if (b == -1) {
+ Com.Printf("\"" + Cmd.Argv(1) + "\" isn't a valid key\n");
+ return;
+ }
+
+ Key.SetBinding(b, null);
+ }
+
+ static void Key_Unbindall_f() {
+ for (int i = 0; i < 256; i++)
+ Key.SetBinding(i, null);
+ }
+
+ static void Key_Bindlist_f() {
+ for (int i = 0; i < 256; i++)
+ if (Globals.keybindings[i] != null && Globals.keybindings[i].length() != 0)
+ Com.Printf(Key.KeynumToString(i) + " \"" + Globals.keybindings[i] + "\"\n");
+ }
+
+ static void ClearStates() {
+ int i;
+
+ Key.anykeydown = 0;
+
+ for (i = 0; i < 256; i++) {
+ if (keydown[i] || key_repeats[i] != 0)
+ Event(i, false, 0);
+ keydown[i] = false;
+ key_repeats[i] = 0;
+ }
+ }
+
+ public static void WriteBindings(RandomAccessFile f) {
+ for (int i = 0; i < 256; i++)
+ if (keybindings[i] != null && keybindings[i].length() > 0)
+ try {
+ f.writeBytes("bind " + KeynumToString(i) + " \"" + keybindings[i] + "\"\n");
+ } catch (IOException ignored) {
+ }
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.client;
+
+import lwjake2.Defines;
+import lwjake2.Globals;
+import lwjake2.game.EDict;
+import lwjake2.game.EntThinkAdapter;
+import lwjake2.game.GameBase;
+import lwjake2.game.trace_t;
+import lwjake2.util.Math3D;
+
+/**
+ * M
+ */
+public final class M {
+
+ /**
+ * Stops the Flies.
+ */
+ public static final EntThinkAdapter M_FliesOff = new EntThinkAdapter() {
+ public String getID() {
+ return "m_fliesoff";
+ }
+
+ public void think(EDict self) {
+ self.s.effects &= ~Defines.EF_FLIES;
+ self.s.sound = 0;
+ }
+ };
+ /**
+ * Starts the Flies as setting the animation flag in the entity.
+ */
+ public static final EntThinkAdapter M_FliesOn = new EntThinkAdapter() {
+ public String getID() {
+ return "m_flies_on";
+ }
+
+ public void think(EDict self) {
+ if (self.waterlevel != 0)
+ return;
+
+ self.s.effects |= Defines.EF_FLIES;
+ self.s.sound = GameBase.gi.soundindex("infantry/inflies1.wav");
+ self.think = M_FliesOff;
+ self.nextthink = GameBase.level.time + 60;
+ }
+ };
+ public static EntThinkAdapter M_droptofloor = new EntThinkAdapter() {
+ public String getID() {
+ return "m_drop_to_floor";
+ }
+
+ public void think(EDict ent) {
+ float[] end = {0, 0, 0};
+ trace_t trace;
+
+ ent.s.origin[2] += 1;
+ Math3D.vectorCopy(ent.s.origin, end);
+ end[2] -= 256;
+
+ trace = GameBase.gi.trace(ent.s.origin, ent.mins, ent.maxs, end,
+ ent, Defines.MASK_MONSTERSOLID);
+
+ if (trace.fraction == 1 || trace.allsolid)
+ return;
+
+ Math3D.vectorCopy(trace.endpos, ent.s.origin);
+
+ GameBase.gi.linkentity(ent);
+ M.M_CheckGround(ent);
+ M_CatagorizePosition(ent);
+ }
+ };
+ /**
+ * Adds some flies after a random time
+ */
+ public static EntThinkAdapter M_FlyCheck = new EntThinkAdapter() {
+ public String getID() {
+ return "m_fly_check";
+ }
+
+ public void think(EDict self) {
+
+ if (self.waterlevel != 0)
+ return;
+
+ if (Globals.rnd.nextFloat() > 0.5)
+ return;
+
+ self.think = M_FliesOn;
+ self.nextthink = GameBase.level.time + 5 + 10
+ * Globals.rnd.nextFloat();
+ }
+ };
+
+ public static void M_CheckGround(EDict ent) {
+ float[] point = {0, 0, 0};
+ trace_t trace;
+
+ if ((ent.flags & (Defines.FL_SWIM | Defines.FL_FLY)) != 0)
+ return;
+
+ if (ent.velocity[2] > 100) {
+ ent.groundentity = null;
+ return;
+ }
+
+ // if the hull point one-quarter unit down is solid the entity is on
+ // ground
+ point[0] = ent.s.origin[0];
+ point[1] = ent.s.origin[1];
+ point[2] = ent.s.origin[2] - 0.25f;
+
+ trace = GameBase.gi.trace(ent.s.origin, ent.mins, ent.maxs, point, ent,
+ Defines.MASK_MONSTERSOLID);
+
+ // check steepness
+ if (trace.plane.normal[2] < 0.7 && !trace.startsolid) {
+ ent.groundentity = null;
+ return;
+ }
+
+ // ent.groundentity = trace.ent;
+ // ent.groundentity_linkcount = trace.ent.linkcount;
+ // if (!trace.startsolid && !trace.allsolid)
+ // VectorCopy (trace.endpos, ent.s.origin);
+ if (!trace.startsolid && !trace.allsolid) {
+ Math3D.vectorCopy(trace.endpos, ent.s.origin);
+ ent.groundentity = trace.ent;
+ ent.groundentity_linkcount = trace.ent.linkcount;
+ ent.velocity[2] = 0;
+ }
+ }
+
+ /**
+ * Returns false if any part of the bottom of the entity is off an edge that
+ * is not a staircase.
+ */
+
+ public static boolean M_CheckBottom(EDict ent) {
+ float[] mins = {0, 0, 0};
+ float[] maxs = {0, 0, 0};
+ float[] start = {0, 0, 0};
+ float[] stop = {0, 0, 0};
+
+ trace_t trace;
+ int x, y;
+ float mid, bottom;
+
+ Math3D.vectorAdd(ent.s.origin, ent.mins, mins);
+ Math3D.vectorAdd(ent.s.origin, ent.maxs, maxs);
+
+ // if all of the points under the corners are solid world, don't bother
+ // with the tougher checks
+ // the corners must be within 16 of the midpoint
+ start[2] = mins[2] - 1;
+ for (x = 0; x <= 1; x++)
+ for (y = 0; y <= 1; y++) {
+ start[0] = x != 0 ? maxs[0] : mins[0];
+ start[1] = y != 0 ? maxs[1] : mins[1];
+ if (GameBase.gi.pointcontents.pointcontents(start) != Defines.CONTENTS_SOLID) {
+ GameBase.c_no++;
+ //
+ // check it for real...
+ //
+ start[2] = mins[2];
+
+ // the midpoint must be within 16 of the bottom
+ start[0] = stop[0] = (mins[0] + maxs[0]) * 0.5f;
+ start[1] = stop[1] = (mins[1] + maxs[1]) * 0.5f;
+ stop[2] = start[2] - 2 * GameBase.STEPSIZE;
+ trace = GameBase.gi.trace(start, Globals.vec3_origin,
+ Globals.vec3_origin, stop, ent,
+ Defines.MASK_MONSTERSOLID);
+
+ if (trace.fraction == 1.0)
+ return false;
+ mid = bottom = trace.endpos[2];
+
+ // the corners must be within 16 of the midpoint
+ for (x = 0; x <= 1; x++)
+ for (y = 0; y <= 1; y++) {
+ start[0] = stop[0] = x != 0 ? maxs[0] : mins[0];
+ start[1] = stop[1] = y != 0 ? maxs[1] : mins[1];
+
+ trace = GameBase.gi.trace(start,
+ Globals.vec3_origin, Globals.vec3_origin,
+ stop, ent, Defines.MASK_MONSTERSOLID);
+
+ if (trace.fraction != 1.0
+ && trace.endpos[2] > bottom)
+ bottom = trace.endpos[2];
+ if (trace.fraction == 1.0
+ || mid - trace.endpos[2] > GameBase.STEPSIZE)
+ return false;
+ }
+
+ GameBase.c_yes++;
+ return true;
+ }
+ }
+
+ GameBase.c_yes++;
+ return true; // we got out easy
+ }
+
+ /**
+ * M_ChangeYaw.
+ */
+ public static void M_ChangeYaw(EDict ent) {
+ float ideal;
+ float current;
+ float move;
+ float speed;
+
+ current = Math3D.angleMod(ent.s.angles[Defines.YAW]);
+ ideal = ent.ideal_yaw;
+
+ if (current == ideal)
+ return;
+
+ move = ideal - current;
+ speed = ent.yaw_speed;
+ if (ideal > current) {
+ if (move >= 180)
+ move = move - 360;
+ } else {
+ if (move <= -180)
+ move = move + 360;
+ }
+ if (move > 0) {
+ if (move > speed)
+ move = speed;
+ } else {
+ if (move < -speed)
+ move = -speed;
+ }
+
+ ent.s.angles[Defines.YAW] = Math3D.angleMod(current + move);
+ }
+
+
+ public static void M_CatagorizePosition(EDict ent) {
+ float[] point = {0, 0, 0};
+ int cont;
+
+ //
+ // get waterlevel
+ //
+ point[0] = ent.s.origin[0];
+ point[1] = ent.s.origin[1];
+ point[2] = ent.s.origin[2] + ent.mins[2] + 1;
+ cont = GameBase.gi.pointcontents.pointcontents(point);
+
+ if (0 == (cont & Defines.MASK_WATER)) {
+ ent.waterlevel = 0;
+ ent.watertype = 0;
+ return;
+ }
+
+ ent.watertype = cont;
+ ent.waterlevel = 1;
+ point[2] += 26;
+ cont = GameBase.gi.pointcontents.pointcontents(point);
+ if (0 == (cont & Defines.MASK_WATER))
+ return;
+
+ ent.waterlevel = 2;
+ point[2] += 22;
+ cont = GameBase.gi.pointcontents.pointcontents(point);
+ if (0 != (cont & Defines.MASK_WATER))
+ ent.waterlevel = 3;
+ }
+
+
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.client;
+
+import lwjake2.Globals;
+import lwjake2.game.Cmd;
+import lwjake2.game.CvarT;
+import lwjake2.qcommon.*;
+import lwjake2.sound.S;
+import lwjake2.sys.NET;
+import lwjake2.sys.Sys;
+import lwjake2.sys.Timer;
+import lwjake2.util.Lib;
+import lwjake2.util.Math3D;
+import lwjake2.util.QuakeFile;
+
+import java.awt.*;
+import java.io.RandomAccessFile;
+import java.util.Arrays;
+
+/**
+ * Menu
+ */
+
+abstract class keyfunc_t {
+ abstract String execute(int key);
+}
+
+public final class Menu extends Key {
+
+ public final static int MAX_MENU_DEPTH = 8;
+ public final static int MAX_SAVEGAMES = 15;
+ public static final int SLIDER_RANGE = 10;
+ public static final menulayer_t[] m_layers = new menulayer_t[MAX_MENU_DEPTH];
+ static final int NUM_CURSOR_FRAMES = 15;
+ static final String menu_in_sound = "misc/menu1.wav";
+ static final String menu_move_sound = "misc/menu2.wav";
+ // won't disrupt the sound
+ static final String menu_out_sound = "misc/menu3.wav";
+ /*
+ * =======================================================================
+ *
+ * MAIN MENU
+ *
+ * =======================================================================
+ */
+ static final int MAIN_ITEMS = 5;
+ /*
+ * =======================================================================
+ *
+ * MULTIPLAYER MENU
+ *
+ * =======================================================================
+ */
+ static final menuframework_s s_multiplayer_menu = new menuframework_s();
+ static final menuaction_s s_join_network_server_action = new menuaction_s();
+ static final menuaction_s s_start_network_server_action = new menuaction_s();
+ static final menuaction_s s_player_setup_action = new menuaction_s();
+ /*
+ * =======================================================================
+ *
+ * KEYS MENU
+ *
+ * =======================================================================
+ */
+ static final String[][] bindnames = {{"+attack", "attack"},
+ {"weapnext", "next weapon"}, {"+forward", "walk forward"},
+ {"+back", "backpedal"}, {"+left", "turn left"},
+ {"+right", "turn right"}, {"+speed", "run"},
+ {"+moveleft", "step left"}, {"+moveright", "step right"},
+ {"+strafe", "sidestep"}, {"+lookup", "look up"},
+ {"+lookdown", "look down"}, {"centerview", "center view"},
+ {"+mlook", "mouse look"}, {"+klook", "keyboard look"},
+ {"+moveup", "up / jump"}, {"+movedown", "down / crouch"}, {
+
+ "inven", "inventory"}, {"invuse", "use item"},
+ {"invdrop", "drop item"}, {"invprev", "prev item"},
+ {"invnext", "next item"}, {
+
+ "cmd help", "help computer"}, {null, null}};
+ static final menuframework_s s_keys_menu = new menuframework_s();
+ static final menuaction_s s_keys_walk_forward_action = new menuaction_s();
+ static final menuaction_s s_keys_backpedal_action = new menuaction_s();
+ static final menuaction_s s_keys_turn_left_action = new menuaction_s();
+ static final menuaction_s s_keys_turn_right_action = new menuaction_s();
+ static final menuaction_s s_keys_run_action = new menuaction_s();
+ static final menuaction_s s_keys_step_left_action = new menuaction_s();
+ static final menuaction_s s_keys_step_right_action = new menuaction_s();
+ static final menuaction_s s_keys_sidestep_action = new menuaction_s();
+ static final menuaction_s s_keys_look_up_action = new menuaction_s();
+ static final menuaction_s s_keys_look_down_action = new menuaction_s();
+ static final menuaction_s s_keys_center_view_action = new menuaction_s();
+ static final menuaction_s s_keys_mouse_look_action = new menuaction_s();
+ static final menuaction_s s_keys_keyboard_look_action = new menuaction_s();
+ static final menuaction_s s_keys_move_up_action = new menuaction_s();
+ static final menuaction_s s_keys_move_down_action = new menuaction_s();
+ static final menuaction_s s_keys_inventory_action = new menuaction_s();
+ static final menuaction_s s_keys_inv_use_action = new menuaction_s();
+ static final menuaction_s s_keys_inv_drop_action = new menuaction_s();
+ static final menuaction_s s_keys_inv_prev_action = new menuaction_s();
+ static final menuaction_s s_keys_inv_next_action = new menuaction_s();
+ static final menuaction_s s_keys_help_computer_action = new menuaction_s();
+ static final menuframework_s s_options_menu = new menuframework_s();
+ static final menuaction_s s_options_defaults_action = new menuaction_s();
+ static final menuaction_s s_options_customize_options_action = new menuaction_s();
+ static final menuslider_s s_options_sensitivity_slider = new menuslider_s();
+ static final menulist_s s_options_freelook_box = new menulist_s();
+ static final menulist_s s_options_noalttab_box = new menulist_s();
+ static final menulist_s s_options_alwaysrun_box = new menulist_s();
+ static final menulist_s s_options_invertmouse_box = new menulist_s();
+ static final menulist_s s_options_lookspring_box = new menulist_s();
+ static final menulist_s s_options_lookstrafe_box = new menulist_s();
+ static final menulist_s s_options_crosshair_box = new menulist_s();
+ static final menuslider_s s_options_sfxvolume_slider = new menuslider_s();
+ static final menulist_s s_options_joystick_box = new menulist_s();
+ static final menulist_s s_options_cdvolume_box = new menulist_s();
+ static final menulist_s s_options_quality_list = new menulist_s();
+ //static menulist_s s_options_compatibility_list = new menulist_s();
+ static final menuaction_s s_options_console_action = new menuaction_s();
+ static final String[] cd_music_items = {"disabled", "enabled"};
+ static final String[] yesno_names = {"no", "yes"};
+ static final String[] crosshair_names = {"none", "cross", "dot", "angle"};
+ static final String[] creditsIndex = new String[256];
+ static final String[] idcredits = {"+QUAKE II BY ID SOFTWARE", "",
+ "+PROGRAMMING", "John Carmack", "John Cash", "Brian Hook", "",
+ "+JAVA PORT BY BYTONIC", "Carsten Weisse", "Holger Zickner", "Rene Stoeckel", "", "+ART",
+ "Adrian Carmack", "Kevin Cloud", "Paul Steed", "", "+LEVEL DESIGN",
+ "Tim Willits", "American McGee", "Christian Antkow",
+ "Paul Jaquays", "Brandon James", "", "+BIZ", "Todd Hollenshead",
+ "Barrett (Bear) Alexander", "Donna Jackson", "", "",
+ "+SPECIAL THANKS", "Ben Donges for beta testing", "", "", "", "",
+ "", "", "+ADDITIONAL SUPPORT", "", "+LINUX PORT AND CTF",
+ "Dave \"Zoid\" Kirsch", "", "+CINEMATIC SEQUENCES",
+ "Ending Cinematic by Blur Studio - ", "Venice, CA", "",
+ "Environment models for Introduction",
+ "Cinematic by Karl Dolgener", "",
+ "Assistance with environment design", "by Cliff Iwai", "",
+ "+SOUND EFFECTS AND MUSIC",
+ "Sound Design by Soundelux Media Labs.",
+ "Music Composed and Produced by",
+ "Soundelux Media Labs. Special thanks",
+ "to Bill Brown, Tom Ozanich, Brian",
+ "Celano, Jeff Eisner, and The Soundelux", "Players.", "",
+ "\"Level Music\" by Sonic Mayhem", "www.sonicmayhem.com", "",
+ "\"Quake II Theme Song\"", "(C) 1997 Rob Zombie. All Rights",
+ "Reserved.", "", "Track 10 (\"Climb\") by Jer Sypult", "",
+ "Voice of computers by", "Carly Staehlin-Taylor", "",
+ "+THANKS TO ACTIVISION", "+UserInputHandler PARTICULAR:", "", "John Tam",
+ "Steve Rosenthal", "Marty Stratton", "Henk Hartong", "",
+ "Quake II(tm) (C)1997 Id Software, Inc.",
+ "All Rights Reserved. Distributed by",
+ "Activision, Inc. under license.",
+ "Quake II(tm), the Id Software name,",
+ "the \"Q II\"(tm) logo and id(tm)",
+ "logo are trademarks of Id Software,",
+ "Inc. Activision(R) is a registered",
+ "trademark of Activision, Inc. All",
+ "other trademarks and trade names are",
+ "properties of their respective owners.", null};
+ static final String[] xatcredits = {"+QUAKE II MISSION PACK: THE RECKONING",
+ "+BY", "+XATRIX ENTERTAINMENT, INC.", "", "+DESIGN AND DIRECTION",
+ "Drew Markham", "", "+PRODUCED BY", "Greg Goodrich", "",
+ "+PROGRAMMING", "Rafael Paiz", "",
+ "+LEVEL DESIGN / ADDITIONAL GAME DESIGN", "Alex Mayberry", "",
+ "+LEVEL DESIGN", "Mal Blackwell", "Dan Koppel", "",
+ "+ART DIRECTION", "Michael \"Maxx\" Kaufman", "",
+ "+COMPUTER GRAPHICS SUPERVISOR AND",
+ "+CHARACTER ANIMATION DIRECTION", "Barry Dempsey", "",
+ "+SENIOR ANIMATOR AND MODELER", "Jason Hoover", "",
+ "+CHARACTER ANIMATION AND", "+MOTION CAPTURE SPECIALIST",
+ "Amit Doron", "", "+ART", "Claire Praderie-Markham",
+ "Viktor Antonov", "Corky Lehmkuhl", "", "+INTRODUCTION ANIMATION",
+ "Dominique Drozdz", "", "+ADDITIONAL LEVEL DESIGN", "Aaron Barber",
+ "Rhett Baldwin", "", "+3D CHARACTER ANIMATION TOOLS",
+ "Gerry Tyra, SA Technology", "",
+ "+ADDITIONAL EDITOR TOOL PROGRAMMING", "Robert Duffy", "",
+ "+ADDITIONAL PROGRAMMING", "Ryan Feltrin", "",
+ "+PRODUCTION COORDINATOR", "Victoria Sylvester", "",
+ "+SOUND DESIGN", "Gary Bradfield", "", "+MUSIC BY", "Sonic Mayhem",
+ "", "", "", "+SPECIAL THANKS", "+TO",
+ "+OUR FRIENDS AT ID SOFTWARE", "", "John Carmack", "John Cash",
+ "Brian Hook", "Adrian Carmack", "Kevin Cloud", "Paul Steed",
+ "Tim Willits", "Christian Antkow", "Paul Jaquays", "Brandon James",
+ "Todd Hollenshead", "Barrett (Bear) Alexander",
+ "Dave \"Zoid\" Kirsch", "Donna Jackson", "", "", "",
+ "+THANKS TO ACTIVISION", "+UserInputHandler PARTICULAR:", "", "Marty Stratton",
+ "Henk \"The Original Ripper\" Hartong", "Kevin Kraff",
+ "Jamey Gottlieb", "Chris Hepburn", "", "+AND THE GAME TESTERS", "",
+ "Tim Vanlaw", "Doug Jacobs", "Steven Rosenthal", "David Baker",
+ "Chris Campbell", "Aaron Casillas", "Steve Elwell",
+ "Derek Johnstone", "Igor Krinitskiy", "Samantha Lee",
+ "Michael Spann", "Chris Toft", "Juan Valdes", "",
+ "+THANKS TO INTERGRAPH COMPUTER SYTEMS", "+UserInputHandler PARTICULAR:", "",
+ "Michael T. Nicolaou", "", "",
+ "Quake II Mission Pack: The Reckoning",
+ "(tm) (C)1998 Id Software, Inc. All",
+ "Rights Reserved. Developed by Xatrix",
+ "Entertainment, Inc. for Id Software,",
+ "Inc. Distributed by Activision Inc.",
+ "under license. Quake(R) is a",
+ "registered trademark of Id Software,",
+ "Inc. Quake II Mission Pack: The",
+ "Reckoning(tm), Quake II(tm), the Id",
+ "Software name, the \"Q II\"(tm) logo",
+ "and id(tm) logo are trademarks of Id",
+ "Software, Inc. Activision(R) is a",
+ "registered trademark of Activision,",
+ "Inc. Xatrix(R) is a registered",
+ "trademark of Xatrix Entertainment,",
+ "Inc. All other trademarks and trade",
+ "names are properties of their", "respective owners.", null};
+ static final String[] roguecredits = {"+QUAKE II MISSION PACK 2: GROUND ZERO",
+ "+BY", "+ROGUE ENTERTAINMENT, INC.", "", "+PRODUCED BY",
+ "Jim Molinets", "", "+PROGRAMMING", "Peter Mack",
+ "Patrick Magruder", "", "+LEVEL DESIGN", "Jim Molinets",
+ "Cameron Lamprecht", "Berenger Fish", "Robert Selitto",
+ "Steve Tietze", "Steve Thoms", "", "+ART DIRECTION",
+ "Rich Fleider", "", "+ART", "Rich Fleider", "Steve Maines",
+ "Won Choi", "", "+ANIMATION SEQUENCES", "Creat Studios",
+ "Steve Maines", "", "+ADDITIONAL LEVEL DESIGN", "Rich Fleider",
+ "Steve Maines", "Peter Mack", "", "+SOUND", "James Grunke", "",
+ "+GROUND ZERO THEME", "+AND", "+MUSIC BY", "Sonic Mayhem", "",
+ "+VWEP MODELS", "Brent \"Hentai\" Dill", "", "", "",
+ "+SPECIAL THANKS", "+TO", "+OUR FRIENDS AT ID SOFTWARE", "",
+ "John Carmack", "John Cash", "Brian Hook", "Adrian Carmack",
+ "Kevin Cloud", "Paul Steed", "Tim Willits", "Christian Antkow",
+ "Paul Jaquays", "Brandon James", "Todd Hollenshead",
+ "Barrett (Bear) Alexander", "Katherine Anna Kang", "Donna Jackson",
+ "Dave \"Zoid\" Kirsch", "", "", "", "+THANKS TO ACTIVISION",
+ "+UserInputHandler PARTICULAR:", "", "Marty Stratton", "Henk Hartong",
+ "Mitch Lasky", "Steve Rosenthal", "Steve Elwell", "",
+ "+AND THE GAME TESTERS", "", "The Ranger Clan",
+ "Dave \"Zoid\" Kirsch", "Nihilistic Software", "Robert Duffy", "",
+ "And Countless Others", "", "", "",
+ "Quake II Mission Pack 2: Ground Zero",
+ "(tm) (C)1998 Id Software, Inc. All",
+ "Rights Reserved. Developed by Rogue",
+ "Entertainment, Inc. for Id Software,",
+ "Inc. Distributed by Activision Inc.",
+ "under license. Quake(R) is a",
+ "registered trademark of Id Software,",
+ "Inc. Quake II Mission Pack 2: Ground",
+ "Zero(tm), Quake II(tm), the Id",
+ "Software name, the \"Q II\"(tm) logo",
+ "and id(tm) logo are trademarks of Id",
+ "Software, Inc. Activision(R) is a",
+ "registered trademark of Activision,",
+ "Inc. Rogue(R) is a registered",
+ "trademark of Rogue Entertainment,",
+ "Inc. All other trademarks and trade",
+ "names are properties of their", "respective owners.", null};
+ static final menuframework_s s_game_menu = new menuframework_s();
+ static final menuaction_s s_easy_game_action = new menuaction_s();
+ static final menuaction_s s_medium_game_action = new menuaction_s();
+ static final menuaction_s s_hard_game_action = new menuaction_s();
+ static final menuaction_s s_load_game_action = new menuaction_s();
+ static final menuaction_s s_save_game_action = new menuaction_s();
+ static final menuaction_s s_credits_action = new menuaction_s();
+ static final menuseparator_s s_blankline = new menuseparator_s();
+ static final menuframework_s s_savegame_menu = new menuframework_s();
+ static final menuframework_s s_loadgame_menu = new menuframework_s();
+ static final menuaction_s[] s_loadgame_actions = new menuaction_s[MAX_SAVEGAMES];
+ //String m_savestrings[] = new String [MAX_SAVEGAMES][32];
+ static final String[] m_savestrings = new String[MAX_SAVEGAMES];
+ static final boolean[] m_savevalid = new boolean[MAX_SAVEGAMES];
+ /*
+ * =============================================================================
+ *
+ * SAVEGAME MENU
+ *
+ * =============================================================================
+ */
+ //static menuframework_s s_savegame_menu;
+ static final menuaction_s[] s_savegame_actions = new menuaction_s[MAX_SAVEGAMES];
+ static final menuframework_s s_joinserver_menu = new menuframework_s();
+ static final menuseparator_s s_joinserver_server_title = new menuseparator_s();
+ static final menuaction_s s_joinserver_search_action = new menuaction_s();
+ static final menuaction_s s_joinserver_address_book_action = new menuaction_s();
+ static final NetadrT[] local_server_netadr = new NetadrT[MAX_LOCAL_SERVERS];
+ static final String[] local_server_names = new String[MAX_LOCAL_SERVERS]; //[80];
+ static final menuaction_s[] s_joinserver_server_actions = new menuaction_s[MAX_LOCAL_SERVERS];
+ /*
+ * =============================================================================
+ *
+ * START SERVER MENU
+ *
+ * =============================================================================
+ */
+ static final menuframework_s s_startserver_menu = new menuframework_s();
+ static final menuaction_s s_startserver_start_action = new menuaction_s();
+ static final menuaction_s s_startserver_dmoptions_action = new menuaction_s();
+ static final menufield_s s_timelimit_field = new menufield_s();
+ static final menufield_s s_fraglimit_field = new menufield_s();
+ static final menufield_s s_maxclients_field = new menufield_s();
+ static final menufield_s s_hostname_field = new menufield_s();
+ static final menulist_s s_startmap_list = new menulist_s();
+ static final menulist_s s_rules_box = new menulist_s();
+ static final String[] dm_coop_names = {"deathmatch", "cooperative"};
+ static final String[] dm_coop_names_rogue = {"deathmatch", "cooperative", "tag"};
+ static final xcommand_t startServer_MenuDraw = new xcommand_t() {
+ public void execute() {
+ StartServer_MenuDraw();
+ }
+ };
+ static final menuframework_s s_dmoptions_menu = new menuframework_s();
+ static final menulist_s s_friendlyfire_box = new menulist_s();
+ static final menulist_s s_falls_box = new menulist_s();
+ /*
+ * =======================================================================
+ *
+ * VIDEO MENU
+ *
+ * =======================================================================
+ */
+ static final menulist_s s_instant_powerups_box = new menulist_s();
+ static final menulist_s s_powerups_box = new menulist_s();
+ static final menulist_s s_spawn_farthest_box = new menulist_s();
+ static final menulist_s s_teamplay_box = new menulist_s();
+ static final menulist_s s_samelevel_box = new menulist_s();
+ static final menulist_s s_force_respawn_box = new menulist_s();
+ static final menulist_s s_armor_box = new menulist_s();
+ static final menulist_s s_allow_exit_box = new menulist_s();
+ static final menulist_s s_infinite_ammo_box = new menulist_s();
+ static final menulist_s s_fixed_fov_box = new menulist_s();
+ static final menulist_s s_quad_drop_box = new menulist_s();
+ // ROGUE
+ static final menulist_s s_no_mines_box = new menulist_s();
+ /*
+ * =============================================================================
+ *
+ * GAME MENU
+ *
+ * =============================================================================
+ */
+ static final menulist_s s_no_nukes_box = new menulist_s();
+ static final menulist_s s_stack_double_box = new menulist_s();
+ static final menulist_s s_no_spheres_box = new menulist_s();
+ //static String yes_no_names[] = { "no", "yes", 0 };
+ static final String[] teamplay_names = {"disabled", "by skin", "by model"};
+ /*
+ * =============================================================================
+ *
+ * DOWNLOADOPTIONS BOOK MENU
+ *
+ * =============================================================================
+ */
+ static final menuframework_s s_downloadoptions_menu = new menuframework_s();
+ static final menuseparator_s s_download_title = new menuseparator_s();
+ static final menulist_s s_allow_download_box = new menulist_s();
+ static final menulist_s s_allow_download_maps_box = new menulist_s();
+ static final menulist_s s_allow_download_models_box = new menulist_s();
+ static final menulist_s s_allow_download_players_box = new menulist_s();
+ static final menulist_s s_allow_download_sounds_box = new menulist_s();
+ static final String[] yes_no_names = {"no", "yes"};
+ static final menuframework_s s_addressbook_menu = new menuframework_s();
+ static final menufield_s[] s_addressbook_fields = new menufield_s[NUM_ADDRESSBOOK_ENTRIES];
+ /*
+ * =============================================================================
+ *
+ * PLAYER CONFIG MENU
+ *
+ * =============================================================================
+ */
+ static final menuframework_s s_player_config_menu = new menuframework_s();
+ static final menufield_s s_player_name_field = new menufield_s();
+ static final menulist_s s_player_model_box = new menulist_s();
+ /*
+ * =============================================================================
+ *
+ * LOADGAME MENU
+ *
+ * =============================================================================
+ */
+ static final menulist_s s_player_skin_box = new menulist_s();
+ static final menulist_s s_player_handedness_box = new menulist_s();
+ static final menulist_s s_player_rate_box = new menulist_s();
+ static final menuseparator_s s_player_skin_title = new menuseparator_s();
+ static final menuseparator_s s_player_model_title = new menuseparator_s();
+ static final menuseparator_s s_player_hand_title = new menuseparator_s();
+ static final menuseparator_s s_player_rate_title = new menuseparator_s();
+ static final menuaction_s s_player_download_action = new menuaction_s();
+ static final playermodelinfo_s[] s_pmi = new playermodelinfo_s[MAX_PLAYERMODELS];
+ static final int[] rate_tbl = {2500, 3200, 5000, 10000, 25000, 0};
+ static final String[] rate_names = {"28.8 Modem", "33.6 Modem", "Single ISDN",
+ "Dual ISDN/Cable", "T1/LAN", "User defined"};
+ static final String[] handedness = {"right", "left", "center"};
+ // =============================================================================
+ /* Support Routines */
+ private static final entity_t entity = new entity_t();
+ public static int m_menudepth;
+ static int m_main_cursor;
+ static boolean m_entersound; // play after drawing a frame, so caching
+ static xcommand_t m_drawfunc;
+ static keyfunc_t m_keyfunc;
+ static final xcommand_t Menu_Multiplayer = new xcommand_t() {
+ public void execute() {
+ Menu_Multiplayer_f();
+ }
+ };
+ static final xcommand_t Menu_Video = new xcommand_t() {
+ public void execute() {
+ Menu_Video_f();
+ }
+ };
+ static final xcommand_t Menu_LoadGame = new xcommand_t() {
+ public void execute() {
+ Menu_LoadGame_f();
+ }
+ };
+ static final xcommand_t Menu_SaveGame = new xcommand_t() {
+ public void execute() {
+ Menu_SaveGame_f();
+ }
+ };
+ static final xcommand_t Menu_DownloadOptions = new xcommand_t() {
+ public void execute() {
+ Menu_DownloadOptions_f();
+ }
+ };
+ static final xcommand_t Menu_AddressBook = new xcommand_t() {
+ public void execute() {
+ Menu_AddressBook_f();
+ }
+ };
+ static final xcommand_t Menu_Quit = new xcommand_t() {
+ public void execute() {
+ Menu_Quit_f();
+ }
+ };
+ /*
+ * ============= DrawCursor
+ *
+ * Draws an animating cursor with the point at x,y. The pic will extend to
+ * the left of x, and both above and below y. =============
+ */
+ static boolean cached;
+ static xcommand_t Main_Draw = new xcommand_t() {
+ public void execute() {
+ Main_Draw();
+ }
+ };
+ static boolean bind_grab;
+ static final xcommand_t Menu_Keys = new xcommand_t() {
+ public void execute() {
+ Menu_Keys_f();
+ }
+ };
+ /*
+ * =======================================================================
+ *
+ * CONTROLS MENU
+ *
+ * =======================================================================
+ */
+ static CvarT win_noalttab;
+ static String compatibility_items[] = {"max compatibility",
+ "max performance"};
+ static String[] s_labels;
+ static String[] s_drivers;
+ static final xcommand_t Menu_Options = new xcommand_t() {
+ public void execute() {
+ Menu_Options_f();
+ }
+ };
+ /*
+ * =============================================================================
+ *
+ * END GAME MENU
+ *
+ * =============================================================================
+ */
+ static int credits_start_time;
+ static String creditsBuffer;
+ static String credits[] = idcredits;
+ static final xcommand_t Menu_Credits = new xcommand_t() {
+ public void execute() {
+ Menu_Credits_f();
+ }
+ };
+ static int m_game_cursor;
+ static final xcommand_t Menu_Main = new xcommand_t() {
+ public void execute() {
+ Menu_Main_f();
+ }
+ };
+ static final xcommand_t Menu_Game = new xcommand_t() {
+ public void execute() {
+ Menu_Game_f();
+ }
+ };
+ static keyfunc_t Main_Key = new keyfunc_t() {
+ public String execute(int key) {
+ return Main_Key(key);
+ }
+ };
+ static String difficulty_names[] = {"easy", "medium",
+ "fuckin shitty hard"};
+ static int m_num_servers;
+ static final xcommand_t Menu_JoinServer = new xcommand_t() {
+ public void execute() {
+ Menu_JoinServer_f();
+ }
+ };
+ static String mapnames[];
+ static int nummaps;
+ static final keyfunc_t startServer_MenuKey = new keyfunc_t() {
+ public String execute(int key) {
+ return StartServer_MenuKey(key);
+ }
+ };
+ static final xcommand_t Menu_StartServer = new xcommand_t() {
+ public void execute() {
+ Menu_StartServer_f();
+ }
+ };
+ /*
+ * =============================================================================
+ *
+ * DMOPTIONS BOOK MENU
+ *
+ * =============================================================================
+ */
+ static String dmoptions_statusbar; //[128];
+ static final xcommand_t Menu_DMOptions = new xcommand_t() {
+ public void execute() {
+ Menu_DMOptions_f();
+ }
+ };
+ static keyfunc_t AddressBook_MenuKey = new keyfunc_t() {
+ public String execute(int key) {
+ return AddressBook_MenuKey_f(key);
+ }
+ };
+ static xcommand_t AddressBook_MenuDraw = new xcommand_t() {
+ public void execute() {
+ AddressBook_MenuDraw_f();
+ }
+ };
+ static String s_pmnames[] = new String[MAX_PLAYERMODELS];
+ static int s_numplayermodels;
+ static int yaw;
+ static final xcommand_t Menu_PlayerConfig = new xcommand_t() {
+ public void execute() {
+ Menu_PlayerConfig_f();
+ }
+ };
+
+ static {
+ for (int n = 0; n < MAX_SAVEGAMES; n++)
+ s_loadgame_actions[n] = new menuaction_s();
+ }
+
+ static {
+ for (int n = 0; n < MAX_SAVEGAMES; n++)
+ m_savestrings[n] = "";
+ }
+
+ static {
+ for (int n = 0; n < MAX_SAVEGAMES; n++)
+ s_savegame_actions[n] = new menuaction_s();
+
+ }
+
+ // user readable information
+ // network address
+ static {
+ for (int n = 0; n < MAX_LOCAL_SERVERS; n++) {
+ local_server_netadr[n] = new NetadrT();
+ local_server_names[n] = "";
+ s_joinserver_server_actions[n] = new menuaction_s();
+ s_joinserver_server_actions[n].n = n;
+ }
+ }
+
+ static {
+ for (int n = 0; n < NUM_ADDRESSBOOK_ENTRIES; n++)
+ s_addressbook_fields[n] = new menufield_s();
+ }
+
+ int keys_cursor;
+
+ /*
+ * =============================================================================
+ *
+ * JOIN SERVER MENU
+ *
+ * =============================================================================
+ */
+
+ static void Banner(String name) {
+ Dimension dim = new Dimension();
+ Globals.re.DrawGetPicSize(dim, name);
+
+ Globals.re.DrawPic(viddef.width / 2 - dim.width / 2,
+ viddef.height / 2 - 110, name);
+ }
+
+ static void PushMenu(xcommand_t draw, keyfunc_t key) { //, String(*key)
+ // (int k) ) {
+ int i;
+
+ if (Cvar.variableValue("maxclients") == 1 && Globals.server_state != 0)
+ Cvar.set("paused", "1");
+
+ // if this menu is already present, drop back to that level
+ // to avoid stacking menus by hotkeys
+ for (i = 0; i < m_menudepth; i++)
+ if (m_layers[i].draw == draw && m_layers[i].key == key) {
+ m_menudepth = i;
+ }
+
+ if (i == m_menudepth) {
+ if (m_menudepth >= MAX_MENU_DEPTH)
+ Com.Error(ERR_FATAL, "PushMenu: MAX_MENU_DEPTH");
+
+ m_layers[m_menudepth].draw = draw;//m_drawfunc;
+ m_layers[m_menudepth].key = key;//m_keyfunc;
+ }
+ m_menudepth++;
+ m_drawfunc = draw;
+ m_keyfunc = key;
+
+ m_entersound = true;
+
+ clientStaticT.key_dest = key_menu;
+ }
+
+ static void ForceMenuOff() {
+ m_drawfunc = null;
+ m_keyfunc = null;
+ clientStaticT.key_dest = key_game;
+ m_menudepth = 0;
+ Key.ClearStates();
+ Cvar.set("paused", "0");
+ }
+
+ static void PopMenu() {
+ S.StartLocalSound(menu_out_sound);
+ m_menudepth--;
+ if (m_menudepth < 0)
+ Com.Error(ERR_FATAL, "PopMenu: depth < 1");
+
+ if (0 < m_menudepth) {
+ m_drawfunc = m_layers[m_menudepth - 1].draw;
+ m_keyfunc = m_layers[m_menudepth - 1].key;
+ }
+
+ if (0 == m_menudepth)
+ ForceMenuOff();
+
+
+ }
+
+ static String Default_MenuKey(menuframework_s m, int key) {
+ String sound = null;
+ menucommon_s item;
+
+ if (m != null) {
+ if ((item = Menu_ItemAtCursor(m)) != null) {
+ if (item.type == MTYPE_FIELD) {
+ if (Field_Key((menufield_s) item, key))
+ return null;
+ }
+ }
+ }
+
+ switch (key) {
+ case K_ESCAPE:
+ PopMenu();
+ return menu_out_sound;
+ case K_KP_UPARROW:
+ case K_UPARROW:
+ if (m != null) {
+ m.cursor--;
+ Menu_AdjustCursor(m, -1);
+ sound = menu_move_sound;
+ }
+ break;
+ case K_TAB:
+ if (m != null) {
+ m.cursor++;
+ Menu_AdjustCursor(m, 1);
+ sound = menu_move_sound;
+ }
+ break;
+ case K_KP_DOWNARROW:
+ case K_DOWNARROW:
+ if (m != null) {
+ m.cursor++;
+ Menu_AdjustCursor(m, 1);
+ sound = menu_move_sound;
+ }
+ break;
+ case K_KP_LEFTARROW:
+ case K_LEFTARROW:
+ if (m != null) {
+ Menu_SlideItem(m, -1);
+ sound = menu_move_sound;
+ }
+ break;
+ case K_KP_RIGHTARROW:
+ case K_RIGHTARROW:
+ if (m != null) {
+ Menu_SlideItem(m, 1);
+ sound = menu_move_sound;
+ }
+ break;
+
+ case K_MOUSE1:
+ case K_MOUSE2:
+ case K_MOUSE3:
+ case K_JOY1:
+ case K_JOY2:
+ case K_JOY3:
+ case K_JOY4:
+ /*
+ * case K_AUX1 : case K_AUX2 : case K_AUX3 : case K_AUX4 : case K_AUX5 :
+ * case K_AUX6 : case K_AUX7 : case K_AUX8 : case K_AUX9 : case K_AUX10 :
+ * case K_AUX11 : case K_AUX12 : case K_AUX13 : case K_AUX14 : case
+ * K_AUX15 : case K_AUX16 : case K_AUX17 : case K_AUX18 : case K_AUX19 :
+ * case K_AUX20 : case K_AUX21 : case K_AUX22 : case K_AUX23 : case
+ * K_AUX24 : case K_AUX25 : case K_AUX26 : case K_AUX27 : case K_AUX28 :
+ * case K_AUX29 : case K_AUX30 : case K_AUX31 : case K_AUX32 :
+ */
+ case K_KP_ENTER:
+ case K_ENTER:
+ if (m != null)
+ Menu_SelectItem(m);
+ sound = menu_move_sound;
+ break;
+ }
+
+ return sound;
+ }
+
+ /*
+ * ================ DrawCharacter
+ *
+ * Draws one solid graphics character cx and cy are in 320*240 coordinates,
+ * and will be centered on higher res screens. ================
+ */
+ public static void DrawCharacter(int cx, int cy, int num) {
+ re.DrawChar(cx + ((viddef.width - 320) >> 1), cy
+ + ((viddef.height - 240) >> 1), num);
+ }
+
+ public static void Print(int cx, int cy, String str) {
+ //while (*str)
+ for (int n = 0; n < str.length(); n++) {
+ DrawCharacter(cx, cy, str.charAt(n) + 128);
+ //str++;
+ cx += 8;
+ }
+ }
+
+ public static void PrintWhite(int cx, int cy, String str) {
+ for (int n = 0; n < str.length(); n++) {
+ DrawCharacter(cx, cy, str.charAt(n));
+ //str++;
+ cx += 8;
+ }
+ }
+
+ public static void DrawPic(int x, int y, String pic) {
+ re.DrawPic(x + ((viddef.width - 320) >> 1), y
+ + ((viddef.height - 240) >> 1), pic);
+ }
+
+ static void DrawCursor(int x, int y, int f) {
+ //char cursorname[80];
+ String cursorname;
+
+ assert (f >= 0) : "negative time and cursor bug";
+
+ f = Math.abs(f);
+
+ if (!cached) {
+ int i;
+
+ for (i = 0; i < NUM_CURSOR_FRAMES; i++) {
+ cursorname = "m_cursor" + i;
+
+ re.RegisterPic(cursorname);
+ }
+ cached = true;
+ }
+
+ cursorname = "m_cursor" + f;
+ re.DrawPic(x, y, cursorname);
+ }
+
+ public static void DrawTextBox(int x, int y, int width, int lines) {
+ int cx, cy;
+ int n;
+
+ // draw left side
+ cx = x;
+ cy = y;
+ DrawCharacter(cx, cy, 1);
+
+ for (n = 0; n < lines; n++) {
+ cy += 8;
+ DrawCharacter(cx, cy, 4);
+ }
+ DrawCharacter(cx, cy + 8, 7);
+
+ // draw middle
+ cx += 8;
+ while (width > 0) {
+ cy = y;
+ DrawCharacter(cx, cy, 2);
+
+ for (n = 0; n < lines; n++) {
+ cy += 8;
+ DrawCharacter(cx, cy, 5);
+ }
+ DrawCharacter(cx, cy + 8, 8);
+
+ width -= 1;
+ cx += 8;
+ }
+
+ // draw right side
+ cy = y;
+ DrawCharacter(cx, cy, 3);
+ for (n = 0; n < lines; n++) {
+ cy += 8;
+ DrawCharacter(cx, cy, 6);
+
+ }
+ DrawCharacter(cx, cy + 8, 9);
+
+ }
+
+ static void Main_Draw() {
+ int i;
+ int w, h;
+ int ystart;
+ int xoffset;
+ int widest = -1;
+ String litname;
+ String[] names = {"m_main_game", "m_main_multiplayer",
+ "m_main_options", "m_main_video", "m_main_quit"};
+ Dimension dim = new Dimension();
+
+ for (i = 0; i < names.length; i++) {
+ Globals.re.DrawGetPicSize(dim, names[i]);
+ w = dim.width;
+ h = dim.height;
+
+ if (w > widest)
+ widest = w;
+ }
+
+ ystart = (Globals.viddef.height / 2 - 110);
+ xoffset = (Globals.viddef.width - widest + 70) / 2;
+
+ for (i = 0; i < names.length; i++) {
+ if (i != m_main_cursor)
+ Globals.re.DrawPic(xoffset, ystart + i * 40 + 13, names[i]);
+ }
+
+ //strcat(litname, "_sel");
+ litname = names[m_main_cursor] + "_sel";
+ Globals.re.DrawPic(xoffset, ystart + m_main_cursor * 40 + 13, litname);
+
+ DrawCursor(xoffset - 25, ystart + m_main_cursor * 40 + 11,
+ (Globals.clientStaticT.realtime / 100) % NUM_CURSOR_FRAMES);
+
+ Globals.re.DrawGetPicSize(dim, "m_main_plaque");
+ w = dim.width;
+ h = dim.height;
+ Globals.re.DrawPic(xoffset - 30 - w, ystart, "m_main_plaque");
+
+ Globals.re.DrawPic(xoffset - 30 - w, ystart + h + 5, "m_main_logo");
+ }
+
+ static String Main_Key(int key) {
+ String sound = menu_move_sound;
+
+ switch (key) {
+ case Key.K_ESCAPE:
+ PopMenu();
+ break;
+
+ case Key.K_KP_DOWNARROW:
+ case Key.K_DOWNARROW:
+ if (++m_main_cursor >= MAIN_ITEMS)
+ m_main_cursor = 0;
+ return sound;
+
+ case Key.K_KP_UPARROW:
+ case Key.K_UPARROW:
+ if (--m_main_cursor < 0)
+ m_main_cursor = MAIN_ITEMS - 1;
+ return sound;
+
+ case Key.K_KP_ENTER:
+ case Key.K_ENTER:
+ m_entersound = true;
+
+ switch (m_main_cursor) {
+ case 0:
+ Menu_Game_f();
+ break;
+
+ case 1:
+ Menu_Multiplayer_f();
+ break;
+
+ case 2:
+ Menu_Options_f();
+ break;
+
+ case 3:
+ Menu_Video_f();
+ break;
+
+ case 4:
+ Menu_Quit_f();
+ break;
+ }
+ }
+
+ return null;
+ }
+
+ static void Menu_Main_f() {
+ PushMenu(new xcommand_t() {
+ public void execute() {
+ Main_Draw();
+ }
+ }, new keyfunc_t() {
+ public String execute(int key) {
+ return Main_Key(key);
+ }
+ });
+ }
+
+ static void Multiplayer_MenuDraw() {
+ Banner("m_banner_multiplayer");
+
+ Menu_AdjustCursor(s_multiplayer_menu, 1);
+ Menu_Draw(s_multiplayer_menu);
+ }
+
+ static void PlayerSetupFunc(Object unused) {
+ Menu_PlayerConfig_f();
+ }
+
+ static void JoinNetworkServerFunc(Object unused) {
+ Menu_JoinServer_f();
+ }
+
+ static void StartNetworkServerFunc(Object unused) {
+ Menu_StartServer_f();
+ }
+
+ static void Multiplayer_MenuInit() {
+ s_multiplayer_menu.x = (int) (viddef.width * 0.50f - 64);
+ s_multiplayer_menu.nitems = 0;
+
+ s_join_network_server_action.type = MTYPE_ACTION;
+ s_join_network_server_action.flags = QMF_LEFT_JUSTIFY;
+ s_join_network_server_action.x = 0;
+ s_join_network_server_action.y = 0;
+ s_join_network_server_action.name = " join network server";
+ s_join_network_server_action.callback = new mcallback() {
+ public void execute(Object o) {
+ JoinNetworkServerFunc(o);
+ }
+
+ };
+
+ s_start_network_server_action.type = MTYPE_ACTION;
+ s_start_network_server_action.flags = QMF_LEFT_JUSTIFY;
+ s_start_network_server_action.x = 0;
+ s_start_network_server_action.y = 10;
+ s_start_network_server_action.name = " start network server";
+ s_start_network_server_action.callback = new mcallback() {
+ public void execute(Object o) {
+ StartNetworkServerFunc(o);
+ }
+ };
+
+ s_player_setup_action.type = MTYPE_ACTION;
+ s_player_setup_action.flags = QMF_LEFT_JUSTIFY;
+ s_player_setup_action.x = 0;
+ s_player_setup_action.y = 20;
+ s_player_setup_action.name = " player setup";
+ s_player_setup_action.callback = new mcallback() {
+ public void execute(Object o) {
+ PlayerSetupFunc(o);
+ }
+ };
+
+ Menu_AddItem(s_multiplayer_menu, s_join_network_server_action);
+ Menu_AddItem(s_multiplayer_menu, s_start_network_server_action);
+ Menu_AddItem(s_multiplayer_menu, s_player_setup_action);
+
+ Menu_SetStatusBar(s_multiplayer_menu, null);
+
+ Menu_Center(s_multiplayer_menu);
+ }
+
+ static String Multiplayer_MenuKey(int key) {
+ return Default_MenuKey(s_multiplayer_menu, key);
+ }
+
+ static void Menu_Multiplayer_f() {
+ Multiplayer_MenuInit();
+ PushMenu(new xcommand_t() {
+ public void execute() {
+ Multiplayer_MenuDraw();
+ }
+ }, new keyfunc_t() {
+ public String execute(int key) {
+ return Multiplayer_MenuKey(key);
+ }
+ });
+ }
+
+ static void UnbindCommand(String command) {
+ int j;
+ String b;
+
+ for (j = 0; j < 256; j++) {
+ b = keybindings[j];
+ if (b == null)
+ continue;
+ if (b.equals(command))
+ Key.SetBinding(j, "");
+ }
+ }
+
+ static void FindKeysForCommand(String command, int twokeys[]) {
+ int count;
+ int j;
+ String b;
+
+ twokeys[0] = twokeys[1] = -1;
+ count = 0;
+
+ for (j = 0; j < 256; j++) {
+ b = keybindings[j];
+ if (b == null)
+ continue;
+
+ if (b.equals(command)) {
+ twokeys[count] = j;
+ count++;
+ if (count == 2)
+ break;
+ }
+ }
+ }
+
+ static void KeyCursorDrawFunc(menuframework_s menu) {
+ if (bind_grab)
+ re.DrawChar(menu.x, menu.y + menu.cursor * 9, '=');
+ else
+ re.DrawChar(menu.x, menu.y + menu.cursor * 9, 12 + (Timer
+ .getCurrentTimeMillis() / 250 & 1));
+ }
+
+ static void DrawKeyBindingFunc(Object self) {
+ int keys[] = {0, 0};
+ menuaction_s a = (menuaction_s) self;
+
+ FindKeysForCommand(bindnames[a.localdata[0]][0], keys);
+
+ if (keys[0] == -1) {
+ Menu_DrawString(a.x + a.parent.x + 16, a.y + a.parent.y, "???");
+ } else {
+ int x;
+ String name;
+
+ name = Key.KeynumToString(keys[0]);
+
+ Menu_DrawString(a.x + a.parent.x + 16, a.y + a.parent.y, name);
+
+ x = name.length() * 8;
+
+ if (keys[1] != -1) {
+ Menu_DrawString(a.x + a.parent.x + 24 + x, a.y + a.parent.y,
+ "or");
+ Menu_DrawString(a.x + a.parent.x + 48 + x, a.y + a.parent.y,
+ Key.KeynumToString(keys[1]));
+ }
+ }
+ }
+
+ static void KeyBindingFunc(Object self) {
+ menuaction_s a = (menuaction_s) self;
+ int keys[] = {0, 0};
+
+ FindKeysForCommand(bindnames[a.localdata[0]][0], keys);
+
+ if (keys[1] != -1)
+ UnbindCommand(bindnames[a.localdata[0]][0]);
+
+ bind_grab = true;
+
+ Menu_SetStatusBar(s_keys_menu, "press a key or button for this action");
+ }
+
+ static void Keys_MenuInit() {
+ int y = 0;
+ int i = 0;
+
+ s_keys_menu.x = (int) (viddef.width * 0.50);
+ s_keys_menu.nitems = 0;
+ s_keys_menu.cursordraw = new mcallback() {
+ public void execute(Object o) {
+ KeyCursorDrawFunc((menuframework_s) o);
+ }
+ };
+
+ s_keys_walk_forward_action.type = MTYPE_ACTION;
+ s_keys_walk_forward_action.flags = QMF_GRAYED;
+ s_keys_walk_forward_action.x = 0;
+ s_keys_walk_forward_action.y = y += 9;
+ s_keys_walk_forward_action.ownerdraw = new mcallback() {
+ public void execute(Object o) {
+ DrawKeyBindingFunc(o);
+ }
+ };
+ s_keys_walk_forward_action.localdata[0] = ++i;
+ s_keys_walk_forward_action.name = bindnames[s_keys_walk_forward_action.localdata[0]][1];
+
+ s_keys_backpedal_action.type = MTYPE_ACTION;
+ s_keys_backpedal_action.flags = QMF_GRAYED;
+ s_keys_backpedal_action.x = 0;
+ s_keys_backpedal_action.y = y += 9;
+ s_keys_backpedal_action.ownerdraw = new mcallback() {
+ public void execute(Object o) {
+ DrawKeyBindingFunc(o);
+ }
+ };
+ s_keys_backpedal_action.localdata[0] = ++i;
+ s_keys_backpedal_action.name = bindnames[s_keys_backpedal_action.localdata[0]][1];
+
+ s_keys_turn_left_action.type = MTYPE_ACTION;
+ s_keys_turn_left_action.flags = QMF_GRAYED;
+ s_keys_turn_left_action.x = 0;
+ s_keys_turn_left_action.y = y += 9;
+ s_keys_turn_left_action.ownerdraw = new mcallback() {
+ public void execute(Object o) {
+ DrawKeyBindingFunc(o);
+ }
+ };
+ s_keys_turn_left_action.localdata[0] = ++i;
+ s_keys_turn_left_action.name = bindnames[s_keys_turn_left_action.localdata[0]][1];
+
+ s_keys_turn_right_action.type = MTYPE_ACTION;
+ s_keys_turn_right_action.flags = QMF_GRAYED;
+ s_keys_turn_right_action.x = 0;
+ s_keys_turn_right_action.y = y += 9;
+ s_keys_turn_right_action.ownerdraw = new mcallback() {
+ public void execute(Object o) {
+ DrawKeyBindingFunc(o);
+ }
+ };
+ s_keys_turn_right_action.localdata[0] = ++i;
+ s_keys_turn_right_action.name = bindnames[s_keys_turn_right_action.localdata[0]][1];
+
+ s_keys_run_action.type = MTYPE_ACTION;
+ s_keys_run_action.flags = QMF_GRAYED;
+ s_keys_run_action.x = 0;
+ s_keys_run_action.y = y += 9;
+ s_keys_run_action.ownerdraw = new mcallback() {
+ public void execute(Object o) {
+ DrawKeyBindingFunc(o);
+ }
+ };
+ s_keys_run_action.localdata[0] = ++i;
+ s_keys_run_action.name = bindnames[s_keys_run_action.localdata[0]][1];
+
+ s_keys_step_left_action.type = MTYPE_ACTION;
+ s_keys_step_left_action.flags = QMF_GRAYED;
+ s_keys_step_left_action.x = 0;
+ s_keys_step_left_action.y = y += 9;
+ s_keys_step_left_action.ownerdraw = new mcallback() {
+ public void execute(Object o) {
+ DrawKeyBindingFunc(o);
+ }
+ };
+ s_keys_step_left_action.localdata[0] = ++i;
+ s_keys_step_left_action.name = bindnames[s_keys_step_left_action.localdata[0]][1];
+
+ s_keys_step_right_action.type = MTYPE_ACTION;
+ s_keys_step_right_action.flags = QMF_GRAYED;
+ s_keys_step_right_action.x = 0;
+ s_keys_step_right_action.y = y += 9;
+ s_keys_step_right_action.ownerdraw = new mcallback() {
+ public void execute(Object o) {
+ DrawKeyBindingFunc(o);
+ }
+ };
+
+ s_keys_step_right_action.localdata[0] = ++i;
+ s_keys_step_right_action.name = bindnames[s_keys_step_right_action.localdata[0]][1];
+
+ s_keys_sidestep_action.type = MTYPE_ACTION;
+ s_keys_sidestep_action.flags = QMF_GRAYED;
+ s_keys_sidestep_action.x = 0;
+ s_keys_sidestep_action.y = y += 9;
+ s_keys_sidestep_action.ownerdraw = new mcallback() {
+ public void execute(Object o) {
+ DrawKeyBindingFunc(o);
+ }
+ };
+
+ s_keys_sidestep_action.localdata[0] = ++i;
+ s_keys_sidestep_action.name = bindnames[s_keys_sidestep_action.localdata[0]][1];
+
+ s_keys_look_up_action.type = MTYPE_ACTION;
+ s_keys_look_up_action.flags = QMF_GRAYED;
+ s_keys_look_up_action.x = 0;
+ s_keys_look_up_action.y = y += 9;
+ s_keys_look_up_action.ownerdraw = new mcallback() {
+ public void execute(Object o) {
+ DrawKeyBindingFunc(o);
+ }
+ };
+
+ s_keys_look_up_action.localdata[0] = ++i;
+ s_keys_look_up_action.name = bindnames[s_keys_look_up_action.localdata[0]][1];
+
+ s_keys_look_down_action.type = MTYPE_ACTION;
+ s_keys_look_down_action.flags = QMF_GRAYED;
+ s_keys_look_down_action.x = 0;
+ s_keys_look_down_action.y = y += 9;
+ s_keys_look_down_action.ownerdraw = new mcallback() {
+ public void execute(Object o) {
+ DrawKeyBindingFunc(o);
+ }
+ };
+
+ s_keys_look_down_action.localdata[0] = ++i;
+ s_keys_look_down_action.name = bindnames[s_keys_look_down_action.localdata[0]][1];
+
+ s_keys_center_view_action.type = MTYPE_ACTION;
+ s_keys_center_view_action.flags = QMF_GRAYED;
+ s_keys_center_view_action.x = 0;
+ s_keys_center_view_action.y = y += 9;
+ s_keys_center_view_action.ownerdraw = new mcallback() {
+ public void execute(Object o) {
+ DrawKeyBindingFunc(o);
+ }
+ };
+
+ s_keys_center_view_action.localdata[0] = ++i;
+ s_keys_center_view_action.name = bindnames[s_keys_center_view_action.localdata[0]][1];
+
+ s_keys_mouse_look_action.type = MTYPE_ACTION;
+ s_keys_mouse_look_action.flags = QMF_GRAYED;
+ s_keys_mouse_look_action.x = 0;
+ s_keys_mouse_look_action.y = y += 9;
+ s_keys_mouse_look_action.ownerdraw = new mcallback() {
+ public void execute(Object o) {
+ DrawKeyBindingFunc(o);
+ }
+ };
+
+ s_keys_mouse_look_action.localdata[0] = ++i;
+ s_keys_mouse_look_action.name = bindnames[s_keys_mouse_look_action.localdata[0]][1];
+
+ s_keys_keyboard_look_action.type = MTYPE_ACTION;
+ s_keys_keyboard_look_action.flags = QMF_GRAYED;
+ s_keys_keyboard_look_action.x = 0;
+ s_keys_keyboard_look_action.y = y += 9;
+ s_keys_keyboard_look_action.ownerdraw = new mcallback() {
+ public void execute(Object o) {
+ DrawKeyBindingFunc(o);
+ }
+ };
+
+ s_keys_keyboard_look_action.localdata[0] = ++i;
+ s_keys_keyboard_look_action.name = bindnames[s_keys_keyboard_look_action.localdata[0]][1];
+
+ s_keys_move_up_action.type = MTYPE_ACTION;
+ s_keys_move_up_action.flags = QMF_GRAYED;
+ s_keys_move_up_action.x = 0;
+ s_keys_move_up_action.y = y += 9;
+ s_keys_move_up_action.ownerdraw = new mcallback() {
+ public void execute(Object o) {
+ DrawKeyBindingFunc(o);
+ }
+ };
+
+ s_keys_move_up_action.localdata[0] = ++i;
+ s_keys_move_up_action.name = bindnames[s_keys_move_up_action.localdata[0]][1];
+
+ s_keys_move_down_action.type = MTYPE_ACTION;
+ s_keys_move_down_action.flags = QMF_GRAYED;
+ s_keys_move_down_action.x = 0;
+ s_keys_move_down_action.y = y += 9;
+ s_keys_move_down_action.ownerdraw = new mcallback() {
+ public void execute(Object o) {
+ DrawKeyBindingFunc(o);
+ }
+ };
+
+ s_keys_move_down_action.localdata[0] = ++i;
+ s_keys_move_down_action.name = bindnames[s_keys_move_down_action.localdata[0]][1];
+
+ s_keys_inventory_action.type = MTYPE_ACTION;
+ s_keys_inventory_action.flags = QMF_GRAYED;
+ s_keys_inventory_action.x = 0;
+ s_keys_inventory_action.y = y += 9;
+ s_keys_inventory_action.ownerdraw = new mcallback() {
+ public void execute(Object o) {
+ DrawKeyBindingFunc(o);
+ }
+ };
+
+ s_keys_inventory_action.localdata[0] = ++i;
+ s_keys_inventory_action.name = bindnames[s_keys_inventory_action.localdata[0]][1];
+
+ s_keys_inv_use_action.type = MTYPE_ACTION;
+ s_keys_inv_use_action.flags = QMF_GRAYED;
+ s_keys_inv_use_action.x = 0;
+ s_keys_inv_use_action.y = y += 9;
+ s_keys_inv_use_action.ownerdraw = new mcallback() {
+ public void execute(Object o) {
+ DrawKeyBindingFunc(o);
+ }
+ };
+
+ s_keys_inv_use_action.localdata[0] = ++i;
+ s_keys_inv_use_action.name = bindnames[s_keys_inv_use_action.localdata[0]][1];
+
+ s_keys_inv_drop_action.type = MTYPE_ACTION;
+ s_keys_inv_drop_action.flags = QMF_GRAYED;
+ s_keys_inv_drop_action.x = 0;
+ s_keys_inv_drop_action.y = y += 9;
+ s_keys_inv_drop_action.ownerdraw = new mcallback() {
+ public void execute(Object o) {
+ DrawKeyBindingFunc(o);
+ }
+ };
+
+ s_keys_inv_drop_action.localdata[0] = ++i;
+ s_keys_inv_drop_action.name = bindnames[s_keys_inv_drop_action.localdata[0]][1];
+
+ s_keys_inv_prev_action.type = MTYPE_ACTION;
+ s_keys_inv_prev_action.flags = QMF_GRAYED;
+ s_keys_inv_prev_action.x = 0;
+ s_keys_inv_prev_action.y = y += 9;
+ s_keys_inv_prev_action.ownerdraw = new mcallback() {
+ public void execute(Object o) {
+ DrawKeyBindingFunc(o);
+ }
+ };
+
+ s_keys_inv_prev_action.localdata[0] = ++i;
+ s_keys_inv_prev_action.name = bindnames[s_keys_inv_prev_action.localdata[0]][1];
+
+ s_keys_inv_next_action.type = MTYPE_ACTION;
+ s_keys_inv_next_action.flags = QMF_GRAYED;
+ s_keys_inv_next_action.x = 0;
+ s_keys_inv_next_action.y = y += 9;
+ s_keys_inv_next_action.ownerdraw = new mcallback() {
+ public void execute(Object o) {
+ DrawKeyBindingFunc(o);
+ }
+ };
+
+ s_keys_inv_next_action.localdata[0] = ++i;
+ s_keys_inv_next_action.name = bindnames[s_keys_inv_next_action.localdata[0]][1];
+
+ s_keys_help_computer_action.type = MTYPE_ACTION;
+ s_keys_help_computer_action.flags = QMF_GRAYED;
+ s_keys_help_computer_action.x = 0;
+ s_keys_help_computer_action.y = y += 9;
+ s_keys_help_computer_action.ownerdraw = new mcallback() {
+ public void execute(Object o) {
+ DrawKeyBindingFunc(o);
+ }
+ };
+
+ s_keys_help_computer_action.localdata[0] = ++i;
+ s_keys_help_computer_action.name = bindnames[s_keys_help_computer_action.localdata[0]][1];
+
+ Menu_AddItem(s_keys_menu, s_keys_walk_forward_action);
+ Menu_AddItem(s_keys_menu, s_keys_backpedal_action);
+ Menu_AddItem(s_keys_menu, s_keys_turn_left_action);
+ Menu_AddItem(s_keys_menu, s_keys_turn_right_action);
+ Menu_AddItem(s_keys_menu, s_keys_run_action);
+ Menu_AddItem(s_keys_menu, s_keys_step_left_action);
+ Menu_AddItem(s_keys_menu, s_keys_step_right_action);
+ Menu_AddItem(s_keys_menu, s_keys_sidestep_action);
+ Menu_AddItem(s_keys_menu, s_keys_look_up_action);
+ Menu_AddItem(s_keys_menu, s_keys_look_down_action);
+ Menu_AddItem(s_keys_menu, s_keys_center_view_action);
+ Menu_AddItem(s_keys_menu, s_keys_mouse_look_action);
+ Menu_AddItem(s_keys_menu, s_keys_keyboard_look_action);
+ Menu_AddItem(s_keys_menu, s_keys_move_up_action);
+ Menu_AddItem(s_keys_menu, s_keys_move_down_action);
+
+ Menu_AddItem(s_keys_menu, s_keys_inventory_action);
+ Menu_AddItem(s_keys_menu, s_keys_inv_use_action);
+ Menu_AddItem(s_keys_menu, s_keys_inv_drop_action);
+ Menu_AddItem(s_keys_menu, s_keys_inv_prev_action);
+ Menu_AddItem(s_keys_menu, s_keys_inv_next_action);
+
+ Menu_AddItem(s_keys_menu, s_keys_help_computer_action);
+
+ Menu_SetStatusBar(s_keys_menu, "enter to change, backspace to clear");
+ Menu_Center(s_keys_menu);
+ }
+
+ static void Keys_MenuDraw_f() {
+ Menu_AdjustCursor(s_keys_menu, 1);
+ Menu_Draw(s_keys_menu);
+ }
+
+ static String Keys_MenuKey_f(int key) {
+ menuaction_s item = (menuaction_s) Menu_ItemAtCursor(s_keys_menu);
+
+ if (bind_grab) {
+ if (key != K_ESCAPE && key != '`') {
+ //char cmd[1024];
+ String cmd;
+
+ //Com_sprintf(cmd, sizeof(cmd), "bind \"%s\" \"%s\"\n",
+ // Key_KeynumToString(key), bindnames[item.localdata[0]][0]);
+ cmd = "bind \"" + Key.KeynumToString(key) + "\" \""
+ + bindnames[item.localdata[0]][0] + "\"";
+ CommandBuffer.InsertText(cmd);
+ }
+
+ Menu_SetStatusBar(s_keys_menu,
+ "enter to change, backspace to clear");
+ bind_grab = false;
+ return menu_out_sound;
+ }
+
+ switch (key) {
+ case K_KP_ENTER:
+ case K_ENTER:
+ KeyBindingFunc(item);
+ return menu_in_sound;
+ case K_BACKSPACE: // delete bindings
+ case K_DEL: // delete bindings
+ case K_KP_DEL:
+ UnbindCommand(bindnames[item.localdata[0]][0]);
+ return menu_out_sound;
+ default:
+ return Default_MenuKey(s_keys_menu, key);
+ }
+ }
+
+ static void Menu_Keys_f() {
+ Keys_MenuInit();
+ PushMenu(new xcommand_t() {
+ public void execute() {
+ Keys_MenuDraw_f();
+ }
+ }, new keyfunc_t() {
+ public String execute(int key) {
+ return Keys_MenuKey_f(key);
+ }
+ });
+ }
+
+ static void CrosshairFunc(Object unused) {
+ Cvar.setValue("crosshair", s_options_crosshair_box.curvalue);
+ }
+
+ static void JoystickFunc(Object unused) {
+ Cvar.setValue("in_joystick", s_options_joystick_box.curvalue);
+ }
+
+ static void CustomizeControlsFunc(Object unused) {
+ Menu_Keys_f();
+ }
+
+ static void AlwaysRunFunc(Object unused) {
+ Cvar.setValue("cl_run", s_options_alwaysrun_box.curvalue);
+ }
+
+ static void FreeLookFunc(Object unused) {
+ Cvar.setValue("freelook", s_options_freelook_box.curvalue);
+ }
+
+ static void MouseSpeedFunc(Object unused) {
+ Cvar.setValue("sensitivity",
+ s_options_sensitivity_slider.curvalue / 2.0F);
+ }
+
+ static void NoAltTabFunc(Object unused) {
+ Cvar.setValue("win_noalttab", s_options_noalttab_box.curvalue);
+ }
+
+ static float ClampCvar(float max, float value) {
+ if (value < (float) 0)
+ return (float) 0;
+ if (value > max)
+ return max;
+ return value;
+ }
+
+ static void ControlsSetMenuItemValues() {
+ s_options_sfxvolume_slider.curvalue = Cvar.variableValue("s_volume") * 10;
+ s_options_cdvolume_box.curvalue = 1 - ((int) Cvar
+ .variableValue("cd_nocd"));
+ //s_options_quality_list.curvalue = 1 - ((int)
+ // Cvar.variableValue("s_loadas8bit"));
+ String s = Cvar.variableString("s_impl");
+ for (int i = 0; i < s_drivers.length; i++) {
+ if (s.equals(s_drivers[i])) {
+ s_options_quality_list.curvalue = i;
+ }
+ }
+
+ s_options_sensitivity_slider.curvalue = (sensitivity.value) * 2;
+
+ Cvar.setValue("cl_run", ClampCvar(1, cl_run.value));
+ s_options_alwaysrun_box.curvalue = (int) cl_run.value;
+
+ s_options_invertmouse_box.curvalue = m_pitch.value < 0 ? 1 : 0;
+
+ Cvar.setValue("lookspring", ClampCvar(1, lookspring.value));
+ s_options_lookspring_box.curvalue = (int) lookspring.value;
+
+ Cvar.setValue("lookstrafe", ClampCvar(1, lookstrafe.value));
+ s_options_lookstrafe_box.curvalue = (int) lookstrafe.value;
+
+ Cvar.setValue("freelook", ClampCvar(1, freelook.value));
+ s_options_freelook_box.curvalue = (int) freelook.value;
+
+ Cvar.setValue("crosshair", ClampCvar(3, Globals.crosshair.value));
+ s_options_crosshair_box.curvalue = (int) Globals.crosshair.value;
+
+ Cvar.setValue("in_joystick", ClampCvar(1, in_joystick.value));
+ s_options_joystick_box.curvalue = (int) in_joystick.value;
+
+ s_options_noalttab_box.curvalue = (int) win_noalttab.value;
+ }
+
+ static void ControlsResetDefaultsFunc(Object unused) {
+ CommandBuffer.AddText("exec default.cfg\n");
+ CommandBuffer.execute();
+
+ ControlsSetMenuItemValues();
+ }
+
+ static void InvertMouseFunc(Object unused) {
+ Cvar.setValue("m_pitch", -m_pitch.value);
+ }
+
+ static void LookspringFunc(Object unused) {
+ Cvar.setValue("lookspring", 1 - lookspring.value);
+ }
+
+ static void LookstrafeFunc(Object unused) {
+ Cvar.setValue("lookstrafe", 1 - lookstrafe.value);
+ }
+
+ static void UpdateVolumeFunc(Object unused) {
+ Cvar.setValue("s_volume", s_options_sfxvolume_slider.curvalue / 10);
+ }
+
+ static void UpdateCDVolumeFunc(Object unused) {
+ Cvar.setValue("cd_nocd", 1 - s_options_cdvolume_box.curvalue);
+ }
+
+ static void ConsoleFunc(Object unused) {
+ /*
+ * * the proper way to do this is probably to have ToggleConsole_f
+ * accept a parameter
+ */
+
+ if (clientStateT.attractloop) {
+ CommandBuffer.AddText("killserver\n");
+ return;
+ }
+
+ Key.ClearTyping();
+ Console.ClearNotify();
+
+ ForceMenuOff();
+ clientStaticT.key_dest = key_console;
+ }
+
+ static void UpdateSoundQualityFunc(Object unused) {
+ boolean driverNotChanged = false;
+ String current = s_drivers[s_options_quality_list.curvalue];
+ driverNotChanged = S.getDriverName().equals(current);
+// if (s_options_quality_list.curvalue != 0) {
+// // Cvar.setValue("s_khz", 22);
+// // Cvar.setValue("s_loadas8bit", 0);
+// driverNotChanged = S.getDriverName().equals("dummy");
+// Cvar.Set("s_impl", "dummy");
+// } else {
+// // Cvar.setValue("s_khz", 11);
+// // Cvar.setValue("s_loadas8bit", 1);
+// driverNotChanged = S.getDriverName().equals("joal");
+// Cvar.Set("s_impl", "joal");
+// }
+
+ //Cvar.setValue("s_primary", s_options_compatibility_list.curvalue);
+
+ if (driverNotChanged) {
+ re.EndFrame();
+ } else {
+ Cvar.set("s_impl", current);
+
+ DrawTextBox(8, 120 - 48, 36, 3);
+ Print(16 + 16, 120 - 48 + 8, "Restarting the sound system. This");
+ Print(16 + 16, 120 - 48 + 16, "could take up to a minute, so");
+ Print(16 + 16, 120 - 48 + 24, "please be patient.");
+
+ // the text box won't show up unless we do a buffer swap
+ re.EndFrame();
+
+ Client.Snd_Restart_f.execute();
+ }
+ }
+
+ static void Options_MenuInit() {
+
+ s_drivers = S.getDriverNames();
+ s_labels = new String[s_drivers.length];
+ for (int i = 0; i < s_drivers.length; i++) {
+ if ("dummy".equals(s_drivers[i])) {
+ s_labels[i] = "off";
+ } else {
+ s_labels[i] = s_drivers[i];
+ }
+ }
+
+ win_noalttab = Cvar.get("win_noalttab", "0", CVAR_ARCHIVE);
+
+ /*
+ * * configure controls menu and menu items
+ */
+ s_options_menu.x = viddef.width / 2;
+ s_options_menu.y = viddef.height / 2 - 58;
+ s_options_menu.nitems = 0;
+
+ s_options_sfxvolume_slider.type = MTYPE_SLIDER;
+ s_options_sfxvolume_slider.x = 0;
+ s_options_sfxvolume_slider.y = 0;
+ s_options_sfxvolume_slider.name = "effects volume";
+ s_options_sfxvolume_slider.callback = new mcallback() {
+ public void execute(Object o) {
+ UpdateVolumeFunc(o);
+ }
+ };
+ s_options_sfxvolume_slider.minvalue = 0;
+ s_options_sfxvolume_slider.maxvalue = 10;
+ s_options_sfxvolume_slider.curvalue = Cvar.variableValue("s_volume") * 10;
+
+ s_options_cdvolume_box.type = MTYPE_SPINCONTROL;
+ s_options_cdvolume_box.x = 0;
+ s_options_cdvolume_box.y = 10;
+ s_options_cdvolume_box.name = "CD music";
+ s_options_cdvolume_box.callback = new mcallback() {
+ public void execute(Object o) {
+ UpdateCDVolumeFunc(o);
+ }
+ };
+ s_options_cdvolume_box.itemnames = cd_music_items;
+ s_options_cdvolume_box.curvalue = 1 - (int) Cvar
+ .variableValue("cd_nocd");
+
+ s_options_quality_list.type = MTYPE_SPINCONTROL;
+ s_options_quality_list.x = 0;
+ s_options_quality_list.y = 20;
+ s_options_quality_list.name = "sound";
+ s_options_quality_list.callback = new mcallback() {
+ public void execute(Object o) {
+ UpdateSoundQualityFunc(o);
+ }
+ };
+ s_options_quality_list.itemnames = s_labels;
+
+ s_options_sensitivity_slider.type = MTYPE_SLIDER;
+ s_options_sensitivity_slider.x = 0;
+ s_options_sensitivity_slider.y = 50;
+ s_options_sensitivity_slider.name = "mouse speed";
+ s_options_sensitivity_slider.callback = new mcallback() {
+ public void execute(Object o) {
+ MouseSpeedFunc(o);
+ }
+ };
+ s_options_sensitivity_slider.minvalue = 2;
+ s_options_sensitivity_slider.maxvalue = 22;
+
+ s_options_alwaysrun_box.type = MTYPE_SPINCONTROL;
+ s_options_alwaysrun_box.x = 0;
+ s_options_alwaysrun_box.y = 60;
+ s_options_alwaysrun_box.name = "always run";
+ s_options_alwaysrun_box.callback = new mcallback() {
+ public void execute(Object o) {
+ AlwaysRunFunc(o);
+ }
+ };
+ s_options_alwaysrun_box.itemnames = yesno_names;
+
+ s_options_invertmouse_box.type = MTYPE_SPINCONTROL;
+ s_options_invertmouse_box.x = 0;
+ s_options_invertmouse_box.y = 70;
+ s_options_invertmouse_box.name = "invert mouse";
+ s_options_invertmouse_box.callback = new mcallback() {
+ public void execute(Object o) {
+ InvertMouseFunc(o);
+ }
+ };
+ s_options_invertmouse_box.itemnames = yesno_names;
+
+ s_options_lookspring_box.type = MTYPE_SPINCONTROL;
+ s_options_lookspring_box.x = 0;
+ s_options_lookspring_box.y = 80;
+ s_options_lookspring_box.name = "lookspring";
+ s_options_lookspring_box.callback = new mcallback() {
+ public void execute(Object o) {
+ LookspringFunc(o);
+ }
+ };
+ s_options_lookspring_box.itemnames = yesno_names;
+
+ s_options_lookstrafe_box.type = MTYPE_SPINCONTROL;
+ s_options_lookstrafe_box.x = 0;
+ s_options_lookstrafe_box.y = 90;
+ s_options_lookstrafe_box.name = "lookstrafe";
+ s_options_lookstrafe_box.callback = new mcallback() {
+ public void execute(Object o) {
+ LookstrafeFunc(o);
+ }
+ };
+ s_options_lookstrafe_box.itemnames = yesno_names;
+
+ s_options_freelook_box.type = MTYPE_SPINCONTROL;
+ s_options_freelook_box.x = 0;
+ s_options_freelook_box.y = 100;
+ s_options_freelook_box.name = "free look";
+ s_options_freelook_box.callback = new mcallback() {
+ public void execute(Object o) {
+ FreeLookFunc(o);
+ }
+ };
+ s_options_freelook_box.itemnames = yesno_names;
+
+ s_options_crosshair_box.type = MTYPE_SPINCONTROL;
+ s_options_crosshair_box.x = 0;
+ s_options_crosshair_box.y = 110;
+ s_options_crosshair_box.name = "crosshair";
+ s_options_crosshair_box.callback = new mcallback() {
+ public void execute(Object o) {
+ CrosshairFunc(o);
+ }
+ };
+ s_options_crosshair_box.itemnames = crosshair_names;
+ /*
+ * s_options_noalttab_box.type = MTYPE_SPINCONTROL;
+ * s_options_noalttab_box.x = 0; s_options_noalttab_box.y = 110;
+ * s_options_noalttab_box.name = "disable alt-tab";
+ * s_options_noalttab_box.callback = NoAltTabFunc;
+ * s_options_noalttab_box.itemnames = yesno_names;
+ */
+ s_options_joystick_box.type = MTYPE_SPINCONTROL;
+ s_options_joystick_box.x = 0;
+ s_options_joystick_box.y = 120;
+ s_options_joystick_box.name = "use joystick";
+ s_options_joystick_box.callback = new mcallback() {
+ public void execute(Object o) {
+ JoystickFunc(o);
+ }
+ };
+ s_options_joystick_box.itemnames = yesno_names;
+
+ s_options_customize_options_action.type = MTYPE_ACTION;
+ s_options_customize_options_action.x = 0;
+ s_options_customize_options_action.y = 140;
+ s_options_customize_options_action.name = "customize controls";
+ s_options_customize_options_action.callback = new mcallback() {
+ public void execute(Object o) {
+ CustomizeControlsFunc(o);
+ }
+ };
+
+ s_options_defaults_action.type = MTYPE_ACTION;
+ s_options_defaults_action.x = 0;
+ s_options_defaults_action.y = 150;
+ s_options_defaults_action.name = "reset defaults";
+ s_options_defaults_action.callback = new mcallback() {
+ public void execute(Object o) {
+ ControlsResetDefaultsFunc(o);
+ }
+ };
+
+ s_options_console_action.type = MTYPE_ACTION;
+ s_options_console_action.x = 0;
+ s_options_console_action.y = 160;
+ s_options_console_action.name = "go to console";
+ s_options_console_action.callback = new mcallback() {
+ public void execute(Object o) {
+ ConsoleFunc(o);
+ }
+ };
+
+ ControlsSetMenuItemValues();
+
+ Menu_AddItem(s_options_menu, s_options_sfxvolume_slider);
+
+ Menu_AddItem(s_options_menu, s_options_cdvolume_box);
+ Menu_AddItem(s_options_menu, s_options_quality_list);
+ // Menu_AddItem(s_options_menu, s_options_compatibility_list);
+ Menu_AddItem(s_options_menu, s_options_sensitivity_slider);
+ Menu_AddItem(s_options_menu, s_options_alwaysrun_box);
+ Menu_AddItem(s_options_menu, s_options_invertmouse_box);
+ Menu_AddItem(s_options_menu, s_options_lookspring_box);
+ Menu_AddItem(s_options_menu, s_options_lookstrafe_box);
+ Menu_AddItem(s_options_menu, s_options_freelook_box);
+ Menu_AddItem(s_options_menu, s_options_crosshair_box);
+ // Menu_AddItem(s_options_menu, s_options_joystick_box);
+ Menu_AddItem(s_options_menu, s_options_customize_options_action);
+ Menu_AddItem(s_options_menu, s_options_defaults_action);
+ Menu_AddItem(s_options_menu, s_options_console_action);
+ }
+
+ static void Options_MenuDraw() {
+ Banner("m_banner_options");
+ Menu_AdjustCursor(s_options_menu, 1);
+ Menu_Draw(s_options_menu);
+ }
+
+ static String Options_MenuKey(int key) {
+ return Default_MenuKey(s_options_menu, key);
+ }
+
+ static void Menu_Options_f() {
+ Options_MenuInit();
+ PushMenu(new xcommand_t() {
+ public void execute() {
+ Options_MenuDraw();
+ }
+ }, new keyfunc_t() {
+ public String execute(int key) {
+ return Options_MenuKey(key);
+ }
+ });
+ }
+
+ static void Menu_Video_f() {
+ VideoDriver.MenuInit();
+ PushMenu(new xcommand_t() {
+ public void execute() {
+ VideoDriver.MenuDraw();
+ }
+ }, new keyfunc_t() {
+ public String execute(int key) {
+ return VideoDriver.MenuKey(key);
+ }
+ });
+ }
+
+ public static void Credits_MenuDraw() {
+ int i, y;
+
+ /*
+ * * draw the credits
+ */
+ for (i = 0, y = (int) (viddef.height - ((clientStaticT.realtime - credits_start_time) / 40.0F)); credits[i] != null
+ && y < viddef.height; y += 10, i++) {
+ int j, stringoffset = 0;
+ boolean bold = false;
+
+ if (y <= -8)
+ continue;
+
+ if (credits[i].length() > 0 && credits[i].charAt(0) == '+') {
+ bold = true;
+ stringoffset = 1;
+ } else {
+ bold = false;
+ stringoffset = 0;
+ }
+
+ for (j = 0; j + stringoffset < credits[i].length(); j++) {
+ int x;
+
+ x = (viddef.width - credits[i].length() * 8 - stringoffset * 8)
+ / 2 + (j + stringoffset) * 8;
+
+ if (bold)
+ re
+ .DrawChar(x, y,
+ credits[i].charAt(j + stringoffset) + 128);
+ else
+ re.DrawChar(x, y, credits[i].charAt(j + stringoffset));
+ }
+ }
+
+ if (y < 0)
+ credits_start_time = clientStaticT.realtime;
+ }
+
+ public static String Credits_Key(int key) {
+ switch (key) {
+ case K_ESCAPE:
+ if (creditsBuffer != null)
+ //FS.FreeFile(creditsBuffer);
+ ;
+ PopMenu();
+ break;
+ }
+
+ return menu_out_sound;
+
+ }
+
+ static void Menu_Credits_f() {
+ int n;
+ int isdeveloper = 0;
+
+ byte b[] = FS.LoadFile("credits");
+
+ if (b != null) {
+ creditsBuffer = new String(b);
+ String line[] = creditsBuffer.split("\r\n");
+
+ for (n = 0; n < line.length; n++) {
+ creditsIndex[n] = line[n];
+ }
+
+ creditsIndex[n] = null;
+ credits = creditsIndex;
+ } else {
+ isdeveloper = FS.Developer_searchpath(1);
+
+ if (isdeveloper == 1) // xatrix
+ credits = xatcredits;
+ else if (isdeveloper == 2) // ROGUE
+ credits = roguecredits;
+ else {
+ credits = idcredits;
+ }
+
+ }
+
+ credits_start_time = clientStaticT.realtime;
+ PushMenu(new xcommand_t() {
+ public void execute() {
+ Credits_MenuDraw();
+ }
+ }, new keyfunc_t() {
+ public String execute(int key) {
+ return Credits_Key(key);
+ }
+ });
+ }
+
+ static void StartGame() {
+ // disable updates and start the cinematic going
+ clientStateT.servercount = -1;
+ ForceMenuOff();
+ Cvar.setValue("deathmatch", 0);
+ Cvar.setValue("coop", 0);
+
+ Cvar.setValue("gamerules", 0); //PGM
+
+ CommandBuffer.AddText("loading ; killserver ; wait ; newgame\n");
+ clientStaticT.key_dest = key_game;
+ }
+
+ static void EasyGameFunc(Object data) {
+ Cvar.forceSet("skill", "0");
+ StartGame();
+ }
+
+ static void MediumGameFunc(Object data) {
+ Cvar.forceSet("skill", "1");
+ StartGame();
+ }
+
+ static void HardGameFunc(Object data) {
+ Cvar.forceSet("skill", "2");
+ StartGame();
+ }
+
+ static void LoadGameFunc(Object unused) {
+ Menu_LoadGame_f();
+ }
+
+ static void SaveGameFunc(Object unused) {
+ Menu_SaveGame_f();
+ }
+
+ static void CreditsFunc(Object unused) {
+ Menu_Credits_f();
+ }
+
+ static void Game_MenuInit() {
+
+ s_game_menu.x = (int) (viddef.width * 0.50);
+ s_game_menu.nitems = 0;
+
+ s_easy_game_action.type = MTYPE_ACTION;
+ s_easy_game_action.flags = QMF_LEFT_JUSTIFY;
+ s_easy_game_action.x = 0;
+ s_easy_game_action.y = 0;
+ s_easy_game_action.name = "easy";
+ s_easy_game_action.callback = new mcallback() {
+ public void execute(Object o) {
+ EasyGameFunc(o);
+ }
+ };
+
+ s_medium_game_action.type = MTYPE_ACTION;
+ s_medium_game_action.flags = QMF_LEFT_JUSTIFY;
+ s_medium_game_action.x = 0;
+ s_medium_game_action.y = 10;
+ s_medium_game_action.name = "medium";
+ s_medium_game_action.callback = new mcallback() {
+ public void execute(Object o) {
+ MediumGameFunc(o);
+ }
+ };
+
+ s_hard_game_action.type = MTYPE_ACTION;
+ s_hard_game_action.flags = QMF_LEFT_JUSTIFY;
+ s_hard_game_action.x = 0;
+ s_hard_game_action.y = 20;
+ s_hard_game_action.name = "hard";
+ s_hard_game_action.callback = new mcallback() {
+ public void execute(Object o) {
+ HardGameFunc(o);
+ }
+ };
+
+ s_blankline.type = MTYPE_SEPARATOR;
+
+ s_load_game_action.type = MTYPE_ACTION;
+ s_load_game_action.flags = QMF_LEFT_JUSTIFY;
+ s_load_game_action.x = 0;
+ s_load_game_action.y = 40;
+ s_load_game_action.name = "load game";
+ s_load_game_action.callback = new mcallback() {
+ public void execute(Object o) {
+ LoadGameFunc(o);
+ }
+ };
+
+ s_save_game_action.type = MTYPE_ACTION;
+ s_save_game_action.flags = QMF_LEFT_JUSTIFY;
+ s_save_game_action.x = 0;
+ s_save_game_action.y = 50;
+ s_save_game_action.name = "save game";
+ s_save_game_action.callback = new mcallback() {
+ public void execute(Object o) {
+ SaveGameFunc(o);
+ }
+ };
+
+ s_credits_action.type = MTYPE_ACTION;
+ s_credits_action.flags = QMF_LEFT_JUSTIFY;
+ s_credits_action.x = 0;
+ s_credits_action.y = 60;
+ s_credits_action.name = "credits";
+ s_credits_action.callback = new mcallback() {
+ public void execute(Object o) {
+ CreditsFunc(o);
+ }
+ };
+
+ Menu_AddItem(s_game_menu, s_easy_game_action);
+ Menu_AddItem(s_game_menu, s_medium_game_action);
+ Menu_AddItem(s_game_menu, s_hard_game_action);
+ Menu_AddItem(s_game_menu, s_blankline);
+ Menu_AddItem(s_game_menu, s_load_game_action);
+ Menu_AddItem(s_game_menu, s_save_game_action);
+ Menu_AddItem(s_game_menu, s_blankline);
+ Menu_AddItem(s_game_menu, s_credits_action);
+
+ Menu_Center(s_game_menu);
+ }
+
+ static void Game_MenuDraw() {
+ Banner("m_banner_game");
+ Menu_AdjustCursor(s_game_menu, 1);
+ Menu_Draw(s_game_menu);
+ }
+
+ // ROGUE
+
+ static String Game_MenuKey(int key) {
+ return Default_MenuKey(s_game_menu, key);
+ }
+
+ static void Menu_Game_f() {
+ Game_MenuInit();
+ PushMenu(new xcommand_t() {
+ public void execute() {
+ Game_MenuDraw();
+ }
+ }, new keyfunc_t() {
+ public String execute(int key) {
+ return Game_MenuKey(key);
+ }
+ });
+ m_game_cursor = 1;
+ }
+
+ /**
+ * Search the save dir for saved games and their names.
+ */
+ static void Create_Savestrings() {
+ int i;
+ QuakeFile f;
+ String name;
+
+ for (i = 0; i < MAX_SAVEGAMES; i++) {
+
+ m_savestrings[i] = "<EMPTY>";
+ name = FS.Gamedir() + "/save/save" + i + "/server.ssv";
+
+ try {
+ f = new QuakeFile(name, "r");
+ String str = f.readString();
+ if (str != null)
+ m_savestrings[i] = str;
+ f.close();
+ m_savevalid[i] = true;
+ } catch (Exception e) {
+ m_savestrings[i] = "<EMPTY>";
+ m_savevalid[i] = false;
+ }
+ }
+ }
+
+ static void LoadGameCallback(Object self) {
+ menuaction_s a = (menuaction_s) self;
+
+ if (m_savevalid[a.localdata[0]])
+ CommandBuffer.AddText("load save" + a.localdata[0] + "\n");
+ ForceMenuOff();
+ }
+
+ static void LoadGame_MenuInit() {
+ int i;
+
+ s_loadgame_menu.x = viddef.width / 2 - 120;
+ s_loadgame_menu.y = viddef.height / 2 - 58;
+ s_loadgame_menu.nitems = 0;
+
+ Create_Savestrings();
+
+ for (i = 0; i < MAX_SAVEGAMES; i++) {
+ s_loadgame_actions[i].name = m_savestrings[i];
+ s_loadgame_actions[i].flags = QMF_LEFT_JUSTIFY;
+ s_loadgame_actions[i].localdata[0] = i;
+ s_loadgame_actions[i].callback = new mcallback() {
+ public void execute(Object o) {
+ LoadGameCallback(o);
+ }
+ };
+
+ s_loadgame_actions[i].x = 0;
+ s_loadgame_actions[i].y = (i) * 10;
+ if (i > 0) // separate from autosave
+ s_loadgame_actions[i].y += 10;
+
+ s_loadgame_actions[i].type = MTYPE_ACTION;
+
+ Menu_AddItem(s_loadgame_menu, s_loadgame_actions[i]);
+ }
+ }
+
+ static void LoadGame_MenuDraw() {
+ Banner("m_banner_load_game");
+ // Menu_AdjustCursor( &s_loadgame_menu, 1 );
+ Menu_Draw(s_loadgame_menu);
+ }
+
+ static String LoadGame_MenuKey(int key) {
+ if (key == K_ESCAPE || key == K_ENTER) {
+ s_savegame_menu.cursor = s_loadgame_menu.cursor - 1;
+ if (s_savegame_menu.cursor < 0)
+ s_savegame_menu.cursor = 0;
+ }
+ return Default_MenuKey(s_loadgame_menu, key);
+ }
+
+ static void Menu_LoadGame_f() {
+ LoadGame_MenuInit();
+ PushMenu(new xcommand_t() {
+ public void execute() {
+ LoadGame_MenuDraw();
+ }
+ }, new keyfunc_t() {
+ public String execute(int key) {
+ return LoadGame_MenuKey(key);
+ }
+ });
+ }
+
+ static void SaveGameCallback(Object self) {
+ menuaction_s a = (menuaction_s) self;
+
+ CommandBuffer.AddText("save save" + a.localdata[0] + "\n");
+ ForceMenuOff();
+ }
+
+ static void SaveGame_MenuDraw() {
+ Banner("m_banner_save_game");
+ Menu_AdjustCursor(s_savegame_menu, 1);
+ Menu_Draw(s_savegame_menu);
+ }
+
+ static void SaveGame_MenuInit() {
+ int i;
+
+ s_savegame_menu.x = viddef.width / 2 - 120;
+ s_savegame_menu.y = viddef.height / 2 - 58;
+ s_savegame_menu.nitems = 0;
+
+ Create_Savestrings();
+
+ // don't include the autosave slot
+ for (i = 0; i < MAX_SAVEGAMES - 1; i++) {
+ s_savegame_actions[i].name = m_savestrings[i + 1];
+ s_savegame_actions[i].localdata[0] = i + 1;
+ s_savegame_actions[i].flags = QMF_LEFT_JUSTIFY;
+ s_savegame_actions[i].callback = new mcallback() {
+ public void execute(Object o) {
+ SaveGameCallback(o);
+ }
+ };
+
+ s_savegame_actions[i].x = 0;
+ s_savegame_actions[i].y = (i) * 10;
+
+ s_savegame_actions[i].type = MTYPE_ACTION;
+
+ Menu_AddItem(s_savegame_menu, s_savegame_actions[i]);
+ }
+ }
+
+ static String SaveGame_MenuKey(int key) {
+ if (key == K_ENTER || key == K_ESCAPE) {
+ s_loadgame_menu.cursor = s_savegame_menu.cursor - 1;
+ if (s_loadgame_menu.cursor < 0)
+ s_loadgame_menu.cursor = 0;
+ }
+ return Default_MenuKey(s_savegame_menu, key);
+ }
+
+ static void Menu_SaveGame_f() {
+ if (0 == Globals.server_state)
+ return; // not playing a game
+
+ SaveGame_MenuInit();
+ PushMenu(new xcommand_t() {
+ public void execute() {
+ SaveGame_MenuDraw();
+ }
+ }, new keyfunc_t() {
+ public String execute(int key) {
+ return SaveGame_MenuKey(key);
+ }
+ });
+ Create_Savestrings();
+ }
+
+ static void AddToServerList(String info) {
+ int i;
+
+ if (m_num_servers == MAX_LOCAL_SERVERS)
+ return;
+
+ String x = info.trim();
+
+ // ignore if duplicated
+
+ for (i = 0; i < m_num_servers; i++)
+ if (x.equals(local_server_names[i]))
+ return;
+
+ local_server_netadr[m_num_servers].set(Globals.net_from);
+ local_server_names[m_num_servers] = x;
+ s_joinserver_server_actions[m_num_servers].name = x;
+ m_num_servers++;
+ }
+
+ static void JoinServerFunc(Object self) {
+ String buffer;
+ int index;
+
+ index = ((menucommon_s) self).n;
+
+ if (Lib.Q_stricmp(local_server_names[index], NO_SERVER_STRING) == 0)
+ return;
+
+ if (index >= m_num_servers)
+ return;
+
+ buffer = "connect " + NET.AdrToString(local_server_netadr[index])
+ + "\n";
+ CommandBuffer.AddText(buffer);
+ ForceMenuOff();
+ }
+
+ static void AddressBookFunc(Object self) {
+ Menu_AddressBook_f();
+ }
+
+ static void NullCursorDraw(Object self) {
+ }
+
+ static void SearchLocalGames() {
+ int i;
+
+ m_num_servers = 0;
+ for (i = 0; i < MAX_LOCAL_SERVERS; i++)
+ local_server_names[i] = NO_SERVER_STRING;
+
+ DrawTextBox(8, 120 - 48, 36, 3);
+ Print(16 + 16, 120 - 48 + 8, "Searching for local servers, this");
+ Print(16 + 16, 120 - 48 + 16, "could take up to a minute, so");
+ Print(16 + 16, 120 - 48 + 24, "please be patient.");
+
+ // the text box won't show up unless we do a buffer swap
+ re.EndFrame();
+
+ // send out info packets
+ Client.PingServers_f.execute();
+ }
+
+ static void SearchLocalGamesFunc(Object self) {
+ SearchLocalGames();
+ }
+
+ static void JoinServer_MenuInit() {
+ int i;
+
+ s_joinserver_menu.x = (int) (viddef.width * 0.50 - 120);
+ s_joinserver_menu.nitems = 0;
+
+ s_joinserver_address_book_action.type = MTYPE_ACTION;
+ s_joinserver_address_book_action.name = "address book";
+ s_joinserver_address_book_action.flags = QMF_LEFT_JUSTIFY;
+ s_joinserver_address_book_action.x = 0;
+ s_joinserver_address_book_action.y = 0;
+ s_joinserver_address_book_action.callback = new mcallback() {
+ public void execute(Object o) {
+ AddressBookFunc(o);
+ }
+ };
+
+ s_joinserver_search_action.type = MTYPE_ACTION;
+ s_joinserver_search_action.name = "refresh server list";
+ s_joinserver_search_action.flags = QMF_LEFT_JUSTIFY;
+ s_joinserver_search_action.x = 0;
+ s_joinserver_search_action.y = 10;
+ s_joinserver_search_action.callback = new mcallback() {
+ public void execute(Object o) {
+ SearchLocalGamesFunc(o);
+ }
+ };
+ s_joinserver_search_action.statusbar = "search for servers";
+
+ s_joinserver_server_title.type = MTYPE_SEPARATOR;
+ s_joinserver_server_title.name = "connect to...";
+ s_joinserver_server_title.x = 80;
+ s_joinserver_server_title.y = 30;
+
+ for (i = 0; i < MAX_LOCAL_SERVERS; i++) {
+ s_joinserver_server_actions[i].type = MTYPE_ACTION;
+ local_server_names[i] = NO_SERVER_STRING;
+ s_joinserver_server_actions[i].name = local_server_names[i];
+ s_joinserver_server_actions[i].flags = QMF_LEFT_JUSTIFY;
+ s_joinserver_server_actions[i].x = 0;
+ s_joinserver_server_actions[i].y = 40 + i * 10;
+ s_joinserver_server_actions[i].callback = new mcallback() {
+ public void execute(Object o) {
+ JoinServerFunc(o);
+ }
+ };
+ s_joinserver_server_actions[i].statusbar = "press ENTER to connect";
+ }
+
+ Menu_AddItem(s_joinserver_menu, s_joinserver_address_book_action);
+ Menu_AddItem(s_joinserver_menu, s_joinserver_server_title);
+ Menu_AddItem(s_joinserver_menu, s_joinserver_search_action);
+
+ for (i = 0; i < 8; i++)
+ Menu_AddItem(s_joinserver_menu, s_joinserver_server_actions[i]);
+
+ Menu_Center(s_joinserver_menu);
+
+ SearchLocalGames();
+ }
+
+ static void JoinServer_MenuDraw() {
+ Banner("m_banner_join_server");
+ Menu_Draw(s_joinserver_menu);
+ }
+
+ static String JoinServer_MenuKey(int key) {
+ return Default_MenuKey(s_joinserver_menu, key);
+ }
+
+ /*
+ * =============================================================================
+ *
+ * ADDRESS BOOK MENU
+ *
+ * =============================================================================
+ */
+
+ static void Menu_JoinServer_f() {
+ JoinServer_MenuInit();
+ PushMenu(new xcommand_t() {
+ public void execute() {
+ JoinServer_MenuDraw();
+ }
+ }, new keyfunc_t() {
+ public String execute(int key) {
+ return JoinServer_MenuKey(key);
+ }
+ });
+ }
+
+ static void DMOptionsFunc(Object self) {
+ if (s_rules_box.curvalue == 1)
+ return;
+ Menu_DMOptions_f();
+ }
+
+ static void RulesChangeFunc(Object self) {
+ // DM
+ if (s_rules_box.curvalue == 0) {
+ s_maxclients_field.statusbar = null;
+ s_startserver_dmoptions_action.statusbar = null;
+ } else if (s_rules_box.curvalue == 1)
+ // coop // PGM
+ {
+ s_maxclients_field.statusbar = "4 maximum for cooperative";
+ if (Lib.atoi(s_maxclients_field.buffer.toString()) > 4)
+ s_maxclients_field.buffer = new StringBuffer("4");
+ s_startserver_dmoptions_action.statusbar = "N/A for cooperative";
+ }
+ // =====
+ // PGM
+ // ROGUE GAMES
+ else if (FS.Developer_searchpath(2) == 2) {
+ if (s_rules_box.curvalue == 2) // tag
+ {
+ s_maxclients_field.statusbar = null;
+ s_startserver_dmoptions_action.statusbar = null;
+ }
+ /*
+ * else if(s_rules_box.curvalue == 3) // deathball {
+ * s_maxclients_field.statusbar = null;
+ * s_startserver_dmoptions_action.statusbar = null; }
+ */
+ }
+ // PGM
+ // =====
+ }
+
+ static void StartServerActionFunc(Object self) {
+ //char startmap[1024];
+ String startmap;
+ int timelimit;
+ int fraglimit;
+ int maxclients;
+ String spot;
+
+ //strcpy(startmap, strchr(mapnames[s_startmap_list.curvalue], '\n') +
+ // 1);
+ String x = mapnames[s_startmap_list.curvalue];
+
+ int pos = x.indexOf('\n');
+ if (pos == -1)
+ startmap = x;
+ else
+ startmap = x.substring(pos + 1, x.length());
+
+ maxclients = Lib.atoi(s_maxclients_field.buffer.toString());
+ timelimit = Lib.atoi(s_timelimit_field.buffer.toString());
+ fraglimit = Lib.atoi(s_fraglimit_field.buffer.toString());
+
+ Cvar.setValue("maxclients", ClampCvar(maxclients, maxclients));
+ Cvar.setValue("timelimit", ClampCvar(timelimit, timelimit));
+ Cvar.setValue("fraglimit", ClampCvar(fraglimit, fraglimit));
+ Cvar.set("hostname", s_hostname_field.buffer.toString());
+ // Cvar.setValue ("deathmatch", !s_rules_box.curvalue );
+ // Cvar.setValue ("coop", s_rules_box.curvalue );
+
+ // PGM
+ if ((s_rules_box.curvalue < 2) || (FS.Developer_searchpath(2) != 2)) {
+ Cvar.setValue("deathmatch", 1 - s_rules_box.curvalue);
+ Cvar.setValue("coop", s_rules_box.curvalue);
+ Cvar.setValue("gamerules", 0);
+ } else {
+ Cvar.setValue("deathmatch", 1);
+ // deathmatch is always true for rogue games, right?
+ Cvar.setValue("coop", 0);
+ // FIXME - this might need to depend on which game we're running
+ Cvar.setValue("gamerules", s_rules_box.curvalue);
+ }
+ // PGM
+
+ spot = null;
+ if (s_rules_box.curvalue == 1) // PGM
+ {
+ if (Lib.Q_stricmp(startmap, "bunk1") == 0)
+ spot = "start";
+ else if (Lib.Q_stricmp(startmap, "mintro") == 0)
+ spot = "start";
+ else if (Lib.Q_stricmp(startmap, "fact1") == 0)
+ spot = "start";
+ else if (Lib.Q_stricmp(startmap, "power1") == 0)
+ spot = "pstart";
+ else if (Lib.Q_stricmp(startmap, "biggun") == 0)
+ spot = "bstart";
+ else if (Lib.Q_stricmp(startmap, "hangar1") == 0)
+ spot = "unitstart";
+ else if (Lib.Q_stricmp(startmap, "city1") == 0)
+ spot = "unitstart";
+ else if (Lib.Q_stricmp(startmap, "boss1") == 0)
+ spot = "bosstart";
+ }
+
+ if (spot != null) {
+ if (Globals.server_state != 0)
+ CommandBuffer.AddText("disconnect\n");
+ CommandBuffer.AddText("gamemap \"*" + startmap + "$" + spot + "\"\n");
+ } else {
+ CommandBuffer.AddText("map " + startmap + "\n");
+ }
+
+ ForceMenuOff();
+ }
+
+ static void StartServer_MenuInit() {
+
+ // =======
+ // PGM
+ // =======
+
+ byte[] buffer = null;
+ String mapsname;
+ String s;
+ int i;
+ RandomAccessFile fp;
+
+ /*
+ * * load the list of map names
+ */
+ mapsname = FS.Gamedir() + "/maps.lst";
+
+ // Check user dir first (default ~/.lwjake2)
+ if ((fp = Lib.fopen(mapsname, "r")) == null) {
+ // Check base dir first (baseq2 folder)
+ mapsname = FS.BaseGamedir() + "/maps.lst";
+ if ((fp = Lib.fopen(mapsname, "r")) == null) {
+ // Open the pak's maplist
+ buffer = FS.LoadFile("maps.lst");
+ if (buffer == null)
+ Com.Error(ERR_DROP, "couldn't find maps.lst\n");
+ } else {
+ try {
+ int len = (int) fp.length();
+ buffer = new byte[len];
+ fp.readFully(buffer);
+ } catch (Exception e) {
+ Com.Error(ERR_DROP, "couldn't load maps.lst\n");
+ }
+ }
+ } else {
+ try {
+ int len = (int) fp.length();
+ buffer = new byte[len];
+ fp.readFully(buffer);
+ } catch (Exception e) {
+ Com.Error(ERR_DROP, "couldn't load maps.lst\n");
+ }
+ }
+
+ s = new String(buffer);
+ String lines[] = s.split("\r\n");
+
+ nummaps = lines.length;
+
+ if (nummaps == 0)
+ Com.Error(ERR_DROP, "no maps in maps.lst\n");
+
+ mapnames = new String[nummaps];
+
+ for (i = 0; i < nummaps; i++) {
+ String shortname, longname, scratch;
+
+ Com.ParseHelp ph = new Com.ParseHelp(lines[i]);
+
+ shortname = Com.Parse(ph).toUpperCase();
+ longname = Com.Parse(ph);
+ scratch = longname + "\n" + shortname;
+ mapnames[i] = scratch;
+ }
+
+ if (fp != null) {
+ Lib.fclose(fp);
+ fp = null;
+
+ } else {
+ FS.FreeFile();
+ }
+
+ /*
+ * * initialize the menu stuff
+ */
+ s_startserver_menu.x = (int) (viddef.width * 0.50);
+ s_startserver_menu.nitems = 0;
+
+ s_startmap_list.type = MTYPE_SPINCONTROL;
+ s_startmap_list.x = 0;
+ s_startmap_list.y = 0;
+ s_startmap_list.name = "initial map";
+ s_startmap_list.itemnames = mapnames;
+
+ s_rules_box.type = MTYPE_SPINCONTROL;
+ s_rules_box.x = 0;
+ s_rules_box.y = 20;
+ s_rules_box.name = "rules";
+
+ // PGM - rogue games only available with rogue DLL.
+ if (FS.Developer_searchpath(2) == 2)
+ s_rules_box.itemnames = dm_coop_names_rogue;
+ else
+ s_rules_box.itemnames = dm_coop_names;
+ // PGM
+
+ if (Cvar.variableValue("coop") != 0)
+ s_rules_box.curvalue = 1;
+ else
+ s_rules_box.curvalue = 0;
+ s_rules_box.callback = new mcallback() {
+ public void execute(Object o) {
+ RulesChangeFunc(o);
+ }
+ };
+
+ s_timelimit_field.type = MTYPE_FIELD;
+ s_timelimit_field.name = "time limit";
+ s_timelimit_field.flags = QMF_NUMBERSONLY;
+ s_timelimit_field.x = 0;
+ s_timelimit_field.y = 36;
+ s_timelimit_field.statusbar = "0 = no limit";
+ s_timelimit_field.length = 3;
+ s_timelimit_field.visible_length = 3;
+ s_timelimit_field.buffer = new StringBuffer(Cvar
+ .variableString("timelimit"));
+
+ s_fraglimit_field.type = MTYPE_FIELD;
+ s_fraglimit_field.name = "frag limit";
+ s_fraglimit_field.flags = QMF_NUMBERSONLY;
+ s_fraglimit_field.x = 0;
+ s_fraglimit_field.y = 54;
+ s_fraglimit_field.statusbar = "0 = no limit";
+ s_fraglimit_field.length = 3;
+ s_fraglimit_field.visible_length = 3;
+ s_fraglimit_field.buffer = new StringBuffer(Cvar
+ .variableString("fraglimit"));
+
+ /*
+ * * maxclients determines the maximum number of players that can join *
+ * the game. If maxclients is only "1" then we should default the menu *
+ * option to 8 players, otherwise use whatever its current value is. *
+ * Clamping will be done when the server is actually started.
+ */
+ s_maxclients_field.type = MTYPE_FIELD;
+ s_maxclients_field.name = "max players";
+ s_maxclients_field.flags = QMF_NUMBERSONLY;
+ s_maxclients_field.x = 0;
+ s_maxclients_field.y = 72;
+ s_maxclients_field.statusbar = null;
+ s_maxclients_field.length = 3;
+ s_maxclients_field.visible_length = 3;
+ if (Cvar.variableValue("maxclients") == 1)
+ s_maxclients_field.buffer = new StringBuffer("8");
+ else
+ s_maxclients_field.buffer = new StringBuffer(Cvar
+ .variableString("maxclients"));
+
+ s_hostname_field.type = MTYPE_FIELD;
+ s_hostname_field.name = "hostname";
+ s_hostname_field.flags = 0;
+ s_hostname_field.x = 0;
+ s_hostname_field.y = 90;
+ s_hostname_field.statusbar = null;
+ s_hostname_field.length = 12;
+ s_hostname_field.visible_length = 12;
+ s_hostname_field.buffer = new StringBuffer(Cvar
+ .variableString("hostname"));
+ s_hostname_field.cursor = s_hostname_field.buffer.length();
+
+ s_startserver_dmoptions_action.type = MTYPE_ACTION;
+ s_startserver_dmoptions_action.name = " deathmatch flags";
+ s_startserver_dmoptions_action.flags = QMF_LEFT_JUSTIFY;
+ s_startserver_dmoptions_action.x = 24;
+ s_startserver_dmoptions_action.y = 108;
+ s_startserver_dmoptions_action.statusbar = null;
+ s_startserver_dmoptions_action.callback = new mcallback() {
+ public void execute(Object o) {
+ DMOptionsFunc(o);
+ }
+ };
+
+ s_startserver_start_action.type = MTYPE_ACTION;
+ s_startserver_start_action.name = " begin";
+ s_startserver_start_action.flags = QMF_LEFT_JUSTIFY;
+ s_startserver_start_action.x = 24;
+ s_startserver_start_action.y = 128;
+ s_startserver_start_action.callback = new mcallback() {
+ public void execute(Object o) {
+ StartServerActionFunc(o);
+ }
+ };
+
+ Menu_AddItem(s_startserver_menu, s_startmap_list);
+ Menu_AddItem(s_startserver_menu, s_rules_box);
+ Menu_AddItem(s_startserver_menu, s_timelimit_field);
+ Menu_AddItem(s_startserver_menu, s_fraglimit_field);
+ Menu_AddItem(s_startserver_menu, s_maxclients_field);
+ Menu_AddItem(s_startserver_menu, s_hostname_field);
+ Menu_AddItem(s_startserver_menu, s_startserver_dmoptions_action);
+ Menu_AddItem(s_startserver_menu, s_startserver_start_action);
+
+ Menu_Center(s_startserver_menu);
+
+ // call this now to set proper inital state
+ RulesChangeFunc(null);
+ }
+
+ static void StartServer_MenuDraw() {
+ Menu_Draw(s_startserver_menu);
+ }
+
+ static String StartServer_MenuKey(int key) {
+ if (key == K_ESCAPE) {
+ if (mapnames != null) {
+ int i;
+
+ for (i = 0; i < nummaps; i++)
+ mapnames[i] = null;
+
+ }
+ mapnames = null;
+ nummaps = 0;
+ }
+
+ return Default_MenuKey(s_startserver_menu, key);
+ }
+
+ static void Menu_StartServer_f() {
+ StartServer_MenuInit();
+ PushMenu(startServer_MenuDraw, startServer_MenuKey);
+ }
+
+ static void setvalue(int flags) {
+ Cvar.setValue("dmflags", flags);
+ dmoptions_statusbar = "dmflags = " + flags;
+ }
+
+ static void DMFlagCallback(Object self) {
+ menulist_s f = (menulist_s) self;
+ int flags;
+ int bit = 0;
+
+ flags = (int) Cvar.variableValue("dmflags");
+
+ if (f == s_friendlyfire_box) {
+ if (f.curvalue != 0)
+ flags &= ~DF_NO_FRIENDLY_FIRE;
+ else
+ flags |= DF_NO_FRIENDLY_FIRE;
+ setvalue(flags);
+ return;
+ } else if (f == s_falls_box) {
+ if (f.curvalue != 0)
+ flags &= ~DF_NO_FALLING;
+ else
+ flags |= DF_NO_FALLING;
+ setvalue(flags);
+ return;
+ } else if (f == s_instant_powerups_box) {
+ bit = DF_INSTANT_ITEMS;
+ } else if (f == s_allow_exit_box) {
+ bit = DF_ALLOW_EXIT;
+ } else if (f == s_powerups_box) {
+ if (f.curvalue != 0)
+ flags &= ~DF_NO_ITEMS;
+ else
+ flags |= DF_NO_ITEMS;
+ setvalue(flags);
+ return;
+ } else if (f == s_spawn_farthest_box) {
+ bit = DF_SPAWN_FARTHEST;
+ } else if (f == s_teamplay_box) {
+ if (f.curvalue == 1) {
+ flags |= DF_SKINTEAMS;
+ flags &= ~DF_MODELTEAMS;
+ } else if (f.curvalue == 2) {
+ flags |= DF_MODELTEAMS;
+ flags &= ~DF_SKINTEAMS;
+ } else {
+ flags &= ~(DF_MODELTEAMS | DF_SKINTEAMS);
+ }
+
+ setvalue(flags);
+ return;
+ } else if (f == s_samelevel_box) {
+ bit = DF_SAME_LEVEL;
+ } else if (f == s_force_respawn_box) {
+ bit = DF_FORCE_RESPAWN;
+ } else if (f == s_armor_box) {
+ if (f.curvalue != 0)
+ flags &= ~DF_NO_ARMOR;
+ else
+ flags |= DF_NO_ARMOR;
+ setvalue(flags);
+ return;
+ } else if (f == s_infinite_ammo_box) {
+ bit = DF_INFINITE_AMMO;
+ } else if (f == s_fixed_fov_box) {
+ bit = DF_FIXED_FOV;
+ } else if (f == s_quad_drop_box) {
+ bit = DF_QUAD_DROP;
+ }
+
+ // =======
+ // ROGUE
+ else if (FS.Developer_searchpath(2) == 2) {
+ if (f == s_no_mines_box) {
+ bit = DF_NO_MINES;
+ } else if (f == s_no_nukes_box) {
+ bit = DF_NO_NUKES;
+ } else if (f == s_stack_double_box) {
+ bit = DF_NO_STACK_DOUBLE;
+ } else if (f == s_no_spheres_box) {
+ bit = DF_NO_SPHERES;
+ }
+ }
+ // ROGUE
+ // =======
+
+ if (f != null) {
+ if (f.curvalue == 0)
+ flags &= ~bit;
+ else
+ flags |= bit;
+ }
+
+ Cvar.setValue("dmflags", flags);
+
+ dmoptions_statusbar = "dmflags = " + flags;
+
+ }
+
+ static void DMOptions_MenuInit() {
+
+ int dmflags = (int) Cvar.variableValue("dmflags");
+ int y = 0;
+
+ s_dmoptions_menu.x = (int) (viddef.width * 0.50);
+ s_dmoptions_menu.nitems = 0;
+
+ s_falls_box.type = MTYPE_SPINCONTROL;
+ s_falls_box.x = 0;
+ s_falls_box.y = y;
+ s_falls_box.name = "falling damage";
+ s_falls_box.callback = new mcallback() {
+ public void execute(Object o) {
+ DMFlagCallback(o);
+ }
+ };
+ s_falls_box.itemnames = yes_no_names;
+ s_falls_box.curvalue = (dmflags & DF_NO_FALLING) == 0 ? 1 : 0;
+
+ s_instant_powerups_box.type = MTYPE_SPINCONTROL;
+ s_instant_powerups_box.x = 0;
+ s_instant_powerups_box.y = y += 10;
+ s_instant_powerups_box.name = "instant powerups";
+ s_instant_powerups_box.callback = new mcallback() {
+ public void execute(Object o) {
+ DMFlagCallback(o);
+ }
+ };
+ s_instant_powerups_box.itemnames = yes_no_names;
+ s_instant_powerups_box.curvalue = (dmflags & DF_INSTANT_ITEMS) != 0 ? 1
+ : 0;
+
+ s_powerups_box.type = MTYPE_SPINCONTROL;
+ s_powerups_box.x = 0;
+ s_powerups_box.y = y += 10;
+ s_powerups_box.name = "allow powerups";
+ s_powerups_box.callback = new mcallback() {
+ public void execute(Object o) {
+ DMFlagCallback(o);
+ }
+ };
+ s_powerups_box.itemnames = yes_no_names;
+ s_powerups_box.curvalue = (dmflags & DF_NO_ITEMS) == 0 ? 1 : 0;
+
+ s_armor_box.type = MTYPE_SPINCONTROL;
+ s_armor_box.x = 0;
+ s_armor_box.y = y += 10;
+ s_armor_box.name = "allow armor";
+ s_armor_box.callback = new mcallback() {
+ public void execute(Object o) {
+ DMFlagCallback(o);
+ }
+ };
+ s_armor_box.itemnames = yes_no_names;
+ s_armor_box.curvalue = (dmflags & DF_NO_ARMOR) == 0 ? 1 : 0;
+
+ s_spawn_farthest_box.type = MTYPE_SPINCONTROL;
+ s_spawn_farthest_box.x = 0;
+ s_spawn_farthest_box.y = y += 10;
+ s_spawn_farthest_box.name = "spawn farthest";
+ s_spawn_farthest_box.callback = new mcallback() {
+ public void execute(Object o) {
+ DMFlagCallback(o);
+ }
+ };
+ s_spawn_farthest_box.itemnames = yes_no_names;
+ s_spawn_farthest_box.curvalue = (dmflags & DF_SPAWN_FARTHEST) != 0 ? 1
+ : 0;
+
+ s_samelevel_box.type = MTYPE_SPINCONTROL;
+ s_samelevel_box.x = 0;
+ s_samelevel_box.y = y += 10;
+ s_samelevel_box.name = "same map";
+ s_samelevel_box.callback = new mcallback() {
+ public void execute(Object o) {
+ DMFlagCallback(o);
+ }
+ };
+ s_samelevel_box.itemnames = yes_no_names;
+ s_samelevel_box.curvalue = (dmflags & DF_SAME_LEVEL) != 0 ? 1 : 0;
+
+ s_force_respawn_box.type = MTYPE_SPINCONTROL;
+ s_force_respawn_box.x = 0;
+ s_force_respawn_box.y = y += 10;
+ s_force_respawn_box.name = "force respawn";
+ s_force_respawn_box.callback = new mcallback() {
+ public void execute(Object o) {
+ DMFlagCallback(o);
+ }
+ };
+ s_force_respawn_box.itemnames = yes_no_names;
+ s_force_respawn_box.curvalue = (dmflags & DF_FORCE_RESPAWN) != 0 ? 1
+ : 0;
+
+ s_teamplay_box.type = MTYPE_SPINCONTROL;
+ s_teamplay_box.x = 0;
+ s_teamplay_box.y = y += 10;
+ s_teamplay_box.name = "teamplay";
+ s_teamplay_box.callback = new mcallback() {
+ public void execute(Object o) {
+ DMFlagCallback(o);
+ }
+ };
+ s_teamplay_box.itemnames = teamplay_names;
+
+ s_allow_exit_box.type = MTYPE_SPINCONTROL;
+ s_allow_exit_box.x = 0;
+ s_allow_exit_box.y = y += 10;
+ s_allow_exit_box.name = "allow exit";
+ s_allow_exit_box.callback = new mcallback() {
+ public void execute(Object o) {
+ DMFlagCallback(o);
+ }
+ };
+ s_allow_exit_box.itemnames = yes_no_names;
+ s_allow_exit_box.curvalue = (dmflags & DF_ALLOW_EXIT) != 0 ? 1 : 0;
+
+ s_infinite_ammo_box.type = MTYPE_SPINCONTROL;
+ s_infinite_ammo_box.x = 0;
+ s_infinite_ammo_box.y = y += 10;
+ s_infinite_ammo_box.name = "infinite ammo";
+ s_infinite_ammo_box.callback = new mcallback() {
+ public void execute(Object o) {
+ DMFlagCallback(o);
+ }
+ };
+ s_infinite_ammo_box.itemnames = yes_no_names;
+ s_infinite_ammo_box.curvalue = (dmflags & DF_INFINITE_AMMO) != 0 ? 1
+ : 0;
+
+ s_fixed_fov_box.type = MTYPE_SPINCONTROL;
+ s_fixed_fov_box.x = 0;
+ s_fixed_fov_box.y = y += 10;
+ s_fixed_fov_box.name = "fixed FOV";
+ s_fixed_fov_box.callback = new mcallback() {
+ public void execute(Object o) {
+ DMFlagCallback(o);
+ }
+ };
+ s_fixed_fov_box.itemnames = yes_no_names;
+ s_fixed_fov_box.curvalue = (dmflags & DF_FIXED_FOV) != 0 ? 1 : 0;
+
+ s_quad_drop_box.type = MTYPE_SPINCONTROL;
+ s_quad_drop_box.x = 0;
+ s_quad_drop_box.y = y += 10;
+ s_quad_drop_box.name = "quad drop";
+ s_quad_drop_box.callback = new mcallback() {
+ public void execute(Object o) {
+ DMFlagCallback(o);
+ }
+ };
+ s_quad_drop_box.itemnames = yes_no_names;
+ s_quad_drop_box.curvalue = (dmflags & DF_QUAD_DROP) != 0 ? 1 : 0;
+
+ s_friendlyfire_box.type = MTYPE_SPINCONTROL;
+ s_friendlyfire_box.x = 0;
+ s_friendlyfire_box.y = y += 10;
+ s_friendlyfire_box.name = "friendly fire";
+ s_friendlyfire_box.callback = new mcallback() {
+ public void execute(Object o) {
+ DMFlagCallback(o);
+ }
+ };
+ s_friendlyfire_box.itemnames = yes_no_names;
+ s_friendlyfire_box.curvalue = (dmflags & DF_NO_FRIENDLY_FIRE) == 0 ? 1
+ : 0;
+
+ // ============
+ // ROGUE
+ if (FS.Developer_searchpath(2) == 2) {
+ s_no_mines_box.type = MTYPE_SPINCONTROL;
+ s_no_mines_box.x = 0;
+ s_no_mines_box.y = y += 10;
+ s_no_mines_box.name = "remove mines";
+ s_no_mines_box.callback = new mcallback() {
+ public void execute(Object o) {
+ DMFlagCallback(o);
+ }
+ };
+ s_no_mines_box.itemnames = yes_no_names;
+ s_no_mines_box.curvalue = (dmflags & DF_NO_MINES) != 0 ? 1 : 0;
+
+ s_no_nukes_box.type = MTYPE_SPINCONTROL;
+ s_no_nukes_box.x = 0;
+ s_no_nukes_box.y = y += 10;
+ s_no_nukes_box.name = "remove nukes";
+ s_no_nukes_box.callback = new mcallback() {
+ public void execute(Object o) {
+ DMFlagCallback(o);
+ }
+ };
+ s_no_nukes_box.itemnames = yes_no_names;
+ s_no_nukes_box.curvalue = (dmflags & DF_NO_NUKES) != 0 ? 1 : 0;
+
+ s_stack_double_box.type = MTYPE_SPINCONTROL;
+ s_stack_double_box.x = 0;
+ s_stack_double_box.y = y += 10;
+ s_stack_double_box.name = "2x/4x stacking off";
+ s_stack_double_box.callback = new mcallback() {
+ public void execute(Object o) {
+ DMFlagCallback(o);
+ }
+ };
+ s_stack_double_box.itemnames = yes_no_names;
+ s_stack_double_box.curvalue = (dmflags & DF_NO_STACK_DOUBLE);
+
+ s_no_spheres_box.type = MTYPE_SPINCONTROL;
+ s_no_spheres_box.x = 0;
+ s_no_spheres_box.y = y += 10;
+ s_no_spheres_box.name = "remove spheres";
+ s_no_spheres_box.callback = new mcallback() {
+ public void execute(Object o) {
+ DMFlagCallback(o);
+ }
+ };
+ s_no_spheres_box.itemnames = yes_no_names;
+ s_no_spheres_box.curvalue = (dmflags & DF_NO_SPHERES) != 0 ? 1 : 0;
+
+ }
+ // ROGUE
+ // ============
+
+ Menu_AddItem(s_dmoptions_menu, s_falls_box);
+ Menu_AddItem(s_dmoptions_menu, s_instant_powerups_box);
+ Menu_AddItem(s_dmoptions_menu, s_powerups_box);
+ Menu_AddItem(s_dmoptions_menu, s_armor_box);
+ Menu_AddItem(s_dmoptions_menu, s_spawn_farthest_box);
+ Menu_AddItem(s_dmoptions_menu, s_samelevel_box);
+ Menu_AddItem(s_dmoptions_menu, s_force_respawn_box);
+ Menu_AddItem(s_dmoptions_menu, s_teamplay_box);
+ Menu_AddItem(s_dmoptions_menu, s_allow_exit_box);
+ Menu_AddItem(s_dmoptions_menu, s_infinite_ammo_box);
+ Menu_AddItem(s_dmoptions_menu, s_fixed_fov_box);
+ Menu_AddItem(s_dmoptions_menu, s_quad_drop_box);
+ Menu_AddItem(s_dmoptions_menu, s_friendlyfire_box);
+
+ // =======
+ // ROGUE
+ if (FS.Developer_searchpath(2) == 2) {
+ Menu_AddItem(s_dmoptions_menu, s_no_mines_box);
+ Menu_AddItem(s_dmoptions_menu, s_no_nukes_box);
+ Menu_AddItem(s_dmoptions_menu, s_stack_double_box);
+ Menu_AddItem(s_dmoptions_menu, s_no_spheres_box);
+ }
+ // ROGUE
+ // =======
+
+ Menu_Center(s_dmoptions_menu);
+
+ // set the original dmflags statusbar
+ DMFlagCallback(null);
+ Menu_SetStatusBar(s_dmoptions_menu, dmoptions_statusbar);
+ }
+
+ static void DMOptions_MenuDraw() {
+ Menu_Draw(s_dmoptions_menu);
+ }
+
+ static String DMOptions_MenuKey(int key) {
+ return Default_MenuKey(s_dmoptions_menu, key);
+ }
+
+ static void Menu_DMOptions_f() {
+ DMOptions_MenuInit();
+ PushMenu(new xcommand_t() {
+ public void execute() {
+ DMOptions_MenuDraw();
+ }
+ }, new keyfunc_t() {
+ public String execute(int key) {
+ return DMOptions_MenuKey(key);
+ }
+ });
+ }
+
+ static void DownloadCallback(Object self) {
+ menulist_s f = (menulist_s) self;
+
+ if (f == s_allow_download_box) {
+ Cvar.setValue("allow_download", f.curvalue);
+ } else if (f == s_allow_download_maps_box) {
+ Cvar.setValue("allow_download_maps", f.curvalue);
+ } else if (f == s_allow_download_models_box) {
+ Cvar.setValue("allow_download_models", f.curvalue);
+ } else if (f == s_allow_download_players_box) {
+ Cvar.setValue("allow_download_players", f.curvalue);
+ } else if (f == s_allow_download_sounds_box) {
+ Cvar.setValue("allow_download_sounds", f.curvalue);
+ }
+ }
+
+ static void DownloadOptions_MenuInit() {
+
+ int y = 0;
+
+ s_downloadoptions_menu.x = (int) (viddef.width * 0.50);
+ s_downloadoptions_menu.nitems = 0;
+
+ s_download_title.type = MTYPE_SEPARATOR;
+ s_download_title.name = "Download Options";
+ s_download_title.x = 48;
+ s_download_title.y = y;
+
+ s_allow_download_box.type = MTYPE_SPINCONTROL;
+ s_allow_download_box.x = 0;
+ s_allow_download_box.y = y += 20;
+ s_allow_download_box.name = "allow downloading";
+ s_allow_download_box.callback = new mcallback() {
+ public void execute(Object o) {
+ DownloadCallback(o);
+ }
+ };
+ s_allow_download_box.itemnames = yes_no_names;
+ s_allow_download_box.curvalue = (Cvar.variableValue("allow_download") != 0) ? 1
+ : 0;
+
+ s_allow_download_maps_box.type = MTYPE_SPINCONTROL;
+ s_allow_download_maps_box.x = 0;
+ s_allow_download_maps_box.y = y += 20;
+ s_allow_download_maps_box.name = "maps";
+ s_allow_download_maps_box.callback = new mcallback() {
+ public void execute(Object o) {
+ DownloadCallback(o);
+ }
+ };
+ s_allow_download_maps_box.itemnames = yes_no_names;
+ s_allow_download_maps_box.curvalue = (Cvar
+ .variableValue("allow_download_maps") != 0) ? 1 : 0;
+
+ s_allow_download_players_box.type = MTYPE_SPINCONTROL;
+ s_allow_download_players_box.x = 0;
+ s_allow_download_players_box.y = y += 10;
+ s_allow_download_players_box.name = "player models/skins";
+ s_allow_download_players_box.callback = new mcallback() {
+ public void execute(Object o) {
+ DownloadCallback(o);
+ }
+ };
+ s_allow_download_players_box.itemnames = yes_no_names;
+ s_allow_download_players_box.curvalue = (Cvar
+ .variableValue("allow_download_players") != 0) ? 1 : 0;
+
+ s_allow_download_models_box.type = MTYPE_SPINCONTROL;
+ s_allow_download_models_box.x = 0;
+ s_allow_download_models_box.y = y += 10;
+ s_allow_download_models_box.name = "models";
+ s_allow_download_models_box.callback = new mcallback() {
+ public void execute(Object o) {
+ DownloadCallback(o);
+ }
+ };
+ s_allow_download_models_box.itemnames = yes_no_names;
+ s_allow_download_models_box.curvalue = (Cvar
+ .variableValue("allow_download_models") != 0) ? 1 : 0;
+
+ s_allow_download_sounds_box.type = MTYPE_SPINCONTROL;
+ s_allow_download_sounds_box.x = 0;
+ s_allow_download_sounds_box.y = y += 10;
+ s_allow_download_sounds_box.name = "sounds";
+ s_allow_download_sounds_box.callback = new mcallback() {
+ public void execute(Object o) {
+ DownloadCallback(o);
+ }
+ };
+ s_allow_download_sounds_box.itemnames = yes_no_names;
+ s_allow_download_sounds_box.curvalue = (Cvar
+ .variableValue("allow_download_sounds") != 0) ? 1 : 0;
+
+ Menu_AddItem(s_downloadoptions_menu, s_download_title);
+ Menu_AddItem(s_downloadoptions_menu, s_allow_download_box);
+ Menu_AddItem(s_downloadoptions_menu, s_allow_download_maps_box);
+ Menu_AddItem(s_downloadoptions_menu, s_allow_download_players_box);
+ Menu_AddItem(s_downloadoptions_menu, s_allow_download_models_box);
+ Menu_AddItem(s_downloadoptions_menu, s_allow_download_sounds_box);
+
+ Menu_Center(s_downloadoptions_menu);
+
+ // skip over title
+ if (s_downloadoptions_menu.cursor == 0)
+ s_downloadoptions_menu.cursor = 1;
+ }
+
+ static void DownloadOptions_MenuDraw() {
+ Menu_Draw(s_downloadoptions_menu);
+ }
+
+ static String DownloadOptions_MenuKey(int key) {
+ return Default_MenuKey(s_downloadoptions_menu, key);
+ }
+
+ static void Menu_DownloadOptions_f() {
+ DownloadOptions_MenuInit();
+ PushMenu(new xcommand_t() {
+ public void execute() {
+ DownloadOptions_MenuDraw();
+ }
+ }, new keyfunc_t() {
+ public String execute(int key) {
+ return DownloadOptions_MenuKey(key);
+ }
+ });
+ }
+
+ static void AddressBook_MenuInit() {
+ s_addressbook_menu.x = viddef.width / 2 - 142;
+ s_addressbook_menu.y = viddef.height / 2 - 58;
+ s_addressbook_menu.nitems = 0;
+
+ for (int i = 0; i < NUM_ADDRESSBOOK_ENTRIES; i++) {
+ CvarT adr = Cvar.get("adr" + i, "", CVAR_ARCHIVE);
+
+ s_addressbook_fields[i].type = MTYPE_FIELD;
+ s_addressbook_fields[i].name = null;
+ s_addressbook_fields[i].callback = null;
+ s_addressbook_fields[i].x = 0;
+ s_addressbook_fields[i].y = i * 18;
+ s_addressbook_fields[i].localdata[0] = i;
+ // put the cursor to the end of text for editing
+ s_addressbook_fields[i].cursor = adr.string.length();
+ s_addressbook_fields[i].length = 60;
+ s_addressbook_fields[i].visible_length = 30;
+
+ s_addressbook_fields[i].buffer = new StringBuffer(adr.string);
+
+ Menu_AddItem(s_addressbook_menu, s_addressbook_fields[i]);
+ }
+ }
+
+ static String AddressBook_MenuKey_f(int key) {
+ if (key == K_ESCAPE) {
+ for (int index = 0; index < NUM_ADDRESSBOOK_ENTRIES; index++) {
+ Cvar.set("adr" + index, s_addressbook_fields[index].buffer.toString());
+ }
+ }
+ return Default_MenuKey(s_addressbook_menu, key);
+ }
+
+ static void AddressBook_MenuDraw_f() {
+ Banner("m_banner_addressbook");
+ Menu_Draw(s_addressbook_menu);
+ }
+
+ static void Menu_AddressBook_f() {
+ AddressBook_MenuInit();
+ PushMenu(new xcommand_t() {
+ public void execute() {
+ AddressBook_MenuDraw_f();
+ }
+ }, new keyfunc_t() {
+ public String execute(int key) {
+ return AddressBook_MenuKey_f(key);
+ }
+ });
+ }
+
+ static void DownloadOptionsFunc(Object self) {
+ Menu_DownloadOptions_f();
+ }
+
+ static void HandednessCallback(Object unused) {
+ Cvar.setValue("hand", s_player_handedness_box.curvalue);
+ }
+
+ static void RateCallback(Object unused) {
+ if (s_player_rate_box.curvalue != rate_tbl.length - 1) //sizeof(rate_tbl)
+ // / sizeof(*
+ // rate_tbl) - 1)
+ Cvar.setValue("rate", rate_tbl[s_player_rate_box.curvalue]);
+ }
+
+ static void ModelCallback(Object unused) {
+ s_player_skin_box.itemnames = s_pmi[s_player_model_box.curvalue].skindisplaynames;
+ s_player_skin_box.curvalue = 0;
+ }
+
+ static boolean IconOfSkinExists(String skin, String pcxfiles[],
+ int npcxfiles) {
+
+ String scratch;
+
+ //strcpy(scratch, skin);
+ scratch = skin;
+ int pos = scratch.lastIndexOf('.');
+ if (pos != -1)
+ scratch = scratch.substring(0, pos) + "_i.pcx";
+
+ else
+ scratch += "_i.pcx";
+
+ for (int i = 0; i < npcxfiles; i++) {
+ if (pcxfiles[i].equals(scratch))
+ return true;
+ }
+
+ return false;
+ }
+
+ static void PlayerConfig_ScanDirectories() {
+ //char findname[1024];
+ String findname;
+ //char scratch[1024];
+ String scratch;
+
+ int ndirs = 0, npms = 0;
+ int a, b, c;
+ String dirnames[];
+
+ String path = null;
+
+ int i;
+
+ //extern String * FS_ListFiles(String , int *, unsigned, unsigned);
+
+ s_numplayermodels = 0;
+
+ /*
+ * * get a list of directories
+ */
+ do {
+ path = FS.NextPath(path);
+ findname = path + "/players/*.*";
+
+ if ((dirnames = FS.ListFiles(findname, 0, SFF_SUBDIR)) != null) {
+ ndirs = dirnames.length;
+ break;
+ }
+ } while (path != null);
+
+ if (dirnames == null)
+ return;
+
+ /*
+ * * go through the subdirectories
+ */
+ npms = ndirs;
+ if (npms > MAX_PLAYERMODELS)
+ npms = MAX_PLAYERMODELS;
+
+ for (i = 0; i < npms; i++) {
+ int k, s;
+ //String a, b, c;
+ String pcxnames[];
+ String skinnames[];
+ int npcxfiles;
+ int nskins = 0;
+
+ if (dirnames[i] == null)
+ continue;
+
+ // verify the existence of tris.md2
+ scratch = dirnames[i];
+ scratch += "/tris.md2";
+ if (Sys.FindFirst(scratch, 0, SFF_SUBDIR | SFF_HIDDEN | SFF_SYSTEM) == null) {
+ //free(dirnames[i]);
+ dirnames[i] = null;
+ Sys.FindClose();
+ continue;
+ }
+ Sys.FindClose();
+
+ // verify the existence of at least one pcx skin
+ scratch = dirnames[i] + "/*.pcx";
+ pcxnames = FS.ListFiles(scratch, 0, 0);
+ npcxfiles = pcxnames.length;
+
+ // count valid skins, which consist of a skin with a matching "_i"
+ // icon
+ for (k = 0; k < npcxfiles - 1; k++) {
+ if (!pcxnames[k].endsWith("_i.pcx")) {
+ //if (!strstr(pcxnames[k], "_i.pcx")) {
+ if (IconOfSkinExists(pcxnames[k], pcxnames, npcxfiles)) {
+ nskins++;
+ }
+ }
+ }
+ if (nskins == 0)
+ continue;
+
+ skinnames = new String[nskins + 1]; //malloc(sizeof(String) *
+ // (nskins + 1));
+ //memset(skinnames, 0, sizeof(String) * (nskins + 1));
+
+ // copy the valid skins
+ for (s = 0, k = 0; k < npcxfiles; k++) {
+
+ if (!pcxnames[k].contains("_i.pcx")) {
+ if (IconOfSkinExists(pcxnames[k], pcxnames, npcxfiles)) {
+ a = pcxnames[k].lastIndexOf('/');
+ b = pcxnames[k].lastIndexOf('\\');
+
+ if (a > b)
+ c = a;
+ else
+ c = b;
+
+ scratch = pcxnames[k].substring(c + 1, pcxnames[k]
+ .length());
+ int pos = scratch.lastIndexOf('.');
+ if (pos != -1)
+ scratch = scratch.substring(0, pos);
+
+ skinnames[s] = scratch;
+ s++;
+ }
+ }
+ }
+
+ // at this point we have a valid player model
+ if (s_pmi[s_numplayermodels] == null)
+ s_pmi[s_numplayermodels] = new playermodelinfo_s();
+
+ s_pmi[s_numplayermodels].nskins = nskins;
+ s_pmi[s_numplayermodels].skindisplaynames = skinnames;
+
+ // make short name for the model
+ a = dirnames[i].lastIndexOf('/');
+ b = dirnames[i].lastIndexOf('\\');
+
+ if (a > b)
+ c = a;
+ else
+ c = b;
+
+ s_pmi[s_numplayermodels].displayname = dirnames[i].substring(c + 1);
+ s_pmi[s_numplayermodels].directory = dirnames[i].substring(c + 1);
+
+ s_numplayermodels++;
+ }
+
+ }
+
+ static int pmicmpfnc(playermodelinfo_s a, playermodelinfo_s b) {
+
+ /*
+ * * sort by male, female, then alphabetical
+ */
+ if (a.directory.equals("male"))
+ return -1;
+ else if (b.directory.equals("male"))
+ return 1;
+
+ if (a.directory.equals("female"))
+ return -1;
+ else if (b.directory.equals("female"))
+ return 1;
+
+ return a.directory.compareTo(b.directory);
+ }
+
+ static boolean PlayerConfig_MenuInit() {
+ /*
+ * extern CvarT * name; extern CvarT * team; extern CvarT * skin;
+ */
+ //har currentdirectory[1024];
+ String currentdirectory;
+ //char currentskin[1024];
+ String currentskin;
+
+ int i = 0;
+
+ int currentdirectoryindex = 0;
+ int currentskinindex = 0;
+
+ CvarT hand = Cvar.get("hand", "0", CVAR_USERINFO | CVAR_ARCHIVE);
+
+ PlayerConfig_ScanDirectories();
+
+ if (s_numplayermodels == 0)
+ return false;
+
+ if (hand.value < 0 || hand.value > 2)
+ Cvar.setValue("hand", 0);
+
+ currentdirectory = skin.string;
+
+ if (currentdirectory.lastIndexOf('/') != -1) {
+ currentskin = Lib.rightFrom(currentdirectory, '/');
+ currentdirectory = Lib.leftFrom(currentdirectory, '/');
+ } else if (currentdirectory.lastIndexOf('\\') != -1) {
+ currentskin = Lib.rightFrom(currentdirectory, '\\');
+ currentdirectory = Lib.leftFrom(currentdirectory, '\\');
+ } else {
+ currentdirectory = "male";
+ currentskin = "grunt";
+ }
+
+ //qsort(s_pmi, s_numplayermodels, sizeof(s_pmi[0]), pmicmpfnc);
+ Arrays.sort(s_pmi, 0, s_numplayermodels, Menu::pmicmpfnc);
+
+ //memset(s_pmnames, 0, sizeof(s_pmnames));
+ s_pmnames = new String[MAX_PLAYERMODELS];
+
+ for (i = 0; i < s_numplayermodels; i++) {
+ s_pmnames[i] = s_pmi[i].displayname;
+ if (Lib.Q_stricmp(s_pmi[i].directory, currentdirectory) == 0) {
+ int j;
+
+ currentdirectoryindex = i;
+
+ for (j = 0; j < s_pmi[i].nskins; j++) {
+ if (Lib
+ .Q_stricmp(s_pmi[i].skindisplaynames[j],
+ currentskin) == 0) {
+ currentskinindex = j;
+ break;
+ }
+ }
+ }
+ }
+
+ s_player_config_menu.x = viddef.width / 2 - 95;
+ s_player_config_menu.y = viddef.height / 2 - 97;
+ s_player_config_menu.nitems = 0;
+
+ s_player_name_field.type = MTYPE_FIELD;
+ s_player_name_field.name = "name";
+ s_player_name_field.callback = null;
+ s_player_name_field.x = 0;
+ s_player_name_field.y = 0;
+ s_player_name_field.length = 20;
+ s_player_name_field.visible_length = 20;
+ s_player_name_field.buffer = new StringBuffer(name.string);
+ s_player_name_field.cursor = name.string.length();
+
+ s_player_model_title.type = MTYPE_SEPARATOR;
+ s_player_model_title.name = "model";
+ s_player_model_title.x = -8;
+ s_player_model_title.y = 60;
+
+ s_player_model_box.type = MTYPE_SPINCONTROL;
+ s_player_model_box.x = -56;
+ s_player_model_box.y = 70;
+ s_player_model_box.callback = new mcallback() {
+ public void execute(Object o) {
+ ModelCallback(o);
+ }
+ };
+ s_player_model_box.cursor_offset = -48;
+ s_player_model_box.curvalue = currentdirectoryindex;
+ s_player_model_box.itemnames = s_pmnames;
+
+ s_player_skin_title.type = MTYPE_SEPARATOR;
+ s_player_skin_title.name = "skin";
+ s_player_skin_title.x = -16;
+ s_player_skin_title.y = 84;
+
+ s_player_skin_box.type = MTYPE_SPINCONTROL;
+ s_player_skin_box.x = -56;
+ s_player_skin_box.y = 94;
+ s_player_skin_box.name = null;
+ s_player_skin_box.callback = null;
+ s_player_skin_box.cursor_offset = -48;
+ s_player_skin_box.curvalue = currentskinindex;
+ s_player_skin_box.itemnames = s_pmi[currentdirectoryindex].skindisplaynames;
+
+ s_player_hand_title.type = MTYPE_SEPARATOR;
+ s_player_hand_title.name = "handedness";
+ s_player_hand_title.x = 32;
+ s_player_hand_title.y = 108;
+
+ s_player_handedness_box.type = MTYPE_SPINCONTROL;
+ s_player_handedness_box.x = -56;
+ s_player_handedness_box.y = 118;
+ s_player_handedness_box.name = null;
+ s_player_handedness_box.cursor_offset = -48;
+ s_player_handedness_box.callback = new mcallback() {
+ public void execute(Object o) {
+ HandednessCallback(o);
+ }
+ };
+ s_player_handedness_box.curvalue = (int) Cvar.variableValue("hand");
+ s_player_handedness_box.itemnames = handedness;
+
+ for (i = 0; i < rate_tbl.length - 1; i++)
+ if (Cvar.variableValue("rate") == rate_tbl[i])
+ break;
+
+ s_player_rate_title.type = MTYPE_SEPARATOR;
+ s_player_rate_title.name = "connect speed";
+ s_player_rate_title.x = 56;
+ s_player_rate_title.y = 156;
+
+ s_player_rate_box.type = MTYPE_SPINCONTROL;
+ s_player_rate_box.x = -56;
+ s_player_rate_box.y = 166;
+ s_player_rate_box.name = null;
+ s_player_rate_box.cursor_offset = -48;
+ s_player_rate_box.callback = new mcallback() {
+ public void execute(Object o) {
+ RateCallback(o);
+ }
+ };
+ s_player_rate_box.curvalue = i;
+ s_player_rate_box.itemnames = rate_names;
+
+ s_player_download_action.type = MTYPE_ACTION;
+ s_player_download_action.name = "download options";
+ s_player_download_action.flags = QMF_LEFT_JUSTIFY;
+ s_player_download_action.x = -24;
+ s_player_download_action.y = 186;
+ s_player_download_action.statusbar = null;
+ s_player_download_action.callback = new mcallback() {
+ public void execute(Object o) {
+ DownloadOptionsFunc(o);
+ }
+ };
+
+ Menu_AddItem(s_player_config_menu, s_player_name_field);
+ Menu_AddItem(s_player_config_menu, s_player_model_title);
+ Menu_AddItem(s_player_config_menu, s_player_model_box);
+ if (s_player_skin_box.itemnames != null) {
+ Menu_AddItem(s_player_config_menu, s_player_skin_title);
+ Menu_AddItem(s_player_config_menu, s_player_skin_box);
+ }
+ Menu_AddItem(s_player_config_menu, s_player_hand_title);
+ Menu_AddItem(s_player_config_menu, s_player_handedness_box);
+ Menu_AddItem(s_player_config_menu, s_player_rate_title);
+ Menu_AddItem(s_player_config_menu, s_player_rate_box);
+ Menu_AddItem(s_player_config_menu, s_player_download_action);
+
+ return true;
+ }
+
+ static void PlayerConfig_MenuDraw() {
+
+ refdef_t refdef = new refdef_t();
+ //char scratch[MAX_QPATH];
+ String scratch;
+
+ //memset(refdef, 0, sizeof(refdef));
+
+ refdef.x = viddef.width / 2;
+ refdef.y = viddef.height / 2 - 72;
+ refdef.width = 144;
+ refdef.height = 168;
+ refdef.fov_x = 40;
+ refdef.fov_y = Math3D
+ .calcFov(refdef.fov_x, refdef.width, refdef.height);
+ refdef.time = clientStaticT.realtime * 0.001f;
+
+ if (s_pmi[s_player_model_box.curvalue].skindisplaynames != null) {
+
+ entity.clear();
+
+ scratch = "players/" + s_pmi[s_player_model_box.curvalue].directory
+ + "/tris.md2";
+
+ entity.model = re.RegisterModel(scratch);
+
+ scratch = "players/"
+ + s_pmi[s_player_model_box.curvalue].directory
+ + "/"
+ + s_pmi[s_player_model_box.curvalue].skindisplaynames[s_player_skin_box.curvalue]
+ + ".pcx";
+
+ entity.skin = re.RegisterSkin(scratch);
+ entity.flags = RF_FULLBRIGHT;
+ entity.origin[0] = 80;
+ entity.origin[1] = 0;
+ entity.origin[2] = 0;
+ Math3D.vectorCopy(entity.origin, entity.oldorigin);
+ entity.frame = 0;
+ entity.oldframe = 0;
+ entity.backlerp = 0.0f;
+ entity.angles[1] = yaw++;
+ if (++yaw > 360)
+ yaw -= 360;
+
+ refdef.areabits = null;
+ refdef.num_entities = 1;
+ refdef.entities = new entity_t[]{entity};
+ refdef.lightstyles = null;
+ refdef.rdflags = RDF_NOWORLDMODEL;
+
+ Menu_Draw(s_player_config_menu);
+
+ DrawTextBox(
+ (int) ((refdef.x) * (320.0F / viddef.width) - 8),
+ (int) ((viddef.height / 2) * (240.0F / viddef.height) - 77),
+ refdef.width / 8, refdef.height / 8);
+ refdef.height += 4;
+
+ re.RenderFrame(refdef);
+
+ scratch = "/players/"
+ + s_pmi[s_player_model_box.curvalue].directory
+ + "/"
+ + s_pmi[s_player_model_box.curvalue].skindisplaynames[s_player_skin_box.curvalue]
+ + "_i.pcx";
+
+ re.DrawPic(s_player_config_menu.x - 40, refdef.y, scratch);
+ }
+ }
+
+ static String PlayerConfig_MenuKey(int key) {
+ int i;
+
+ if (key == K_ESCAPE) {
+ String scratch;
+
+ Cvar.set("name", s_player_name_field.buffer.toString());
+
+ scratch = s_pmi[s_player_model_box.curvalue].directory
+ + "/"
+ + s_pmi[s_player_model_box.curvalue].skindisplaynames[s_player_skin_box.curvalue];
+
+ Cvar.set("skin", scratch);
+
+ for (i = 0; i < s_numplayermodels; i++) {
+ int j;
+
+ for (j = 0; j < s_pmi[i].nskins; j++) {
+ if (s_pmi[i].skindisplaynames[j] != null)
+ s_pmi[i].skindisplaynames[j] = null;
+ }
+ s_pmi[i].skindisplaynames = null;
+ s_pmi[i].nskins = 0;
+ }
+ }
+ return Default_MenuKey(s_player_config_menu, key);
+ }
+
+ static void Menu_PlayerConfig_f() {
+ if (!PlayerConfig_MenuInit()) {
+ Menu_SetStatusBar(s_multiplayer_menu,
+ "No valid player models found");
+ return;
+ }
+ Menu_SetStatusBar(s_multiplayer_menu, null);
+ PushMenu(new xcommand_t() {
+ public void execute() {
+ PlayerConfig_MenuDraw();
+ }
+ }, new keyfunc_t() {
+ public String execute(int key) {
+ return PlayerConfig_MenuKey(key);
+ }
+ });
+ }
+
+ static String Quit_Key(int key) {
+ switch (key) {
+ case K_ESCAPE:
+ case 'n':
+ case 'N':
+ PopMenu();
+ break;
+
+ case 'Y':
+ case 'y':
+ clientStaticT.key_dest = key_console;
+ Client.Quit_f.execute();
+ break;
+
+ default:
+ break;
+ }
+
+ return null;
+
+ }
+
+ static void Quit_Draw() {
+ int w, h;
+ Dimension d = new Dimension();
+ re.DrawGetPicSize(d, "quit");
+ w = d.width;
+ h = d.height;
+ re.DrawPic((viddef.width - w) / 2, (viddef.height - h) / 2, "quit");
+ }
+
+ static void Menu_Quit_f() {
+ PushMenu(new xcommand_t() {
+ public void execute() {
+ Quit_Draw();
+ }
+ }, new keyfunc_t() {
+ public String execute(int key) {
+ return Quit_Key(key);
+ }
+ });
+ }
+
+ /**
+ * Init
+ */
+ public static void Init() {
+ Cmd.AddCommand("menu_main", Menu_Main);
+ Cmd.AddCommand("menu_game", Menu_Game);
+ Cmd.AddCommand("menu_loadgame", Menu_LoadGame);
+ Cmd.AddCommand("menu_savegame", Menu_SaveGame);
+ Cmd.AddCommand("menu_joinserver", Menu_JoinServer);
+ Cmd.AddCommand("menu_addressbook", Menu_AddressBook);
+ Cmd.AddCommand("menu_startserver", Menu_StartServer);
+ Cmd.AddCommand("menu_dmoptions", Menu_DMOptions);
+ Cmd.AddCommand("menu_playerconfig", Menu_PlayerConfig);
+ Cmd.AddCommand("menu_downloadoptions", Menu_DownloadOptions);
+ Cmd.AddCommand("menu_credits", Menu_Credits);
+ Cmd.AddCommand("menu_multiplayer", Menu_Multiplayer);
+ Cmd.AddCommand("menu_video", Menu_Video);
+ Cmd.AddCommand("menu_options", Menu_Options);
+ Cmd.AddCommand("menu_keys", Menu_Keys);
+ Cmd.AddCommand("menu_quit", Menu_Quit);
+
+ for (int i = 0; i < m_layers.length; i++) {
+ m_layers[i] = new menulayer_t();
+ }
+ }
+
+ /*
+ * ================= Draw =================
+ */
+ static void Draw() {
+ if (clientStaticT.key_dest != key_menu)
+ return;
+
+ // repaint everything next frame
+ SCR.DirtyScreen();
+
+ // dim everything behind it down
+ if (clientStateT.cinematictime > 0)
+ re.DrawFill(0, 0, viddef.width, viddef.height, 0);
+ else
+ re.DrawFadeScreen();
+
+ m_drawfunc.execute();
+
+ // delay playing the enter sound until after the
+ // menu has been drawn, to avoid delay while
+ // caching images
+ if (m_entersound) {
+ S.StartLocalSound(menu_in_sound);
+ m_entersound = false;
+ }
+ }
+
+ /*
+ * ================= Keydown =================
+ */
+ static void Keydown(int key) {
+ String s;
+
+ if (m_keyfunc != null)
+ if ((s = m_keyfunc.execute(key)) != null)
+ S.StartLocalSound(s);
+ }
+
+ public static void Action_DoEnter(menuaction_s a) {
+ if (a.callback != null)
+ a.callback.execute(a);
+ }
+
+ public static void Action_Draw(menuaction_s a) {
+ if ((a.flags & QMF_LEFT_JUSTIFY) != 0) {
+ if ((a.flags & QMF_GRAYED) != 0)
+ Menu_DrawStringDark(a.x + a.parent.x + LCOLUMN_OFFSET, a.y
+ + a.parent.y, a.name);
+ else
+ Menu_DrawString(a.x + a.parent.x + LCOLUMN_OFFSET, a.y
+ + a.parent.y, a.name);
+ } else {
+ if ((a.flags & QMF_GRAYED) != 0)
+ Menu_DrawStringR2LDark(a.x + a.parent.x + LCOLUMN_OFFSET, a.y
+ + a.parent.y, a.name);
+ else
+ Menu_DrawStringR2L(a.x + a.parent.x + LCOLUMN_OFFSET, a.y
+ + a.parent.y, a.name);
+ }
+ if (a.ownerdraw != null)
+ a.ownerdraw.execute(a);
+ }
+
+ /*
+ * =======================================================================
+ *
+ * QUIT MENU
+ *
+ * =======================================================================
+ */
+
+ public static void Field_DoEnter(menufield_s f) {
+ if (f.callback != null) {
+ f.callback.execute(f);
+ }
+ }
+
+ public static void Field_Draw(menufield_s f) {
+ int i;
+ String tempbuffer;
+ //[128] = "";
+
+ if (f.name != null)
+ Menu_DrawStringR2LDark(f.x + f.parent.x + LCOLUMN_OFFSET, f.y
+ + f.parent.y, f.name);
+
+ //strncpy(tempbuffer, f.buffer + f.visible_offset, f.visible_length);
+ String s = f.buffer.toString();
+ tempbuffer = s.substring(f.visible_offset, s.length());
+ re.DrawChar(f.x + f.parent.x + 16, f.y + f.parent.y - 4, 18);
+ re.DrawChar(f.x + f.parent.x + 16, f.y + f.parent.y + 4, 24);
+
+ re.DrawChar(f.x + f.parent.x + 24 + f.visible_length * 8, f.y
+ + f.parent.y - 4, 20);
+ re.DrawChar(f.x + f.parent.x + 24 + f.visible_length * 8, f.y
+ + f.parent.y + 4, 26);
+
+ for (i = 0; i < f.visible_length; i++) {
+ re
+ .DrawChar(f.x + f.parent.x + 24 + i * 8, f.y + f.parent.y
+ - 4, 19);
+ re
+ .DrawChar(f.x + f.parent.x + 24 + i * 8, f.y + f.parent.y
+ + 4, 25);
+ }
+
+ Menu_DrawString(f.x + f.parent.x + 24, f.y + f.parent.y, tempbuffer);
+
+ if (Menu_ItemAtCursor(f.parent) == f) {
+ int offset;
+
+ if (f.visible_offset != 0)
+ offset = f.visible_length;
+ else
+ offset = f.cursor;
+
+ if ((Timer.getCurrentTimeMillis() / 250 & 1) != 0) {
+ re.DrawChar(f.x + f.parent.x + (offset + 2) * 8 + 8, f.y
+ + f.parent.y, 11);
+ } else {
+ re.DrawChar(f.x + f.parent.x + (offset + 2) * 8 + 8, f.y
+ + f.parent.y, ' ');
+ }
+ }
+ }
+
+ public static boolean Field_Key(menufield_s f, int k) {
+ char key = (char) k;
+
+ switch (key) {
+ case K_KP_SLASH:
+ key = '/';
+ break;
+ case K_KP_MINUS:
+ key = '-';
+ break;
+ case K_KP_PLUS:
+ key = '+';
+ break;
+ case K_KP_HOME:
+ key = '7';
+ break;
+ case K_KP_UPARROW:
+ key = '8';
+ break;
+ case K_KP_PGUP:
+ key = '9';
+ break;
+ case K_KP_LEFTARROW:
+ key = '4';
+ break;
+ case K_KP_5:
+ key = '5';
+ break;
+ case K_KP_RIGHTARROW:
+ key = '6';
+ break;
+ case K_KP_END:
+ key = '1';
+ break;
+ case K_KP_DOWNARROW:
+ key = '2';
+ break;
+ case K_KP_PGDN:
+ key = '3';
+ break;
+ case K_KP_INS:
+ key = '0';
+ break;
+ case K_KP_DEL:
+ key = '.';
+ break;
+ }
+
+ if (key > 127) {
+ switch (key) {
+ case K_DEL:
+ default:
+ return false;
+ }
+ }
+
+ /*
+ * * support pasting from the clipboard
+ */
+ if ((Character.toUpperCase(key) == 'V' && keydown[K_CTRL])
+ || (((key == K_INS) || (key == K_KP_INS)) && keydown[K_SHIFT])) {
+ String cbd;
+
+ if ((cbd = Sys.GetClipboardData()) != null) {
+ //strtok(cbd, "\n\r\b");
+ String lines[] = cbd.split("\r\n");
+ if (lines.length > 0 && lines[0].length() != 0) {
+ //strncpy(f.buffer, cbd, f.length - 1);
+ f.buffer = new StringBuffer(lines[0]);
+ f.cursor = f.buffer.length();
+
+ f.visible_offset = f.cursor - f.visible_length;
+
+ if (f.visible_offset < 0)
+ f.visible_offset = 0;
+ }
+ }
+ return true;
+ }
+
+ switch (key) {
+ case K_KP_LEFTARROW:
+ case K_LEFTARROW:
+ case K_BACKSPACE:
+ if (f.cursor > 0) {
+ f.buffer.deleteCharAt(f.cursor - 1);
+ //memmove(f.buffer[f.cursor - 1], f.buffer[f.cursor], strlen(&
+ // f.buffer[f.cursor]) + 1);
+ f.cursor--;
+
+ if (f.visible_offset != 0) {
+ f.visible_offset--;
+ }
+ }
+ break;
+
+ case K_KP_DEL:
+ case K_DEL:
+ //memmove(& f.buffer[f.cursor], & f.buffer[f.cursor + 1], strlen(&
+ // f.buffer[f.cursor + 1]) + 1);
+ f.buffer.deleteCharAt(f.cursor);
+ break;
+
+ case K_KP_ENTER:
+ case K_ENTER:
+ case K_ESCAPE:
+ case K_TAB:
+ return false;
+
+ case K_SPACE:
+ default:
+ if (!Character.isDigit(key) && (f.flags & QMF_NUMBERSONLY) != 0)
+ return false;
+
+ if (f.cursor < f.length) {
+ f.buffer.append(key);
+ f.cursor++;
+
+ if (f.cursor > f.visible_length) {
+ f.visible_offset++;
+ }
+ }
+ }
+
+ return true;
+ }
+
+ public static void Menu_AddItem(menuframework_s menu, menucommon_s item) {
+ if (menu.nitems == 0)
+ menu.nslots = 0;
+
+ if (menu.nitems < MAXMENUITEMS) {
+ menu.items[menu.nitems] = item;
+ menu.items[menu.nitems].parent = menu;
+ menu.nitems++;
+ }
+
+ menu.nslots = Menu_TallySlots(menu);
+ }
+
+ // =============================================================================
+ /* Menu Subsystem */
+
+ /*
+ * * Menu_AdjustCursor * * This function takes the given menu, the
+ * direction, and attempts * to adjust the menu's cursor so that it's at the
+ * next available * slot.
+ */
+ public static void Menu_AdjustCursor(menuframework_s m, int dir) {
+ menucommon_s citem;
+
+ /*
+ * * see if it's in a valid spot
+ */
+ if (m.cursor >= 0 && m.cursor < m.nitems) {
+ if ((citem = Menu_ItemAtCursor(m)) != null) {
+ if (citem.type != MTYPE_SEPARATOR)
+ return;
+ }
+ }
+
+ /*
+ * * it's not in a valid spot, so crawl in the direction indicated until
+ * we * find a valid spot
+ */
+ if (dir == 1) {
+ while (true) {
+ citem = Menu_ItemAtCursor(m);
+ if (citem != null)
+ if (citem.type != MTYPE_SEPARATOR)
+ break;
+ m.cursor += dir;
+ if (m.cursor >= m.nitems)
+ m.cursor = 0;
+ }
+ } else {
+ while (true) {
+ citem = Menu_ItemAtCursor(m);
+ if (citem != null)
+ if (citem.type != MTYPE_SEPARATOR)
+ break;
+ m.cursor += dir;
+ if (m.cursor < 0)
+ m.cursor = m.nitems - 1;
+ }
+ }
+ }
+
+ public static void Menu_Center(menuframework_s menu) {
+ int height;
+
+ height = menu.items[menu.nitems - 1].y;
+ height += 10;
+
+ menu.y = (viddef.height - height) / 2;
+ }
+
+ public static void Menu_Draw(menuframework_s menu) {
+ int i;
+ menucommon_s item;
+
+ /*
+ * * draw contents
+ */
+ for (i = 0; i < menu.nitems; i++) {
+ switch (menu.items[i].type) {
+ case MTYPE_FIELD:
+ Field_Draw((menufield_s) menu.items[i]);
+ break;
+ case MTYPE_SLIDER:
+ Slider_Draw((menuslider_s) menu.items[i]);
+ break;
+ case MTYPE_LIST:
+ MenuList_Draw((menulist_s) menu.items[i]);
+ break;
+ case MTYPE_SPINCONTROL:
+ SpinControl_Draw((menulist_s) menu.items[i]);
+ break;
+ case MTYPE_ACTION:
+ Action_Draw((menuaction_s) menu.items[i]);
+ break;
+ case MTYPE_SEPARATOR:
+ Separator_Draw((menuseparator_s) menu.items[i]);
+ break;
+ }
+ }
+
+ item = Menu_ItemAtCursor(menu);
+
+ if (item != null && item.cursordraw != null) {
+ item.cursordraw.execute(item);
+ } else if (menu.cursordraw != null) {
+ menu.cursordraw.execute(menu);
+ } else if (item != null && item.type != MTYPE_FIELD) {
+ if ((item.flags & QMF_LEFT_JUSTIFY) != 0) {
+ re.DrawChar(menu.x + item.x - 24 + item.cursor_offset, menu.y
+ + item.y, 12 + (Timer.getCurrentTimeMillis() / 250 & 1));
+ } else {
+ re.DrawChar(menu.x + item.cursor_offset, menu.y + item.y,
+ 12 + (Timer.getCurrentTimeMillis() / 250 & 1));
+ }
+ }
+
+ if (item != null) {
+ if (item.statusbarfunc != null)
+ item.statusbarfunc.execute(item);
+ else if (item.statusbar != null)
+ Menu_DrawStatusBar(item.statusbar);
+ else
+ Menu_DrawStatusBar(menu.statusbar);
+
+ } else {
+ Menu_DrawStatusBar(menu.statusbar);
+ }
+ }
+
+ public static void Menu_DrawStatusBar(String string) {
+ if (string != null) {
+ int l = string.length();
+ int maxcol = viddef.width / 8;
+ int col = maxcol / 2 - l / 2;
+
+ re.DrawFill(0, viddef.height - 8, viddef.width, 8, 4);
+ Menu_DrawString(col * 8, viddef.height - 8, string);
+ } else {
+ re.DrawFill(0, viddef.height - 8, viddef.width, 8, 0);
+ }
+ }
+
+ public static void Menu_DrawString(int x, int y, String string) {
+ int i;
+
+ for (i = 0; i < string.length(); i++) {
+ re.DrawChar((x + i * 8), y, string.charAt(i));
+ }
+ }
+
+ public static void Menu_DrawStringDark(int x, int y, String string) {
+ int i;
+
+ for (i = 0; i < string.length(); i++) {
+ re.DrawChar((x + i * 8), y, string.charAt(i) + 128);
+ }
+ }
+
+ public static void Menu_DrawStringR2L(int x, int y, String string) {
+ int i;
+
+ int l = string.length();
+ for (i = 0; i < l; i++) {
+ re.DrawChar((x - i * 8), y, string.charAt(l - i - 1));
+ }
+ }
+
+ public static void Menu_DrawStringR2LDark(int x, int y, String string) {
+ int i;
+
+ int l = string.length();
+ for (i = 0; i < l; i++) {
+ re.DrawChar((x - i * 8), y, string.charAt(l - i - 1) + 128);
+ }
+ }
+
+ public static menucommon_s Menu_ItemAtCursor(menuframework_s m) {
+ if (m.cursor < 0 || m.cursor >= m.nitems)
+ return null;
+
+ return m.items[m.cursor];
+ }
+
+ static void Menu_SelectItem(menuframework_s s) {
+ menucommon_s item = Menu_ItemAtCursor(s);
+
+ if (item != null) {
+ switch (item.type) {
+ case MTYPE_FIELD:
+ Field_DoEnter((menufield_s) item);
+ return;
+ case MTYPE_ACTION:
+ Action_DoEnter((menuaction_s) item);
+ return;
+ case MTYPE_LIST:
+ // Menulist_DoEnter( ( menulist_s ) item );
+ return;
+ case MTYPE_SPINCONTROL:
+ // SpinControl_DoEnter( ( menulist_s ) item );
+ }
+ }
+ }
+
+ public static void Menu_SetStatusBar(menuframework_s m, String string) {
+ m.statusbar = string;
+ }
+
+ public static void Menu_SlideItem(menuframework_s s, int dir) {
+ menucommon_s item = Menu_ItemAtCursor(s);
+
+ if (item != null) {
+ switch (item.type) {
+ case MTYPE_SLIDER:
+ Slider_DoSlide((menuslider_s) item, dir);
+ break;
+ case MTYPE_SPINCONTROL:
+ SpinControl_DoSlide((menulist_s) item, dir);
+ break;
+ }
+ }
+ }
+
+ public static int Menu_TallySlots(menuframework_s menu) {
+ int i;
+ int total = 0;
+
+ for (i = 0; i < menu.nitems; i++) {
+ if (menu.items[i].type == MTYPE_LIST) {
+ int nitems = 0;
+ String n[] = ((menulist_s) menu.items[i]).itemnames;
+
+ while (n[nitems] != null)
+ nitems++;
+
+ total += nitems;
+ } else {
+ total++;
+ }
+ }
+
+ return total;
+ }
+
+ public static void Menulist_DoEnter(menulist_s l) {
+ int start;
+
+ start = l.y / 10 + 1;
+
+ l.curvalue = l.parent.cursor - start;
+
+ if (l.callback != null)
+ l.callback.execute(l);
+ }
+
+ public static void MenuList_Draw(menulist_s l) {
+ String n[];
+ int y = 0;
+
+ Menu_DrawStringR2LDark(l.x + l.parent.x + LCOLUMN_OFFSET, l.y
+ + l.parent.y, l.name);
+
+ n = l.itemnames;
+
+ re.DrawFill(l.x - 112 + l.parent.x, l.parent.y + l.y + l.curvalue * 10
+ + 10, 128, 10, 16);
+ int i = 0;
+
+ while (n[i] != null) {
+ Menu_DrawStringR2LDark(l.x + l.parent.x + LCOLUMN_OFFSET, l.y
+ + l.parent.y + y + 10, n[i]);
+
+ i++;
+ y += 10;
+ }
+ }
+
+ public static void Separator_Draw(menuseparator_s s) {
+ if (s.name != null)
+ Menu_DrawStringR2LDark(s.x + s.parent.x, s.y + s.parent.y, s.name);
+ }
+
+ public static void Slider_DoSlide(menuslider_s s, int dir) {
+ s.curvalue += dir;
+
+ if (s.curvalue > s.maxvalue)
+ s.curvalue = s.maxvalue;
+ else if (s.curvalue < s.minvalue)
+ s.curvalue = s.minvalue;
+
+ if (s.callback != null)
+ s.callback.execute(s);
+ }
+
+ public static void Slider_Draw(menuslider_s s) {
+ int i;
+
+ Menu_DrawStringR2LDark(s.x + s.parent.x + LCOLUMN_OFFSET, s.y
+ + s.parent.y, s.name);
+
+ s.range = (s.curvalue - s.minvalue) / (s.maxvalue - s.minvalue);
+
+ if (s.range < 0)
+ s.range = 0;
+ if (s.range > 1)
+ s.range = 1;
+ re.DrawChar(s.x + s.parent.x + RCOLUMN_OFFSET, s.y + s.parent.y, 128);
+ for (i = 0; i < SLIDER_RANGE; i++)
+ re.DrawChar(RCOLUMN_OFFSET + s.x + i * 8 + s.parent.x + 8, s.y
+ + s.parent.y, 129);
+ re.DrawChar(RCOLUMN_OFFSET + s.x + i * 8 + s.parent.x + 8, s.y
+ + s.parent.y, 130);
+ re
+ .DrawChar(
+ (int) (8 + RCOLUMN_OFFSET + s.parent.x + s.x + (SLIDER_RANGE - 1)
+ * 8 * s.range), s.y + s.parent.y, 131);
+ }
+
+ public static void SpinControl_DoEnter(menulist_s s) {
+ s.curvalue++;
+ if (s.itemnames[s.curvalue] == null)
+ s.curvalue = 0;
+
+ if (s.callback != null)
+ s.callback.execute(s);
+ }
+
+ public static void SpinControl_DoSlide(menulist_s s, int dir) {
+ s.curvalue += dir;
+
+ if (s.curvalue < 0)
+ s.curvalue = 0;
+ else if (s.curvalue >= s.itemnames.length || s.itemnames[s.curvalue] == null)
+ s.curvalue--;
+
+ if (s.callback != null)
+ s.callback.execute(s);
+ }
+
+ public static void SpinControl_Draw(menulist_s s) {
+ //char buffer[100];
+
+ if (s.name != null) {
+ Menu_DrawStringR2LDark(s.x + s.parent.x + LCOLUMN_OFFSET, s.y
+ + s.parent.y, s.name);
+ }
+
+ if (s.itemnames[s.curvalue].indexOf('\n') == -1) {
+ Menu_DrawString(RCOLUMN_OFFSET + s.x + s.parent.x,
+ s.y + s.parent.y, s.itemnames[s.curvalue]);
+ } else {
+ String line1, line2;
+ line1 = Lib.leftFrom(s.itemnames[s.curvalue], '\n');
+ Menu_DrawString(RCOLUMN_OFFSET + s.x + s.parent.x,
+ s.y + s.parent.y, line1);
+
+ line2 = Lib.rightFrom(s.itemnames[s.curvalue], '\n');
+
+ int pos = line2.indexOf('\n');
+ if (pos != -1)
+ line2 = line2.substring(0, pos);
+
+ Menu_DrawString(RCOLUMN_OFFSET + s.x + s.parent.x, s.y + s.parent.y
+ + 10, line2);
+ }
+ }
+
+ public static class menulayer_t {
+ xcommand_t draw;
+
+ keyfunc_t key;
+ }
+
+ static class menuframework_s {
+ final menucommon_s[] items = new menucommon_s[64];
+ int x, y;
+ int cursor;
+ int nitems;
+ int nslots;
+ String statusbar;
+
+ //void (*cursordraw)( struct _tag_menuframework *m );
+ mcallback cursordraw;
+
+ }
+
+ abstract static class mcallback {
+ abstract public void execute(Object self);
+ }
+
+ static class menucommon_s {
+ final int[] localdata = {0, 0, 0, 0};
+ int type;
+ String name = "";
+ int x, y;
+ menuframework_s parent;
+ int cursor_offset;
+ int flags;
+
+ int n = -1; //position in an array.
+
+ String statusbar;
+
+ mcallback callback;
+
+ mcallback statusbarfunc;
+
+ mcallback ownerdraw;
+
+ mcallback cursordraw;
+ }
+
+ static class menufield_s extends menucommon_s {
+ //char buffer[80];
+ StringBuffer buffer; //allow deletion.
+
+ int cursor;
+
+ int length;
+
+ int visible_length;
+
+ int visible_offset;
+ }
+
+ static class menuslider_s extends menucommon_s {
+
+ float minvalue;
+
+ float maxvalue;
+
+ float curvalue;
+
+ float range;
+ }
+
+ static class menulist_s extends menucommon_s {
+ int curvalue;
+
+ String itemnames[];
+ }
+
+ static class menuaction_s extends menucommon_s {
+
+ }
+
+ static class menuseparator_s extends menucommon_s {
+
+ }
+
+ static class playermodelinfo_s {
+ int nskins;
+
+ String skindisplaynames[];
+
+ //char displayname[MAX_DISPLAYNAME];
+ String displayname;
+
+ //char directory[MAX_QPATH];
+ String directory;
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.client;
+
+import lwjake2.Defines;
+import lwjake2.Globals;
+import lwjake2.game.Cmd;
+import lwjake2.game.CvarT;
+import lwjake2.qcommon.*;
+import lwjake2.sound.S;
+import lwjake2.sys.Timer;
+import lwjake2.util.Lib;
+import lwjake2.util.Vargs;
+
+import java.awt.*;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.Arrays;
+
+/**
+ * SCR
+ */
+public final class SCR extends Globals {
+
+ // cl_scrn.c -- master for refresh, status bar, console, chat, notify, etc
+
+ static final int STAT_MINUS = 10; // num frame for '-' stats digit
+
+ /*
+ * full screen console put up loading plaque blanked background with loading
+ * plaque blanked background with menu cinematics full screen image for quit
+ * and victory
+ *
+ * end of unit intermissions
+ */
+ static final int ICON_WIDTH = 24;
+ static final int ICON_HEIGHT = 24;
+ static final int CHAR_WIDTH = 16;
+ static final int ICON_SPACE = 8;
+
+ // scr_vrect ist in Globals definiert
+ // position of render window on screen
+ /*
+ * ================ SCR_DrawLayout
+ *
+ * ================
+ */
+ static final int STAT_LAYOUTS = 13;
+ static final String[][] sb_nums = {
+ {"num_0", "num_1", "num_2", "num_3", "num_4", "num_5", "num_6",
+ "num_7", "num_8", "num_9", "num_minus"},
+ {"anum_0", "anum_1", "anum_2", "anum_3", "anum_4", "anum_5",
+ "anum_6", "anum_7", "anum_8", "anum_9", "anum_minus"}};
+ static final dirty_t scr_dirty = new dirty_t();
+ static final dirty_t[] scr_old_dirty = {new dirty_t(), new dirty_t()};
+ static final graphsamp_t[] values = new graphsamp_t[1024];
+ static final dirty_t clear = new dirty_t();
+ /*
+ * ================== SCR_UpdateScreen
+ *
+ * This is called every frame, and can also be called explicitly to flush
+ * text to the screen. ==================
+ */
+ private static final float[] separation = {0, 0};
+ private static final cinematics_t cin = new cinematics_t();
+ private static final byte[] compressed = new byte[0x20000];
+ public static CvarT fps = new CvarT();
+ static float scr_con_current; // aproaches scr_conlines at scr_conspeed
+ static float scr_conlines; // 0.0 to 1.0 lines of console to display
+ static boolean scr_initialized; // ready to draw
+ static int scr_draw_loading;
+ static CvarT scr_viewsize;
+ static CvarT scr_conspeed;
+ static CvarT scr_centertime;
+ static CvarT scr_showturtle;
+ static CvarT scr_showpause;
+ static CvarT scr_printspeed;
+ static CvarT scr_netgraph;
+ static CvarT scr_timegraph;
+ static CvarT scr_debuggraph;
+ static CvarT scr_graphheight;
+ static CvarT scr_graphscale;
+ /*
+ * ===============================================================================
+ *
+ * BAR GRAPHS
+ *
+ * ===============================================================================
+ */
+ static CvarT scr_graphshift;
+ static CvarT scr_drawall;
+ static String crosshair_pic;
+ static int crosshair_width, crosshair_height;
+ /*
+ * ===============================================================================
+ *
+ * CENTER PRINTING
+ *
+ * ===============================================================================
+ */
+ static int current;
+ // char scr_centerstring[1024];
+ static String scr_centerstring;
+ static float scr_centertime_start; // for slow victory printing
+ static float scr_centertime_off;
+ static int scr_center_lines;
+ static int scr_erase_center;
+ // =============================================================================
+ private static int lastframes = 0;
+ private static int lasttime = 0;
+ private static String fpsvalue = "";
+ // ============================================================================
+ private static final xcommand_t updateScreenCallback = new xcommand_t() {
+ public void execute() {
+ UpdateScreen2();
+ }
+ };
+
+ static {
+ for (int n = 0; n < 1024; n++)
+ values[n] = new graphsamp_t();
+ }
+
+ /*
+ * ============== SCR_DebugGraph ==============
+ */
+ public static void DebugGraph(float value, int color) {
+ values[current & 1023].value = value;
+ values[current & 1023].color = color;
+ current++;
+ }
+
+ // =============================================================================
+
+ /*
+ * ============== SCR_DrawDebugGraph ==============
+ */
+ static void DrawDebugGraph() {
+ int a, x, y, w, i, h;
+ float v;
+ int color;
+
+ // draw the graph
+
+ w = scr_vrect.width;
+
+ x = scr_vrect.x;
+ y = scr_vrect.y + scr_vrect.height;
+ re.DrawFill(x, (int) (y - scr_graphheight.value), w,
+ (int) scr_graphheight.value, 8);
+
+ for (a = 0; a < w; a++) {
+ i = (current - 1 - a + 1024) & 1023;
+ v = values[i].value;
+ color = values[i].color;
+ v = v * scr_graphscale.value + scr_graphshift.value;
+
+ if (v < 0)
+ v += scr_graphheight.value
+ * (1 + (int) (-v / scr_graphheight.value));
+ h = (int) v % (int) scr_graphheight.value;
+ re.DrawFill(x + w - 1 - a, y - h, 1, h, color);
+ }
+ }
+
+ /*
+ * ============== SCR_CenterPrint
+ *
+ * Called for important messages that should stay in the center of the
+ * screen for a few moments ==============
+ */
+ static void CenterPrint(String str) {
+ //char *s;
+ int s;
+ StringBuilder line = new StringBuilder(64);
+ int i, j, l;
+
+ //strncpy (scr_centerstring, str, sizeof(scr_centerstring)-1);
+ scr_centerstring = str;
+ scr_centertime_off = scr_centertime.value;
+ scr_centertime_start = clientStateT.time;
+
+ // count the number of lines for centering
+ scr_center_lines = 1;
+ s = 0;
+ while (s < str.length()) {
+ if (str.charAt(s) == '\n')
+ scr_center_lines++;
+ s++;
+ }
+
+ // echo it to the console
+ Com
+ .Printf("\n\n\35\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\37\n\n");
+
+ s = 0;
+
+ if (str.length() != 0) {
+ do {
+ // scan the width of the line
+
+ for (l = 0; l < 40 && (l + s) < str.length(); l++)
+ if (str.charAt(s + l) == '\n' || str.charAt(s + l) == 0)
+ break;
+ for (i = 0; i < (40 - l) / 2; i++)
+ line.append(' ');
+
+ for (j = 0; j < l; j++) {
+ line.append(str.charAt(s + j));
+ }
+
+ line.append('\n');
+
+ Com.Printf(line.toString());
+
+ while (s < str.length() && str.charAt(s) != '\n')
+ s++;
+
+ if (s == str.length())
+ break;
+ s++; // skip the \n
+ } while (true);
+ }
+ Com
+ .Printf("\n\n\35\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\37\n\n");
+ Console.ClearNotify();
+ }
+
+ // =============================================================================
+
+ static void DrawCenterString() {
+ String cs = scr_centerstring + "\0";
+ int start;
+ int l;
+ int j;
+ int x, y;
+ int remaining;
+
+ if (cs == null || cs.length() == 0)
+ return;
+
+ // the finale prints the characters one at a time
+ remaining = 9999;
+
+ scr_erase_center = 0;
+ start = 0;
+
+ if (scr_center_lines <= 4)
+ y = (int) (viddef.height * 0.35);
+ else
+ y = 48;
+
+ do {
+ // scan the width of the line
+ for (l = 0; l < 40; l++)
+ if (start + l == cs.length() - 1
+ || cs.charAt(start + l) == '\n')
+ break;
+ x = (viddef.width - l * 8) / 2;
+ SCR.AddDirtyPoint(x, y);
+ for (j = 0; j < l; j++, x += 8) {
+ re.DrawChar(x, y, cs.charAt(start + j));
+ if (remaining == 0)
+ return;
+ remaining--;
+ }
+ SCR.AddDirtyPoint(x, y + 8);
+
+ y += 8;
+
+ while (start < cs.length() && cs.charAt(start) != '\n')
+ start++;
+
+ if (start == cs.length())
+ break;
+ start++; // skip the \n
+ } while (true);
+ }
+
+ static void CheckDrawCenterString() {
+ scr_centertime_off -= clientStaticT.frametime;
+
+ if (scr_centertime_off <= 0)
+ return;
+
+ DrawCenterString();
+ }
+
+ /*
+ * ================= SCR_CalcVrect
+ *
+ * Sets scr_vrect, the coordinates of the rendered window =================
+ */
+ static void CalcVrect() {
+ int size;
+
+ // bound viewsize
+ if (scr_viewsize.value < 40)
+ Cvar.set("viewsize", "40");
+ if (scr_viewsize.value > 100)
+ Cvar.set("viewsize", "100");
+
+ size = (int) scr_viewsize.value;
+
+ scr_vrect.width = viddef.width * size / 100;
+ scr_vrect.width &= ~7;
+
+ scr_vrect.height = viddef.height * size / 100;
+ scr_vrect.height &= ~1;
+
+ scr_vrect.x = (viddef.width - scr_vrect.width) / 2;
+ scr_vrect.y = (viddef.height - scr_vrect.height) / 2;
+ }
+
+ /*
+ * ================= SCR_SizeUp_f
+ *
+ * Keybinding command =================
+ */
+ static void SizeUp_f() {
+ Cvar.setValue("viewsize", scr_viewsize.value + 10);
+ }
+
+ /*
+ * ================= SCR_SizeDown_f
+ *
+ * Keybinding command =================
+ */
+ static void SizeDown_f() {
+ Cvar.setValue("viewsize", scr_viewsize.value - 10);
+ }
+
+ /*
+ * ============== SCR_TileClear
+ *
+ * Clear any parts of the tiled background that were drawn on last frame
+ * ==============
+ */
+
+ /*
+ * ================= SCR_Sky_f
+ *
+ * Set a specific sky and rotation speed =================
+ */
+ static void Sky_f() {
+ float rotate;
+ float[] axis = {0, 0, 0};
+
+ if (Cmd.Argc() < 2) {
+ Com.Printf("Usage: sky <basename> <rotate> <axis x y z>\n");
+ return;
+ }
+ if (Cmd.Argc() > 2)
+ rotate = Float.parseFloat(Cmd.Argv(2));
+ else
+ rotate = 0;
+ if (Cmd.Argc() == 6) {
+ axis[0] = Float.parseFloat(Cmd.Argv(3));
+ axis[1] = Float.parseFloat(Cmd.Argv(4));
+ axis[2] = Float.parseFloat(Cmd.Argv(5));
+ } else {
+ axis[0] = 0;
+ axis[1] = 0;
+ axis[2] = 1;
+ }
+
+ re.SetSky(Cmd.Argv(1), rotate, axis);
+ }
+
+ /*
+ * ================== SCR_Init ==================
+ */
+ static void Init() {
+ scr_viewsize = Cvar.get("viewsize", "100", CVAR_ARCHIVE);
+ scr_conspeed = Cvar.get("scr_conspeed", "3", 0);
+ scr_showturtle = Cvar.get("scr_showturtle", "0", 0);
+ scr_showpause = Cvar.get("scr_showpause", "1", 0);
+ scr_centertime = Cvar.get("scr_centertime", "2.5", 0);
+ scr_printspeed = Cvar.get("scr_printspeed", "8", 0);
+ scr_netgraph = Cvar.get("netgraph", "1", 0);
+ scr_timegraph = Cvar.get("timegraph", "1", 0);
+ scr_debuggraph = Cvar.get("debuggraph", "1", 0);
+ scr_graphheight = Cvar.get("graphheight", "32", 0);
+ scr_graphscale = Cvar.get("graphscale", "1", 0);
+ scr_graphshift = Cvar.get("graphshift", "0", 0);
+ scr_drawall = Cvar.get("scr_drawall", "1", 0);
+ fps = Cvar.get("fps", "0", 0);
+
+ //
+ // register our commands
+ //
+ Cmd.AddCommand("timerefresh", new xcommand_t() {
+ public void execute() {
+ TimeRefresh_f();
+ }
+ });
+ Cmd.AddCommand("loading", new xcommand_t() {
+ public void execute() {
+ Loading_f();
+ }
+ });
+ Cmd.AddCommand("sizeup", new xcommand_t() {
+ public void execute() {
+ SizeUp_f();
+ }
+ });
+ Cmd.AddCommand("sizedown", new xcommand_t() {
+ public void execute() {
+ SizeDown_f();
+ }
+ });
+ Cmd.AddCommand("sky", new xcommand_t() {
+ public void execute() {
+ Sky_f();
+ }
+ });
+
+ scr_initialized = true;
+ }
+
+ // ===============================================================
+
+ /*
+ * ============== SCR_DrawNet ==============
+ */
+ static void DrawNet() {
+ if (clientStaticT.netchan.outgoing_sequence - clientStaticT.netchan.incoming_acknowledged < CMD_BACKUP - 1)
+ return;
+
+ re.DrawPic(scr_vrect.x + 64, scr_vrect.y, "net");
+ }
+
+ /*
+ * ============== SCR_DrawPause ==============
+ */
+ static void DrawPause() {
+ Dimension dim = new Dimension();
+
+ if (scr_showpause.value == 0) // turn off for screenshots
+ return;
+
+ if (cl_paused.value == 0)
+ return;
+
+ re.DrawGetPicSize(dim, "pause");
+ re.DrawPic((viddef.width - dim.width) / 2, viddef.height / 2 + 8,
+ "pause");
+ }
+
+ /*
+ * ============== SCR_DrawLoading ==============
+ */
+ static void DrawLoading() {
+ Dimension dim = new Dimension();
+
+ if (scr_draw_loading == 0)
+ return;
+
+ scr_draw_loading = 0;
+ re.DrawGetPicSize(dim, "loading");
+ re.DrawPic((viddef.width - dim.width) / 2,
+ (viddef.height - dim.height) / 2, "loading");
+ }
+
+ /*
+ * ================== SCR_RunConsole
+ *
+ * Scroll it up or down ==================
+ */
+ static void RunConsole() {
+ // decide on the height of the console
+ if (clientStaticT.key_dest == key_console)
+ scr_conlines = 0.5f; // half screen
+ else
+ scr_conlines = 0; // none visible
+
+ if (scr_conlines < scr_con_current) {
+ scr_con_current -= scr_conspeed.value * clientStaticT.frametime;
+ if (scr_conlines > scr_con_current)
+ scr_con_current = scr_conlines;
+
+ } else if (scr_conlines > scr_con_current) {
+ scr_con_current += scr_conspeed.value * clientStaticT.frametime;
+ if (scr_conlines < scr_con_current)
+ scr_con_current = scr_conlines;
+ }
+ }
+
+ /*
+ * ================== SCR_DrawConsole ==================
+ */
+ static void DrawConsole() {
+ Console.CheckResize();
+
+ if (clientStaticT.state == ca_disconnected || clientStaticT.state == ca_connecting) { // forced
+ // full
+ // screen
+ // console
+ Console.DrawConsole(1.0f);
+ return;
+ }
+
+ if (clientStaticT.state != ca_active || !clientStateT.refresh_prepped) { // connected, but
+ // can't render
+ Console.DrawConsole(0.5f);
+ re.DrawFill(0, viddef.height / 2, viddef.width, viddef.height / 2,
+ 0);
+ return;
+ }
+
+ if (scr_con_current != 0) {
+ Console.DrawConsole(scr_con_current);
+ } else {
+ if (clientStaticT.key_dest == key_game || clientStaticT.key_dest == key_message)
+ Console.DrawNotify(); // only draw notify in game
+ }
+ }
+
+ /*
+ * ================ SCR_BeginLoadingPlaque ================
+ */
+ public static void BeginLoadingPlaque() {
+ S.StopAllSounds();
+ clientStateT.sound_prepped = false; // don't play ambients
+
+ if (clientStaticT.disable_screen != 0)
+ return;
+ if (developer.value != 0)
+ return;
+ if (clientStaticT.state == ca_disconnected)
+ return; // if at console, don't bring up the plaque
+ if (clientStaticT.key_dest == key_console)
+ return;
+ if (clientStateT.cinematictime > 0)
+ scr_draw_loading = 2; // clear to balack first
+ else
+ scr_draw_loading = 1;
+
+ UpdateScreen();
+ clientStaticT.disable_screen = Timer.getCurrentTimeMillis();
+ clientStaticT.disable_servercount = clientStateT.servercount;
+ }
+
+ /*
+ * ================ SCR_EndLoadingPlaque ================
+ */
+ public static void EndLoadingPlaque() {
+ clientStaticT.disable_screen = 0;
+ Console.ClearNotify();
+ }
+
+ /*
+ * ================ SCR_Loading_f ================
+ */
+ static void Loading_f() {
+ BeginLoadingPlaque();
+ }
+
+ /*
+ * ================ SCR_TimeRefresh_f ================
+ */
+ static void TimeRefresh_f() {
+ int i;
+ int start, stop;
+ float time;
+
+ if (clientStaticT.state != ca_active)
+ return;
+
+ start = Timer.getCurrentTimeMillis();
+
+ if (Cmd.Argc() == 2) { // run without page flipping
+ re.BeginFrame(0);
+ for (i = 0; i < 128; i++) {
+ clientStateT.refdef.viewangles[1] = i / 128.0f * 360.0f;
+ re.RenderFrame(clientStateT.refdef);
+ }
+ re.EndFrame();
+ } else {
+ for (i = 0; i < 128; i++) {
+ clientStateT.refdef.viewangles[1] = i / 128.0f * 360.0f;
+
+ re.BeginFrame(0);
+ re.RenderFrame(clientStateT.refdef);
+ re.EndFrame();
+ }
+ }
+
+ stop = Timer.getCurrentTimeMillis();
+ time = (stop - start) / 1000.0f;
+ Com.Printf("%f seconds (%f fps)\n", new Vargs(2).add(time).add(
+ 128.0f / time));
+ }
+
+ static void DirtyScreen() {
+ AddDirtyPoint(0, 0);
+ AddDirtyPoint(viddef.width - 1, viddef.height - 1);
+ }
+
+ static void TileClear() {
+ int i;
+ int top, bottom, left, right;
+ clear.clear();
+
+ if (scr_drawall.value != 0)
+ DirtyScreen(); // for power vr or broken page flippers...
+
+ if (scr_con_current == 1.0f)
+ return; // full screen console
+ if (scr_viewsize.value == 100)
+ return; // full screen rendering
+ if (clientStateT.cinematictime > 0)
+ return; // full screen cinematic
+
+ // erase rect will be the union of the past three frames
+ // so tripple buffering works properly
+ clear.set(scr_dirty);
+ for (i = 0; i < 2; i++) {
+ if (scr_old_dirty[i].x1 < clear.x1)
+ clear.x1 = scr_old_dirty[i].x1;
+ if (scr_old_dirty[i].x2 > clear.x2)
+ clear.x2 = scr_old_dirty[i].x2;
+ if (scr_old_dirty[i].y1 < clear.y1)
+ clear.y1 = scr_old_dirty[i].y1;
+ if (scr_old_dirty[i].y2 > clear.y2)
+ clear.y2 = scr_old_dirty[i].y2;
+ }
+
+ scr_old_dirty[1].set(scr_old_dirty[0]);
+ scr_old_dirty[0].set(scr_dirty);
+
+ scr_dirty.x1 = 9999;
+ scr_dirty.x2 = -9999;
+ scr_dirty.y1 = 9999;
+ scr_dirty.y2 = -9999;
+
+ // don't bother with anything convered by the console)
+ top = (int) (scr_con_current * viddef.height);
+ if (top >= clear.y1)
+ clear.y1 = top;
+
+ if (clear.y2 <= clear.y1)
+ return; // nothing disturbed
+
+ top = scr_vrect.y;
+ bottom = top + scr_vrect.height - 1;
+ left = scr_vrect.x;
+ right = left + scr_vrect.width - 1;
+
+ if (clear.y1 < top) { // clear above view screen
+ i = clear.y2 < top - 1 ? clear.y2 : top - 1;
+ re.DrawTileClear(clear.x1, clear.y1, clear.x2 - clear.x1 + 1, i
+ - clear.y1 + 1, "backtile");
+ clear.y1 = top;
+ }
+ if (clear.y2 > bottom) { // clear below view screen
+ i = clear.y1 > bottom + 1 ? clear.y1 : bottom + 1;
+ re.DrawTileClear(clear.x1, i, clear.x2 - clear.x1 + 1, clear.y2 - i
+ + 1, "backtile");
+ clear.y2 = bottom;
+ }
+ if (clear.x1 < left) { // clear left of view screen
+ i = clear.x2 < left - 1 ? clear.x2 : left - 1;
+ re.DrawTileClear(clear.x1, clear.y1, i - clear.x1 + 1, clear.y2
+ - clear.y1 + 1, "backtile");
+ clear.x1 = left;
+ }
+ if (clear.x2 > right) { // clear left of view screen
+ i = clear.x1 > right + 1 ? clear.x1 : right + 1;
+ re.DrawTileClear(i, clear.y1, clear.x2 - i + 1, clear.y2 - clear.y1
+ + 1, "backtile");
+ clear.x2 = right;
+ }
+
+ }
+
+ /*
+ * ================ SizeHUDString
+ *
+ * Allow embedded \n in the string ================
+ */
+ static void SizeHUDString(String string, Dimension dim) {
+ int lines, width, current;
+
+ lines = 1;
+ width = 0;
+
+ current = 0;
+ for (int i = 0; i < string.length(); i++) {
+ if (string.charAt(i) == '\n') {
+ lines++;
+ current = 0;
+ } else {
+ current++;
+ if (current > width)
+ width = current;
+ }
+
+ }
+
+ dim.width = width * 8;
+ dim.height = lines * 8;
+ }
+
+ static void DrawHUDString(String string, int x, int y,
+ int xor) {
+ int margin;
+ //char line[1024];
+ StringBuffer line = new StringBuffer(1024);
+ int i;
+
+ margin = x;
+
+ for (int l = 0; l < string.length(); ) {
+ // scan out one line of text from the string
+ line = new StringBuffer(1024);
+ while (l < string.length() && string.charAt(l) != '\n') {
+ line.append(string.charAt(l));
+ l++;
+ }
+
+ if (320 != 0)
+ x = margin + (320 - line.length() * 8) / 2;
+ else
+ x = margin;
+ for (i = 0; i < line.length(); i++) {
+ re.DrawChar(x, y, line.charAt(i) ^ xor);
+ x += 8;
+ }
+ if (l < string.length()) {
+ l++; // skip the \n
+ x = margin;
+ y += 8;
+ }
+ }
+ }
+
+ // =======================================================
+
+ /*
+ * ============== SCR_DrawField ==============
+ */
+ static void DrawField(int x, int y, int color, int width, int value) {
+ char ptr;
+ String num;
+ int l;
+ int frame;
+
+ if (width < 1)
+ return;
+
+ // draw number string
+ if (width > 5)
+ width = 5;
+
+ AddDirtyPoint(x, y);
+ AddDirtyPoint(x + width * CHAR_WIDTH + 2, y + 23);
+
+ num = "" + value;
+ l = num.length();
+ if (l > width)
+ l = width;
+ x += 2 + CHAR_WIDTH * (width - l);
+
+ ptr = num.charAt(0);
+
+ for (int i = 0; i < l; i++) {
+ ptr = num.charAt(i);
+ if (ptr == '-')
+ frame = STAT_MINUS;
+ else
+ frame = ptr - '0';
+
+ re.DrawPic(x, y, sb_nums[color][frame]);
+ x += CHAR_WIDTH;
+ }
+ }
+
+ /*
+ * =============== SCR_TouchPics
+ *
+ * Allows rendering code to cache all needed sbar graphics ===============
+ */
+ static void TouchPics() {
+ int i, j;
+
+ for (i = 0; i < 2; i++)
+ for (j = 0; j < 11; j++)
+ re.RegisterPic(sb_nums[i][j]);
+
+ if (crosshair.value != 0.0f) {
+ if (crosshair.value > 3.0f || crosshair.value < 0.0f)
+ crosshair.value = 3.0f;
+
+ crosshair_pic = "ch" + (int) crosshair.value;
+ Dimension dim = new Dimension();
+ re.DrawGetPicSize(dim, crosshair_pic);
+ crosshair_width = dim.width;
+ crosshair_height = dim.height;
+ if (crosshair_width == 0)
+ crosshair_pic = "";
+ }
+ }
+
+ /*
+ * ================ SCR_ExecuteLayoutString
+ *
+ * ================
+ */
+ static void ExecuteLayoutString(String s) {
+ int x, y;
+ int value;
+ String token;
+ int width;
+ int index;
+ ClientInfo ci;
+
+ if (clientStaticT.state != ca_active || !clientStateT.refresh_prepped)
+ return;
+
+ // if (!s[0])
+ if (s == null || s.length() == 0)
+ return;
+
+ x = 0;
+ y = 0;
+ width = 3;
+
+ Com.ParseHelp ph = new Com.ParseHelp(s);
+
+ while (!ph.isEof()) {
+ token = Com.Parse(ph);
+ if (token.equals("xl")) {
+ token = Com.Parse(ph);
+ x = Lib.atoi(token);
+ continue;
+ }
+ if (token.equals("xr")) {
+ token = Com.Parse(ph);
+ x = viddef.width + Lib.atoi(token);
+ continue;
+ }
+ if (token.equals("xv")) {
+ token = Com.Parse(ph);
+ x = viddef.width / 2 - 160 + Lib.atoi(token);
+ continue;
+ }
+
+ if (token.equals("yt")) {
+ token = Com.Parse(ph);
+ y = Lib.atoi(token);
+ continue;
+ }
+ if (token.equals("yb")) {
+ token = Com.Parse(ph);
+ y = viddef.height + Lib.atoi(token);
+ continue;
+ }
+ if (token.equals("yv")) {
+ token = Com.Parse(ph);
+ y = viddef.height / 2 - 120 + Lib.atoi(token);
+ continue;
+ }
+
+ if (token.equals("pic")) { // draw a pic from a stat number
+ token = Com.Parse(ph);
+ value = clientStateT.frame.playerstate.stats[Lib.atoi(token)];
+ if (value >= MAX_IMAGES)
+ Com.Error(ERR_DROP, "Pic >= MAX_IMAGES");
+ if (clientStateT.configstrings[CS_IMAGES + value] != null) {
+ AddDirtyPoint(x, y);
+ AddDirtyPoint(x + 23, y + 23);
+ re.DrawPic(x, y, clientStateT.configstrings[CS_IMAGES + value]);
+ }
+ continue;
+ }
+
+ if (token.equals("client")) { // draw a deathmatch client block
+ int score, ping, time;
+
+ token = Com.Parse(ph);
+ x = viddef.width / 2 - 160 + Lib.atoi(token);
+ token = Com.Parse(ph);
+ y = viddef.height / 2 - 120 + Lib.atoi(token);
+ AddDirtyPoint(x, y);
+ AddDirtyPoint(x + 159, y + 31);
+
+ token = Com.Parse(ph);
+ value = Lib.atoi(token);
+ if (value >= MAX_CLIENTS || value < 0)
+ Com.Error(ERR_DROP, "client >= MAX_CLIENTS");
+ ci = clientStateT.clientinfo[value];
+
+ token = Com.Parse(ph);
+ score = Lib.atoi(token);
+
+ token = Com.Parse(ph);
+ ping = Lib.atoi(token);
+
+ token = Com.Parse(ph);
+ time = Lib.atoi(token);
+
+ Console.DrawAltString(x + 32, y, ci.name);
+ Console.DrawString(x + 32, y + 8, "Score: ");
+ Console.DrawAltString(x + 32 + 7 * 8, y + 8, "" + score);
+ Console.DrawString(x + 32, y + 16, "Ping: " + ping);
+ Console.DrawString(x + 32, y + 24, "Time: " + time);
+
+ if (ci.icon == null)
+ ci = clientStateT.baseclientinfo;
+ re.DrawPic(x, y, ci.iconname);
+ continue;
+ }
+
+ if (token.equals("ctf")) { // draw a ctf client block
+ int score, ping;
+
+ token = Com.Parse(ph);
+ x = viddef.width / 2 - 160 + Lib.atoi(token);
+ token = Com.Parse(ph);
+ y = viddef.height / 2 - 120 + Lib.atoi(token);
+ AddDirtyPoint(x, y);
+ AddDirtyPoint(x + 159, y + 31);
+
+ token = Com.Parse(ph);
+ value = Lib.atoi(token);
+ if (value >= MAX_CLIENTS || value < 0)
+ Com.Error(ERR_DROP, "client >= MAX_CLIENTS");
+ ci = clientStateT.clientinfo[value];
+
+ token = Com.Parse(ph);
+ score = Lib.atoi(token);
+
+ token = Com.Parse(ph);
+ ping = Lib.atoi(token);
+ if (ping > 999)
+ ping = 999;
+
+ // sprintf(block, "%3d %3d %-12.12s", score, ping, ci->name);
+ String block = Com.sprintf("%3d %3d %-12.12s", new Vargs(3)
+ .add(score).add(ping).add(ci.name));
+
+ if (value == clientStateT.playernum)
+ Console.DrawAltString(x, y, block);
+ else
+ Console.DrawString(x, y, block);
+ continue;
+ }
+
+ if (token.equals("picn")) { // draw a pic from a name
+ token = Com.Parse(ph);
+ AddDirtyPoint(x, y);
+ AddDirtyPoint(x + 23, y + 23);
+ re.DrawPic(x, y, token);
+ continue;
+ }
+
+ if (token.equals("num")) { // draw a number
+ token = Com.Parse(ph);
+ width = Lib.atoi(token);
+ token = Com.Parse(ph);
+ value = clientStateT.frame.playerstate.stats[Lib.atoi(token)];
+ DrawField(x, y, 0, width, value);
+ continue;
+ }
+
+ if (token.equals("hnum")) { // health number
+ int color;
+
+ width = 3;
+ value = clientStateT.frame.playerstate.stats[STAT_HEALTH];
+ if (value > 25)
+ color = 0; // green
+ else if (value > 0)
+ color = (clientStateT.frame.serverframe >> 2) & 1; // flash
+ else
+ color = 1;
+
+ if ((clientStateT.frame.playerstate.stats[STAT_FLASHES] & 1) != 0)
+ re.DrawPic(x, y, "field_3");
+
+ DrawField(x, y, color, width, value);
+ continue;
+ }
+
+
+ if (token.equals("stat_string")) {
+ token = Com.Parse(ph);
+ index = Lib.atoi(token);
+ if (index < 0 || index >= MAX_CONFIGSTRINGS)
+ Com.Error(ERR_DROP, "Bad stat_string index");
+ index = clientStateT.frame.playerstate.stats[index];
+ if (index < 0 || index >= MAX_CONFIGSTRINGS)
+ Com.Error(ERR_DROP, "Bad stat_string index");
+ Console.DrawString(x, y, clientStateT.configstrings[index]);
+ continue;
+ }
+
+ if (token.equals("cstring")) {
+ token = Com.Parse(ph);
+ DrawHUDString(token, x, y, 0);
+ continue;
+ }
+
+ if (token.equals("string")) {
+ token = Com.Parse(ph);
+ Console.DrawString(x, y, token);
+ continue;
+ }
+
+ if (token.equals("cstring2")) {
+ token = Com.Parse(ph);
+ DrawHUDString(token, x, y, 0x80);
+ continue;
+ }
+
+ if (token.equals("string2")) {
+ token = Com.Parse(ph);
+ Console.DrawAltString(x, y, token);
+ continue;
+ }
+
+ if (token.equals("if")) { // draw a number
+ token = Com.Parse(ph);
+ value = clientStateT.frame.playerstate.stats[Lib.atoi(token)];
+ if (value == 0) {
+ // skip to endif
+ while (!ph.isEof() && !(token = Com.Parse(ph)).equals("endif")) ;
+ }
+ }
+
+ }
+ }
+
+ /*
+ * ================ SCR_DrawStats
+ *
+ * The status bar is a small layout program that is based on the stats array
+ * ================
+ */
+ static void DrawStats() {
+ //TODO:
+ SCR.ExecuteLayoutString(clientStateT.configstrings[CS_STATUSBAR]);
+ }
+
+ static void DrawLayout() {
+ if (clientStateT.frame.playerstate.stats[STAT_LAYOUTS] != 0)
+ SCR.ExecuteLayoutString(clientStateT.layout);
+ }
+
+ static void UpdateScreen2() {
+ int numframes;
+ int i;
+ // if the screen is disabled (loading plaque is up, or vid mode
+ // changing)
+ // do nothing at all
+ if (clientStaticT.disable_screen != 0) {
+ if (Timer.getCurrentTimeMillis() - clientStaticT.disable_screen > 120000) {
+ clientStaticT.disable_screen = 0;
+ Com.Printf("Loading plaque timed out.\n");
+ }
+ return;
+ }
+
+ if (!scr_initialized || !con.initialized)
+ return; // not initialized yet
+
+ /*
+ * * range check cl_camera_separation so we don't inadvertently fry
+ * someone's * brain
+ */
+ if (cl_stereo_separation.value > 1.0)
+ Cvar.setValue("cl_stereo_separation", 1.0f);
+ else if (cl_stereo_separation.value < 0)
+ Cvar.setValue("cl_stereo_separation", 0.0f);
+
+ if (cl_stereo.value != 0) {
+ numframes = 2;
+ separation[0] = -cl_stereo_separation.value / 2;
+ separation[1] = cl_stereo_separation.value / 2;
+ } else {
+ separation[0] = 0;
+ separation[1] = 0;
+ numframes = 1;
+ }
+
+ for (i = 0; i < numframes; i++) {
+ re.BeginFrame(separation[i]);
+
+ if (scr_draw_loading == 2) { // loading plaque over black screen
+ Dimension dim = new Dimension();
+
+ re.CinematicSetPalette(null);
+ scr_draw_loading = 0; // false
+ re.DrawGetPicSize(dim, "loading");
+ re.DrawPic((viddef.width - dim.width) / 2,
+ (viddef.height - dim.height) / 2, "loading");
+ }
+ // if a cinematic is supposed to be running, handle menus
+ // and console specially
+ else if (clientStateT.cinematictime > 0) {
+ if (clientStaticT.key_dest == key_menu) {
+ if (clientStateT.cinematicpalette_active) {
+ re.CinematicSetPalette(null);
+ clientStateT.cinematicpalette_active = false;
+ }
+ Menu.Draw();
+ } else if (clientStaticT.key_dest == key_console) {
+ if (clientStateT.cinematicpalette_active) {
+ re.CinematicSetPalette(null);
+ clientStateT.cinematicpalette_active = false;
+ }
+ DrawConsole();
+ } else {
+ // TODO implement cinematics completely
+ DrawCinematic();
+ }
+ } else {
+ // make sure the game palette is active
+ if (clientStateT.cinematicpalette_active) {
+ re.CinematicSetPalette(null);
+ clientStateT.cinematicpalette_active = false;
+ }
+
+ // do 3D refresh drawing, and then update the screen
+ CalcVrect();
+
+ // clear any dirty part of the background
+ TileClear();
+
+ V.RenderView(separation[i]);
+
+ DrawStats();
+
+ if ((clientStateT.frame.playerstate.stats[STAT_LAYOUTS] & 1) != 0)
+ DrawLayout();
+ if ((clientStateT.frame.playerstate.stats[STAT_LAYOUTS] & 2) != 0)
+ CL_inv.DrawInventory();
+
+ DrawNet();
+ CheckDrawCenterString();
+ DrawFPS();
+
+ //
+ // if (scr_timegraph->value)
+ // SCR_DebugGraph (cls.frametime*300, 0);
+ //
+ // if (scr_debuggraph->value || scr_timegraph->value ||
+ // scr_netgraph->value)
+ // SCR_DrawDebugGraph ();
+ //
+ DrawPause();
+ DrawConsole();
+ Menu.Draw();
+ DrawLoading();
+ }
+ }
+
+ Globals.re.EndFrame();
+ }
+
+ /*
+ * ================= SCR_DrawCrosshair =================
+ */
+ static void DrawCrosshair() {
+ if (crosshair.value == 0.0f)
+ return;
+
+ if (crosshair.modified) {
+ crosshair.modified = false;
+ SCR.TouchPics();
+ }
+
+ if (crosshair_pic.length() == 0)
+ return;
+
+ re.DrawPic(scr_vrect.x + ((scr_vrect.width - crosshair_width) >> 1),
+ scr_vrect.y + ((scr_vrect.height - crosshair_height) >> 1),
+ crosshair_pic);
+ }
+
+ // wird anstelle von der richtigen UpdateScreen benoetigt
+ public static void UpdateScreen() {
+ Globals.re.updateScreen(updateScreenCallback);
+ }
+
+ /*
+ * ================= SCR_AddDirtyPoint =================
+ */
+ static void AddDirtyPoint(int x, int y) {
+ if (x < scr_dirty.x1)
+ scr_dirty.x1 = x;
+ if (x > scr_dirty.x2)
+ scr_dirty.x2 = x;
+ if (y < scr_dirty.y1)
+ scr_dirty.y1 = y;
+ if (y > scr_dirty.y2)
+ scr_dirty.y2 = y;
+ }
+
+ static void DrawFPS() {
+ if (fps.value > 0.0f) {
+ if (fps.modified) {
+ fps.modified = false;
+ Cvar.setValue("cl_maxfps", 1000);
+ }
+
+ int diff = clientStaticT.realtime - lasttime;
+ if (diff > (int) (fps.value * 1000)) {
+ fpsvalue = (clientStaticT.framecount - lastframes) * 100000 / diff
+ / 100.0f + " fps";
+ lastframes = clientStaticT.framecount;
+ lasttime = clientStaticT.realtime;
+ }
+ int x = viddef.width - 8 * fpsvalue.length() - 2;
+ for (int i = 0; i < fpsvalue.length(); i++) {
+ re.DrawChar(x, 2, fpsvalue.charAt(i));
+ x += 8;
+ }
+ } else if (fps.modified) {
+ fps.modified = false;
+ Cvar.setValue("cl_maxfps", 90);
+ }
+ }
+
+ /*
+ * =================================================================
+ *
+ * cl_cin.c
+ *
+ * Play Cinematics
+ *
+ * =================================================================
+ */
+
+ /**
+ * LoadPCX
+ */
+ static int LoadPCX(String filename, byte[] palette) {
+ qfiles.pcx_t pcx;
+
+ // load the file
+ ByteBuffer raw = FS.LoadMappedFile(filename);
+
+ if (raw == null) {
+ VideoDriver.Printf(Defines.PRINT_DEVELOPER, "Bad pcx file " + filename
+ + '\n');
+ return 0;
+ }
+
+ // parse the PCX file
+ pcx = new qfiles.pcx_t(raw);
+
+ if (pcx.manufacturer != 0x0a || pcx.version != 5 || pcx.encoding != 1
+ || pcx.bits_per_pixel != 8 || pcx.xmax >= 640
+ || pcx.ymax >= 480) {
+
+ VideoDriver.Printf(Defines.PRINT_ALL, "Bad pcx file " + filename + '\n');
+ return 0;
+ }
+
+ int width = pcx.xmax - pcx.xmin + 1;
+ int height = pcx.ymax - pcx.ymin + 1;
+
+ byte[] pix = new byte[width * height];
+
+ if (palette != null) {
+ raw.position(raw.limit() - 768);
+ raw.get(palette);
+ }
+
+ if (SCR.cin != null) {
+ SCR.cin.pic = pix;
+ SCR.cin.width = width;
+ SCR.cin.height = height;
+ }
+
+ //
+ // decode pcx
+ //
+ int count = 0;
+ byte dataByte = 0;
+ int runLength = 0;
+ int x, y;
+
+ // simple counter for buffer indexing
+ int p = 0;
+
+ for (y = 0; y < height; y++) {
+ for (x = 0; x < width; ) {
+
+ dataByte = pcx.data.get(p++);
+
+ if ((dataByte & 0xC0) == 0xC0) {
+ runLength = dataByte & 0x3F;
+ dataByte = pcx.data.get(p++);
+ // write runLength pixel
+ while (runLength-- > 0) {
+ pix[count++] = dataByte;
+ x++;
+ }
+ } else {
+ // write one pixel
+ pix[count++] = dataByte;
+ x++;
+ }
+ }
+ }
+ return width * height;
+ }
+
+ /**
+ * StopCinematic
+ */
+ static void StopCinematic() {
+ if (cin.restart_sound) {
+ // done
+ clientStateT.cinematictime = 0;
+ cin.pic = null;
+ cin.pic_pending = null;
+ if (clientStateT.cinematicpalette_active) {
+ re.CinematicSetPalette(null);
+ clientStateT.cinematicpalette_active = false;
+ }
+ if (clientStateT.cinematic_file != null) {
+ // free the mapped byte buffer
+ clientStateT.cinematic_file = null;
+ }
+ if (cin.hnodes1 != null) {
+ cin.hnodes1 = null;
+ }
+
+ S.disableStreaming();
+ cin.restart_sound = false;
+ }
+ }
+
+ /**
+ * FinishCinematic
+ * <p>
+ * Called when either the cinematic completes, or it is aborted
+ */
+ static void FinishCinematic() {
+ // tell the server to advance to the next map / cinematic
+ MSG.WriteByte(clientStaticT.netchan.message, clc_stringcmd);
+ SZ.Print(clientStaticT.netchan.message, "nextserver " + clientStateT.servercount + '\n');
+ }
+
+ /**
+ * SmallestNode1
+ */
+ private static int SmallestNode1(int numhnodes) {
+
+ int best = 99999999;
+ int bestnode = -1;
+ for (int i = 0; i < numhnodes; i++) {
+ if (cin.h_used[i] != 0)
+ continue;
+ if (cin.h_count[i] == 0)
+ continue;
+ if (cin.h_count[i] < best) {
+ best = cin.h_count[i];
+ bestnode = i;
+ }
+ }
+
+ if (bestnode == -1)
+ return -1;
+
+ cin.h_used[bestnode] = 1; // true
+ return bestnode;
+ }
+
+ /**
+ * Huff1TableInit
+ * <p>
+ * Reads the 64k counts table and initializes the node trees.
+ */
+ private static void Huff1TableInit() {
+ int[] node;
+ byte[] counts = new byte[256];
+ int numhnodes;
+
+ cin.hnodes1 = new int[256 * 256 * 2];
+ Arrays.fill(cin.hnodes1, 0);
+
+ for (int prev = 0; prev < 256; prev++) {
+ Arrays.fill(cin.h_count, 0);
+ Arrays.fill(cin.h_used, 0);
+
+ // read a row of counts
+ clientStateT.cinematic_file.get(counts);
+ for (int j = 0; j < 256; j++)
+ cin.h_count[j] = counts[j] & 0xFF;
+
+ // build the nodes
+ numhnodes = 256;
+ int nodebase = prev * 256 * 2;
+ int index = 0;
+ node = cin.hnodes1;
+ while (numhnodes != 511) {
+ index = nodebase + (numhnodes - 256) * 2;
+
+ // pick two lowest counts
+ node[index] = SmallestNode1(numhnodes);
+ if (node[index] == -1)
+ break; // no more
+
+ node[index + 1] = SmallestNode1(numhnodes);
+ if (node[index + 1] == -1)
+ break;
+
+ cin.h_count[numhnodes] = cin.h_count[node[index]] + cin.h_count[node[index + 1]];
+ numhnodes++;
+ }
+
+ cin.numhnodes1[prev] = numhnodes - 1;
+ }
+ }
+
+ // ==========================================================================
+
+ /**
+ * Huff1Decompress
+ */
+ private static byte[] Huff1Decompress(int size) {
+ // get decompressed count
+ int count = (SCR.compressed[0] & 0xFF) | ((SCR.compressed[1] & 0xFF) << 8) | ((SCR.compressed[2] & 0xFF) << 16) | ((SCR.compressed[3] & 0xFF) << 24);
+ // used as index for in[];
+ int input = 4;
+ byte[] out = new byte[count];
+ // used as index for out[];
+ int out_p = 0;
+
+ // read bits
+
+ int hnodesbase = -256 * 2; // nodes 0-255 aren't stored
+ int index = hnodesbase;
+ int[] hnodes = cin.hnodes1;
+ int nodenum = cin.numhnodes1[0];
+ int inbyte;
+ while (count != 0) {
+ inbyte = SCR.compressed[input++] & 0xFF;
+
+ if (nodenum < 256) {
+ index = hnodesbase + (nodenum << 9);
+ out[out_p++] = (byte) nodenum;
+ if (--count == 0)
+ break;
+ nodenum = cin.numhnodes1[nodenum];
+ }
+ nodenum = hnodes[index + nodenum * 2 + (inbyte & 1)];
+ inbyte >>= 1;
+
+ if (nodenum < 256) {
+ index = hnodesbase + (nodenum << 9);
+ out[out_p++] = (byte) nodenum;
+ if (--count == 0)
+ break;
+ nodenum = cin.numhnodes1[nodenum];
+ }
+ nodenum = hnodes[index + nodenum * 2 + (inbyte & 1)];
+ inbyte >>= 1;
+
+ if (nodenum < 256) {
+ index = hnodesbase + (nodenum << 9);
+ out[out_p++] = (byte) nodenum;
+ if (--count == 0)
+ break;
+ nodenum = cin.numhnodes1[nodenum];
+ }
+ nodenum = hnodes[index + nodenum * 2 + (inbyte & 1)];
+ inbyte >>= 1;
+
+ if (nodenum < 256) {
+ index = hnodesbase + (nodenum << 9);
+ out[out_p++] = (byte) nodenum;
+ if (--count == 0)
+ break;
+ nodenum = cin.numhnodes1[nodenum];
+ }
+ nodenum = hnodes[index + nodenum * 2 + (inbyte & 1)];
+ inbyte >>= 1;
+
+ if (nodenum < 256) {
+ index = hnodesbase + (nodenum << 9);
+ out[out_p++] = (byte) nodenum;
+ if (--count == 0)
+ break;
+ nodenum = cin.numhnodes1[nodenum];
+ }
+ nodenum = hnodes[index + nodenum * 2 + (inbyte & 1)];
+ inbyte >>= 1;
+
+ if (nodenum < 256) {
+ index = hnodesbase + (nodenum << 9);
+ out[out_p++] = (byte) nodenum;
+ if (--count == 0)
+ break;
+ nodenum = cin.numhnodes1[nodenum];
+ }
+ nodenum = hnodes[index + nodenum * 2 + (inbyte & 1)];
+ inbyte >>= 1;
+
+ if (nodenum < 256) {
+ index = hnodesbase + (nodenum << 9);
+ out[out_p++] = (byte) nodenum;
+ if (--count == 0)
+ break;
+ nodenum = cin.numhnodes1[nodenum];
+ }
+ nodenum = hnodes[index + nodenum * 2 + (inbyte & 1)];
+ inbyte >>= 1;
+
+ if (nodenum < 256) {
+ index = hnodesbase + (nodenum << 9);
+ out[out_p++] = (byte) nodenum;
+ if (--count == 0)
+ break;
+ nodenum = cin.numhnodes1[nodenum];
+ }
+ nodenum = hnodes[index + nodenum * 2 + (inbyte & 1)];
+ inbyte >>= 1;
+ }
+
+ if (input != size && input != size + 1) {
+ Com.Printf("Decompression overread by " + (input - size));
+ }
+
+ return out;
+ }
+
+ /**
+ * ReadNextFrame
+ */
+ static byte[] ReadNextFrame() {
+
+ ByteBuffer file = clientStateT.cinematic_file;
+
+ // read the next frame
+ int command = file.getInt();
+
+ if (command == 2) {
+ // last frame marker
+ return null;
+ }
+
+ if (command == 1) {
+ // read palette
+ file.get(clientStateT.cinematicpalette);
+ // dubious.... exposes an edge case
+ clientStateT.cinematicpalette_active = false;
+ }
+ // decompress the next frame
+ int size = file.getInt();
+ if (size > compressed.length || size < 1)
+ Com.Error(ERR_DROP, "Bad compressed frame size:" + size);
+
+ file.get(compressed, 0, size);
+
+ // read sound
+ int start = clientStateT.cinematicframe * cin.s_rate / 14;
+ int end = (clientStateT.cinematicframe + 1) * cin.s_rate / 14;
+ int count = end - start;
+
+ S.RawSamples(count, cin.s_rate, cin.s_width, cin.s_channels, file.slice());
+ // skip the sound samples
+ file.position(file.position() + count * cin.s_width * cin.s_channels);
+
+ byte[] pic = Huff1Decompress(size);
+ clientStateT.cinematicframe++;
+
+ return pic;
+ }
+
+ /**
+ * RunCinematic
+ */
+ static void RunCinematic() {
+ if (clientStateT.cinematictime <= 0) {
+ StopCinematic();
+ return;
+ }
+
+ if (clientStateT.cinematicframe == -1) {
+ // static image
+ return;
+ }
+
+ if (clientStaticT.key_dest != key_game) {
+ // pause if menu or console is up
+ clientStateT.cinematictime = clientStaticT.realtime - clientStateT.cinematicframe * 1000 / 14;
+ return;
+ }
+
+ int frame = (int) ((clientStaticT.realtime - clientStateT.cinematictime) * 14.0f / 1000);
+
+ if (frame <= clientStateT.cinematicframe)
+ return;
+
+ if (frame > clientStateT.cinematicframe + 1) {
+ Com.Println("Dropped frame: " + frame + " > "
+ + (clientStateT.cinematicframe + 1));
+ clientStateT.cinematictime = clientStaticT.realtime - clientStateT.cinematicframe * 1000 / 14;
+ }
+
+ cin.pic = cin.pic_pending;
+ cin.pic_pending = ReadNextFrame();
+
+ if (cin.pic_pending == null) {
+ StopCinematic();
+ FinishCinematic();
+ // hack to get the black screen behind loading
+ clientStateT.cinematictime = 1;
+ BeginLoadingPlaque();
+ clientStateT.cinematictime = 0;
+ }
+ }
+
+ /**
+ * DrawCinematic
+ * <p>
+ * Returns true if a cinematic is active, meaning the view rendering should
+ * be skipped.
+ */
+ static void DrawCinematic() {
+ if (clientStateT.cinematictime <= 0) {
+ return;
+ }
+
+ if (clientStaticT.key_dest == key_menu) {
+ // blank screen and pause if menu is up
+ Globals.re.CinematicSetPalette(null);
+ clientStateT.cinematicpalette_active = false;
+ return;
+ }
+
+ if (!clientStateT.cinematicpalette_active) {
+ re.CinematicSetPalette(clientStateT.cinematicpalette);
+ clientStateT.cinematicpalette_active = true;
+ }
+
+ if (cin.pic == null)
+ return;
+
+ Globals.re.DrawStretchRaw(0, 0, viddef.width, viddef.height, cin.width, cin.height, cin.pic);
+
+ }
+
+ /**
+ * PlayCinematic
+ */
+ static void PlayCinematic(String arg) {
+
+ // make sure CD isn't playing music
+ //CDAudio.Stop();
+
+ clientStateT.cinematicframe = 0;
+ if (arg.endsWith(".pcx")) {
+ // static pcx image
+ String name = "pics/" + arg;
+ int size = LoadPCX(name, clientStateT.cinematicpalette);
+ clientStateT.cinematicframe = -1;
+ clientStateT.cinematictime = 1;
+ EndLoadingPlaque();
+ clientStaticT.state = ca_active;
+ if (size == 0 || cin.pic == null) {
+ Com.Println(name + " not found.");
+ clientStateT.cinematictime = 0;
+ }
+ return;
+ }
+
+ String name = "video/" + arg;
+ clientStateT.cinematic_file = FS.LoadMappedFile(name);
+ if (clientStateT.cinematic_file == null) {
+ //Com.Error(ERR_DROP, "Cinematic " + name + " not found.\n");
+ FinishCinematic();
+ // done
+ clientStateT.cinematictime = 0;
+ return;
+ }
+
+ EndLoadingPlaque();
+
+ clientStaticT.state = ca_active;
+
+ clientStateT.cinematic_file.order(ByteOrder.LITTLE_ENDIAN);
+ ByteBuffer file = clientStateT.cinematic_file;
+ cin.width = file.getInt();
+ cin.height = file.getInt();
+ cin.s_rate = file.getInt();
+ cin.s_width = file.getInt();
+ cin.s_channels = file.getInt();
+
+ Huff1TableInit();
+
+ cin.restart_sound = true;
+ clientStateT.cinematicframe = 0;
+ cin.pic = ReadNextFrame();
+ clientStateT.cinematictime = Timer.getCurrentTimeMillis();
+ }
+
+ static class dirty_t {
+ int x1;
+
+ int x2;
+
+ int y1;
+
+ int y2;
+
+ void set(dirty_t src) {
+ x1 = src.x1;
+ x2 = src.x2;
+ y1 = src.y1;
+ y2 = src.y2;
+ }
+
+ void clear() {
+ x1 = x2 = y1 = y2 = 0;
+ }
+ }
+
+ // typedef struct
+ // {
+ // float value;
+ // int color;
+ // } graphsamp_t;
+ static class graphsamp_t {
+ float value;
+
+ int color;
+ }
+
+ private static class cinematics_t {
+ final int[] numhnodes1 = new int[256];
+ final int[] h_used = new int[512];
+ final int[] h_count = new int[512];
+ boolean restart_sound;
+ int s_rate;
+ int s_width;
+ int s_channels;
+ int width;
+ int height;
+ byte[] pic;
+ byte[] pic_pending;
+ // order 1 huffman stuff
+ int[] hnodes1; // [256][256][2];
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.client;
+
+import lwjake2.Globals;
+import lwjake2.game.Cmd;
+import lwjake2.game.CvarT;
+import lwjake2.qcommon.Com;
+import lwjake2.qcommon.Cvar;
+import lwjake2.qcommon.xcommand_t;
+import lwjake2.sys.Timer;
+import lwjake2.util.Math3D;
+import lwjake2.util.Vargs;
+
+import java.nio.FloatBuffer;
+
+/**
+ * V
+ */
+public final class V extends Globals {
+
+ static final dlight_t[] r_dlights = new dlight_t[MAX_DLIGHTS];
+ static final entity_t[] r_entities = new entity_t[MAX_ENTITIES];
+ static final lightstyle_t[] r_lightstyles = new lightstyle_t[MAX_LIGHTSTYLES];
+ /*
+ * ============= V_Viewpos_f =============
+ */
+ static final xcommand_t Viewpos_f = new xcommand_t() {
+ public void execute() {
+ Com.Printf("(%i %i %i) : %i\n", new Vargs(4).add(
+ (int) clientStateT.refdef.vieworg[0]).add((int) clientStateT.refdef.vieworg[1])
+ .add((int) clientStateT.refdef.vieworg[2]).add(
+ (int) clientStateT.refdef.viewangles[YAW]));
+ }
+ };
+ // stack variable
+ private static final float[] origin = {0, 0, 0};
+ static CvarT cl_testblend;
+ static CvarT cl_testparticles;
+ static CvarT cl_testentities;
+ static CvarT cl_testlights;
+ static CvarT cl_stats;
+ static int r_numdlights;
+ static int r_numentities;
+ //static particle_t[] r_particles = new particle_t[MAX_PARTICLES];
+ static int r_numparticles;
+
+ static {
+ for (int i = 0; i < r_dlights.length; i++)
+ r_dlights[i] = new dlight_t();
+ for (int i = 0; i < r_entities.length; i++)
+ r_entities[i] = new entity_t();
+ for (int i = 0; i < r_lightstyles.length; i++)
+ r_lightstyles[i] = new lightstyle_t();
+ }
+
+ /*
+ * ==================== V_ClearScene
+ *
+ * Specifies the model that will be used as the world ====================
+ */
+ static void ClearScene() {
+ r_numdlights = 0;
+ r_numentities = 0;
+ r_numparticles = 0;
+ }
+
+ /*
+ * ===================== V_AddEntity
+ *
+ * =====================
+ */
+ static void AddEntity(entity_t ent) {
+ if (r_numentities >= MAX_ENTITIES)
+ return;
+ r_entities[r_numentities++].set(ent);
+ }
+
+ /*
+ * ===================== V_AddParticle
+ *
+ * =====================
+ */
+ static void AddParticle(float[] org, int color, float alpha) {
+ if (r_numparticles >= MAX_PARTICLES)
+ return;
+
+ int i = r_numparticles++;
+
+ int c = particle_t.colorTable[color];
+ c |= (int) (alpha * 255) << 24;
+ particle_t.colorArray.put(i, c);
+
+ i *= 3;
+ FloatBuffer vertexBuf = particle_t.vertexArray;
+ vertexBuf.put(i++, org[0]);
+ vertexBuf.put(i++, org[1]);
+ vertexBuf.put(i++, org[2]);
+ }
+
+ /*
+ * ===================== V_AddLight
+ *
+ * =====================
+ */
+ static void AddLight(float[] org, float intensity, float r, float g, float b) {
+ dlight_t dl;
+
+ if (r_numdlights >= MAX_DLIGHTS)
+ return;
+ dl = r_dlights[r_numdlights++];
+ Math3D.vectorCopy(org, dl.origin);
+ dl.intensity = intensity;
+ dl.color[0] = r;
+ dl.color[1] = g;
+ dl.color[2] = b;
+ }
+
+ /*
+ * ===================== V_AddLightStyle
+ *
+ * =====================
+ */
+ static void AddLightStyle(int style, float r, float g, float b) {
+ lightstyle_t ls;
+
+ if (style < 0 || style > MAX_LIGHTSTYLES)
+ Com.Error(ERR_DROP, "Bad light style " + style);
+ ls = r_lightstyles[style];
+
+ ls.white = r + g + b;
+ ls.rgb[0] = r;
+ ls.rgb[1] = g;
+ ls.rgb[2] = b;
+ }
+
+ /*
+ * ================ V_TestParticles
+ *
+ * If cl_testparticles is set, create 4096 particles in the view
+ * ================
+ */
+ static void TestParticles() {
+ int i, j;
+ float d, r, u;
+
+ r_numparticles = 0;
+ for (i = 0; i < MAX_PARTICLES; i++) {
+ d = i * 0.25f;
+ r = 4 * ((i & 7) - 3.5f);
+ u = 4 * (((i >> 3) & 7) - 3.5f);
+
+ for (j = 0; j < 3; j++)
+ origin[j] = clientStateT.refdef.vieworg[j] + clientStateT.v_forward[j] * d
+ + clientStateT.v_right[j] * r + clientStateT.v_up[j] * u;
+
+ AddParticle(origin, 8, cl_testparticles.value);
+ }
+ }
+
+ /*
+ * ================ V_TestEntities
+ *
+ * If cl_testentities is set, create 32 player models ================
+ */
+ static void TestEntities() {
+ int i, j;
+ float f, r;
+ entity_t ent;
+
+ r_numentities = 32;
+ //memset (r_entities, 0, sizeof(r_entities));
+ for (i = 0; i < r_entities.length; i++)
+ r_entities[i].clear();
+
+ for (i = 0; i < r_numentities; i++) {
+ ent = r_entities[i];
+
+ r = 64 * ((i % 4) - 1.5f);
+ f = 64 * (i / 4) + 128;
+
+ for (j = 0; j < 3; j++)
+ ent.origin[j] = clientStateT.refdef.vieworg[j] + clientStateT.v_forward[j] * f
+ + clientStateT.v_right[j] * r;
+
+ ent.model = clientStateT.baseclientinfo.model;
+ ent.skin = clientStateT.baseclientinfo.skin;
+ }
+ }
+
+ /*
+ * ================ V_TestLights
+ *
+ * If cl_testlights is set, create 32 lights models ================
+ */
+ static void TestLights() {
+ int i, j;
+ float f, r;
+ dlight_t dl;
+
+ r_numdlights = 32;
+ //memset (r_dlights, 0, sizeof(r_dlights));
+ for (i = 0; i < r_dlights.length; i++)
+ r_dlights[i] = new dlight_t();
+
+ for (i = 0; i < r_numdlights; i++) {
+ dl = r_dlights[i];
+
+ r = 64 * ((i % 4) - 1.5f);
+ f = 64 * (i / 4) + 128;
+
+ for (j = 0; j < 3; j++)
+ dl.origin[j] = clientStateT.refdef.vieworg[j] + clientStateT.v_forward[j] * f
+ + clientStateT.v_right[j] * r;
+ dl.color[0] = ((i % 6) + 1) & 1;
+ dl.color[1] = (((i % 6) + 1) & 2) >> 1;
+ dl.color[2] = (((i % 6) + 1) & 4) >> 2;
+ dl.intensity = 200;
+ }
+ }
+
+ /*
+ * ================== V_RenderView
+ *
+ * ==================
+ */
+ static void RenderView(float stereo_separation) {
+ // extern int entitycmpfnc( const entity_t *, const entity_t * );
+ //
+ if (clientStaticT.state != ca_active)
+ return;
+
+ if (!clientStateT.refresh_prepped)
+ return; // still loading
+
+ if (cl_timedemo.value != 0.0f) {
+ if (clientStateT.timedemo_start == 0)
+ clientStateT.timedemo_start = Timer.getCurrentTimeMillis();
+ clientStateT.timedemo_frames++;
+ }
+
+ // an invalid frame will just use the exact previous refdef
+ // we can't use the old frame if the video mode has changed, though...
+ if (clientStateT.frame.valid && (clientStateT.force_refdef || cl_paused.value == 0.0f)) {
+ clientStateT.force_refdef = false;
+
+ V.ClearScene();
+
+ // build a refresh entity list and calc cl.sim*
+ // this also calls CL_CalcViewValues which loads
+ // v_forward, etc.
+ CL_ents.AddEntities();
+
+ if (cl_testparticles.value != 0.0f)
+ TestParticles();
+ if (cl_testentities.value != 0.0f)
+ TestEntities();
+ if (cl_testlights.value != 0.0f)
+ TestLights();
+ if (cl_testblend.value != 0.0f) {
+ clientStateT.refdef.blend[0] = 1.0f;
+ clientStateT.refdef.blend[1] = 0.5f;
+ clientStateT.refdef.blend[2] = 0.25f;
+ clientStateT.refdef.blend[3] = 0.5f;
+ }
+
+ // offset vieworg appropriately if we're doing stereo separation
+ if (stereo_separation != 0) {
+ float[] tmp = new float[3];
+
+ Math3D.vectorScale(clientStateT.v_right, stereo_separation, tmp);
+ Math3D.vectorAdd(clientStateT.refdef.vieworg, tmp, clientStateT.refdef.vieworg);
+ }
+
+ // never let it sit exactly on a node line, because a water plane
+ // can
+ // dissapear when viewed with the eye exactly on it.
+ // the server protocol only specifies to 1/8 pixel, so add 1/16 in
+ // each axis
+ clientStateT.refdef.vieworg[0] += 1.0 / 16;
+ clientStateT.refdef.vieworg[1] += 1.0 / 16;
+ clientStateT.refdef.vieworg[2] += 1.0 / 16;
+
+ clientStateT.refdef.x = scr_vrect.x;
+ clientStateT.refdef.y = scr_vrect.y;
+ clientStateT.refdef.width = scr_vrect.width;
+ clientStateT.refdef.height = scr_vrect.height;
+ clientStateT.refdef.fov_y = Math3D.calcFov(clientStateT.refdef.fov_x, clientStateT.refdef.width,
+ clientStateT.refdef.height);
+ clientStateT.refdef.time = clientStateT.time * 0.001f;
+
+ clientStateT.refdef.areabits = clientStateT.frame.areabits;
+
+ if (cl_add_entities.value == 0.0f)
+ r_numentities = 0;
+ if (cl_add_particles.value == 0.0f)
+ r_numparticles = 0;
+ if (cl_add_lights.value == 0.0f)
+ r_numdlights = 0;
+ if (cl_add_blend.value == 0) {
+ Math3D.vectorClear(clientStateT.refdef.blend);
+ }
+
+ clientStateT.refdef.num_entities = r_numentities;
+ clientStateT.refdef.entities = r_entities;
+ clientStateT.refdef.num_particles = r_numparticles;
+ clientStateT.refdef.num_dlights = r_numdlights;
+ clientStateT.refdef.dlights = r_dlights;
+ clientStateT.refdef.lightstyles = r_lightstyles;
+
+ clientStateT.refdef.rdflags = clientStateT.frame.playerstate.rdflags;
+
+ // sort entities for better cache locality
+ // !!! useless in Java !!!
+ //Arrays.sort(cl.refdef.entities, entitycmpfnc);
+ }
+
+ re.RenderFrame(clientStateT.refdef);
+
+ if (cl_stats.value != 0.0f)
+ Com.Printf("ent:%i lt:%i part:%i\n", new Vargs(3).add(
+ r_numentities).add(r_numdlights).add(r_numparticles));
+
+ SCR.AddDirtyPoint(scr_vrect.x, scr_vrect.y);
+ SCR.AddDirtyPoint(scr_vrect.x + scr_vrect.width - 1, scr_vrect.y
+ + scr_vrect.height - 1);
+
+ SCR.DrawCrosshair();
+ }
+
+ public static void Init() {
+
+ Cmd.AddCommand("viewpos", Viewpos_f);
+
+ crosshair = Cvar.get("crosshair", "0", CVAR_ARCHIVE);
+
+ cl_testblend = Cvar.get("cl_testblend", "0", 0);
+ cl_testparticles = Cvar.get("cl_testparticles", "0", 0);
+ cl_testentities = Cvar.get("cl_testentities", "0", 0);
+ cl_testlights = Cvar.get("cl_testlights", "0", 0);
+
+ cl_stats = Cvar.get("cl_stats", "0", 0);
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.client;
+
+import lwjake2.Defines;
+import lwjake2.Globals;
+import lwjake2.game.Cmd;
+import lwjake2.game.CvarT;
+import lwjake2.qcommon.Com;
+import lwjake2.qcommon.Cvar;
+import lwjake2.qcommon.xcommand_t;
+import lwjake2.render.Renderer;
+import lwjake2.sound.S;
+import lwjake2.sys.UserInputHandler;
+import lwjake2.util.Vargs;
+
+import java.awt.*;
+
+/**
+ * VideoDriver is a video driver.
+ * <p>
+ * source: client/vid.h linux/vid_so.c
+ *
+ * @author cwei
+ */
+public class VideoDriver extends Globals {
+ // Main windowed and fullscreen graphics interface module. This module
+ // is used for both the software and OpenGL rendering versions of the
+ // Quake refresh engine.
+
+ // Global variables used internally by this module
+ // Globals.viddef
+ // global video state; used by other modules
+
+ // Structure containing functions exported from refresh DLL
+ // Globals.re;
+
+ static final String[] resolutions =
+ {
+ "[320 240 ]",
+ "[400 300 ]",
+ "[512 384 ]",
+ "[640 480 ]",
+ "[800 600 ]",
+ "[960 720 ]",
+ "[1024 768 ]",
+ "[1152 864 ]",
+ "[1280 1024]",
+ "[1600 1200]",
+ "[2048 1536]",
+ "user mode",
+ };
+ static final String[] yesno_names =
+ {
+ "no",
+ "yes",
+ };
+ /*
+ ** VID_GetModeInfo
+ */
+ static final vidmode_t[] vid_modes =
+ {
+ new vidmode_t("Mode 0: 320x240", 320, 240, 0),
+ new vidmode_t("Mode 1: 400x300", 400, 300, 1),
+ new vidmode_t("Mode 2: 512x384", 512, 384, 2),
+ new vidmode_t("Mode 3: 640x480", 640, 480, 3),
+ new vidmode_t("Mode 4: 800x600", 800, 600, 4),
+ new vidmode_t("Mode 5: 960x720", 960, 720, 5),
+ new vidmode_t("Mode 6: 1024x768", 1024, 768, 6),
+ new vidmode_t("Mode 7: 1152x864", 1152, 864, 7),
+ new vidmode_t("Mode 8: 1280x1024", 1280, 1024, 8),
+ new vidmode_t("Mode 9: 1600x1200", 1600, 1200, 9),
+ new vidmode_t("Mode 10: 2048x1536", 2048, 1536, 10),
+ new vidmode_t("Mode 11: user", 640, 480, 11)};
+ static final Menu.menuframework_s s_opengl_menu = new Menu.menuframework_s();
+ static final Menu.menulist_s s_mode_list = new Menu.menulist_s();
+ // const char so_file[] = "/etc/quake2.conf";
+ static final Menu.menulist_s s_ref_list = new Menu.menulist_s();
+ static final Menu.menuslider_s s_tq_slider = new Menu.menuslider_s();
+ static final Menu.menuslider_s s_screensize_slider = new Menu.menuslider_s();
+ static final Menu.menuslider_s s_brightness_slider = new Menu.menuslider_s();
+ static final Menu.menulist_s s_fs_box = new Menu.menulist_s();
+ /*
+ ====================================================================
+
+ MENU INTERACTION
+
+ ====================================================================
+ */
+ static final Menu.menulist_s s_stipple_box = new Menu.menulist_s();
+ static final Menu.menulist_s s_paletted_texture_box = new Menu.menulist_s();
+ static final Menu.menulist_s s_windowed_mouse = new Menu.menulist_s();
+ static final Menu.menuaction_s s_apply_action = new Menu.menuaction_s();
+ static final Menu.menuaction_s s_defaults_action = new Menu.menuaction_s();
+ // Console variables that we need to access from this module
+ static CvarT vid_gamma;
+ static CvarT vid_ref; // Name of Refresh DLL loaded
+ static CvarT vid_xpos; // X coordinate of window position
+ /*
+ ==========================================================================
+
+ DLL GLUE
+
+ ==========================================================================
+ */
+ static CvarT vid_ypos; // Y coordinate of window position
+ static CvarT vid_width;
+ // ==========================================================================
+ static CvarT vid_height;
+ static CvarT vid_fullscreen;
+ // Global variables used internally by this module
+ // void *reflib_library; // Handle to refresh DLL
+ static boolean reflib_active = false;
+ static vidmode_t fs_modes[];
+ static CvarT gl_mode;
+ static CvarT gl_driver;
+ static CvarT gl_picmip;
+ static CvarT gl_ext_palettedtexture;
+ static CvarT sw_mode;
+ // ==========================================================================
+ //
+ // vid_menu.c
+ //
+ // ==========================================================================
+ static CvarT sw_stipplealpha;
+ static CvarT _windowed_mouse;
+ static Menu.menuframework_s s_current_menu; // referenz
+ static String[] fs_resolutions;
+ static int mode_x;
+ static String[] refs;
+ static String[] drivers;
+
+ public static void Printf(int print_level, String fmt) {
+ Printf(print_level, fmt, null);
+ }
+
+ public static void Printf(int print_level, String fmt, Vargs vargs) {
+ // static qboolean inupdate;
+ if (print_level == Defines.PRINT_ALL)
+ Com.Printf(fmt, vargs);
+ else
+ Com.DPrintf(fmt, vargs);
+ }
+
+ /*
+ ============
+ VID_Restart_f
+
+ Console command to re-start the video mode and refresh DLL. We do this
+ simply by setting the modified flag for the vid_ref variable, which will
+ cause the entire video mode and refresh DLL to be reset on the next frame.
+ ============
+ */
+ static void Restart_f() {
+ vid_modes[11].width = (int) vid_width.value;
+ vid_modes[11].height = (int) vid_height.value;
+
+ vid_ref.modified = true;
+ }
+
+ public static boolean GetModeInfo(Dimension dim, int mode) {
+ if (fs_modes == null) initModeList();
+
+ vidmode_t[] modes = vid_modes;
+ if (vid_fullscreen.value != 0.0f) modes = fs_modes;
+
+ if (mode < 0 || mode >= modes.length)
+ return false;
+
+ dim.width = modes[mode].width;
+ dim.height = modes[mode].height;
+
+ return true;
+ }
+
+ /*
+ ** VID_NewWindow
+ */
+ public static void NewWindow(int width, int height) {
+ Globals.viddef.width = width;
+ Globals.viddef.height = height;
+ }
+
+ static void FreeReflib() {
+ if (Globals.re != null) {
+ Globals.re.getKeyboardHandler().Close();
+ UserInputHandler.Shutdown();
+ }
+
+ Globals.re = null;
+ reflib_active = false;
+ }
+
+ /*
+ ==============
+ VID_LoadRefresh
+ ==============
+ */
+ static boolean LoadRefresh(String name) {
+
+ if (reflib_active) {
+ Globals.re.getKeyboardHandler().Close();
+ UserInputHandler.Shutdown();
+
+ Globals.re.Shutdown();
+ FreeReflib();
+ }
+
+ Com.Printf("------- Loading " + name + " -------\n");
+
+ boolean found = false;
+
+ String[] driverNames = Renderer.getDriverNames();
+ for (String driverName : driverNames) {
+ if (driverName.equals(name)) {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ Com.Printf("LoadLibrary(\"" + name + "\") failed\n");
+ return false;
+ }
+
+ Com.Printf("LoadLibrary(\"" + name + "\")\n");
+ Globals.re = Renderer.getDriver(name);
+
+ if (Globals.re == null) {
+ Com.Error(Defines.ERR_FATAL, name + " can't load but registered");
+ }
+
+ if (Globals.re.apiVersion() != Defines.API_VERSION) {
+ FreeReflib();
+ Com.Error(Defines.ERR_FATAL, name + " has incompatible api_version");
+ }
+
+ UserInputHandler.Real_IN_Init();
+
+ if (!Globals.re.Init((int) vid_xpos.value, (int) vid_ypos.value)) {
+ Globals.re.Shutdown();
+ FreeReflib();
+ return false;
+ }
+
+ /* Init KBD */
+ Globals.re.getKeyboardHandler().Init();
+
+ Com.Printf("------------------------------------\n");
+ reflib_active = true;
+ return true;
+ }
+
+ /*
+ ============
+ VID_CheckChanges
+
+ This function gets called once just before drawing each frame, and it's sole purpose in life
+ is to check to see if any of the video mode parameters have changed, and if they have to
+ update the rendering DLL and/or video mode to match.
+ ============
+ */
+ public static void CheckChanges() {
+ CvarT gl_mode;
+
+ if (vid_ref.modified) {
+ S.StopAllSounds();
+ }
+
+ while (vid_ref.modified) {
+ /*
+ ** refresh has changed
+ */
+ vid_ref.modified = false;
+ vid_fullscreen.modified = true;
+ Globals.clientStateT.refresh_prepped = false;
+ Globals.clientStaticT.disable_screen = 1.0f; // true;
+
+
+ if (!LoadRefresh(vid_ref.string)) {
+ String renderer;
+ if (vid_ref.string.equals(Renderer.getPreferedName())) {
+ // try the default renderer as fallback after prefered
+ renderer = Renderer.getDefaultName();
+ } else {
+ // try the prefered renderer as first fallback
+ renderer = Renderer.getPreferedName();
+ }
+ if (vid_ref.string.equals(Renderer.getDefaultName())) {
+ renderer = vid_ref.string;
+ Com.Printf("Refresh failed\n");
+ gl_mode = Cvar.get("gl_mode", "0", 0);
+ if (gl_mode.value != 0.0f) {
+ Com.Printf("Trying mode 0\n");
+ Cvar.setValue("gl_mode", 0);
+ if (!LoadRefresh(vid_ref.string))
+ Com.Error(Defines.ERR_FATAL, "Couldn't fall back to " + renderer + " refresh!");
+ } else
+ Com.Error(Defines.ERR_FATAL, "Couldn't fall back to " + renderer + " refresh!");
+ }
+
+ Cvar.set("vid_ref", renderer);
+
+ /*
+ * drop the console if we fail to load a refresh
+ */
+ if (Globals.clientStaticT.key_dest != Defines.key_console) {
+ try {
+ Console.ToggleConsole_f.execute();
+ } catch (Exception ignored) {
+ }
+ }
+ }
+ Globals.clientStaticT.disable_screen = 0.0f; //false;
+ }
+ }
+
+ /*
+ ============
+ VID_Init
+ ============
+ */
+ public static void Init() {
+ /* Create the video variables so we know how to start the graphics drivers */
+ vid_ref = Cvar.get("vid_ref", Renderer.getPreferedName(), CVAR_ARCHIVE);
+ vid_xpos = Cvar.get("vid_xpos", "3", CVAR_ARCHIVE);
+ vid_ypos = Cvar.get("vid_ypos", "22", CVAR_ARCHIVE);
+ vid_width = Cvar.get("vid_width", "640", CVAR_ARCHIVE);
+ vid_height = Cvar.get("vid_height", "480", CVAR_ARCHIVE);
+ vid_fullscreen = Cvar.get("vid_fullscreen", "0", CVAR_ARCHIVE);
+ vid_gamma = Cvar.get("vid_gamma", "1", CVAR_ARCHIVE);
+
+ vid_modes[11].width = (int) vid_width.value;
+ vid_modes[11].height = (int) vid_height.value;
+
+ /* Add some console commands that we want to handle */
+ Cmd.AddCommand("vid_restart", new xcommand_t() {
+ public void execute() {
+ Restart_f();
+ }
+ });
+
+ /* Disable the 3Dfx splash screen */
+ // putenv("FX_GLIDE_NO_SPLASH=0");
+
+ /* Start the graphics mode and load refresh DLL */
+ CheckChanges();
+ }
+
+ /*
+ ============
+ VID_Shutdown
+ ============
+ */
+ public static void Shutdown() {
+ if (reflib_active) {
+ Globals.re.getKeyboardHandler().Close();
+ UserInputHandler.Shutdown();
+
+ Globals.re.Shutdown();
+ FreeReflib();
+ }
+ }
+
+ static void DriverCallback(Object unused) {
+ s_current_menu = s_opengl_menu; // s_software_menu;
+ }
+
+ static void ScreenSizeCallback(Object s) {
+ Menu.menuslider_s slider = (Menu.menuslider_s) s;
+
+ Cvar.setValue("viewsize", slider.curvalue * 10);
+ }
+
+ static void BrightnessCallback(Object s) {
+ Menu.menuslider_s slider = (Menu.menuslider_s) s;
+
+ // if ( stricmp( vid_ref.string, "soft" ) == 0 ||
+ // stricmp( vid_ref.string, "softx" ) == 0 )
+ if (vid_ref.string.equalsIgnoreCase("soft") ||
+ vid_ref.string.equalsIgnoreCase("softx")) {
+ float gamma = (0.8f - (slider.curvalue / 10.0f - 0.5f)) + 0.5f;
+
+ Cvar.setValue("vid_gamma", gamma);
+ }
+ }
+
+ static void ResetDefaults(Object unused) {
+ MenuInit();
+ }
+
+ static void ApplyChanges(Object unused) {
+
+ /*
+ ** invert sense so greater = brighter, and scale to a range of 0.5 to 1.3
+ */
+ // the original was modified, because on CRTs it was too dark.
+ // the slider range is [5; 13]
+ // gamma: [1.1; 0.7]
+ float gamma = (0.4f - (s_brightness_slider.curvalue / 20.0f - 0.25f)) + 0.7f;
+ // modulate: [1.0; 2.6]
+ float modulate = s_brightness_slider.curvalue * 0.2f;
+
+ Cvar.setValue("vid_gamma", gamma);
+ Cvar.setValue("gl_modulate", modulate);
+ Cvar.setValue("sw_stipplealpha", s_stipple_box.curvalue);
+ Cvar.setValue("gl_picmip", 3 - s_tq_slider.curvalue);
+ Cvar.setValue("vid_fullscreen", s_fs_box.curvalue);
+ Cvar.setValue("gl_ext_palettedtexture", s_paletted_texture_box.curvalue);
+ Cvar.setValue("gl_mode", s_mode_list.curvalue);
+ Cvar.setValue("_windowed_mouse", s_windowed_mouse.curvalue);
+
+ Cvar.set("vid_ref", drivers[s_ref_list.curvalue]);
+ Cvar.set("gl_driver", drivers[s_ref_list.curvalue]);
+ if (gl_driver.modified)
+ vid_ref.modified = true;
+
+ Menu.ForceMenuOff();
+ }
+
+ static void initModeList() {
+ DisplayMode[] modes = re.getModeList();
+ fs_resolutions = new String[modes.length];
+ fs_modes = new vidmode_t[modes.length];
+ for (int i = 0; i < modes.length; i++) {
+ DisplayMode m = modes[i];
+ StringBuilder sb = new StringBuilder(18);
+ sb.append('[');
+ sb.append(m.getWidth());
+ sb.append(' ');
+ sb.append(m.getHeight());
+ while (sb.length() < 10) sb.append(' ');
+ sb.append(']');
+ fs_resolutions[i] = sb.toString();
+ sb.setLength(0);
+ sb.append("Mode ");
+ sb.append(i);
+ sb.append(':');
+ sb.append(m.getWidth());
+ sb.append('x');
+ sb.append(m.getHeight());
+ fs_modes[i] = new vidmode_t(sb.toString(), m.getWidth(), m.getHeight(), i);
+ }
+ }
+
+ private static void initRefs() {
+ drivers = Renderer.getDriverNames();
+ refs = new String[drivers.length];
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < drivers.length; i++) {
+ sb.setLength(0);
+ sb.append("[OpenGL ").append(drivers[i]);
+ while (sb.length() < 16) sb.append(" ");
+ sb.append("]");
+ refs[i] = sb.toString();
+ }
+ }
+
+ /*
+ ** VID_MenuInit
+ */
+ public static void MenuInit() {
+
+ initRefs();
+
+ if (gl_driver == null)
+ gl_driver = Cvar.get("gl_driver", Renderer.getPreferedName(), 0);
+ if (gl_picmip == null)
+ gl_picmip = Cvar.get("gl_picmip", "0", 0);
+ if (gl_mode == null)
+ gl_mode = Cvar.get("gl_mode", "3", 0);
+ if (sw_mode == null)
+ sw_mode = Cvar.get("sw_mode", "0", 0);
+ if (gl_ext_palettedtexture == null)
+ gl_ext_palettedtexture = Cvar.get("gl_ext_palettedtexture", "1", CVAR_ARCHIVE);
+
+ if (sw_stipplealpha == null)
+ sw_stipplealpha = Cvar.get("sw_stipplealpha", "0", CVAR_ARCHIVE);
+
+ if (_windowed_mouse == null)
+ _windowed_mouse = Cvar.get("_windowed_mouse", "0", CVAR_ARCHIVE);
+
+ s_mode_list.curvalue = (int) gl_mode.value;
+ if (vid_fullscreen.value != 0.0f) {
+ s_mode_list.itemnames = fs_resolutions;
+ if (s_mode_list.curvalue >= fs_resolutions.length - 1) {
+ s_mode_list.curvalue = 0;
+ }
+ mode_x = fs_modes[s_mode_list.curvalue].width;
+ } else {
+ s_mode_list.itemnames = resolutions;
+ if (s_mode_list.curvalue >= resolutions.length - 1) {
+ s_mode_list.curvalue = 0;
+ }
+ mode_x = vid_modes[s_mode_list.curvalue].width;
+ }
+
+ if (SCR.scr_viewsize == null)
+ SCR.scr_viewsize = Cvar.get("viewsize", "100", CVAR_ARCHIVE);
+
+ s_screensize_slider.curvalue = (int) (SCR.scr_viewsize.value / 10);
+
+ for (int i = 0; i < drivers.length; i++) {
+ if (vid_ref.string.equals(drivers[i])) {
+ s_ref_list.curvalue = i;
+ }
+ }
+
+ s_opengl_menu.x = (int) (viddef.width * 0.50f);
+ s_opengl_menu.nitems = 0;
+
+ s_ref_list.type = MTYPE_SPINCONTROL;
+ s_ref_list.name = "driver";
+ s_ref_list.x = 0;
+ s_ref_list.y = 0;
+ s_ref_list.callback = new Menu.mcallback() {
+ public void execute(Object self) {
+ DriverCallback(self);
+ }
+ };
+ s_ref_list.itemnames = refs;
+
+ s_mode_list.type = MTYPE_SPINCONTROL;
+ s_mode_list.name = "video mode";
+ s_mode_list.x = 0;
+ s_mode_list.y = 10;
+
+ s_screensize_slider.type = MTYPE_SLIDER;
+ s_screensize_slider.x = 0;
+ s_screensize_slider.y = 20;
+ s_screensize_slider.name = "screen size";
+ s_screensize_slider.minvalue = 3;
+ s_screensize_slider.maxvalue = 12;
+ s_screensize_slider.callback = new Menu.mcallback() {
+ public void execute(Object self) {
+ ScreenSizeCallback(self);
+ }
+ };
+ s_brightness_slider.type = MTYPE_SLIDER;
+ s_brightness_slider.x = 0;
+ s_brightness_slider.y = 30;
+ s_brightness_slider.name = "brightness";
+ s_brightness_slider.callback = new Menu.mcallback() {
+ public void execute(Object self) {
+ BrightnessCallback(self);
+ }
+ };
+ s_brightness_slider.minvalue = 5;
+ s_brightness_slider.maxvalue = 13;
+ s_brightness_slider.curvalue = (1.3f - vid_gamma.value + 0.5f) * 10;
+
+ s_fs_box.type = MTYPE_SPINCONTROL;
+ s_fs_box.x = 0;
+ s_fs_box.y = 40;
+ s_fs_box.name = "fullscreen";
+ s_fs_box.itemnames = yesno_names;
+ s_fs_box.curvalue = (int) vid_fullscreen.value;
+ s_fs_box.callback = new Menu.mcallback() {
+ public void execute(Object o) {
+ int fs = ((Menu.menulist_s) o).curvalue;
+ if (fs == 0) {
+ s_mode_list.itemnames = resolutions;
+ int i = vid_modes.length - 2;
+ while (i > 0 && vid_modes[i].width > mode_x) i--;
+ s_mode_list.curvalue = i;
+ } else {
+ s_mode_list.itemnames = fs_resolutions;
+ int i = fs_modes.length - 1;
+ while (i > 0 && fs_modes[i].width > mode_x) i--;
+ s_mode_list.curvalue = i;
+ }
+ }
+ };
+
+ s_defaults_action.type = MTYPE_ACTION;
+ s_defaults_action.name = "reset to default";
+ s_defaults_action.x = 0;
+ s_defaults_action.y = 90;
+ s_defaults_action.callback = new Menu.mcallback() {
+ public void execute(Object self) {
+ ResetDefaults(self);
+ }
+ };
+
+ s_apply_action.type = MTYPE_ACTION;
+ s_apply_action.name = "apply";
+ s_apply_action.x = 0;
+ s_apply_action.y = 100;
+ s_apply_action.callback = new Menu.mcallback() {
+ public void execute(Object self) {
+ ApplyChanges(self);
+ }
+ };
+
+
+ s_stipple_box.type = MTYPE_SPINCONTROL;
+ s_stipple_box.x = 0;
+ s_stipple_box.y = 60;
+ s_stipple_box.name = "stipple alpha";
+ s_stipple_box.curvalue = (int) sw_stipplealpha.value;
+ s_stipple_box.itemnames = yesno_names;
+
+ s_windowed_mouse.type = MTYPE_SPINCONTROL;
+ s_windowed_mouse.x = 0;
+ s_windowed_mouse.y = 72;
+ s_windowed_mouse.name = "windowed mouse";
+ s_windowed_mouse.curvalue = (int) _windowed_mouse.value;
+ s_windowed_mouse.itemnames = yesno_names;
+
+ s_tq_slider.type = MTYPE_SLIDER;
+ s_tq_slider.x = 0;
+ s_tq_slider.y = 60;
+ s_tq_slider.name = "texture quality";
+ s_tq_slider.minvalue = 0;
+ s_tq_slider.maxvalue = 3;
+ s_tq_slider.curvalue = 3 - gl_picmip.value;
+
+ s_paletted_texture_box.type = MTYPE_SPINCONTROL;
+ s_paletted_texture_box.x = 0;
+ s_paletted_texture_box.y = 70;
+ s_paletted_texture_box.name = "8-bit textures";
+ s_paletted_texture_box.itemnames = yesno_names;
+ s_paletted_texture_box.curvalue = (int) gl_ext_palettedtexture.value;
+
+ Menu.Menu_AddItem(s_opengl_menu, s_ref_list);
+ Menu.Menu_AddItem(s_opengl_menu, s_mode_list);
+ Menu.Menu_AddItem(s_opengl_menu, s_screensize_slider);
+ Menu.Menu_AddItem(s_opengl_menu, s_brightness_slider);
+ Menu.Menu_AddItem(s_opengl_menu, s_fs_box);
+ Menu.Menu_AddItem(s_opengl_menu, s_tq_slider);
+ Menu.Menu_AddItem(s_opengl_menu, s_paletted_texture_box);
+
+ Menu.Menu_AddItem(s_opengl_menu, s_defaults_action);
+ Menu.Menu_AddItem(s_opengl_menu, s_apply_action);
+
+ Menu.Menu_Center(s_opengl_menu);
+ s_opengl_menu.x -= 8;
+ }
+
+ /*
+ ================
+ VID_MenuDraw
+ ================
+ */
+ static void MenuDraw() {
+ s_current_menu = s_opengl_menu;
+
+ /*
+ ** draw the banner
+ */
+ Dimension dim = new Dimension();
+ re.DrawGetPicSize(dim, "m_banner_video");
+ re.DrawPic(viddef.width / 2 - dim.width / 2, viddef.height / 2 - 110, "m_banner_video");
+
+ /*
+ ** move cursor to a reasonable starting position
+ */
+ Menu.Menu_AdjustCursor(s_current_menu, 1);
+
+ /*
+ ** draw the menu
+ */
+ Menu.Menu_Draw(s_current_menu);
+ }
+
+ /*
+ ================
+ VID_MenuKey
+ ================
+ */
+ static String MenuKey(int key) {
+ Menu.menuframework_s m = s_current_menu;
+ final String sound = "misc/menu1.wav";
+
+ switch (key) {
+ case K_ESCAPE:
+ Menu.PopMenu();
+ return null;
+ case K_UPARROW:
+ m.cursor--;
+ Menu.Menu_AdjustCursor(m, -1);
+ break;
+ case K_DOWNARROW:
+ m.cursor++;
+ Menu.Menu_AdjustCursor(m, 1);
+ break;
+ case K_LEFTARROW:
+ Menu.Menu_SlideItem(m, -1);
+ break;
+ case K_RIGHTARROW:
+ Menu.Menu_SlideItem(m, 1);
+ break;
+ case K_ENTER:
+ Menu.Menu_SelectItem(m);
+ break;
+ }
+
+ return sound;
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.client;
+
+import lwjake2.game.entity_state_t;
+
+public class centity_t {
+ public final entity_state_t current = new entity_state_t(null);
+ final entity_state_t baseline = new entity_state_t(null); // delta from this if not from a previous frame
+ final entity_state_t prev = new entity_state_t(null); // will always be valid, but might just be a copy of current
+ final float[] lerp_origin = {0, 0, 0}; // for trails (variable hz)
+ int serverframe; // if not current, this ent isn't in the frame
+ int trailcount; // for diminishing grenade trails
+ int fly_stoptime;
+}
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.client;
+
+/**
+ * cl_sustain_t
+ */
+public class cl_sustain_t {
+ final float[] org = new float[3];
+ final float[] dir = new float[3];
+ int id;
+ int type;
+ int endtime;
+ int nextthink;
+ int thinkinterval;
+ int color;
+ int count;
+ int magnitude;
+ ThinkAdapter think;
+
+ void clear() {
+ org[0] = org[1] = org[2] =
+ dir[0] = dir[1] = dir[2] =
+ id = type = endtime = nextthink = thinkinterval = color = count = magnitude = 0;
+ think = null;
+ }
+
+ static abstract class ThinkAdapter {
+ abstract void think(cl_sustain_t self);
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.client;
+
+import lwjake2.Defines;
+
+/**
+ * console_t
+ */
+public final class console_t {
+ final byte[] text = new byte[Defines.CON_TEXTSIZE];
+ final float[] times = new float[Defines.NUM_CON_TIMES]; // cls.realtime time the line was generated
+ boolean initialized;
+ int current; // line where next message will be printed
+ int x; // offset in current line for next print
+ int display; // bottom of console displays this line
+ int ormask; // high bit mask for colored characters
+ int linewidth; // characters across screen
+ int totallines; // total lines in console scrollback
+ float cursorspeed;
+ int vislines;
+ // for transparent notify lines
+}
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.client;
+
+/**
+ * cparticle_t
+ *
+ * @author cwei
+ */
+public class cparticle_t {
+
+ public final float[] org = {0, 0, 0}; // vec3_t
+ public final float[] vel = {0, 0, 0}; // vec3_t
+ public final float[] accel = {0, 0, 0}; // vec3_t
+ public cparticle_t next;
+ public float time;
+ public float color;
+ //public float colorvel;
+ public float alpha;
+ public float alphavel;
+}
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.client;
+
+public class dlight_t {
+ public final float[] origin = {0, 0, 0};
+ public final float[] color = {0, 0, 0};
+ public float intensity;
+}
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.client;
+
+import lwjake2.render.Image;
+import lwjake2.render.Model;
+import lwjake2.util.Math3D;
+
+public class entity_t implements Cloneable {
+ public final float[] angles = {0, 0, 0};
+ /*
+ ** most recent data
+ */
+ public final float[] origin = {0, 0, 0}; // also used as RF_BEAM's "from"
+ /*
+ ** previous data for lerping
+ */
+ public final float[] oldorigin = {0, 0, 0}; // also used as RF_BEAM's "to"
+ //ptr
+ public Model model; // opaque type outside refresh
+ public int frame; // also used as RF_BEAM's diameter
+ public int oldframe;
+
+ /*
+ ** misc
+ */
+ public float backlerp; // 0.0 = current, 1.0 = old
+ public int skinnum; // also used as RF_BEAM's palette index
+
+ public int lightstyle; // for flashing entities
+ public float alpha; // ignore if RF_TRANSLUCENT isn't set
+
+ // reference
+ public Image skin; // NULL for inline skin
+ public int flags;
+
+
+ public void set(entity_t src) {
+ this.model = src.model;
+ Math3D.vectorCopy(src.angles, this.angles);
+ Math3D.vectorCopy(src.origin, this.origin);
+ this.frame = src.frame;
+ Math3D.vectorCopy(src.oldorigin, this.oldorigin);
+ this.oldframe = src.oldframe;
+ this.backlerp = src.backlerp;
+ this.skinnum = src.skinnum;
+ this.lightstyle = src.lightstyle;
+ this.alpha = src.alpha;
+ this.skin = src.skin;
+ this.flags = src.flags;
+ }
+
+ public void clear() {
+ model = null;
+ Math3D.vectorClear(angles);
+ Math3D.vectorClear(origin);
+ frame = 0;
+ Math3D.vectorClear(oldorigin);
+ oldframe = 0;
+ backlerp = 0;
+ skinnum = 0;
+ lightstyle = 0;
+ alpha = 0;
+ skin = null;
+ flags = 0;
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.client;
+
+import lwjake2.game.player_state_t;
+
+import java.util.Arrays;
+
+public class frame_t {
+
+ public static final int MAX_MAP_AREAS = 256;
+ public final player_state_t playerstate = new player_state_t(); // mem
+ final byte[] areabits = new byte[MAX_MAP_AREAS / 8]; // portalarea visibility bits
+ public int servertime; // server time the message is valid for (in msec)
+ public int num_entities;
+ public int parse_entities; // non-masked index into cl_parse_entities array
+ boolean valid; // cleared if delta parsing was invalid
+ int serverframe;
+ int deltaframe;
+
+ public void set(frame_t from) {
+ valid = from.valid;
+ serverframe = from.serverframe;
+ deltaframe = from.deltaframe;
+ num_entities = from.num_entities;
+ parse_entities = from.parse_entities;
+ System.arraycopy(from.areabits, 0, areabits, 0, areabits.length);
+ playerstate.set(from.playerstate);
+ }
+
+ public void reset() {
+ valid = false;
+ serverframe = servertime = deltaframe = 0;
+ Arrays.fill(areabits, (byte) 0);
+ playerstate.clear();
+ num_entities = parse_entities = 0;
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.client;
+
+/**
+ * kbutton_t
+ */
+public class kbutton_t {
+ final int[] down = new int[2]; // key nums holding it down
+ public int state;
+ long downtime; // msec timestamp
+ long msec; // msec down this frame
+}
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.client;
+
+public class lightstyle_t {
+ public final float[] rgb = {0, 0, 0}; // 0.0 - 2.0
+ public float white; // highest of rgb
+}
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.client;
+
+import lwjake2.Defines;
+import lwjake2.util.Lib;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.FloatBuffer;
+import java.nio.IntBuffer;
+
+public class particle_t {
+
+ public static final FloatBuffer vertexArray = Lib.newFloatBuffer(Defines.MAX_PARTICLES * 3);
+ public static final int[] colorTable = new int[256];
+ // lwjgl renderer needs a ByteBuffer
+ private static final ByteBuffer colorByteArray = Lib.newByteBuffer(Defines.MAX_PARTICLES * Lib.SIZEOF_INT, ByteOrder.LITTLE_ENDIAN);
+ public static final IntBuffer colorArray = colorByteArray.asIntBuffer();
+
+
+ public static void setColorPalette(int[] palette) {
+ for (int i = 0; i < 256; i++) {
+ colorTable[i] = palette[i] & 0x00FFFFFF;
+ }
+ }
+
+ public static ByteBuffer getColorAsByteBuffer() {
+ return colorByteArray;
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.client;
+
+public class refdef_t {
+ public final float[] vieworg = {0, 0, 0};
+ public final float[] viewangles = {0, 0, 0};
+ public final float[] blend = {0, 0, 0, 0}; // rgba 0-1 full screen blend
+ public int x, y, width, height;// in virtual screen coordinates
+ public float fov_x, fov_y;
+ public float time; // time is uesed to auto animate
+ public int rdflags; // RDF_UNDERWATER, etc
+
+ public byte areabits[]; // if not NULL, only areas with set bits will be drawn
+
+ public lightstyle_t lightstyles[]; // [MAX_LIGHTSTYLES]
+
+ public int num_entities;
+ public entity_t entities[];
+
+ public int num_dlights;
+ public dlight_t dlights[];
+
+ public int num_particles;
+ //public particle_t particles[];
+}
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.client;
+
+import lwjake2.qcommon.xcommand_t;
+import lwjake2.render.Image;
+import lwjake2.render.Model;
+import lwjake2.sys.KBD;
+
+import java.awt.*;
+
+/**
+ * refexport_t
+ *
+ * @author cwei
+ */
+public interface refexport_t {
+ // ============================================================================
+ // public interface for Renderer implementations
+ //
+ // ref.h, refexport_t
+ // ============================================================================
+ //
+ // these are the functions exported by the refresh module
+ //
+ // called when the library is loaded
+ boolean Init(int vid_xpos, int vid_ypos);
+
+ // called before the library is unloaded
+ void Shutdown();
+
+ // All data that will be used in a level should be
+ // registered before rendering any frames to prevent disk hits,
+ // but they can still be registered at a later time
+ // if necessary.
+ //
+ // EndRegistration will free any remaining data that wasn't registered.
+ // Any model_s or skin_s pointers from before the BeginRegistration
+ // are no longer valid after EndRegistration.
+ //
+ // Skins and images need to be differentiated, because skins
+ // are flood filled to eliminate mip map edge errors, and pics have
+ // an implicit "pics/" prepended to the name. (a pic name that starts with a
+ // slash will not use the "pics/" prefix or the ".pcx" postfix)
+ void BeginRegistration(String map);
+
+ Model RegisterModel(String name);
+
+ Image RegisterSkin(String name);
+
+ Image RegisterPic(String name);
+
+ void SetSky(String name, float rotate, /* vec3_t */
+ float[] axis);
+
+ void EndRegistration();
+
+ void RenderFrame(refdef_t fd);
+
+ void DrawGetPicSize(Dimension dim /* int *w, *h */, String name);
+
+ // will return 0 0 if not found
+ void DrawPic(int x, int y, String name);
+
+ void DrawStretchPic(int x, int y, int w, int h, String name);
+
+ void DrawChar(int x, int y, int num); // num is 8 bit ASCII
+
+ void DrawTileClear(int x, int y, int w, int h, String name);
+
+ void DrawFill(int x, int y, int w, int h, int c);
+
+ void DrawFadeScreen();
+
+ // Draw images for cinematic rendering (which can have a different palette). Note that calls
+ void DrawStretchRaw(int x, int y, int w, int h, int cols, int rows, byte[] data);
+
+ /*
+ ** video mode and refresh state management entry points
+ */
+ /* 256 r,g,b values; null = game palette, size = 768 bytes */
+ void CinematicSetPalette(final byte[] palette);
+
+ void BeginFrame(float camera_separation);
+
+ void EndFrame();
+
+ void AppActivate(boolean activate);
+
+ /**
+ *
+ *
+ */
+ void updateScreen(xcommand_t callback);
+
+ int apiVersion();
+
+ DisplayMode[] getModeList();
+
+ KBD getKeyboardHandler();
+}
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.client;
+
+public class viddef_t {
+ public int width, height;
+}
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.client;
+
+/**
+ * vidmode_t
+ *
+ * @author cwei
+ */
+public class vidmode_t {
+ final String description;
+ final int mode;
+ int width, height;
+
+ vidmode_t(String description, int width, int height, int mode) {
+ this.description = description;
+ this.width = width;
+ this.height = height;
+ this.mode = mode;
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.client;
+
+/**
+ * vrect_t
+ *
+ * @author cwei
+ */
+public class vrect_t {
+ public int x;
+ public int y;
+ public int width;
+ public int height;
+ vrect_t pnext;
+}
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.game;
+
+import lwjake2.util.QuakeFile;
+
+import java.io.IOException;
+
+public class Client {
+
+ // known to server
+ public final player_state_t ps = new player_state_t(); // communicated by server to clients
+ public final int index;
+ // this structure is cleared on each PutClientInServer(),
+ // except for 'client->pers'
+ public int ping;
+ // private to game
+ public client_persistant_t pers = new client_persistant_t();
+ public client_respawn_t resp = new client_respawn_t();
+ public pmove_state_t old_pmove = new pmove_state_t(); // for detecting out-of-pmove changes
+ public boolean showscores; // set layout stat
+ public boolean showinventory; // set layout stat
+ public boolean showhelp;
+ public boolean showhelpicon;
+ public int buttons;
+ public int oldbuttons;
+ public int latched_buttons;
+ // sum up damage over an entire frame, so
+ // shotgun blasts give a single big kick
+ public float v_dmg_roll, v_dmg_pitch, v_dmg_time; // damage kicks
+ public float fall_time, fall_value; // for view drop on fall
+ public float[] damage_blend = {0, 0, 0};
+ public float[] v_angle = {0, 0, 0}; // aiming direction
+ public float bobtime; // so off-ground doesn't change it
+ public float[] oldviewangles = {0, 0, 0};
+ public float[] oldvelocity = {0, 0, 0};
+ public float next_drown_time;
+ public int old_waterlevel;
+ public int breather_sound;
+ public int machinegun_shots; // for weapon raising
+ // animation vars
+ public int anim_end;
+ public int anim_priority;
+ public boolean anim_duck;
+ public boolean anim_run;
+ // powerup timers
+ public float quad_framenum;
+ public float invincible_framenum;
+ public float enviro_framenum;
+ public float pickup_msg_time;
+ public float flood_locktill; // locked from talking
+ public float flood_when[] = new float[10]; // when messages were said
+ public int flood_whenhead; // head pointer for when said
+ public float respawn_time; // can respawn when time > this
+ public EDict chase_target; // player we are chasing
+ public boolean update_chase; // need to update chase info?
+
+ public Client(int index) {
+ this.index = index;
+ }
+
+ /**
+ * Clears the game client structure.
+ */
+ public void clear() {
+ ping = 0;
+
+ pers = new client_persistant_t();
+ resp = new client_respawn_t();
+ old_pmove = new pmove_state_t();
+
+ showscores = false; // set layout stat
+ showinventory = false; // set layout stat
+ showhelp = false;
+ showhelpicon = false;
+
+
+ buttons = oldbuttons = latched_buttons = 0;
+ v_dmg_roll = v_dmg_pitch = v_dmg_time = 0;
+ fall_time = fall_value = 0;
+ damage_blend = new float[3];
+ v_angle = new float[3];
+ bobtime = 0;
+
+ oldviewangles = new float[3];
+
+ oldvelocity = new float[3];
+
+ next_drown_time = 0;
+
+ old_waterlevel = 0;
+
+ breather_sound = 0;
+ machinegun_shots = 0;
+
+ anim_end = 0;
+ anim_priority = 0;
+ anim_duck = false;
+ anim_run = false;
+
+ // powerup timers
+ quad_framenum = 0;
+ invincible_framenum = 0;
+ enviro_framenum = 0;
+
+ pickup_msg_time = 0;
+
+ flood_locktill = 0; // locked from talking
+ flood_when = new float[10]; // when messages were said
+ flood_whenhead = 0; // head pointer for when said
+
+ respawn_time = 0; // can respawn when time > this
+
+ chase_target = null; // player we are chasing
+ update_chase = false; // need to update chase info?
+ }
+
+ /**
+ * Reads a game client from the file.
+ */
+ public void read(QuakeFile f) throws IOException {
+
+ ps.load(f);
+
+ ping = f.readInt();
+
+ pers.read(f);
+ resp.read(f);
+
+ old_pmove.load(f);
+
+ showscores = f.readInt() != 0;
+ showinventory = f.readInt() != 0;
+ showhelp = f.readInt() != 0;
+ showhelpicon = f.readInt() != 0;
+
+ buttons = f.readInt();
+ oldbuttons = f.readInt();
+ latched_buttons = f.readInt();
+
+ v_dmg_roll = f.readFloat();
+ v_dmg_pitch = f.readFloat();
+ v_dmg_time = f.readFloat();
+ fall_time = f.readFloat();
+ fall_value = f.readFloat();
+
+ damage_blend[0] = f.readFloat();
+ damage_blend[1] = f.readFloat();
+ damage_blend[2] = f.readFloat();
+
+ v_angle[0] = f.readFloat();
+ v_angle[1] = f.readFloat();
+ v_angle[2] = f.readFloat();
+
+ bobtime = f.readFloat();
+
+ oldviewangles[0] = f.readFloat();
+ oldviewangles[1] = f.readFloat();
+ oldviewangles[2] = f.readFloat();
+
+ oldvelocity[0] = f.readFloat();
+ oldvelocity[1] = f.readFloat();
+ oldvelocity[2] = f.readFloat();
+
+ next_drown_time = f.readFloat();
+
+ old_waterlevel = f.readInt();
+ breather_sound = f.readInt();
+ machinegun_shots = f.readInt();
+ anim_end = f.readInt();
+ anim_priority = f.readInt();
+ anim_duck = f.readInt() != 0;
+ anim_run = f.readInt() != 0;
+
+ quad_framenum = f.readFloat();
+ invincible_framenum = f.readFloat();
+ enviro_framenum = f.readFloat();
+
+ pickup_msg_time = f.readFloat();
+ flood_locktill = f.readFloat();
+ flood_when[0] = f.readFloat();
+ flood_when[1] = f.readFloat();
+ flood_when[2] = f.readFloat();
+ flood_when[3] = f.readFloat();
+ flood_when[4] = f.readFloat();
+ flood_when[5] = f.readFloat();
+ flood_when[6] = f.readFloat();
+ flood_when[7] = f.readFloat();
+ flood_when[8] = f.readFloat();
+ flood_when[9] = f.readFloat();
+ flood_whenhead = f.readInt();
+ respawn_time = f.readFloat();
+ chase_target = f.readEdictRef();
+ update_chase = f.readInt() != 0;
+
+ if (f.readInt() != 8765)
+ System.err.println("game client load failed for num=" + index);
+ }
+
+ /**
+ * Writes a game_client_t (a player) to a file.
+ */
+ public void write(QuakeFile f) throws IOException {
+ ps.write(f);
+
+ f.writeInt(ping);
+
+ pers.write(f);
+ resp.write(f);
+
+ old_pmove.write(f);
+
+ f.writeInt(showscores ? 1 : 0);
+ f.writeInt(showinventory ? 1 : 0);
+ f.writeInt(showhelp ? 1 : 0);
+ f.writeInt(showhelpicon ? 1 : 0);
+
+ f.writeInt(buttons);
+ f.writeInt(oldbuttons);
+ f.writeInt(latched_buttons);
+
+ f.writeFloat(v_dmg_roll);
+ f.writeFloat(v_dmg_pitch);
+ f.writeFloat(v_dmg_time);
+ f.writeFloat(fall_time);
+ f.writeFloat(fall_value);
+
+ f.writeFloat(damage_blend[0]);
+ f.writeFloat(damage_blend[1]);
+ f.writeFloat(damage_blend[2]);
+
+ f.writeFloat(v_angle[0]);
+ f.writeFloat(v_angle[1]);
+ f.writeFloat(v_angle[2]);
+
+ f.writeFloat(bobtime);
+
+ f.writeFloat(oldviewangles[0]);
+ f.writeFloat(oldviewangles[1]);
+ f.writeFloat(oldviewangles[2]);
+
+ f.writeFloat(oldvelocity[0]);
+ f.writeFloat(oldvelocity[1]);
+ f.writeFloat(oldvelocity[2]);
+
+ f.writeFloat(next_drown_time);
+
+ f.writeInt(old_waterlevel);
+ f.writeInt(breather_sound);
+ f.writeInt(machinegun_shots);
+ f.writeInt(anim_end);
+ f.writeInt(anim_priority);
+ f.writeInt(anim_duck ? 1 : 0);
+ f.writeInt(anim_run ? 1 : 0);
+
+ f.writeFloat(quad_framenum);
+ f.writeFloat(invincible_framenum);
+ f.writeFloat(enviro_framenum);
+
+ f.writeFloat(pickup_msg_time);
+ f.writeFloat(flood_locktill);
+ f.writeFloat(flood_when[0]);
+ f.writeFloat(flood_when[1]);
+ f.writeFloat(flood_when[2]);
+ f.writeFloat(flood_when[3]);
+ f.writeFloat(flood_when[4]);
+ f.writeFloat(flood_when[5]);
+ f.writeFloat(flood_when[6]);
+ f.writeFloat(flood_when[7]);
+ f.writeFloat(flood_when[8]);
+ f.writeFloat(flood_when[9]);
+ f.writeInt(flood_whenhead);
+ f.writeFloat(respawn_time);
+ f.writeEdictRef(chase_target);
+ f.writeInt(update_chase ? 1 : 0);
+
+ f.writeInt(8765);
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.game;
+
+import lwjake2.Defines;
+import lwjake2.Globals;
+import lwjake2.qcommon.*;
+import lwjake2.server.SV_GAME;
+import lwjake2.util.Lib;
+
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.Vector;
+
+/**
+ * Cmd
+ */
+public final class Cmd {
+ private static final int ALIAS_LOOP_COUNT = 16;
+ private static final xcommand_t Wait_f = new xcommand_t() {
+ public void execute() {
+ Globals.cmd_wait = true;
+ }
+ };
+ private static final String[] cmd_argv = new String[Defines.MAX_STRING_TOKENS];
+ private static final Comparator<Integer> PlayerSort = (o1, o2) -> {
+
+ int anum1 = GameBase.game.clients[o1].ps.stats[Defines.STAT_FRAGS];
+ int bnum1 = GameBase.game.clients[o2].ps.stats[Defines.STAT_FRAGS];
+
+ return Integer.compare(anum1, bnum1);
+ };
+ private static final char[] expanded = new char[Defines.MAX_STRING_CHARS];
+ private static final char[] temporary = new char[Defines.MAX_STRING_CHARS];
+ private static cmd_function_t cmd_functions = null;
+ private static final xcommand_t List_f = new xcommand_t() {
+ public void execute() {
+ cmd_function_t cmd = Cmd.cmd_functions;
+ int i = 0;
+
+ while (cmd != null) {
+ Com.Printf(cmd.name + '\n');
+ i++;
+ cmd = cmd.next;
+ }
+ Com.Printf(i + " commands\n");
+ }
+ };
+ private static int cmd_argc;
+ private static final xcommand_t Exec_f = new xcommand_t() {
+ public void execute() {
+ if (Cmd.Argc() != 2) {
+ Com.Printf("exec <filename> : execute a script file\n");
+ return;
+ }
+
+ byte[] f = null;
+ f = FS.LoadFile(Cmd.Argv(1));
+ if (f == null) {
+ Com.Printf("couldn't exec " + Cmd.Argv(1) + "\n");
+ return;
+ }
+ Com.Printf("execing " + Cmd.Argv(1) + "\n");
+
+ CommandBuffer.InsertText(new String(f));
+
+ FS.FreeFile();
+ }
+ };
+ private static final xcommand_t Echo_f = new xcommand_t() {
+ public void execute() {
+ for (int i = 1; i < Cmd.Argc(); i++) {
+ Com.Printf(Cmd.Argv(i) + " ");
+ }
+ Com.Printf("'\n");
+ }
+ };
+ private static final xcommand_t Alias_f = new xcommand_t() {
+ public void execute() {
+ cmdalias_t a = null;
+ if (Cmd.Argc() == 1) {
+ Com.Printf("Current alias commands:\n");
+ for (a = Globals.cmd_alias; a != null; a = a.next) {
+ Com.Printf(a.name + " : " + a.value);
+ }
+ return;
+ }
+
+ String s = Cmd.Argv(1);
+ if (s.length() > Defines.MAX_ALIAS_NAME) {
+ Com.Printf("Alias name is too long\n");
+ return;
+ }
+
+ // if the alias already exists, reuse it
+ for (a = Globals.cmd_alias; a != null; a = a.next) {
+ if (s.equalsIgnoreCase(a.name)) {
+ a.value = null;
+ break;
+ }
+ }
+
+ if (a == null) {
+ a = new cmdalias_t();
+ a.next = Globals.cmd_alias;
+ Globals.cmd_alias = a;
+ }
+ a.name = s;
+
+ // copy the rest of the command line
+ StringBuilder cmd = new StringBuilder();
+ int c = Cmd.Argc();
+ for (int i = 2; i < c; i++) {
+ cmd.append(Cmd.Argv(i));
+ if (i != (c - 1))
+ cmd.append(" ");
+ }
+ cmd.append("\n");
+
+ a.value = cmd.toString();
+ }
+ };
+ private static String cmd_args;
+
+ /**
+ * Register our commands.
+ */
+ public static void Init() {
+
+ Cmd.AddCommand("exec", Exec_f);
+ Cmd.AddCommand("echo", Echo_f);
+ Cmd.AddCommand("cmdlist", List_f);
+ Cmd.AddCommand("alias", Alias_f);
+ Cmd.AddCommand("wait", Wait_f);
+ }
+
+ /**
+ * Cmd_MacroExpandString.
+ */
+ private static char[] MacroExpandString(char text[], int len) {
+ int i, j, count;
+ boolean inquote;
+
+ char scan[];
+
+ String token;
+ inquote = false;
+
+ scan = text;
+
+ if (len >= Defines.MAX_STRING_CHARS) {
+ Com.Printf("Line exceeded " + Defines.MAX_STRING_CHARS
+ + " chars, discarded.\n");
+ return null;
+ }
+
+ count = 0;
+
+ for (i = 0; i < len; i++) {
+ if (scan[i] == '"')
+ inquote = !inquote;
+
+ if (inquote)
+ continue; // don't expand inside quotes
+
+ if (scan[i] != '$')
+ continue;
+
+ // scan out the complete macro, without $
+ Com.ParseHelp ph = new Com.ParseHelp(text, i + 1);
+ token = Com.Parse(ph);
+
+ if (ph.data == null)
+ continue;
+
+ token = Cvar.variableString(token);
+
+ j = token.length();
+
+ len += j;
+
+ if (len >= Defines.MAX_STRING_CHARS) {
+ Com.Printf("Expanded line exceeded " + Defines.MAX_STRING_CHARS
+ + " chars, discarded.\n");
+ return null;
+ }
+
+ System.arraycopy(scan, 0, temporary, 0, i);
+ System.arraycopy(token.toCharArray(), 0, temporary, i, token.length());
+ System.arraycopy(ph.data, ph.index, temporary, i + j, len - ph.index - j);
+
+ System.arraycopy(temporary, 0, expanded, 0, 0);
+ scan = expanded;
+ i--;
+ if (++count == 100) {
+ Com.Printf("Macro expansion loop, discarded.\n");
+ return null;
+ }
+ }
+
+ if (inquote) {
+ Com.Printf("Line has unmatched quote, discarded.\n");
+ return null;
+ }
+
+ return scan;
+ }
+
+ /**
+ * Cmd_TokenizeString
+ * <p>
+ * Parses the given string into command line tokens. $Cvars will be expanded
+ * unless they are in a quoted token.
+ */
+ public static void TokenizeString(char text[], boolean macroExpand) {
+ String com_token;
+
+ cmd_argc = 0;
+ cmd_args = "";
+
+ int len = Lib.strlen(text);
+
+ // macro expand the text
+ if (macroExpand)
+ text = MacroExpandString(text, len);
+
+ if (text == null)
+ return;
+
+ len = Lib.strlen(text);
+
+ Com.ParseHelp ph = new Com.ParseHelp(text);
+
+ while (true) {
+
+ // skip whitespace up to a /n
+ char c = ph.skipwhitestoeol();
+
+ if (c == '\n') { // a newline seperates commands in the buffer
+ c = ph.nextchar();
+ break;
+ }
+
+ if (c == 0)
+ return;
+
+ // set cmd_args to everything after the first arg
+ if (cmd_argc == 1) {
+ cmd_args = new String(text, ph.index, len - ph.index);
+ cmd_args.trim();
+ }
+
+ com_token = Com.Parse(ph);
+
+ if (ph.data == null)
+ return;
+
+ if (cmd_argc < Defines.MAX_STRING_TOKENS) {
+ cmd_argv[cmd_argc] = com_token;
+ cmd_argc++;
+ }
+ }
+ }
+
+ public static void AddCommand(String cmd_name, xcommand_t function) {
+ cmd_function_t cmd;
+ //Com.DPrintf("Cmd_AddCommand: " + cmd_name + "\n");
+ // fail if the command is a variable name
+ if ((Cvar.variableString(cmd_name)).length() > 0) {
+ Com.Printf("Cmd_AddCommand: " + cmd_name
+ + " already defined as a var\n");
+ return;
+ }
+
+ // fail if the command already exists
+ for (cmd = cmd_functions; cmd != null; cmd = cmd.next) {
+ if (cmd_name.equals(cmd.name)) {
+ Com
+ .Printf("Cmd_AddCommand: " + cmd_name
+ + " already defined\n");
+ return;
+ }
+ }
+
+ cmd = new cmd_function_t();
+ cmd.name = cmd_name;
+
+ cmd.function = function;
+ cmd.next = cmd_functions;
+ cmd_functions = cmd;
+ }
+
+ /**
+ * Cmd_RemoveCommand
+ */
+ public static void RemoveCommand(String cmd_name) {
+ cmd_function_t cmd, back = null;
+
+ back = cmd = cmd_functions;
+
+ while (true) {
+
+ if (cmd == null) {
+ Com.Printf("Cmd_RemoveCommand: " + cmd_name + " not added\n");
+ return;
+ }
+ if (0 == Lib.strcmp(cmd_name, cmd.name)) {
+ if (cmd == cmd_functions)
+ cmd_functions = cmd.next;
+ else
+ back.next = cmd.next;
+ return;
+ }
+ back = cmd;
+ cmd = cmd.next;
+ }
+ }
+
+ public static int Argc() {
+ return cmd_argc;
+ }
+
+ public static String Argv(int i) {
+ if (i < 0 || i >= cmd_argc)
+ return "";
+ return cmd_argv[i];
+ }
+
+ public static String Args() {
+ return cmd_args;
+ }
+
+ /**
+ * Cmd_ExecuteString
+ * <p>
+ * A complete command line has been parsed, so try to execute it
+ * FIXME: lookupnoadd the token to speed search?
+ */
+ public static void ExecuteString(String text) {
+
+ cmd_function_t cmd;
+ cmdalias_t a;
+
+ TokenizeString(text.toCharArray(), true);
+
+ // execute the command line
+ if (Argc() == 0)
+ return; // no tokens
+
+ // check functions
+ for (cmd = cmd_functions; cmd != null; cmd = cmd.next) {
+ if (cmd_argv[0].equalsIgnoreCase(cmd.name)) {
+ if (null == cmd.function) { // forward to server command
+ Cmd.ExecuteString("cmd " + text);
+ } else {
+ cmd.function.execute();
+ }
+ return;
+ }
+ }
+
+ // check alias
+ for (a = Globals.cmd_alias; a != null; a = a.next) {
+
+ if (cmd_argv[0].equalsIgnoreCase(a.name)) {
+
+ if (++Globals.alias_count == ALIAS_LOOP_COUNT) {
+ Com.Printf("ALIAS_LOOP_COUNT\n");
+ return;
+ }
+ CommandBuffer.InsertText(a.value);
+ return;
+ }
+ }
+
+ // check cvars
+ if (Cvar.command())
+ return;
+
+ // send it as a server command if we are connected
+ Cmd.ForwardToServer();
+ }
+
+ /**
+ * Cmd_Give_f
+ * <p>
+ * Give items to a client.
+ */
+ private static void Give_f(EDict ent) {
+ String name;
+ gitem_t it;
+ int index;
+ int i;
+ boolean give_all;
+ EDict it_ent;
+
+ if (GameBase.deathmatch.value != 0 && GameBase.sv_cheats.value == 0) {
+ SV_GAME.PF_cprintfhigh(ent,
+ "You must run the server with '+set cheats 1' to enable this command.\n");
+ return;
+ }
+
+ name = Cmd.Args();
+
+ give_all = 0 == Lib.Q_stricmp(name, "all");
+
+ if (give_all || 0 == Lib.Q_stricmp(Cmd.Argv(1), "health")) {
+ if (Cmd.Argc() == 3)
+ ent.health = Lib.atoi(Cmd.Argv(2));
+ else
+ ent.health = ent.max_health;
+ if (!give_all)
+ return;
+ }
+
+ if (give_all || 0 == Lib.Q_stricmp(name, "weapons")) {
+ if (!give_all) {
+ }
+ }
+
+ }
+
+ /**
+ * Cmd_God_f
+ * <p>
+ * Sets client to godmode
+ * <p>
+ * argv(0) god
+ */
+ private static void God_f(EDict ent) {
+ String msg;
+
+ if (GameBase.deathmatch.value != 0 && GameBase.sv_cheats.value == 0) {
+ SV_GAME.PF_cprintfhigh(ent,
+ "You must run the server with '+set cheats 1' to enable this command.\n");
+ return;
+ }
+
+ ent.flags ^= Defines.FL_GODMODE;
+ if (0 == (ent.flags & Defines.FL_GODMODE))
+ msg = "godmode OFF\n";
+ else
+ msg = "godmode ON\n";
+
+ SV_GAME.PF_cprintf(ent, Defines.PRINT_HIGH, msg);
+ }
+
+ /**
+ * Cmd_Notarget_f
+ * <p>
+ * Sets client to notarget
+ * <p>
+ * argv(0) notarget.
+ */
+ private static void Notarget_f(EDict ent) {
+ String msg;
+
+ if (GameBase.deathmatch.value != 0 && GameBase.sv_cheats.value == 0) {
+ SV_GAME.PF_cprintfhigh(ent,
+ "You must run the server with '+set cheats 1' to enable this command.\n");
+ return;
+ }
+
+ ent.flags ^= Defines.FL_NOTARGET;
+ if (0 == (ent.flags & Defines.FL_NOTARGET))
+ msg = "notarget OFF\n";
+ else
+ msg = "notarget ON\n";
+
+ SV_GAME.PF_cprintfhigh(ent, msg);
+ }
+
+ /**
+ * Cmd_Noclip_f
+ * <p>
+ * argv(0) noclip.
+ */
+ private static void Noclip_f(EDict ent) {
+ String msg;
+
+ if (GameBase.deathmatch.value != 0 && GameBase.sv_cheats.value == 0) {
+ SV_GAME.PF_cprintfhigh(ent,
+ "You must run the server with '+set cheats 1' to enable this command.\n");
+ return;
+ }
+
+ if (ent.movetype == Defines.MOVETYPE_NOCLIP) {
+ ent.movetype = Defines.MOVETYPE_WALK;
+ msg = "noclip OFF\n";
+ } else {
+ ent.movetype = Defines.MOVETYPE_NOCLIP;
+ msg = "noclip ON\n";
+ }
+
+ SV_GAME.PF_cprintfhigh(ent, msg);
+ }
+
+ /**
+ * Cmd_Use_f
+ * <p>
+ * Use an inventory item.
+ */
+ private static void Use_f(EDict ent) {
+
+ }
+
+ /**
+ * Cmd_Drop_f
+ * <p>
+ * Drop an inventory item.
+ */
+ private static void Drop_f(EDict ent) {
+ }
+
+ /**
+ * Cmd_Inven_f.
+ */
+ private static void Inven_f(EDict ent) {
+ int i;
+ Client cl;
+
+ cl = ent.client;
+
+ cl.showscores = false;
+ cl.showhelp = false;
+
+ if (cl.showinventory) {
+ cl.showinventory = false;
+ return;
+ }
+
+ cl.showinventory = true;
+
+ GameBase.gi.WriteByte(Defines.svc_inventory);
+ for (i = 0; i < Defines.MAX_ITEMS; i++) {
+ GameBase.gi.WriteShort(cl.pers.inventory[i]);
+ }
+ GameBase.gi.unicast(ent, true);
+ }
+
+ /**
+ * Cmd_InvUse_f.
+ */
+ private static void InvUse_f(EDict ent) {
+ }
+
+ /**
+ * Cmd_WeapPrev_f.
+ */
+ private static void WeapPrev_f(EDict ent) {
+ }
+
+ /**
+ * Cmd_WeapNext_f.
+ */
+ private static void WeapNext_f(EDict ent) {
+ }
+
+ /**
+ * Cmd_WeapLast_f.
+ */
+ private static void WeapLast_f(EDict ent) {
+
+ }
+
+ /**
+ * Cmd_InvDrop_f
+ */
+ private static void InvDrop_f(EDict ent) {
+ }
+
+ /**
+ * Cmd_Score_f
+ * <p>
+ * Display the scoreboard.
+ */
+ private static void Score_f(EDict ent) {
+ ent.client.showinventory = false;
+ ent.client.showhelp = false;
+
+ if (0 == GameBase.deathmatch.value && 0 == GameBase.coop.value)
+ return;
+
+ if (ent.client.showscores) {
+ ent.client.showscores = false;
+ return;
+ }
+
+ ent.client.showscores = true;
+ PlayerHud.DeathmatchScoreboard(ent);
+ }
+
+ /**
+ * Cmd_Help_f
+ * <p>
+ * Display the current help message.
+ */
+ private static void Help_f(EDict ent) {
+ // this is for backwards compatability
+ if (GameBase.deathmatch.value != 0) {
+ Score_f(ent);
+ return;
+ }
+
+ ent.client.showinventory = false;
+ ent.client.showscores = false;
+
+ if (ent.client.showhelp
+ && (ent.client.pers.game_helpchanged == GameBase.game.helpchanged)) {
+ ent.client.showhelp = false;
+ return;
+ }
+
+ ent.client.showhelp = true;
+ ent.client.pers.helpchanged = 0;
+ PlayerHud.HelpComputer(ent);
+ }
+
+
+ /**
+ * Cmd_PutAway_f
+ */
+ private static void PutAway_f(EDict ent) {
+ ent.client.showscores = false;
+ ent.client.showhelp = false;
+ ent.client.showinventory = false;
+ }
+
+ /**
+ * Cmd_Players_f
+ */
+ private static void Players_f(EDict ent) {
+ int i;
+ int count;
+ String small;
+ StringBuilder large;
+
+ Integer index[] = new Integer[256];
+
+ count = 0;
+ for (i = 0; i < GameBase.maxclients.value; i++) {
+ if (GameBase.game.clients[i].pers.connected) {
+ index[count] = i;
+ count++;
+ }
+ }
+
+ // sort by frags
+ Arrays.sort(index, 0, count - 1, Cmd.PlayerSort);
+
+ // print information
+ large = new StringBuilder();
+
+ for (i = 0; i < count; i++) {
+ small = GameBase.game.clients[index[i]].ps.stats[Defines.STAT_FRAGS]
+ + " "
+ + GameBase.game.clients[index[i]].pers.netname
+ + "\n";
+
+ if (small.length() + large.length() > 1024 - 100) {
+ // can't print all of them in one packet
+ large.append("...\n");
+ break;
+ }
+ large.append(small);
+ }
+
+ SV_GAME.PF_cprintfhigh(ent, large + "\n" + count + " players\n");
+ }
+
+ /**
+ * Cmd_Wave_f
+ */
+ private static void Wave_f(EDict ent) {
+ int i;
+
+ i = Lib.atoi(Cmd.Argv(1));
+
+ // can't wave when ducked
+ if ((ent.client.ps.pmove.pm_flags & Move.PMF_DUCKED) != 0)
+ return;
+
+ if (ent.client.anim_priority > Defines.ANIM_WAVE)
+ return;
+
+ ent.client.anim_priority = Defines.ANIM_WAVE;
+
+ switch (i) {
+ case 0:
+ SV_GAME.PF_cprintfhigh(ent, "flipoff\n");
+ ent.s.frame = M_Player.FRAME_flip01 - 1;
+ ent.client.anim_end = M_Player.FRAME_flip12;
+ break;
+ case 1:
+ SV_GAME.PF_cprintfhigh(ent, "salute\n");
+ ent.s.frame = M_Player.FRAME_salute01 - 1;
+ ent.client.anim_end = M_Player.FRAME_salute11;
+ break;
+ case 2:
+ SV_GAME.PF_cprintfhigh(ent, "taunt\n");
+ ent.s.frame = M_Player.FRAME_taunt01 - 1;
+ ent.client.anim_end = M_Player.FRAME_taunt17;
+ break;
+ case 3:
+ SV_GAME.PF_cprintfhigh(ent, "wave\n");
+ ent.s.frame = M_Player.FRAME_wave01 - 1;
+ ent.client.anim_end = M_Player.FRAME_wave11;
+ break;
+ case 4:
+ default:
+ SV_GAME.PF_cprintfhigh(ent, "point\n");
+ ent.s.frame = M_Player.FRAME_point01 - 1;
+ ent.client.anim_end = M_Player.FRAME_point12;
+ break;
+ }
+ }
+
+ /**
+ * Command to print the players own position.
+ */
+ private static void ShowPosition_f(EDict ent) {
+ SV_GAME.PF_cprintfhigh(ent, "pos=" + Lib.vtofsbeaty(ent.s.origin) + "\n");
+ }
+
+ /**
+ * Cmd_Say_f
+ */
+ private static void Say_f(EDict ent, boolean team, boolean arg0) {
+
+ int i, j;
+ EDict other;
+ String text;
+ Client cl;
+
+ if (Cmd.Argc() < 2 && !arg0)
+ return;
+
+ if (0 == ((int) (GameBase.dmflags.value) & (Defines.DF_MODELTEAMS | Defines.DF_SKINTEAMS)))
+ team = false;
+
+ if (team)
+ text = "(" + ent.client.pers.netname + "): ";
+ else
+ text = "" + ent.client.pers.netname + ": ";
+
+ if (arg0) {
+ text += Cmd.Argv(0);
+ text += " ";
+ text += Cmd.Args();
+ } else {
+ if (Cmd.Args().startsWith("\""))
+ text += Cmd.Args().substring(1, Cmd.Args().length() - 1);
+ else
+ text += Cmd.Args();
+ }
+
+ // don't let text be too long for malicious reasons
+ if (text.length() > 150)
+ //text[150] = 0;
+ text = text.substring(0, 150);
+
+ text += "\n";
+
+ if (GameBase.flood_msgs.value != 0) {
+ cl = ent.client;
+
+ if (GameBase.level.time < cl.flood_locktill) {
+ SV_GAME.PF_cprintfhigh(ent, "You can't talk for "
+ + (int) (cl.flood_locktill - GameBase.level.time)
+ + " more seconds\n");
+ return;
+ }
+ i = (int) (cl.flood_whenhead - GameBase.flood_msgs.value + 1);
+ if (i < 0)
+ i = (10) + i;
+ if (cl.flood_when[i] != 0
+ && GameBase.level.time - cl.flood_when[i] < GameBase.flood_persecond.value) {
+ cl.flood_locktill = GameBase.level.time + GameBase.flood_waitdelay.value;
+ SV_GAME.PF_cprintf(ent, Defines.PRINT_CHAT,
+ "Flood protection: You can't talk for "
+ + (int) GameBase.flood_waitdelay.value
+ + " seconds.\n");
+ return;
+ }
+
+ cl.flood_whenhead = (cl.flood_whenhead + 1) % 10;
+ cl.flood_when[cl.flood_whenhead] = GameBase.level.time;
+ }
+
+ if (Globals.dedicated.value != 0)
+ SV_GAME.PF_cprintf(null, Defines.PRINT_CHAT, "" + text + "");
+
+ for (j = 1; j <= GameBase.game.maxclients; j++) {
+ other = GameBase.g_edicts[j];
+ if (!other.inuse)
+ continue;
+ if (other.client == null)
+ continue;
+ if (team) {
+ if (!GameUtil.OnSameTeam(ent, other))
+ continue;
+ }
+ SV_GAME.PF_cprintf(other, Defines.PRINT_CHAT, "" + text + "");
+ }
+
+ }
+
+ /**
+ * Returns the playerlist. TODO: The list is badly formatted at the moment.
+ */
+ private static void PlayerList_f(EDict ent) {
+ int i;
+ String st;
+ StringBuilder text;
+ EDict e2;
+
+ // connect time, ping, score, name
+ text = new StringBuilder();
+
+ for (i = 0; i < GameBase.maxclients.value; i++) {
+ e2 = GameBase.g_edicts[1 + i];
+ if (!e2.inuse)
+ continue;
+
+ st = ""
+ + (GameBase.level.framenum - e2.client.resp.enterframe)
+ / 600
+ + ":"
+ + ((GameBase.level.framenum - e2.client.resp.enterframe) % 600)
+ / 10 + " " + e2.client.ping + " " + e2.client.resp.score
+ + " " + e2.client.pers.netname + " "
+ + (e2.client.resp.spectator ? " (spectator)" : "") + "\n";
+
+ if (text.length() + st.length() > 1024 - 50) {
+ text.append("And more...\n");
+ SV_GAME.PF_cprintfhigh(ent, "" + text + "");
+ return;
+ }
+ text.append(st);
+ }
+ SV_GAME.PF_cprintfhigh(ent, text.toString());
+ }
+
+ /**
+ * Adds the current command line as a clc_stringcmd to the client message.
+ * things like godmode, noclip, etc, are commands directed to the server, so
+ * when they are typed in at the console, they will need to be forwarded.
+ */
+ private static void ForwardToServer() {
+ String cmd;
+
+ cmd = Cmd.Argv(0);
+ if (Globals.clientStaticT.state <= Defines.ca_connected || cmd.charAt(0) == '-'
+ || cmd.charAt(0) == '+') {
+ Com.Printf("Unknown command \"" + cmd + "\"\n");
+ return;
+ }
+
+ MSG.WriteByte(Globals.clientStaticT.netchan.message, Defines.clc_stringcmd);
+ SZ.Print(Globals.clientStaticT.netchan.message, cmd);
+ if (Cmd.Argc() > 1) {
+ SZ.Print(Globals.clientStaticT.netchan.message, " ");
+ SZ.Print(Globals.clientStaticT.netchan.message, Cmd.Args());
+ }
+ }
+
+ /**
+ * Cmd_CompleteCommand.
+ */
+ public static Vector<String> CompleteCommand(String partial) {
+ Vector<String> cmds = new Vector<>();
+
+ // check for match
+ for (cmd_function_t cmd = cmd_functions; cmd != null; cmd = cmd.next)
+ if (cmd.name.startsWith(partial))
+ cmds.add(cmd.name);
+ for (cmdalias_t a = Globals.cmd_alias; a != null; a = a.next)
+ if (a.name.startsWith(partial))
+ cmds.add(a.name);
+
+ return cmds;
+ }
+
+ /**
+ * Processes the commands the player enters in the quake console.
+ */
+ public static void ClientCommand(EDict ent) {
+ String cmd;
+
+ if (ent.client == null)
+ return; // not fully in game yet
+
+ cmd = GameBase.gi.argv(0).toLowerCase();
+
+ if (cmd.equals("players")) {
+ Players_f(ent);
+ return;
+ }
+ if (cmd.equals("say")) {
+ Say_f(ent, false, false);
+ return;
+ }
+ if (cmd.equals("say_team")) {
+ Say_f(ent, true, false);
+ return;
+ }
+ if (cmd.equals("score")) {
+ Score_f(ent);
+ return;
+ }
+ if (cmd.equals("help")) {
+ Help_f(ent);
+ return;
+ }
+
+ if (GameBase.level.intermissiontime != 0)
+ return;
+
+ switch (cmd) {
+ case "use":
+ Use_f(ent);
+ break;
+ case "drop":
+ Drop_f(ent);
+ break;
+ case "give":
+ Give_f(ent);
+ break;
+ case "god":
+ God_f(ent);
+ break;
+ case "notarget":
+ Notarget_f(ent);
+ break;
+ case "noclip":
+ Noclip_f(ent);
+ break;
+ case "inven":
+ Inven_f(ent);
+ break;
+ case "invuse":
+ InvUse_f(ent);
+ break;
+ case "invdrop":
+ InvDrop_f(ent);
+ break;
+ case "weapprev":
+ WeapPrev_f(ent);
+ break;
+ case "weapnext":
+ WeapNext_f(ent);
+ break;
+ case "weaplast":
+ WeapLast_f(ent);
+ break;
+ case "putaway":
+ PutAway_f(ent);
+ break;
+ case "wave":
+ Wave_f(ent);
+ break;
+ case "playerlist":
+ PlayerList_f(ent);
+ break;
+ case "showposition":
+ ShowPosition_f(ent);
+ break;
+ default:
+ // anything that doesn't match a command will be a chat
+ Say_f(ent, false, true);
+ break;
+ }
+ }
+
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.game;
+
+/**
+ * CvarT implements the struct CvarT of the C version
+ */
+public final class CvarT {
+ public String name;
+ public String string;
+ public String latched_string;
+ public int flags = 0;
+ public boolean modified = false;
+ public float value = 0.0f;
+ public CvarT next = null;
+}
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.game;
+
+import lwjake2.Defines;
+import lwjake2.qcommon.Com;
+import lwjake2.util.Lib;
+import lwjake2.util.QuakeFile;
+
+import java.io.IOException;
+
+public class EDict {
+
+ /**
+ * common integrated data blocks.
+ */
+ public final moveinfo_t moveinfo = new moveinfo_t();
+ public final monsterinfo_t monsterinfo = new monsterinfo_t();
+ /**
+ * Integrated entity state.
+ */
+ public entity_state_t s = new entity_state_t(this);
+ public boolean inuse;
+ public int linkcount;
+ /**
+ * FIXME: move these fields to a server private sv_entity_t. linked to a
+ * division node or leaf.
+ */
+ public link_t area = new link_t(this);
+ /**
+ * if -1, use headnode instead.
+ */
+ public int num_clusters;
+ public int clusternums[] = new int[Defines.MAX_ENT_CLUSTERS];
+ /**
+ * unused if num_clusters != -1.
+ */
+ public int headnode;
+ public int areanum, areanum2;
+ /**
+ * SVF_NOCLIENT, SVF_DEADMONSTER, SVF_MONSTER, etc.
+ */
+ public int svflags;
+ public float[] mins = {0, 0, 0};
+ //================================
+ public float[] maxs = {0, 0, 0};
+ public float[] absmin = {0, 0, 0};
+ public float[] absmax = {0, 0, 0};
+ public float[] size = {0, 0, 0};
+ public int solid;
+ public int clipmask;
+ //================================
+ public int movetype;
+ public int flags;
+ public String model = null;
+ /**
+ * sv.time when the object was freed.
+ */
+ public float freetime;
+ //
+ // only used locally in game, not by server
+ //
+ public String message = null;
+ public String classname = "";
+ public int spawnflags;
+ public float timestamp;
+ /**
+ * set in qe3, -1 = up, -2 = down
+ */
+ public float angle;
+ public String target = null;
+ public String targetname = null;
+ public String team = null;
+ public String pathtarget = null;
+ public EDict target_ent = null;
+ public float speed, accel, decel;
+ public float[] movedir = {0, 0, 0};
+ public float[] pos1 = {0, 0, 0};
+ public float[] pos2 = {0, 0, 0};
+ public float[] velocity = {0, 0, 0};
+ public float[] avelocity = {0, 0, 0};
+ public int mass;
+ public float air_finished;
+ /**
+ * per entity gravity multiplier (1.0 is normal).
+ */
+ public float gravity;
+ /**
+ * use for lowgrav artifact, flares.
+ */
+
+ public float yaw_speed;
+ public float ideal_yaw;
+ public float nextthink;
+ public EntThinkAdapter prethink = null;
+ public EntThinkAdapter think = null;
+ public EntBlockedAdapter blocked = null;
+ public EntTouchAdapter touch = null;
+ public EntUseAdapter use = null;
+ /**
+ * Are all these legit? do we need more/less of them?
+ */
+ public float touch_debounce_time;
+ public float pain_debounce_time;
+ public float damage_debounce_time;
+ /**
+ * Move to clientinfo.
+ */
+ public float fly_sound_debounce_time;
+ public float last_move_time;
+ public int health;
+ public int max_health;
+ public int gib_health;
+ public int show_hostile;
+ public float powerarmor_time;
+ /**
+ * target_changelevel.
+ */
+ public String map = null;
+ /**
+ * Height above origin where eyesight is determined.
+ */
+ public int viewheight;
+ public int dmg;
+ public int radius_dmg;
+ public float dmg_radius;
+ /**
+ * make this a spawntemp var?
+ */
+ public int sounds;
+ public int count;
+ public EDict chain = null;
+ public EDict enemy = null;
+ public EDict oldenemy = null;
+ public EDict activator = null;
+ public EDict groundentity = null;
+ public int groundentity_linkcount;
+ public EDict teamchain = null;
+ public EDict teammaster = null;
+ /**
+ * can go in client only.
+ */
+ public EDict mynoise = null;
+ public EDict mynoise2 = null;
+ public int noise_index;
+ public int noise_index2;
+ public float volume;
+ public float attenuation;
+ /**
+ * Timing variables.
+ */
+ public float wait;
+ /**
+ * before firing targets...
+ */
+ public float delay;
+ public float random;
+ public float teleport_time;
+ public int watertype;
+ public int waterlevel;
+ public float[] move_origin = {0, 0, 0};
+ public float[] move_angles = {0, 0, 0};
+ /**
+ * move this to clientinfo? .
+ */
+ public int light_level;
+ /**
+ * also used as areaportal number.
+ */
+ public int style;
+ public gitem_t item; // for bonus items
+ public Client client;
+ public EDict owner;
+ /**
+ * Introduced by rst.
+ */
+ public int index;
+
+ /**
+ * Constructor.
+ */
+ public EDict(int i) {
+ s.number = i;
+ index = i;
+ }
+
+ /**
+ * Used during level loading.
+ */
+ public void cleararealinks() {
+ area = new link_t(this);
+ }
+
+ /////////////////////////////////////////////////
+
+ public boolean setField(String key, String value) {
+
+ if (key.equals("classname")) {
+ classname = GameSpawn.ED_NewString(value);
+ return true;
+ } // F_LSTRING),
+
+ if (key.equals("model")) {
+ model = GameSpawn.ED_NewString(value);
+ return true;
+ } // F_LSTRING),
+
+ if (key.equals("spawnflags")) {
+ spawnflags = Lib.atoi(value);
+ return true;
+ } // F_INT),
+
+ if (key.equals("speed")) {
+ speed = Lib.atof(value);
+ return true;
+ } // F_FLOAT),
+
+ if (key.equals("accel")) {
+ accel = Lib.atof(value);
+ return true;
+ } // F_FLOAT),
+
+ if (key.equals("decel")) {
+ decel = Lib.atof(value);
+ return true;
+ } // F_FLOAT),
+
+ if (key.equals("target")) {
+ target = GameSpawn.ED_NewString(value);
+ return true;
+ } // F_LSTRING),
+
+ if (key.equals("targetname")) {
+ targetname = GameSpawn.ED_NewString(value);
+ return true;
+ } // F_LSTRING),
+
+ if (key.equals("pathtarget")) {
+ pathtarget = GameSpawn.ED_NewString(value);
+ return true;
+ } // F_LSTRING),
+
+ if (key.equals("message")) {
+ message = GameSpawn.ED_NewString(value);
+ return true;
+ } // F_LSTRING),
+
+ if (key.equals("team")) {
+ team = GameSpawn.ED_NewString(value);
+ Com.dprintln("Monster Team:" + team);
+ return true;
+ } // F_LSTRING),
+
+ if (key.equals("wait")) {
+ wait = Lib.atof(value);
+ return true;
+ } // F_FLOAT),
+
+ if (key.equals("delay")) {
+ delay = Lib.atof(value);
+ return true;
+ } // F_FLOAT),
+
+ if (key.equals("random")) {
+ random = Lib.atof(value);
+ return true;
+ } // F_FLOAT),
+
+ if (key.equals("move_origin")) {
+ move_origin = Lib.atov(value);
+ return true;
+ } // F_VECTOR),
+
+ if (key.equals("move_angles")) {
+ move_angles = Lib.atov(value);
+ return true;
+ } // F_VECTOR),
+
+ if (key.equals("style")) {
+ style = Lib.atoi(value);
+ return true;
+ } // F_INT),
+
+ if (key.equals("count")) {
+ count = Lib.atoi(value);
+ return true;
+ } // F_INT),
+
+ if (key.equals("health")) {
+ health = Lib.atoi(value);
+ return true;
+ } // F_INT),
+
+ if (key.equals("sounds")) {
+ sounds = Lib.atoi(value);
+ return true;
+ } // F_INT),
+
+ if (key.equals("light")) {
+ return true;
+ } // F_IGNORE),
+
+ if (key.equals("dmg")) {
+ dmg = Lib.atoi(value);
+ return true;
+ } // F_INT),
+
+ if (key.equals("mass")) {
+ mass = Lib.atoi(value);
+ return true;
+ } // F_INT),
+
+ if (key.equals("volume")) {
+ volume = Lib.atof(value);
+ return true;
+ } // F_FLOAT),
+
+ if (key.equals("attenuation")) {
+ attenuation = Lib.atof(value);
+ return true;
+ } // F_FLOAT),
+
+ if (key.equals("map")) {
+ map = GameSpawn.ED_NewString(value);
+ return true;
+ } // F_LSTRING),
+
+ if (key.equals("origin")) {
+ s.origin = Lib.atov(value);
+ return true;
+ } // F_VECTOR),
+
+ if (key.equals("angles")) {
+ s.angles = Lib.atov(value);
+ return true;
+ } // F_VECTOR),
+
+ if (key.equals("angle")) {
+ s.angles = new float[]{0, Lib.atof(value), 0};
+ return true;
+ } // F_ANGLEHACK),
+
+ if (key.equals("item")) {
+ GameBase.gi.error("ent.set(\"item\") called.");
+ return true;
+ } // F_ITEM)
+
+ return false;
+ }
+
+ /**
+ * Writes the entity to the file.
+ */
+ public void write(QuakeFile f) throws IOException {
+
+ s.write(f);
+ f.writeBoolean(inuse);
+ f.writeInt(linkcount);
+ f.writeInt(num_clusters);
+
+ f.writeInt(9999);
+
+ if (clusternums == null)
+ f.writeInt(-1);
+ else {
+ f.writeInt(Defines.MAX_ENT_CLUSTERS);
+ for (int n = 0; n < Defines.MAX_ENT_CLUSTERS; n++)
+ f.writeInt(clusternums[n]);
+
+ }
+ f.writeInt(headnode);
+ f.writeInt(areanum);
+ f.writeInt(areanum2);
+ f.writeInt(svflags);
+ f.writeVector(mins);
+ f.writeVector(maxs);
+ f.writeVector(absmin);
+ f.writeVector(absmax);
+ f.writeVector(size);
+ f.writeInt(solid);
+ f.writeInt(clipmask);
+
+ f.writeInt(movetype);
+ f.writeInt(flags);
+
+ f.writeString(model);
+ f.writeFloat(freetime);
+ f.writeString(message);
+ f.writeString(classname);
+ f.writeInt(spawnflags);
+ f.writeFloat(timestamp);
+
+ f.writeFloat(angle);
+
+ f.writeString(target);
+ f.writeString(targetname);
+ f.writeString(team);
+ f.writeString(pathtarget);
+
+ f.writeEdictRef(target_ent);
+
+ f.writeFloat(speed);
+ f.writeFloat(accel);
+ f.writeFloat(decel);
+
+ f.writeVector(movedir);
+
+ f.writeVector(pos1);
+ f.writeVector(pos2);
+
+ f.writeVector(velocity);
+ f.writeVector(avelocity);
+
+ f.writeInt(mass);
+ f.writeFloat(air_finished);
+
+ f.writeFloat(gravity);
+
+ f.writeFloat(yaw_speed);
+ f.writeFloat(ideal_yaw);
+
+ f.writeFloat(nextthink);
+
+ f.writeAdapter(prethink);
+ f.writeAdapter(think);
+ f.writeAdapter(blocked);
+
+ f.writeAdapter(touch);
+ f.writeAdapter(use);
+
+ f.writeFloat(touch_debounce_time);
+ f.writeFloat(pain_debounce_time);
+ f.writeFloat(damage_debounce_time);
+
+ f.writeFloat(fly_sound_debounce_time);
+ f.writeFloat(last_move_time);
+
+ f.writeInt(health);
+ f.writeInt(max_health);
+
+ f.writeInt(gib_health);
+ f.writeInt(show_hostile);
+
+ f.writeFloat(powerarmor_time);
+
+ f.writeString(map);
+
+ f.writeInt(viewheight);
+ f.writeInt(dmg);
+ f.writeInt(radius_dmg);
+ f.writeFloat(dmg_radius);
+
+ f.writeInt(sounds);
+ f.writeInt(count);
+
+ f.writeEdictRef(chain);
+ f.writeEdictRef(enemy);
+ f.writeEdictRef(oldenemy);
+ f.writeEdictRef(activator);
+ f.writeEdictRef(groundentity);
+ f.writeInt(groundentity_linkcount);
+ f.writeEdictRef(teamchain);
+ f.writeEdictRef(teammaster);
+
+ f.writeEdictRef(mynoise);
+ f.writeEdictRef(mynoise2);
+
+ f.writeInt(noise_index);
+ f.writeInt(noise_index2);
+
+ f.writeFloat(volume);
+ f.writeFloat(attenuation);
+ f.writeFloat(wait);
+ f.writeFloat(delay);
+ f.writeFloat(random);
+
+ f.writeFloat(teleport_time);
+
+ f.writeInt(watertype);
+ f.writeInt(waterlevel);
+ f.writeVector(move_origin);
+ f.writeVector(move_angles);
+
+ f.writeInt(light_level);
+ f.writeInt(style);
+
+ f.writeItem(item);
+
+ moveinfo.write(f);
+ monsterinfo.write(f);
+ if (client == null)
+ f.writeInt(-1);
+ else
+ f.writeInt(client.index);
+
+ f.writeEdictRef(owner);
+
+ // rst's checker :-)
+ f.writeInt(9876);
+ }
+
+ /**
+ * Reads the entity from the file.
+ */
+ public void read(QuakeFile f) throws IOException {
+ s.read(f);
+ inuse = f.readBoolean();
+ linkcount = f.readInt();
+ num_clusters = f.readInt();
+
+ if (f.readInt() != 9999)
+ new Throwable("wrong read pos!").printStackTrace();
+
+ int len = f.readInt();
+
+ if (len == -1)
+ clusternums = null;
+ else {
+ clusternums = new int[Defines.MAX_ENT_CLUSTERS];
+ for (int n = 0; n < Defines.MAX_ENT_CLUSTERS; n++)
+ clusternums[n] = f.readInt();
+ }
+
+ headnode = f.readInt();
+ areanum = f.readInt();
+ areanum2 = f.readInt();
+ svflags = f.readInt();
+ mins = f.readVector();
+ maxs = f.readVector();
+ absmin = f.readVector();
+ absmax = f.readVector();
+ size = f.readVector();
+ solid = f.readInt();
+ clipmask = f.readInt();
+
+ movetype = f.readInt();
+ flags = f.readInt();
+
+ model = f.readString();
+ freetime = f.readFloat();
+ message = f.readString();
+ classname = f.readString();
+ spawnflags = f.readInt();
+ timestamp = f.readFloat();
+
+ angle = f.readFloat();
+
+ target = f.readString();
+ targetname = f.readString();
+ team = f.readString();
+ pathtarget = f.readString();
+
+ target_ent = f.readEdictRef();
+
+ speed = f.readFloat();
+ accel = f.readFloat();
+ decel = f.readFloat();
+
+ movedir = f.readVector();
+
+ pos1 = f.readVector();
+ pos2 = f.readVector();
+
+ velocity = f.readVector();
+ avelocity = f.readVector();
+
+ mass = f.readInt();
+ air_finished = f.readFloat();
+
+ gravity = f.readFloat();
+
+ yaw_speed = f.readFloat();
+ ideal_yaw = f.readFloat();
+
+ nextthink = f.readFloat();
+
+ prethink = (EntThinkAdapter) f.readAdapter();
+ think = (EntThinkAdapter) f.readAdapter();
+ blocked = (EntBlockedAdapter) f.readAdapter();
+
+ touch = (EntTouchAdapter) f.readAdapter();
+ use = (EntUseAdapter) f.readAdapter();
+
+ touch_debounce_time = f.readFloat();
+ pain_debounce_time = f.readFloat();
+ damage_debounce_time = f.readFloat();
+
+ fly_sound_debounce_time = f.readFloat();
+ last_move_time = f.readFloat();
+
+ health = f.readInt();
+ max_health = f.readInt();
+
+ gib_health = f.readInt();
+ show_hostile = f.readInt();
+
+ powerarmor_time = f.readFloat();
+
+ map = f.readString();
+
+ viewheight = f.readInt();
+ dmg = f.readInt();
+ radius_dmg = f.readInt();
+ dmg_radius = f.readFloat();
+
+ sounds = f.readInt();
+ count = f.readInt();
+
+ chain = f.readEdictRef();
+ enemy = f.readEdictRef();
+
+ oldenemy = f.readEdictRef();
+ activator = f.readEdictRef();
+ groundentity = f.readEdictRef();
+
+ groundentity_linkcount = f.readInt();
+ teamchain = f.readEdictRef();
+ teammaster = f.readEdictRef();
+
+ mynoise = f.readEdictRef();
+ mynoise2 = f.readEdictRef();
+
+ noise_index = f.readInt();
+ noise_index2 = f.readInt();
+
+ volume = f.readFloat();
+ attenuation = f.readFloat();
+ wait = f.readFloat();
+ delay = f.readFloat();
+ random = f.readFloat();
+
+ teleport_time = f.readFloat();
+
+ watertype = f.readInt();
+ waterlevel = f.readInt();
+ move_origin = f.readVector();
+ move_angles = f.readVector();
+
+ light_level = f.readInt();
+ style = f.readInt();
+
+
+ moveinfo.read(f);
+ monsterinfo.read(f);
+
+ int ndx = f.readInt();
+ if (ndx == -1)
+ client = null;
+ else
+ client = GameBase.game.clients[ndx];
+
+ owner = f.readEdictRef();
+
+ // rst's checker :-)
+ if (f.readInt() != 9876)
+ System.err.println("ent load check failed for num " + index);
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.game;
+
+/**
+ * Helps for filtering the iteration over the gedicts[] array, see GFind(). RST.
+ */
+
+public class EdictFindFilter {
+ boolean matches(EDict e, String s) {
+ return false;
+ }
+
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.game;
+
+/**
+ * Helps for iterating over the gedicts[] array. RST.
+ */
+
+public class EdictIterator {
+ public EDict o;
+ int i;
+
+ EdictIterator() {
+ this.i = 0;
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.game;
+
+public abstract class EndianHandler {
+ private static final int mask = 0xFF;
+
+
+ public static int swapInt(int i) {
+
+ int a = i & mask;
+ i >>>= 8;
+
+ a <<= 24;
+
+ int b = i & mask;
+
+ i >>>= 8;
+ b <<= 16;
+
+ int c = i & mask;
+ i >>>= 8;
+ c <<= 8;
+
+ return i | c | b | a;
+ }
+
+
+}
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.game;
+
+public abstract class EntBlockedAdapter extends SuperAdapter {
+ // move to moveinfo?
+ public abstract void blocked(EDict self, EDict other);
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.game;
+
+public abstract class EntDodgeAdapter extends SuperAdapter {
+}
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.game;
+
+public abstract class EntInteractAdapter extends SuperAdapter {
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.game;
+
+public abstract class EntThinkAdapter extends SuperAdapter {
+ public abstract void think(EDict self);
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.game;
+
+public abstract class EntTouchAdapter extends SuperAdapter {
+ public abstract void touch(EDict self, EDict other, cplane_t plane, csurface_t surf);
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.game;
+
+public abstract class EntUseAdapter extends SuperAdapter {
+ public abstract void use(EDict self, EDict other, EDict activator);
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.game;
+
+import lwjake2.Defines;
+
+
+class GameAI {
+
+ /**
+ * Called once each frame to set level.sight_client to the player to be
+ * checked for in findtarget.
+ * <p>
+ * If all clients are either dead or in notarget, sight_client will be null.
+ * <p>
+ * In coop games, sight_client will cycle between the clients.
+ */
+ static void AI_SetSightClient() {
+ EDict ent;
+ int start, check;
+
+ if (GameBase.level.sight_client == null)
+ start = 1;
+ else
+ start = GameBase.level.sight_client.index;
+
+ check = start;
+ while (true) {
+ check++;
+ if (check > GameBase.game.maxclients)
+ check = 1;
+ ent = GameBase.g_edicts[check];
+
+ if (ent.inuse && ent.health > 0
+ && (ent.flags & Defines.FL_NOTARGET) == 0) {
+ GameBase.level.sight_client = ent;
+ return; // got one
+ }
+ if (check == start) {
+ GameBase.level.sight_client = null;
+ return; // nobody to see
+ }
+ }
+ }
+
+
+}
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/**
+ * Father of all GameObjects.
+ */
+
+package lwjake2.game;
+
+import lwjake2.Defines;
+import lwjake2.client.M;
+import lwjake2.qcommon.Com;
+import lwjake2.server.SV;
+import lwjake2.server.SV_WORLD;
+import lwjake2.util.Lib;
+import lwjake2.util.Math3D;
+
+import java.util.StringTokenizer;
+
+public class GameBase {
+ public static final cplane_t dummyplane = new cplane_t();
+ public static final game_locals_t game = new game_locals_t();
+ public static final pushed_t[] pushed = new pushed_t[Defines.MAX_EDICTS];
+ public static final int STEPSIZE = 18;
+ public static final EdictFindFilter findByTarget = new EdictFindFilter() {
+ public boolean matches(EDict e, String s) {
+ return e.targetname != null && e.targetname.equalsIgnoreCase(s);
+ }
+ };
+ public static final EdictFindFilter findByClass = new EdictFindFilter() {
+ public boolean matches(EDict e, String s) {
+ return e.classname.equalsIgnoreCase(s);
+ }
+ };
+ private final static float STOP_EPSILON = 0.1f;
+ /**
+ * Searches all active entities for the next one that holds the matching
+ * string at fieldofs (use the FOFS() macro) in the structure.
+ * <p>
+ * Searches beginning at the edict after from, or the beginning if null null
+ * will be returned if the end of the list is reached.
+ */
+
+ private static final int MAXCHOICES = 8;
+ private static final float[] VEC_UP = {0, -1, 0};
+ private static final float[] MOVEDIR_UP = {0, 0, 1};
+ private static final float[] VEC_DOWN = {0, -2, 0};
+ private static final float[] MOVEDIR_DOWN = {0, 0, -1};
+ /**
+ * G_TouchTriggers
+ */
+
+ private static final EDict[] touch = new EDict[Defines.MAX_EDICTS];
+ public static level_locals_t level = new level_locals_t();
+ public static game_import_t gi = new game_import_t();
+ public static spawn_temp_t st = new spawn_temp_t();
+ public static int sm_meat_index;
+ public static int snd_fry;
+ public static int meansOfDeath;
+ public static int num_edicts;
+ public static EDict g_edicts[] = new EDict[Defines.MAX_EDICTS];
+ public static CvarT deathmatch = new CvarT();
+ public static CvarT coop = new CvarT();
+ public static CvarT dmflags = new CvarT();
+ public static CvarT fraglimit = new CvarT();
+ public static CvarT timelimit = new CvarT();
+ public static CvarT password = new CvarT();
+ public static CvarT spectator_password = new CvarT();
+ public static CvarT needpass = new CvarT();
+ public static CvarT maxclients = new CvarT();
+ public static CvarT maxspectators = new CvarT();
+ public static CvarT maxentities = new CvarT();
+ public static CvarT g_select_empty = new CvarT();
+ public static CvarT filterban = new CvarT();
+ public static CvarT sv_maxvelocity = new CvarT();
+ public static CvarT sv_gravity = new CvarT();
+ public static CvarT sv_rollspeed = new CvarT();
+ public static CvarT sv_rollangle = new CvarT();
+ public static CvarT gun_x = new CvarT();
+ public static CvarT gun_y = new CvarT();
+ public static CvarT gun_z = new CvarT();
+ public static CvarT run_pitch = new CvarT();
+ public static CvarT run_roll = new CvarT();
+ public static CvarT bob_up = new CvarT();
+ public static CvarT bob_pitch = new CvarT();
+ public static CvarT bob_roll = new CvarT();
+ public static CvarT sv_cheats = new CvarT();
+ public static CvarT flood_msgs = new CvarT();
+ public static CvarT flood_persecond = new CvarT();
+ public static CvarT flood_waitdelay = new CvarT();
+ public static CvarT sv_maplist = new CvarT();
+ public static int pushed_p;
+ public static EDict obstacle;
+ public static int c_yes, c_no;
+
+ static {
+ for (int n = 0; n < Defines.MAX_EDICTS; n++)
+ g_edicts[n] = new EDict(n);
+ }
+
+ static {
+ for (int n = 0; n < Defines.MAX_EDICTS; n++)
+ pushed[n] = new pushed_t();
+ }
+
+ /**
+ * Slide off of the impacting object returns the blocked flags (1 = floor, 2 =
+ * step / wall).
+ */
+ public static void ClipVelocity(float[] in, float[] normal, float[] out,
+ float overbounce) {
+ float backoff;
+ float change;
+ int i, blocked;
+
+ blocked = 0;
+ if (normal[2] > 0)
+ blocked |= 1; // floor
+ if (normal[2] == 0.0f)
+ blocked |= 2; // step
+
+ backoff = Math3D.dotProduct(in, normal) * overbounce;
+
+ for (i = 0; i < 3; i++) {
+ change = normal[i] * backoff;
+ out[i] = in[i] - change;
+ if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON)
+ out[i] = 0;
+ }
+
+ }
+
+ /**
+ * Searches all active entities for the next one that holds the matching
+ * string at fieldofs (use the FOFS() macro) in the structure.
+ * <p>
+ * Searches beginning at the edict after from, or the beginning if null null
+ * will be returned if the end of the list is reached.
+ */
+
+ public static EdictIterator G_Find(EdictIterator from, EdictFindFilter eff,
+ String s) {
+
+ if (from == null)
+ from = new EdictIterator();
+ else
+ from.i++;
+
+ for (; from.i < num_edicts; from.i++) {
+ from.o = g_edicts[from.i];
+ if (from.o.classname == null) {
+ Com.Printf("edict with classname = null" + from.o.index);
+ }
+
+ if (!from.o.inuse)
+ continue;
+
+ if (eff.matches(from.o, s))
+ return from;
+ }
+
+ return null;
+ }
+
+ // comfort version (rst)
+ public static EDict G_FindEdict(EdictIterator from, EdictFindFilter eff,
+ String s) {
+ EdictIterator ei = G_Find(from, eff, s);
+ if (ei == null)
+ return null;
+ else
+ return ei.o;
+ }
+
+ /**
+ * Returns entities that have origins within a spherical area.
+ */
+ public static EdictIterator findradius(EdictIterator from, float[] org,
+ float rad) {
+ float[] eorg = {0, 0, 0};
+ int j;
+
+ if (from == null)
+ from = new EdictIterator();
+ else
+ from.i++;
+
+ for (; from.i < num_edicts; from.i++) {
+ from.o = g_edicts[from.i];
+ if (!from.o.inuse)
+ continue;
+
+ if (from.o.solid == Defines.SOLID_NOT)
+ continue;
+
+ for (j = 0; j < 3; j++)
+ eorg[j] = org[j]
+ - (from.o.s.origin[j] + (from.o.mins[j] + from.o.maxs[j]) * 0.5f);
+
+ if (Math3D.vectorLength(eorg) > rad)
+ continue;
+ return from;
+ }
+
+ return null;
+ }
+
+ public static EDict G_PickTarget(String targetname) {
+ int num_choices = 0;
+ EDict choice[] = new EDict[MAXCHOICES];
+
+ if (targetname == null) {
+ gi.dprintf("G_PickTarget called with null targetname\n");
+ return null;
+ }
+
+ EdictIterator es = null;
+
+ while ((es = G_Find(es, findByTarget, targetname)) != null) {
+ choice[num_choices++] = es.o;
+ if (num_choices == MAXCHOICES)
+ break;
+ }
+
+ if (num_choices == 0) {
+ gi.dprintf("G_PickTarget: target " + targetname + " not found\n");
+ return null;
+ }
+
+ return choice[Lib.rand() % num_choices];
+ }
+
+ public static void G_SetMovedir(float[] angles, float[] movedir) {
+ if (Math3D.vectorEquals(angles, VEC_UP)) {
+ Math3D.vectorCopy(MOVEDIR_UP, movedir);
+ } else if (Math3D.vectorEquals(angles, VEC_DOWN)) {
+ Math3D.vectorCopy(MOVEDIR_DOWN, movedir);
+ } else {
+ Math3D.angleVectors(angles, movedir, null, null);
+ }
+
+ Math3D.vectorClear(angles);
+ }
+
+ public static String G_CopyString(String in) {
+ return in;
+ }
+
+ public static void G_TouchTriggers(EDict ent) {
+ int i, num;
+ EDict hit;
+
+ // dead things don't activate triggers!
+ if ((ent.client != null || (ent.svflags & Defines.SVF_MONSTER) != 0)
+ && (ent.health <= 0))
+ return;
+
+ num = gi.BoxEdicts(ent.absmin, ent.absmax, touch, Defines.MAX_EDICTS,
+ Defines.AREA_TRIGGERS);
+
+ // be careful, it is possible to have an entity in this
+ // list removed before we get to it (killtriggered)
+ for (i = 0; i < num; i++) {
+ hit = touch[i];
+
+ if (!hit.inuse)
+ continue;
+
+ if (hit.touch == null)
+ continue;
+
+ hit.touch.touch(hit, ent, dummyplane, null);
+ }
+ }
+
+ /**
+ * G_RunEntity
+ */
+ private static void G_RunEntity(EDict ent) {
+
+ if (ent.prethink != null)
+ ent.prethink.think(ent);
+
+ switch (ent.movetype) {
+ case Defines.MOVETYPE_PUSH:
+ case Defines.MOVETYPE_STOP:
+ SV.SV_Physics_Pusher(ent);
+ break;
+ case Defines.MOVETYPE_NONE:
+ SV.SV_Physics_None(ent);
+ break;
+ case Defines.MOVETYPE_NOCLIP:
+ SV.SV_Physics_Noclip(ent);
+ break;
+ case Defines.MOVETYPE_STEP:
+ SV.SV_Physics_Step(ent);
+ break;
+ case Defines.MOVETYPE_TOSS:
+ case Defines.MOVETYPE_BOUNCE:
+ case Defines.MOVETYPE_FLY:
+ case Defines.MOVETYPE_FLYMISSILE:
+ SV.SV_Physics_Toss(ent);
+ break;
+ default:
+ gi.error("SV_Physics: bad movetype " + ent.movetype);
+ }
+ }
+
+ public static void ClearBounds(float[] mins, float[] maxs) {
+ mins[0] = mins[1] = mins[2] = 99999;
+ maxs[0] = maxs[1] = maxs[2] = -99999;
+ }
+
+ public static void AddPointToBounds(float[] v, float[] mins, float[] maxs) {
+ int i;
+ float val;
+
+ for (i = 0; i < 3; i++) {
+ val = v[i];
+ if (val < mins[i])
+ mins[i] = val;
+ if (val > maxs[i])
+ maxs[i] = val;
+ }
+ }
+
+ public static void ShutdownGame() {
+ gi.dprintf("==== ShutdownGame ====\n");
+ }
+
+ /**
+ * ClientEndServerFrames.
+ */
+ private static void ClientEndServerFrames() {
+ int i;
+ EDict ent;
+
+ // calc the player views now that all pushing
+ // and damage has been added
+ for (i = 0; i < maxclients.value; i++) {
+ ent = g_edicts[1 + i];
+ if (!ent.inuse || null == ent.client)
+ continue;
+ PlayerView.ClientEndServerFrame(ent);
+ }
+
+ }
+
+ /**
+ * Returns the created target changelevel.
+ */
+ private static EDict CreateTargetChangeLevel(String map) {
+ EDict ent;
+
+ ent = GameUtil.G_Spawn();
+ ent.classname = "target_changelevel";
+ level.nextmap = map;
+ ent.map = level.nextmap;
+ return ent;
+ }
+
+ /**
+ * The timelimit or fraglimit has been exceeded.
+ */
+ private static void EndDMLevel() {
+ EDict ent;
+ //char * s, * t, * f;
+ //static const char * seps = " ,\n\r";
+ String s, t, f;
+ String seps = " ,\n\r";
+
+ // stay on same level flag
+ if (((int) dmflags.value & Defines.DF_SAME_LEVEL) != 0) {
+ PlayerHud.BeginIntermission(CreateTargetChangeLevel(level.mapname));
+ return;
+ }
+
+ // see if it's in the map list
+ if (sv_maplist.string.length() > 0) {
+ s = sv_maplist.string;
+ f = null;
+ StringTokenizer tk = new StringTokenizer(s, seps);
+
+ while (tk.hasMoreTokens()) {
+ t = tk.nextToken();
+
+ // store first map
+ if (f == null)
+ f = t;
+
+ if (t.equalsIgnoreCase(level.mapname)) {
+ // it's in the list, go to the next one
+ if (!tk.hasMoreTokens()) {
+ // end of list, go to first one
+ if (f == null) // there isn't a first one, same level
+ PlayerHud.BeginIntermission(CreateTargetChangeLevel(level.mapname));
+ else
+ PlayerHud.BeginIntermission(CreateTargetChangeLevel(f));
+ } else
+ PlayerHud.BeginIntermission(CreateTargetChangeLevel(tk.nextToken()));
+ return;
+ }
+ }
+ }
+
+ //not in the map list
+ if (level.nextmap.length() > 0) // go to a specific map
+ PlayerHud.BeginIntermission(CreateTargetChangeLevel(level.nextmap));
+ else { // search for a changelevel
+ EdictIterator edit = null;
+ edit = G_Find(edit, findByClass, "target_changelevel");
+ if (edit == null) { // the map designer didn't include a
+ // changelevel,
+ // so create a fake ent that goes back to the same level
+ PlayerHud.BeginIntermission(CreateTargetChangeLevel(level.mapname));
+ return;
+ }
+ ent = edit.o;
+ PlayerHud.BeginIntermission(ent);
+ }
+ }
+
+ /**
+ * CheckNeedPass.
+ */
+ private static void CheckNeedPass() {
+ int need;
+
+ // if password or spectator_password has changed, update needpass
+ // as needed
+ if (password.modified || spectator_password.modified) {
+ password.modified = spectator_password.modified = false;
+
+ need = 0;
+
+ if ((password.string.length() > 0)
+ && 0 != Lib.Q_stricmp(password.string, "none"))
+ need |= 1;
+ if ((spectator_password.string.length() > 0)
+ && 0 != Lib.Q_stricmp(spectator_password.string, "none"))
+ need |= 2;
+
+ gi.cvar_set("needpass", "" + need);
+ }
+ }
+
+ /**
+ * CheckDMRules.
+ */
+ private static void CheckDMRules() {
+ int i;
+ Client cl;
+
+ if (level.intermissiontime != 0)
+ return;
+
+ if (0 == deathmatch.value)
+ return;
+
+ if (timelimit.value != 0) {
+ if (level.time >= timelimit.value * 60) {
+ gi.bprintf(Defines.PRINT_HIGH, "Timelimit hit.\n");
+ EndDMLevel();
+ return;
+ }
+ }
+
+ if (fraglimit.value != 0) {
+ for (i = 0; i < maxclients.value; i++) {
+ cl = game.clients[i];
+ if (!g_edicts[i + 1].inuse)
+ continue;
+
+ if (cl.resp.score >= fraglimit.value) {
+ gi.bprintf(Defines.PRINT_HIGH, "Fraglimit hit.\n");
+ EndDMLevel();
+ return;
+ }
+ }
+ }
+ }
+
+ /**
+ * Exits a level.
+ */
+ private static void ExitLevel() {
+ int i;
+ EDict ent;
+
+ String command = "gamemap \"" + level.changemap + "\"\n";
+ gi.AddCommandString(command);
+ level.changemap = null;
+ level.exitintermission = false;
+ level.intermissiontime = 0;
+ ClientEndServerFrames();
+
+ // clear some things before going to next level
+ for (i = 0; i < maxclients.value; i++) {
+ ent = g_edicts[1 + i];
+ if (!ent.inuse)
+ continue;
+ if (ent.health > ent.client.pers.max_health)
+ ent.health = ent.client.pers.max_health;
+ }
+ }
+
+ /**
+ * G_RunFrame
+ * <p>
+ * Advances the world by Defines.FRAMETIME (0.1) seconds.
+ */
+ public static void G_RunFrame() {
+ int i;
+ EDict ent;
+
+ level.framenum++;
+ level.time = level.framenum * Defines.FRAMETIME;
+
+ // choose a client for monsters to target this frame
+ GameAI.AI_SetSightClient();
+
+ // exit intermissions
+
+ if (level.exitintermission) {
+ ExitLevel();
+ return;
+ }
+
+ //
+ // treat each object in turn
+ // even the world gets a chance to think
+ //
+
+ for (i = 0; i < num_edicts; i++) {
+ ent = g_edicts[i];
+ if (!ent.inuse)
+ continue;
+
+ level.current_entity = ent;
+
+ Math3D.vectorCopy(ent.s.origin, ent.s.old_origin);
+
+ // if the ground entity moved, make sure we are still on it
+ if ((ent.groundentity != null)
+ && (ent.groundentity.linkcount != ent.groundentity_linkcount)) {
+ ent.groundentity = null;
+ if (0 == (ent.flags & (Defines.FL_SWIM | Defines.FL_FLY))
+ && (ent.svflags & Defines.SVF_MONSTER) != 0) {
+ M.M_CheckGround(ent);
+ }
+ }
+
+ if (i > 0 && i <= maxclients.value) {
+ PlayerClient.clientBeginServerFrame(ent);
+ continue;
+ }
+
+ G_RunEntity(ent);
+ }
+
+ // see if it is time to end a deathmatch
+ CheckDMRules();
+
+ // see if needpass needs updated
+ CheckNeedPass();
+
+ // build the playerstate_t structures for all players
+ ClientEndServerFrames();
+ }
+
+ /**
+ * This return a pointer to the structure with all entry points and global
+ * variables.
+ */
+
+ public static void GetGameApi(game_import_t imp) {
+ gi = imp;
+ gi.pointcontents = new Move.PointContentsAdapter() {
+ public int pointcontents(float[] o) {
+ return SV_WORLD.SV_PointContents(o);
+ }
+ };
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.game;
+
+import lwjake2.Defines;
+import lwjake2.Globals;
+import lwjake2.util.Lib;
+import lwjake2.util.Math3D;
+
+class GameFunc {
+
+ /**
+ * PLATS
+ * <p>
+ * movement options:
+ * <p>
+ * linear smooth start, hard stop smooth start, smooth stop
+ * <p>
+ * start end acceleration speed deceleration begin sound end sound target
+ * fired when reaching end wait at end
+ * <p>
+ * object characteristics that use move segments
+ * --------------------------------------------- movetype_push, or
+ * movetype_stop action when touched action when blocked action when used
+ * disabled? auto trigger spawning
+ */
+
+ private final static int PLAT_LOW_TRIGGER = 1;
+ private final static int STATE_TOP = 0;
+ private final static int STATE_BOTTOM = 1;
+ private final static int STATE_UP = 2;
+ private final static int STATE_DOWN = 3;
+
+ private final static int TRAIN_START_ON = 1;
+ private final static int TRAIN_TOGGLE = 2;
+ private final static int TRAIN_BLOCK_STOPS = 4;
+ private static final EntThinkAdapter Move_Done = new EntThinkAdapter() {
+ public String getID() {
+ return "move_done";
+ }
+
+ public void think(EDict ent) {
+ Math3D.vectorClear(ent.velocity);
+ ent.moveinfo.endfunc.think(ent);
+ }
+ };
+ private static final EntThinkAdapter Move_Final = new EntThinkAdapter() {
+ public String getID() {
+ return "move_final";
+ }
+
+ public void think(EDict ent) {
+
+ if (ent.moveinfo.remaining_distance == 0) {
+ Move_Done.think(ent);
+ return;
+ }
+
+ Math3D.vectorScale(ent.moveinfo.dir,
+ ent.moveinfo.remaining_distance / Defines.FRAMETIME,
+ ent.velocity);
+
+ ent.think = Move_Done;
+ ent.nextthink = GameBase.level.time + Defines.FRAMETIME;
+ }
+ };
+ private static final EntThinkAdapter Move_Begin = new EntThinkAdapter() {
+ public String getID() {
+ return "move_begin";
+ }
+
+ public void think(EDict ent) {
+
+ float frames;
+
+ if ((ent.moveinfo.speed * Defines.FRAMETIME) >= ent.moveinfo.remaining_distance) {
+ Move_Final.think(ent);
+ return;
+ }
+ Math3D.vectorScale(ent.moveinfo.dir, ent.moveinfo.speed,
+ ent.velocity);
+ frames = (float) Math
+ .floor((ent.moveinfo.remaining_distance / ent.moveinfo.speed)
+ / Defines.FRAMETIME);
+ ent.moveinfo.remaining_distance -= frames * ent.moveinfo.speed
+ * Defines.FRAMETIME;
+ ent.nextthink = GameBase.level.time + (frames * Defines.FRAMETIME);
+ ent.think = Move_Final;
+ }
+ };
+ private static final EntThinkAdapter AngleMove_Done = new EntThinkAdapter() {
+ public String getID() {
+ return "agnle_move_done";
+ }
+
+ public void think(EDict ent) {
+ Math3D.vectorClear(ent.avelocity);
+ ent.moveinfo.endfunc.think(ent);
+ }
+ };
+ private static final EntThinkAdapter AngleMove_Final = new EntThinkAdapter() {
+ public String getID() {
+ return "angle_move_final";
+ }
+
+ public void think(EDict ent) {
+ float[] move = {0, 0, 0};
+
+ if (ent.moveinfo.state == STATE_UP)
+ Math3D.vectorSubtract(ent.moveinfo.end_angles, ent.s.angles,
+ move);
+ else
+ Math3D.vectorSubtract(ent.moveinfo.start_angles, ent.s.angles,
+ move);
+
+ if (Math3D.vectorEquals(move, Globals.vec3_origin)) {
+ AngleMove_Done.think(ent);
+ return;
+ }
+
+ Math3D.vectorScale(move, 1.0f / Defines.FRAMETIME, ent.avelocity);
+
+ ent.think = AngleMove_Done;
+ ent.nextthink = GameBase.level.time + Defines.FRAMETIME;
+ }
+ };
+ static final EntThinkAdapter AngleMove_Begin = new EntThinkAdapter() {
+ public String getID() {
+ return "angle_move_begin";
+ }
+
+ public void think(EDict ent) {
+ float[] destdelta = {0, 0, 0};
+ float len;
+ float traveltime;
+ float frames;
+
+ // set destdelta to the vector needed to move
+ if (ent.moveinfo.state == STATE_UP)
+ Math3D.vectorSubtract(ent.moveinfo.end_angles, ent.s.angles,
+ destdelta);
+ else
+ Math3D.vectorSubtract(ent.moveinfo.start_angles, ent.s.angles,
+ destdelta);
+
+ // calculate length of vector
+ len = Math3D.vectorLength(destdelta);
+
+ // divide by speed to get time to reach dest
+ traveltime = len / ent.moveinfo.speed;
+
+ if (traveltime < Defines.FRAMETIME) {
+ AngleMove_Final.think(ent);
+ return;
+ }
+
+ frames = (float) (Math.floor(traveltime / Defines.FRAMETIME));
+
+ // scale the destdelta vector by the time spent traveling to get
+ // velocity
+ Math3D.vectorScale(destdelta, 1.0f / traveltime, ent.avelocity);
+
+ // set nextthink to trigger a think when dest is reached
+ ent.nextthink = GameBase.level.time + frames * Defines.FRAMETIME;
+ ent.think = AngleMove_Final;
+ }
+ };
+ private static final EntThinkAdapter Think_AccelMove = new EntThinkAdapter() {
+ public String getID() {
+ return "thinc_accelmove";
+ }
+
+ public void think(EDict ent) {
+ ent.moveinfo.remaining_distance -= ent.moveinfo.current_speed;
+
+ if (ent.moveinfo.current_speed == 0) // starting or blocked
+ plat_CalcAcceleratedMove(ent.moveinfo);
+
+ plat_Accelerate(ent.moveinfo);
+
+ // will the entire move complete on next frame?
+ if (ent.moveinfo.remaining_distance <= ent.moveinfo.current_speed) {
+ Move_Final.think(ent);
+ return;
+ }
+
+ Math3D.vectorScale(ent.moveinfo.dir,
+ ent.moveinfo.current_speed * 10, ent.velocity);
+ ent.nextthink = GameBase.level.time + Defines.FRAMETIME;
+ ent.think = Think_AccelMove;
+ }
+ };
+ private static final EntThinkAdapter plat_hit_bottom = new EntThinkAdapter() {
+ public String getID() {
+ return "plat_hit_bottom";
+ }
+
+ public void think(EDict ent) {
+
+ if (0 == (ent.flags & Defines.FL_TEAMSLAVE)) {
+ if (ent.moveinfo.sound_end != 0)
+ GameBase.gi.sound(ent, Defines.CHAN_NO_PHS_ADD
+ + Defines.CHAN_VOICE, ent.moveinfo.sound_end, 1,
+ Defines.ATTN_STATIC, 0);
+ ent.s.sound = 0;
+ }
+ ent.moveinfo.state = STATE_BOTTOM;
+ }
+ };
+
+ //
+ // Support routines for movement (changes in origin using velocity)
+ //
+ private static final EntThinkAdapter plat_go_down = new EntThinkAdapter() {
+ public String getID() {
+ return "plat_go_down";
+ }
+
+ public void think(EDict ent) {
+ if (0 == (ent.flags & Defines.FL_TEAMSLAVE)) {
+ if (ent.moveinfo.sound_start != 0)
+ GameBase.gi.sound(ent, Defines.CHAN_NO_PHS_ADD
+ + Defines.CHAN_VOICE, ent.moveinfo.sound_start, 1,
+ Defines.ATTN_STATIC, 0);
+ ent.s.sound = ent.moveinfo.sound_middle;
+ }
+ ent.moveinfo.state = STATE_DOWN;
+ Move_Calc(ent, ent.moveinfo.end_origin, plat_hit_bottom);
+ }
+ };
+ private static final EntThinkAdapter plat_hit_top = new EntThinkAdapter() {
+ public String getID() {
+ return "plat_hit_top";
+ }
+
+ public void think(EDict ent) {
+ if (0 == (ent.flags & Defines.FL_TEAMSLAVE)) {
+ if (ent.moveinfo.sound_end != 0)
+ GameBase.gi.sound(ent, Defines.CHAN_NO_PHS_ADD
+ + Defines.CHAN_VOICE, ent.moveinfo.sound_end, 1,
+ Defines.ATTN_STATIC, 0);
+ ent.s.sound = 0;
+ }
+ ent.moveinfo.state = STATE_TOP;
+
+ ent.think = plat_go_down;
+ ent.nextthink = GameBase.level.time + 3;
+ }
+ };
+ private static final EntBlockedAdapter plat_blocked = new EntBlockedAdapter() {
+ public String getID() {
+ return "plat_blocked";
+ }
+
+ public void blocked(EDict self, EDict other) {
+ if (0 == (other.svflags & Defines.SVF_MONSTER)
+ && (null == other.client)) {
+ // if it's still there, nuke it
+ if (other != null)
+ GameMisc.BecomeExplosion1(other);
+ return;
+ }
+
+ if (self.moveinfo.state == STATE_UP)
+ plat_go_down.think(self);
+ else if (self.moveinfo.state == STATE_DOWN)
+ plat_go_up(self);
+
+ }
+ };
+
+ //
+ // Support routines for angular movement (changes in angle using avelocity)
+ //
+ private static final EntUseAdapter Use_Plat = new EntUseAdapter() {
+ public String getID() {
+ return "use_plat";
+ }
+
+ public void use(EDict ent, EDict other, EDict activator) {
+ if (ent.think != null)
+ return; // already down
+ plat_go_down.think(ent);
+ }
+ };
+ private static final EntTouchAdapter Touch_Plat_Center = new EntTouchAdapter() {
+ public String getID() {
+ return "touch_plat_center";
+ }
+
+ public void touch(EDict ent, EDict other, cplane_t plane,
+ csurface_t surf) {
+ if (other.client == null)
+ return;
+
+ if (other.health <= 0)
+ return;
+
+ ent = ent.enemy; // now point at the plat, not the trigger
+ if (ent.moveinfo.state == STATE_BOTTOM)
+ plat_go_up(ent);
+ else if (ent.moveinfo.state == STATE_TOP) {
+ ent.nextthink = GameBase.level.time + 1; // the player is still
+ // on the plat, so
+ // delay going down
+ }
+ }
+ };
+
+
+ /*
+ * ======================================================================
+ *
+ * BUTTONS
+ *
+ * ======================================================================
+ */
+
+ private static final EntBlockedAdapter train_blocked = new EntBlockedAdapter() {
+ public String getID() {
+ return "train_blocked";
+ }
+
+ public void blocked(EDict self, EDict other) {
+ if (0 == (other.svflags & Defines.SVF_MONSTER)
+ && (null == other.client)) {
+ // if it's still there, nuke it
+ if (other != null)
+ GameMisc.BecomeExplosion1(other);
+ return;
+ }
+
+ if (GameBase.level.time < self.touch_debounce_time)
+ return;
+
+ if (self.dmg == 0)
+ return;
+ self.touch_debounce_time = GameBase.level.time + 0.5f;
+ }
+ };
+ private static final EntThinkAdapter train_wait = new EntThinkAdapter() {
+ public String getID() {
+ return "train_wait";
+ }
+
+ public void think(EDict self) {
+ if (self.target_ent.pathtarget != null) {
+ String savetarget;
+ EDict ent;
+
+ ent = self.target_ent;
+ savetarget = ent.target;
+ ent.target = ent.pathtarget;
+ GameUtil.G_UseTargets(ent, self.activator);
+ ent.target = savetarget;
+
+ // make sure we didn't get killed by a killtarget
+ if (!self.inuse)
+ return;
+ }
+
+ if (self.moveinfo.wait != 0) {
+ if (self.moveinfo.wait > 0) {
+ self.nextthink = GameBase.level.time + self.moveinfo.wait;
+ self.think = train_next;
+ } else if (0 != (self.spawnflags & TRAIN_TOGGLE)) // && wait < 0
+ {
+ train_next.think(self);
+ self.spawnflags &= ~TRAIN_START_ON;
+ Math3D.vectorClear(self.velocity);
+ self.nextthink = 0;
+ }
+
+ if (0 == (self.flags & Defines.FL_TEAMSLAVE)) {
+ if (self.moveinfo.sound_end != 0)
+ GameBase.gi.sound(self, Defines.CHAN_NO_PHS_ADD
+ + Defines.CHAN_VOICE, self.moveinfo.sound_end,
+ 1, Defines.ATTN_STATIC, 0);
+ self.s.sound = 0;
+ }
+ } else {
+ train_next.think(self);
+ }
+ }
+ };
+ private static final EntThinkAdapter train_next = new EntThinkAdapter() {
+ public String getID() {
+ return "train_next";
+ }
+
+ public void think(EDict self) {
+ EDict ent = null;
+ float[] dest = {0, 0, 0};
+ boolean first;
+
+ first = true;
+
+ boolean dogoto = true;
+ while (dogoto) {
+ if (null == self.target) {
+ // gi.dprintf ("train_next: no next target\n");
+ return;
+ }
+
+ ent = GameBase.G_PickTarget(self.target);
+ if (null == ent) {
+ GameBase.gi.dprintf("train_next: bad target " + self.target
+ + "\n");
+ return;
+ }
+
+ self.target = ent.target;
+ dogoto = false;
+ // check for a teleport path_corner
+ if ((ent.spawnflags & 1) != 0) {
+ if (!first) {
+ GameBase.gi
+ .dprintf("connected teleport path_corners, see "
+ + ent.classname
+ + " at "
+ + Lib.vtos(ent.s.origin) + "\n");
+ return;
+ }
+ first = false;
+ Math3D.vectorSubtract(ent.s.origin, self.mins,
+ self.s.origin);
+ Math3D.vectorCopy(self.s.origin, self.s.old_origin);
+ self.s.event = Defines.EV_OTHER_TELEPORT;
+ GameBase.gi.linkentity(self);
+ dogoto = true;
+ }
+ }
+ self.moveinfo.wait = ent.wait;
+ self.target_ent = ent;
+
+ if (0 == (self.flags & Defines.FL_TEAMSLAVE)) {
+ if (self.moveinfo.sound_start != 0)
+ GameBase.gi.sound(self, Defines.CHAN_NO_PHS_ADD
+ + Defines.CHAN_VOICE, self.moveinfo.sound_start, 1,
+ Defines.ATTN_STATIC, 0);
+ self.s.sound = self.moveinfo.sound_middle;
+ }
+
+ Math3D.vectorSubtract(ent.s.origin, self.mins, dest);
+ self.moveinfo.state = STATE_TOP;
+ Math3D.vectorCopy(self.s.origin, self.moveinfo.start_origin);
+ Math3D.vectorCopy(dest, self.moveinfo.end_origin);
+ Move_Calc(self, dest, train_wait);
+ self.spawnflags |= TRAIN_START_ON;
+ }
+ };
+ public static final EntUseAdapter train_use = new EntUseAdapter() {
+ public String getID() {
+ return "train_use";
+ }
+
+ public void use(EDict self, EDict other, EDict activator) {
+ self.activator = activator;
+
+ if ((self.spawnflags & TRAIN_START_ON) != 0) {
+ if (0 == (self.spawnflags & TRAIN_TOGGLE))
+ return;
+ self.spawnflags &= ~TRAIN_START_ON;
+ Math3D.vectorClear(self.velocity);
+ self.nextthink = 0;
+ } else {
+ if (self.target_ent != null)
+ train_resume(self);
+ else
+ train_next.think(self);
+ }
+ }
+ };
+ public static final EntThinkAdapter func_train_find = new EntThinkAdapter() {
+ public String getID() {
+ return "func_train_find";
+ }
+
+ public void think(EDict self) {
+ EDict ent;
+
+ if (null == self.target) {
+ GameBase.gi.dprintf("train_find: no target\n");
+ return;
+ }
+ ent = GameBase.G_PickTarget(self.target);
+ if (null == ent) {
+ GameBase.gi.dprintf("train_find: target " + self.target
+ + " not found\n");
+ return;
+ }
+ self.target = ent.target;
+
+ Math3D.vectorSubtract(ent.s.origin, self.mins, self.s.origin);
+ GameBase.gi.linkentity(self);
+
+ // if not triggered, start immediately
+ if (null == self.targetname)
+ self.spawnflags |= TRAIN_START_ON;
+
+ if ((self.spawnflags & TRAIN_START_ON) != 0) {
+ self.nextthink = GameBase.level.time + Defines.FRAMETIME;
+ self.think = train_next;
+ self.activator = self;
+ }
+ }
+ };
+ /*
+ * QUAKED trigger_elevator (0.3 0.1 0.6) (-8 -8 -8) (8 8 8)
+ */
+ private static final EntUseAdapter trigger_elevator_use = new EntUseAdapter() {
+ public String getID() {
+ return "trigger_elevator_use";
+ }
+
+ public void use(EDict self, EDict other, EDict activator) {
+ EDict target;
+
+
+ if (null == other.pathtarget) {
+ GameBase.gi.dprintf("elevator used with no pathtarget\n");
+ return;
+ }
+
+ target = GameBase.G_PickTarget(other.pathtarget);
+ if (null == target) {
+ GameBase.gi.dprintf("elevator used with bad pathtarget: "
+ + other.pathtarget + "\n");
+ }
+
+ }
+ };
+ private static final EntThinkAdapter trigger_elevator_init = new EntThinkAdapter() {
+ public String getID() {
+ return "trigger_elevator_init";
+ }
+
+ public void think(EDict self) {
+ if (null == self.target) {
+ GameBase.gi.dprintf("trigger_elevator has no target\n");
+ return;
+ }
+
+ self.use = trigger_elevator_use;
+ self.svflags = Defines.SVF_NOCLIENT;
+ }
+ };
+
+ /*
+ * QUAKED func_door_rotating (0 .5 .8) ? START_OPEN REVERSE CRUSHER
+ * NOMONSTER ANIMATED TOGGLE X_AXIS Y_AXIS TOGGLE causes the door to wait in
+ * both the start and end states for a trigger event.
+ *
+ * START_OPEN the door to moves to its destination when spawned, and operate
+ * in reverse. It is used to temporarily or permanently close off an area
+ * when triggered (not useful for touch or takedamage doors). NOMONSTER
+ * monsters will not trigger this door
+ *
+ * You need to have an origin brush as part of this entity. The center of
+ * that brush will be the point around which it is rotated. It will rotate
+ * around the Z axis by default. You can check either the X_AXIS or Y_AXIS
+ * box to change that.
+ *
+ * "distance" is how many degrees the door will be rotated. "speed"
+ * determines how fast the door moves; default value is 100.
+ *
+ * REVERSE will cause the door to rotate in the opposite direction.
+ *
+ * "message" is printed when the door is touched if it is a trigger door and
+ * it hasn't been fired yet "angle" determines the opening direction
+ * "targetname" if set, no touch field will be spawned and a remote button
+ * or trigger field activates the door. "health" if set, door must be shot
+ * open "speed" movement speed (100 default) "wait" wait before returning (3
+ * default, -1 = never return) "dmg" damage to inflict when blocked (2
+ * default) "sounds" 1) silent 2) light 3) medium 4) heavy
+ */
+ static final EntThinkAdapter SP_trigger_elevator = new EntThinkAdapter() {
+ public String getID() {
+ return "sp_trigger_elevator";
+ }
+
+ public void think(EDict self) {
+ self.think = trigger_elevator_init;
+ self.nextthink = GameBase.level.time + Defines.FRAMETIME;
+ }
+ };
+ private static final EntThinkAdapter func_timer_think = new EntThinkAdapter() {
+ public String getID() {
+ return "func_timer_think";
+ }
+
+ public void think(EDict self) {
+ GameUtil.G_UseTargets(self, self.activator);
+ self.nextthink = GameBase.level.time + self.wait + Lib.crandom()
+ * self.random;
+ }
+ };
+ private static final EntUseAdapter func_timer_use = new EntUseAdapter() {
+ public String getID() {
+ return "func_timer_use";
+ }
+
+ public void use(EDict self, EDict other, EDict activator) {
+ self.activator = activator;
+
+ // if on, turn it off
+ if (self.nextthink != 0) {
+ self.nextthink = 0;
+ return;
+ }
+
+ // turn it on
+ if (self.delay != 0)
+ self.nextthink = GameBase.level.time + self.delay;
+ else
+ func_timer_think.think(self);
+ }
+ };
+ private static final EntUseAdapter func_conveyor_use = new EntUseAdapter() {
+ public String getID() {
+ return "func_conveyor_use";
+ }
+
+ public void use(EDict self, EDict other, EDict activator) {
+ if ((self.spawnflags & 1) != 0) {
+ self.speed = 0;
+ self.spawnflags &= ~1;
+ } else {
+ self.speed = self.count;
+ self.spawnflags |= 1;
+ }
+
+ if (0 == (self.spawnflags & 2))
+ self.count = 0;
+ }
+ };
+
+ /*
+ * QUAKED func_train (0 .5 .8) ? START_ON TOGGLE BLOCK_STOPS Trains are
+ * moving platforms that players can ride. The targets origin specifies the
+ * min point of the train at each corner. The train spawns at the first
+ * target it is pointing at. If the train is the target of a button or
+ * trigger, it will not begin moving until activated. speed default 100 dmg
+ * default 2 noise looping sound to play when the train is in motion
+ *
+ */
+ static final EntThinkAdapter SP_func_conveyor = new EntThinkAdapter() {
+ public String getID() {
+ return "sp_func_conveyor";
+ }
+
+ public void think(EDict self) {
+
+ if (0 == self.speed)
+ self.speed = 100;
+
+ if (0 == (self.spawnflags & 1)) {
+ self.count = (int) self.speed;
+ self.speed = 0;
+ }
+
+ self.use = func_conveyor_use;
+
+ GameBase.gi.setmodel(self, self.model);
+ self.solid = Defines.SOLID_BSP;
+ GameBase.gi.linkentity(self);
+ }
+ };
+
+
+ private static void Move_Calc(EDict ent, float[] dest, EntThinkAdapter func) {
+ Math3D.vectorClear(ent.velocity);
+ Math3D.vectorSubtract(dest, ent.s.origin, ent.moveinfo.dir);
+ ent.moveinfo.remaining_distance = Math3D
+ .vectorNormalize(ent.moveinfo.dir);
+
+ ent.moveinfo.endfunc = func;
+
+ if (ent.moveinfo.speed == ent.moveinfo.accel
+ && ent.moveinfo.speed == ent.moveinfo.decel) {
+ if (GameBase.level.current_entity == ((ent.flags & Defines.FL_TEAMSLAVE) != 0 ? ent.teammaster
+ : ent)) {
+ Move_Begin.think(ent);
+ } else {
+ ent.nextthink = GameBase.level.time + Defines.FRAMETIME;
+ ent.think = Move_Begin;
+ }
+ } else {
+ // accelerative
+ ent.moveinfo.current_speed = 0;
+ ent.think = Think_AccelMove;
+ ent.nextthink = GameBase.level.time + Defines.FRAMETIME;
+ }
+ }
+
+ /**
+ * Think_AccelMove
+ * <p>
+ * The team has completed a frame of movement, so change the speed for the
+ * next frame.
+ */
+ private static float AccelerationDistance(float target, float rate) {
+ return target * ((target / rate) + 1) / 2;
+ }
+
+ private static void plat_CalcAcceleratedMove(moveinfo_t moveinfo) {
+ float accel_dist;
+ float decel_dist;
+
+ moveinfo.move_speed = moveinfo.speed;
+
+ if (moveinfo.remaining_distance < moveinfo.accel) {
+ moveinfo.current_speed = moveinfo.remaining_distance;
+ return;
+ }
+
+ accel_dist = AccelerationDistance(moveinfo.speed, moveinfo.accel);
+ decel_dist = AccelerationDistance(moveinfo.speed, moveinfo.decel);
+
+ if ((moveinfo.remaining_distance - accel_dist - decel_dist) < 0) {
+ float f;
+
+ f = (moveinfo.accel + moveinfo.decel)
+ / (moveinfo.accel * moveinfo.decel);
+ moveinfo.move_speed = (float) ((-2 + Math.sqrt(4 - 4 * f
+ * (-2 * moveinfo.remaining_distance))) / (2 * f));
+ decel_dist = AccelerationDistance(moveinfo.move_speed,
+ moveinfo.decel);
+ }
+
+ moveinfo.decel_distance = decel_dist;
+ }
+
+ private static void plat_Accelerate(moveinfo_t moveinfo) {
+ // are we decelerating?
+ if (moveinfo.remaining_distance <= moveinfo.decel_distance) {
+ if (moveinfo.remaining_distance < moveinfo.decel_distance) {
+ if (moveinfo.next_speed != 0) {
+ moveinfo.current_speed = moveinfo.next_speed;
+ moveinfo.next_speed = 0;
+ return;
+ }
+ if (moveinfo.current_speed > moveinfo.decel)
+ moveinfo.current_speed -= moveinfo.decel;
+ }
+ return;
+ }
+
+ // are we at full speed and need to start decelerating during this move?
+ if (moveinfo.current_speed == moveinfo.move_speed)
+ if ((moveinfo.remaining_distance - moveinfo.current_speed) < moveinfo.decel_distance) {
+ float p1_distance;
+ float p2_distance;
+ float distance;
+
+ p1_distance = moveinfo.remaining_distance
+ - moveinfo.decel_distance;
+ p2_distance = moveinfo.move_speed
+ * (1.0f - (p1_distance / moveinfo.move_speed));
+ distance = p1_distance + p2_distance;
+ moveinfo.current_speed = moveinfo.move_speed;
+ moveinfo.next_speed = moveinfo.move_speed - moveinfo.decel
+ * (p2_distance / distance);
+ return;
+ }
+
+ // are we accelerating?
+ if (moveinfo.current_speed < moveinfo.speed) {
+ float old_speed;
+ float p1_distance;
+ float p1_speed;
+ float p2_distance;
+ float distance;
+
+ old_speed = moveinfo.current_speed;
+
+ // figure simple acceleration up to move_speed
+ moveinfo.current_speed += moveinfo.accel;
+ if (moveinfo.current_speed > moveinfo.speed)
+ moveinfo.current_speed = moveinfo.speed;
+
+ // are we accelerating throughout this entire move?
+ if ((moveinfo.remaining_distance - moveinfo.current_speed) >= moveinfo.decel_distance)
+ return;
+
+ // during this move we will accelrate from current_speed to
+ // move_speed
+ // and cross over the decel_distance; figure the average speed for
+ // the
+ // entire move
+ p1_distance = moveinfo.remaining_distance - moveinfo.decel_distance;
+ p1_speed = (old_speed + moveinfo.move_speed) / 2.0f;
+ p2_distance = moveinfo.move_speed
+ * (1.0f - (p1_distance / p1_speed));
+ distance = p1_distance + p2_distance;
+ moveinfo.current_speed = (p1_speed * (p1_distance / distance))
+ + (moveinfo.move_speed * (p2_distance / distance));
+ moveinfo.next_speed = moveinfo.move_speed - moveinfo.decel
+ * (p2_distance / distance);
+ }
+
+ // we are at constant velocity (move_speed)
+ }
+
+ private static void plat_go_up(EDict ent) {
+ if (0 == (ent.flags & Defines.FL_TEAMSLAVE)) {
+ if (ent.moveinfo.sound_start != 0)
+ GameBase.gi.sound(ent, Defines.CHAN_NO_PHS_ADD
+ + Defines.CHAN_VOICE, ent.moveinfo.sound_start, 1,
+ Defines.ATTN_STATIC, 0);
+ ent.s.sound = ent.moveinfo.sound_middle;
+ }
+ ent.moveinfo.state = STATE_UP;
+ Move_Calc(ent, ent.moveinfo.start_origin, plat_hit_top);
+ }
+
+ private static void plat_spawn_inside_trigger(EDict ent) {
+ EDict trigger;
+ float[] tmin = {0, 0, 0}, tmax = {0, 0, 0};
+
+ //
+ // middle trigger
+ //
+ trigger = GameUtil.G_Spawn();
+ trigger.touch = Touch_Plat_Center;
+ trigger.movetype = Defines.MOVETYPE_NONE;
+ trigger.solid = Defines.SOLID_TRIGGER;
+ trigger.enemy = ent;
+
+ tmin[0] = ent.mins[0] + 25;
+ tmin[1] = ent.mins[1] + 25;
+ tmin[2] = ent.mins[2];
+
+ tmax[0] = ent.maxs[0] - 25;
+ tmax[1] = ent.maxs[1] - 25;
+ tmax[2] = ent.maxs[2] + 8;
+
+ tmin[2] = tmax[2] - (ent.pos1[2] - ent.pos2[2] + GameBase.st.lip);
+
+ if ((ent.spawnflags & PLAT_LOW_TRIGGER) != 0)
+ tmax[2] = tmin[2] + 8;
+
+ if (tmax[0] - tmin[0] <= 0) {
+ tmin[0] = (ent.mins[0] + ent.maxs[0]) * 0.5f;
+ tmax[0] = tmin[0] + 1;
+ }
+ if (tmax[1] - tmin[1] <= 0) {
+ tmin[1] = (ent.mins[1] + ent.maxs[1]) * 0.5f;
+ tmax[1] = tmin[1] + 1;
+ }
+
+ Math3D.vectorCopy(tmin, trigger.mins);
+ Math3D.vectorCopy(tmax, trigger.maxs);
+
+ GameBase.gi.linkentity(trigger);
+ }
+
+ /**
+ * QUAKED func_plat (0 .5 .8) ? PLAT_LOW_TRIGGER speed default 150
+ * <p>
+ * Plats are always drawn in the extended position, so they will light
+ * correctly.
+ * <p>
+ * If the plat is the target of another trigger or button, it will start out
+ * disabled in the extended position until it is trigger, when it will lower
+ * and become a normal plat.
+ * <p>
+ * "speed" overrides default 200. "accel" overrides default 500 "lip"
+ * overrides default 8 pixel lip
+ * <p>
+ * If the "height" key is set, that will determine the amount the plat
+ * moves, instead of being implicitly determoveinfoned by the model's
+ * height.
+ * <p>
+ * Set "sounds" to one of the following: 1) base fast 2) chain slow
+ */
+ static void SP_func_plat(EDict ent) {
+ Math3D.vectorClear(ent.s.angles);
+ ent.solid = Defines.SOLID_BSP;
+ ent.movetype = Defines.MOVETYPE_PUSH;
+
+ GameBase.gi.setmodel(ent, ent.model);
+
+ ent.blocked = plat_blocked;
+
+ if (0 == ent.speed)
+ ent.speed = 20;
+ else
+ ent.speed *= 0.1;
+
+ if (ent.accel == 0)
+ ent.accel = 5;
+ else
+ ent.accel *= 0.1;
+
+ if (ent.decel == 0)
+ ent.decel = 5;
+ else
+ ent.decel *= 0.1;
+
+ if (ent.dmg == 0)
+ ent.dmg = 2;
+
+ if (GameBase.st.lip == 0)
+ GameBase.st.lip = 8;
+
+ // pos1 is the top position, pos2 is the bottom
+ Math3D.vectorCopy(ent.s.origin, ent.pos1);
+ Math3D.vectorCopy(ent.s.origin, ent.pos2);
+ if (GameBase.st.height != 0)
+ ent.pos2[2] -= GameBase.st.height;
+ else
+ ent.pos2[2] -= (ent.maxs[2] - ent.mins[2]) - GameBase.st.lip;
+
+ ent.use = Use_Plat;
+
+ plat_spawn_inside_trigger(ent); // the "start moving" trigger
+
+ if (ent.targetname != null) {
+ ent.moveinfo.state = STATE_UP;
+ } else {
+ Math3D.vectorCopy(ent.pos2, ent.s.origin);
+ GameBase.gi.linkentity(ent);
+ ent.moveinfo.state = STATE_BOTTOM;
+ }
+
+ ent.moveinfo.speed = ent.speed;
+ ent.moveinfo.accel = ent.accel;
+ ent.moveinfo.decel = ent.decel;
+ ent.moveinfo.wait = ent.wait;
+ Math3D.vectorCopy(ent.pos1, ent.moveinfo.start_origin);
+ Math3D.vectorCopy(ent.s.angles, ent.moveinfo.start_angles);
+ Math3D.vectorCopy(ent.pos2, ent.moveinfo.end_origin);
+ Math3D.vectorCopy(ent.s.angles, ent.moveinfo.end_angles);
+
+ ent.moveinfo.sound_start = GameBase.gi.soundindex("plats/pt1_strt.wav");
+ ent.moveinfo.sound_middle = GameBase.gi.soundindex("plats/pt1_mid.wav");
+ ent.moveinfo.sound_end = GameBase.gi.soundindex("plats/pt1_end.wav");
+ }
+
+ /**
+ * QUAKED func_door (0 .5 .8) ? START_OPEN x CRUSHER NOMONSTER ANIMATED
+ * TOGGLE ANIMATED_FAST TOGGLE wait in both the start and end states for a
+ * trigger event. START_OPEN the door to moves to its destination when
+ * spawned, and operate in reverse. It is used to temporarily or permanently
+ * close off an area when triggered (not useful for touch or takedamage
+ * doors). NOMONSTER monsters will not trigger this door
+ * <p>
+ * "message" is printed when the door is touched if it is a trigger door and
+ * it hasn't been fired yet "angle" determines the opening direction
+ * "targetname" if set, no touch field will be spawned and a remote button
+ * or trigger field activates the door. "health" if set, door must be shot
+ * open "speed" movement speed (100 default) "wait" wait before returning (3
+ * default, -1 = never return) "lip" lip remaining at end of move (8
+ * default) "dmg" damage to inflict when blocked (2 default) "sounds" 1)
+ * silent 2) light 3) medium 4) heavy
+ */
+
+ static void door_use_areaportals(EDict self) {
+ EDict t = null;
+
+ if (self.target == null)
+ return;
+
+ EdictIterator edit = null;
+
+ while ((edit = GameBase
+ .G_Find(edit, GameBase.findByTarget, self.target)) != null) {
+ t = edit.o;
+ if (Lib.Q_stricmp(t.classname, "func_areaportal") == 0) {
+ GameBase.gi.SetAreaPortalState(t.style, false);
+ }
+ }
+ }
+
+
+ private static void train_resume(EDict self) {
+ EDict ent;
+ float[] dest = {0, 0, 0};
+
+ ent = self.target_ent;
+
+ Math3D.vectorSubtract(ent.s.origin, self.mins, dest);
+ self.moveinfo.state = STATE_TOP;
+ Math3D.vectorCopy(self.s.origin, self.moveinfo.start_origin);
+ Math3D.vectorCopy(dest, self.moveinfo.end_origin);
+ Move_Calc(self, dest, train_wait);
+ self.spawnflags |= TRAIN_START_ON;
+
+ }
+
+ static void SP_func_train(EDict self) {
+ self.movetype = Defines.MOVETYPE_PUSH;
+
+ Math3D.vectorClear(self.s.angles);
+ self.blocked = train_blocked;
+ if ((self.spawnflags & TRAIN_BLOCK_STOPS) != 0)
+ self.dmg = 0;
+ else {
+ if (0 == self.dmg)
+ self.dmg = 100;
+ }
+ self.solid = Defines.SOLID_BSP;
+ GameBase.gi.setmodel(self, self.model);
+
+ if (GameBase.st.noise != null)
+ self.moveinfo.sound_middle = GameBase.gi
+ .soundindex(GameBase.st.noise);
+
+ if (0 == self.speed)
+ self.speed = 100;
+
+ self.moveinfo.speed = self.speed;
+ self.moveinfo.accel = self.moveinfo.decel = self.moveinfo.speed;
+
+ self.use = train_use;
+
+ GameBase.gi.linkentity(self);
+
+ if (self.target != null) {
+ // start trains on the second frame, to make sure their targets have
+ // had
+ // a chance to spawn
+ self.nextthink = GameBase.level.time + Defines.FRAMETIME;
+ self.think = func_train_find;
+ } else {
+ GameBase.gi.dprintf("func_train without a target at "
+ + Lib.vtos(self.absmin) + "\n");
+ }
+ }
+
+ static void SP_func_timer(EDict self) {
+ if (0 == self.wait)
+ self.wait = 1.0f;
+
+ self.use = func_timer_use;
+ self.think = func_timer_think;
+
+ if (self.random >= self.wait) {
+ self.random = self.wait - Defines.FRAMETIME;
+ GameBase.gi.dprintf("func_timer at " + Lib.vtos(self.s.origin)
+ + " has random >= wait\n");
+ }
+
+ if ((self.spawnflags & 1) != 0) {
+ self.nextthink = GameBase.level.time + 1.0f + GameBase.st.pausetime
+ + self.delay + self.wait + Lib.crandom() * self.random;
+ self.activator = self;
+ }
+
+ self.svflags = Defines.SVF_NOCLIENT;
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.game;
+
+import lwjake2.Defines;
+import lwjake2.Globals;
+import lwjake2.util.Lib;
+import lwjake2.util.Math3D;
+
+import java.util.Calendar;
+
+public class GameMisc {
+ public static final int START_OFF = 1;
+ public static final EntUseAdapter Use_Areaportal = new EntUseAdapter() {
+ public String getID() {
+ return "use_areaportal";
+ }
+
+ public void use(EDict ent, EDict other, EDict activator) {
+ ent.count ^= 1; // toggle state
+ // gi.dprintf ("portalstate: %i = %i\n", ent.style, ent.count);
+ GameBase.gi.SetAreaPortalState(ent.style, ent.count != 0);
+ }
+ };
+ /**
+ * QUAKED path_corner (.5 .3 0) (-8 -8 -8) (8 8 8) TELEPORT Target: next
+ * path corner Pathtarget: gets used when an entity that has this
+ * path_corner targeted touches it
+ */
+ public static final EntTouchAdapter path_corner_touch = new EntTouchAdapter() {
+ public String getID() {
+ return "path_corner_touch";
+ }
+
+ public void touch(EDict self, EDict other, cplane_t plane,
+ csurface_t surf) {
+ float[] v = {0, 0, 0};
+ EDict next;
+
+ if (other.enemy != null)
+ return;
+
+ if (self.pathtarget != null) {
+ String savetarget;
+
+ savetarget = self.target;
+ self.target = self.pathtarget;
+ GameUtil.G_UseTargets(self, other);
+ self.target = savetarget;
+ }
+
+ if (self.target != null)
+ next = GameBase.G_PickTarget(self.target);
+ else
+ next = null;
+
+ if ((next != null) && (next.spawnflags & 1) != 0) {
+ Math3D.vectorCopy(next.s.origin, v);
+ v[2] += next.mins[2];
+ v[2] -= other.mins[2];
+ Math3D.vectorCopy(v, other.s.origin);
+ next = GameBase.G_PickTarget(next.target);
+ other.s.event = Defines.EV_OTHER_TELEPORT;
+ }
+
+
+ }
+ };
+ /*
+ * QUAKED point_combat (0.5 0.3 0) (-8 -8 -8) (8 8 8) Hold Makes this the
+ * target of a monster and it will head here when first activated before
+ * going after the activator. If hold is selected, it will stay here.
+ */
+ public static final EntTouchAdapter point_combat_touch = new EntTouchAdapter() {
+ public String getID() {
+ return "point_combat_touch";
+ }
+
+ public void touch(EDict self, EDict other, cplane_t plane,
+ csurface_t surf) {
+ EDict activator;
+
+
+ }
+ };
+ /*
+ * QUAKED viewthing (0 .5 .8) (-8 -8 -8) (8 8 8) Just for the debugging
+ * level. Don't use
+ */
+ public static final EntThinkAdapter TH_viewthing = new EntThinkAdapter() {
+ public String getID() {
+ return "th_viewthing";
+ }
+
+ public void think(EDict ent) {
+ ent.s.frame = (ent.s.frame + 1) % 7;
+ ent.nextthink = GameBase.level.time + Defines.FRAMETIME;
+ }
+ };
+ public static final EntUseAdapter light_use = new EntUseAdapter() {
+ public String getID() {
+ return "light_use";
+ }
+
+ public void use(EDict self, EDict other, EDict activator) {
+ if ((self.spawnflags & START_OFF) != 0) {
+ GameBase.gi.configstring(Defines.CS_LIGHTS + self.style, "m");
+ self.spawnflags &= ~START_OFF;
+ } else {
+ GameBase.gi.configstring(Defines.CS_LIGHTS + self.style, "a");
+ self.spawnflags |= START_OFF;
+ }
+ }
+ };
+ public static final EntThinkAdapter commander_body_think = new EntThinkAdapter() {
+ public String getID() {
+ return "commander_body_think";
+ }
+
+ public void think(EDict self) {
+ if (++self.s.frame < 24)
+ self.nextthink = GameBase.level.time + Defines.FRAMETIME;
+ else
+ self.nextthink = 0;
+
+ if (self.s.frame == 22)
+ GameBase.gi.sound(self, Defines.CHAN_BODY, GameBase.gi
+ .soundindex("tank/thud.wav"), 1, Defines.ATTN_NORM, 0);
+ }
+ };
+ public static final EntThinkAdapter func_clock_think = new EntThinkAdapter() {
+ public String getID() {
+ return "func_clock_think";
+ }
+
+ public void think(EDict self) {
+ if (null == self.enemy) {
+
+ EdictIterator es = null;
+
+ es = GameBase.G_Find(es, GameBase.findByTarget, self.target);
+ if (es != null)
+ self.enemy = es.o;
+ if (self.enemy == null)
+ return;
+ }
+
+ if ((self.spawnflags & 1) != 0) {
+ func_clock_format_countdown(self);
+ self.health++;
+ } else if ((self.spawnflags & 2) != 0) {
+ func_clock_format_countdown(self);
+ self.health--;
+ } else {
+ Calendar c = Calendar.getInstance();
+ self.message = "" + c.get(Calendar.HOUR_OF_DAY) + ":"
+ + c.get(Calendar.MINUTE) + ":" + c.get(Calendar.SECOND);
+
+ /*
+ * struct tm * ltime; time_t gmtime;
+ *
+ * time(& gmtime); ltime = localtime(& gmtime);
+ * Com_sprintf(self.message, CLOCK_MESSAGE_SIZE, "%2i:%2i:%2i",
+ * ltime.tm_hour, ltime.tm_min, ltime.tm_sec); if
+ * (self.message[3] == ' ') self.message[3] = '0'; if
+ * (self.message[6] == ' ') self.message[6] = '0';
+ */
+ }
+
+ self.enemy.message = self.message;
+ self.enemy.use.use(self.enemy, self, self);
+
+ if (((self.spawnflags & 1) != 0 && (self.health > self.wait))
+ || ((self.spawnflags & 2) != 0 && (self.health < self.wait))) {
+ if (self.pathtarget != null) {
+ String savetarget;
+ String savemessage;
+
+ savetarget = self.target;
+ savemessage = self.message;
+ self.target = self.pathtarget;
+ self.message = null;
+ GameUtil.G_UseTargets(self, self.activator);
+ self.target = savetarget;
+ self.message = savemessage;
+ }
+
+ if (0 == (self.spawnflags & 8))
+ return;
+
+ func_clock_reset(self);
+
+ if ((self.spawnflags & 4) != 0)
+ return;
+ }
+
+ self.nextthink = GameBase.level.time + 1;
+
+ }
+ };
+ public static final EntUseAdapter func_clock_use = new EntUseAdapter() {
+ public String getID() {
+ return "func_clock_use";
+ }
+
+ public void use(EDict self, EDict other, EDict activator) {
+ if (0 == (self.spawnflags & 8))
+ self.use = null;
+ if (self.activator != null)
+ return;
+ self.activator = activator;
+ self.think.think(self);
+ }
+ };
+ public static final EntThinkAdapter SP_misc_teleporter_dest = new EntThinkAdapter() {
+ public String getID() {
+ return "SP_misc_teleporter_dest";
+ }
+
+ public void think(EDict ent) {
+ GameBase.gi.setmodel(ent, "models/objects/dmspot/tris.md2");
+ ent.s.skinnum = 0;
+ ent.solid = Defines.SOLID_BBOX;
+ // ent.s.effects |= EF_FLIES;
+ Math3D.vectorSet(ent.mins, -32, -32, -24);
+ Math3D.vectorSet(ent.maxs, 32, 32, -16);
+ GameBase.gi.linkentity(ent);
+ }
+ };
+ public static final EntThinkAdapter gib_think = new EntThinkAdapter() {
+ public String getID() {
+ return "gib_think";
+ }
+
+ public void think(EDict self) {
+ self.s.frame++;
+ self.nextthink = GameBase.level.time + Defines.FRAMETIME;
+
+ if (self.s.frame == 10) {
+ self.think = GameUtil.G_FreeEdictA;
+ self.nextthink = GameBase.level.time + 8
+ + Globals.rnd.nextFloat() * 10;
+ }
+ }
+ };
+ /**
+ * QUAKED func_areaportal (0 0 0) ?
+ * <p>
+ * This is a non-visible object that divides the world into areas that are
+ * seperated when this portal is not activated. Usually enclosed in the
+ * middle of a door.
+ */
+
+ static final EntThinkAdapter SP_func_areaportal = new EntThinkAdapter() {
+ public String getID() {
+ return "sp_func_areaportal";
+ }
+
+ public void think(EDict ent) {
+ ent.use = Use_Areaportal;
+ ent.count = 0; // always start closed;
+ }
+ };
+ static final EntUseAdapter misc_blackhole_use = new EntUseAdapter() {
+ public String getID() {
+ return "misc_blavkhole_use";
+ }
+
+ public void use(EDict ent, EDict other, EDict activator) {
+ /*
+ * gi.WriteByte (svc_temp_entity); gi.WriteByte (TE_BOSSTPORT);
+ * gi.WritePosition (ent.s.origin); gi.multicast (ent.s.origin,
+ * MULTICAST_PVS);
+ */
+ GameUtil.G_FreeEdict(ent);
+ }
+ };
+ // don't let field width of any clock messages change, or it
+ // could cause an overwrite after a game load
+ static final EntThinkAdapter misc_blackhole_think = new EntThinkAdapter() {
+ public String getID() {
+ return "misc_blackhole_think";
+ }
+
+ public void think(EDict self) {
+
+ if (++self.s.frame < 19)
+ self.nextthink = GameBase.level.time + Defines.FRAMETIME;
+ else {
+ self.s.frame = 0;
+ self.nextthink = GameBase.level.time + Defines.FRAMETIME;
+ }
+ }
+ };
+ static final EntThinkAdapter misc_eastertank_think = new EntThinkAdapter() {
+ public String getID() {
+ return "misc_eastertank_think";
+ }
+
+ public void think(EDict self) {
+ if (++self.s.frame < 293)
+ self.nextthink = GameBase.level.time + Defines.FRAMETIME;
+ else {
+ self.s.frame = 254;
+ self.nextthink = GameBase.level.time + Defines.FRAMETIME;
+ }
+ }
+ };
+ static final EntThinkAdapter misc_easterchick_think = new EntThinkAdapter() {
+ public String getID() {
+ return "misc_easterchick_think";
+ }
+
+ public void think(EDict self) {
+ if (++self.s.frame < 247)
+ self.nextthink = GameBase.level.time + Defines.FRAMETIME;
+ else {
+ self.s.frame = 208;
+ self.nextthink = GameBase.level.time + Defines.FRAMETIME;
+ }
+ }
+ };
+ /*
+ * QUAKED misc_easterchick2 (1 .5 0) (-32 -32 0) (32 32 32)
+ */
+ static final EntThinkAdapter misc_easterchick2_think = new EntThinkAdapter() {
+ public String getID() {
+ return "misc_easterchick2_think";
+ }
+
+ public void think(EDict self) {
+ if (++self.s.frame < 287)
+ self.nextthink = GameBase.level.time + Defines.FRAMETIME;
+ else {
+ self.s.frame = 248;
+ self.nextthink = GameBase.level.time + Defines.FRAMETIME;
+ }
+ }
+ };
+
+
+ //=====================================================
+ /*
+ * QUAKED misc_banner (1 .5 0) (-4 -4 -4) (4 4 4) The origin is the bottom
+ * of the banner. The banner is 128 tall.
+ */
+ static final EntThinkAdapter misc_banner_think = new EntThinkAdapter() {
+ public String getID() {
+ return "misc_banner_think";
+ }
+
+ public void think(EDict ent) {
+ ent.s.frame = (ent.s.frame + 1) % 16;
+ ent.nextthink = GameBase.level.time + Defines.FRAMETIME;
+ }
+ };
+ static final EntUseAdapter misc_viper_use = new EntUseAdapter() {
+ public String getID() {
+ return "misc_viper_use";
+ }
+
+ public void use(EDict self, EDict other, EDict activator) {
+ self.svflags &= ~Defines.SVF_NOCLIENT;
+ self.use = GameFunc.train_use;
+ GameFunc.train_use.use(self, other, activator);
+ }
+ };
+ /*
+ * QUAKED misc_viper_bomb (1 0 0) (-8 -8 -8) (8 8 8) "dmg" how much boom
+ * should the bomb make?
+ */
+ static final EntTouchAdapter misc_viper_bomb_touch = new EntTouchAdapter() {
+ public String getID() {
+ return "misc_viper_bomb_touch";
+ }
+
+ public void touch(EDict self, EDict other, cplane_t plane,
+ csurface_t surf) {
+ GameUtil.G_UseTargets(self, self.activator);
+
+ self.s.origin[2] = self.absmin[2] + 1;
+ BecomeExplosion2(self);
+ }
+ };
+ static final EntThinkAdapter misc_viper_bomb_prethink = new EntThinkAdapter() {
+ public String getID() {
+ return "misc_viper_bomb_prethink";
+ }
+
+ public void think(EDict self) {
+
+ float[] v = {0, 0, 0};
+ float diff;
+
+ self.groundentity = null;
+
+ diff = self.timestamp - GameBase.level.time;
+ if (diff < -1.0)
+ diff = -1.0f;
+
+ Math3D.vectorScale(self.moveinfo.dir, 1.0f + diff, v);
+ v[2] = diff;
+
+ diff = self.s.angles[2];
+ Math3D.vecToAngles(v, self.s.angles);
+ self.s.angles[2] = diff + 10;
+
+ }
+ };
+ static final EntUseAdapter misc_viper_bomb_use = new EntUseAdapter() {
+ public String getID() {
+ return "misc_viper_bomb_use";
+ }
+
+ public void use(EDict self, EDict other, EDict activator) {
+ EDict viper = null;
+
+ self.solid = Defines.SOLID_BBOX;
+ self.svflags &= ~Defines.SVF_NOCLIENT;
+ self.s.effects |= Defines.EF_ROCKET;
+ self.use = null;
+ self.movetype = Defines.MOVETYPE_TOSS;
+ self.prethink = misc_viper_bomb_prethink;
+ self.touch = misc_viper_bomb_touch;
+ self.activator = activator;
+
+ EdictIterator es = null;
+
+ es = GameBase.G_Find(es, GameBase.findByClass, "misc_viper");
+ if (es != null)
+ viper = es.o;
+
+ Math3D.vectorScale(viper.moveinfo.dir, viper.moveinfo.speed,
+ self.velocity);
+
+ self.timestamp = GameBase.level.time;
+ Math3D.vectorCopy(viper.moveinfo.dir, self.moveinfo.dir);
+ }
+ };
+ static final EntUseAdapter misc_strogg_ship_use = new EntUseAdapter() {
+ public String getID() {
+ return "misc_strogg_ship_use";
+ }
+
+ public void use(EDict self, EDict other, EDict activator) {
+ self.svflags &= ~Defines.SVF_NOCLIENT;
+ self.use = GameFunc.train_use;
+ GameFunc.train_use.use(self, other, activator);
+ }
+ };
+ /*
+ * QUAKED misc_satellite_dish (1 .5 0) (-64 -64 0) (64 64 128)
+ */
+ static final EntThinkAdapter misc_satellite_dish_think = new EntThinkAdapter() {
+ public String getID() {
+ return "misc_satellite_dish_think";
+ }
+
+ public void think(EDict self) {
+ self.s.frame++;
+ if (self.s.frame < 38)
+ self.nextthink = GameBase.level.time + Defines.FRAMETIME;
+ }
+ };
+ static final EntUseAdapter misc_satellite_dish_use = new EntUseAdapter() {
+ public String getID() {
+ return "misc_satellite_dish_use";
+ }
+
+ public void use(EDict self, EDict other, EDict activator) {
+ self.s.frame = 0;
+ self.think = misc_satellite_dish_think;
+ self.nextthink = GameBase.level.time + Defines.FRAMETIME;
+ }
+ };
+ static final EntUseAdapter target_string_use = new EntUseAdapter() {
+ public String getID() {
+ return "target_string_use";
+ }
+
+ public void use(EDict self, EDict other, EDict activator) {
+ EDict e;
+ int n, l;
+ char c;
+
+ l = self.message.length();
+ for (e = self.teammaster; e != null; e = e.teamchain) {
+ if (e.count == 0)
+ continue;
+ n = e.count - 1;
+ if (n >= l) {
+ e.s.frame = 12;
+ continue;
+ }
+
+ c = self.message.charAt(n);
+ if (c >= '0' && c <= '9')
+ e.s.frame = c - '0';
+ else if (c == '-')
+ e.s.frame = 10;
+ else if (c == ':')
+ e.s.frame = 11;
+ else
+ e.s.frame = 12;
+ }
+ }
+ };
+ public static EntUseAdapter func_explosive_use = new EntUseAdapter() {
+ public String getID() {
+ return "func_explosive_use";
+ }
+
+ public void use(EDict self, EDict other, EDict activator) {
+ }
+ };
+ public static EntTouchAdapter barrel_touch = new EntTouchAdapter() {
+ public String getID() {
+ return "barrel_touch";
+ }
+
+ public void touch(EDict self, EDict other, cplane_t plane,
+ csurface_t surf) {
+ float ratio;
+ float[] v = {0, 0, 0};
+
+ if ((null == other.groundentity) || (other.groundentity == self))
+ return;
+
+ ratio = (float) other.mass / (float) self.mass;
+ Math3D.vectorSubtract(self.s.origin, other.s.origin, v);
+ }
+ };
+ public static EntUseAdapter commander_body_use = new EntUseAdapter() {
+ public String getID() {
+ return "commander_body_use";
+ }
+
+ public void use(EDict self, EDict other, EDict activator) {
+ self.think = commander_body_think;
+ self.nextthink = GameBase.level.time + Defines.FRAMETIME;
+ GameBase.gi.sound(self, Defines.CHAN_BODY, GameBase.gi
+ .soundindex("tank/pain.wav"), 1, Defines.ATTN_NORM, 0);
+ }
+ };
+ public static EntThinkAdapter commander_body_drop = new EntThinkAdapter() {
+ public String getID() {
+ return "commander_body_group";
+ }
+
+ public void think(EDict self) {
+ self.movetype = Defines.MOVETYPE_TOSS;
+ self.s.origin[2] += 2;
+ }
+ };
+ public static EntTouchAdapter gib_touch = new EntTouchAdapter() {
+ public String getID() {
+ return "gib_touch";
+ }
+
+ public void touch(EDict self, EDict other, cplane_t plane,
+ csurface_t surf) {
+ float[] normal_angles = {0, 0, 0}, right = {0, 0, 0};
+
+ if (null == self.groundentity)
+ return;
+
+ self.touch = null;
+
+ if (plane != null) {
+ GameBase.gi.sound(self, Defines.CHAN_VOICE, GameBase.gi
+ .soundindex("misc/fhit3.wav"), 1, Defines.ATTN_NORM, 0);
+
+ Math3D.vecToAngles(plane.normal, normal_angles);
+ Math3D.angleVectors(normal_angles, null, right, null);
+ Math3D.vecToAngles(right, self.s.angles);
+
+ if (self.s.modelindex == GameBase.sm_meat_index) {
+ self.s.frame++;
+ self.think = gib_think;
+ self.nextthink = GameBase.level.time + Defines.FRAMETIME;
+ }
+ }
+ }
+ };
+
+ public static void SP_path_corner(EDict self) {
+ if (self.targetname == null) {
+ GameBase.gi.dprintf("path_corner with no targetname at "
+ + Lib.vtos(self.s.origin) + "\n");
+ GameUtil.G_FreeEdict(self);
+ return;
+ }
+
+ self.solid = Defines.SOLID_TRIGGER;
+ self.touch = path_corner_touch;
+ Math3D.vectorSet(self.mins, -8, -8, -8);
+ Math3D.vectorSet(self.maxs, 8, 8, 8);
+ self.svflags |= Defines.SVF_NOCLIENT;
+ GameBase.gi.linkentity(self);
+ }
+
+ public static void SP_point_combat(EDict self) {
+ if (GameBase.deathmatch.value != 0) {
+ GameUtil.G_FreeEdict(self);
+ return;
+ }
+ self.solid = Defines.SOLID_TRIGGER;
+ self.touch = point_combat_touch;
+ Math3D.vectorSet(self.mins, -8, -8, -16);
+ Math3D.vectorSet(self.maxs, 8, 8, 16);
+ self.svflags = Defines.SVF_NOCLIENT;
+ GameBase.gi.linkentity(self);
+ }
+
+ /*
+ * QUAKED light (0 1 0) (-8 -8 -8) (8 8 8) START_OFF Non-displayed light.
+ * Default light value is 300. Default style is 0. If targeted, will toggle
+ * between on and off. Default _cone value is 10 (used to set size of light
+ * for spotlights)
+ */
+
+ public static void SP_viewthing(EDict ent) {
+ GameBase.gi.dprintf("viewthing spawned\n");
+
+ ent.movetype = Defines.MOVETYPE_NONE;
+ ent.solid = Defines.SOLID_BBOX;
+ ent.s.renderfx = Defines.RF_FRAMELERP;
+ Math3D.vectorSet(ent.mins, -16, -16, -24);
+ Math3D.vectorSet(ent.maxs, 16, 16, 32);
+ ent.s.modelindex = GameBase.gi
+ .modelindex("models/objects/banner/tris.md2");
+ GameBase.gi.linkentity(ent);
+ ent.nextthink = GameBase.level.time + 0.5f;
+ ent.think = TH_viewthing;
+ }
+
+ /*
+ * QUAKED info_null (0 0.5 0) (-4 -4 -4) (4 4 4) Used as a positional target
+ * for spotlights, etc.
+ */
+ public static void SP_info_null(EDict self) {
+ GameUtil.G_FreeEdict(self);
+ }
+
+ /*
+ * QUAKED func_wall (0 .5 .8) ? TRIGGER_SPAWN TOGGLE START_ON ANIMATED
+ * ANIMATED_FAST This is just a solid wall if not inhibited
+ *
+ * TRIGGER_SPAWN the wall will not be present until triggered it will then
+ * blink in to existance; it will kill anything that was in it's way
+ *
+ * TOGGLE only valid for TRIGGER_SPAWN walls this allows the wall to be
+ * turned on and off
+ *
+ * START_ON only valid for TRIGGER_SPAWN walls the wall will initially be
+ * present
+ */
+
+ /*
+ * QUAKED info_notnull (0 0.5 0) (-4 -4 -4) (4 4 4) Used as a positional
+ * target for lightning.
+ */
+ public static void SP_info_notnull(EDict self) {
+ Math3D.vectorCopy(self.s.origin, self.absmin);
+ Math3D.vectorCopy(self.s.origin, self.absmax);
+ }
+
+ public static void SP_light(EDict self) {
+ // no targeted lights in deathmatch, because they cause global messages
+ if (null == self.targetname || GameBase.deathmatch.value != 0) {
+ GameUtil.G_FreeEdict(self);
+ return;
+ }
+
+ if (self.style >= 32) {
+ self.use = light_use;
+ if ((self.spawnflags & START_OFF) != 0)
+ GameBase.gi.configstring(Defines.CS_LIGHTS + self.style, "a");
+ else
+ GameBase.gi.configstring(Defines.CS_LIGHTS + self.style, "m");
+ }
+ }
+
+
+ public static void SP_misc_blackhole(EDict ent) {
+ ent.movetype = Defines.MOVETYPE_NONE;
+ ent.solid = Defines.SOLID_NOT;
+ Math3D.vectorSet(ent.mins, -64, -64, 0);
+ Math3D.vectorSet(ent.maxs, 64, 64, 8);
+ ent.s.modelindex = GameBase.gi
+ .modelindex("models/objects/black/tris.md2");
+ ent.s.renderfx = Defines.RF_TRANSLUCENT;
+ ent.use = misc_blackhole_use;
+ ent.think = misc_blackhole_think;
+ ent.nextthink = GameBase.level.time + 2 * Defines.FRAMETIME;
+ GameBase.gi.linkentity(ent);
+ }
+
+ /*
+ * QUAKED misc_explobox (0 .5 .8) (-16 -16 0) (16 16 40) Large exploding
+ * box. You can override its mass (100), health (80), and dmg (150).
+ */
+
+ public static void SP_misc_eastertank(EDict ent) {
+ ent.movetype = Defines.MOVETYPE_NONE;
+ ent.solid = Defines.SOLID_BBOX;
+ Math3D.vectorSet(ent.mins, -32, -32, -16);
+ Math3D.vectorSet(ent.maxs, 32, 32, 32);
+ ent.s.modelindex = GameBase.gi
+ .modelindex("models/monsters/tank/tris.md2");
+ ent.s.frame = 254;
+ ent.think = misc_eastertank_think;
+ ent.nextthink = GameBase.level.time + 2 * Defines.FRAMETIME;
+ GameBase.gi.linkentity(ent);
+ }
+
+ public static void SP_misc_easterchick(EDict ent) {
+ ent.movetype = Defines.MOVETYPE_NONE;
+ ent.solid = Defines.SOLID_BBOX;
+ Math3D.vectorSet(ent.mins, -32, -32, 0);
+ Math3D.vectorSet(ent.maxs, 32, 32, 32);
+ ent.s.modelindex = GameBase.gi
+ .modelindex("models/monsters/bitch/tris.md2");
+ ent.s.frame = 208;
+ ent.think = misc_easterchick_think;
+ ent.nextthink = GameBase.level.time + 2 * Defines.FRAMETIME;
+ GameBase.gi.linkentity(ent);
+ }
+
+ public static void SP_misc_easterchick2(EDict ent) {
+ ent.movetype = Defines.MOVETYPE_NONE;
+ ent.solid = Defines.SOLID_BBOX;
+ Math3D.vectorSet(ent.mins, -32, -32, 0);
+ Math3D.vectorSet(ent.maxs, 32, 32, 32);
+ ent.s.modelindex = GameBase.gi
+ .modelindex("models/monsters/bitch/tris.md2");
+ ent.s.frame = 248;
+ ent.think = misc_easterchick2_think;
+ ent.nextthink = GameBase.level.time + 2 * Defines.FRAMETIME;
+ GameBase.gi.linkentity(ent);
+ }
+
+ //
+ // miscellaneous specialty items
+ //
+
+
+ public static void SP_misc_banner(EDict ent) {
+ ent.movetype = Defines.MOVETYPE_NONE;
+ ent.solid = Defines.SOLID_NOT;
+ ent.s.modelindex = GameBase.gi
+ .modelindex("models/objects/banner/tris.md2");
+ ent.s.frame = Lib.rand() % 16;
+ GameBase.gi.linkentity(ent);
+
+ ent.think = misc_banner_think;
+ ent.nextthink = GameBase.level.time + Defines.FRAMETIME;
+ }
+
+
+
+ /*
+ * QUAKED misc_easterchick (1 .5 0) (-32 -32 0) (32 32 32)
+ */
+
+ public static void SP_misc_viper(EDict ent) {
+ if (null == ent.target) {
+ GameBase.gi.dprintf("misc_viper without a target at "
+ + Lib.vtos(ent.absmin) + "\n");
+ GameUtil.G_FreeEdict(ent);
+ return;
+ }
+
+ if (0 == ent.speed)
+ ent.speed = 300;
+
+ ent.movetype = Defines.MOVETYPE_PUSH;
+ ent.solid = Defines.SOLID_NOT;
+ ent.s.modelindex = GameBase.gi
+ .modelindex("models/ships/viper/tris.md2");
+ Math3D.vectorSet(ent.mins, -16, -16, 0);
+ Math3D.vectorSet(ent.maxs, 16, 16, 32);
+
+ ent.think = GameFunc.func_train_find;
+ ent.nextthink = GameBase.level.time + Defines.FRAMETIME;
+ ent.use = misc_viper_use;
+ ent.svflags |= Defines.SVF_NOCLIENT;
+ ent.moveinfo.accel = ent.moveinfo.decel = ent.moveinfo.speed = ent.speed;
+
+ GameBase.gi.linkentity(ent);
+ }
+
+ /*
+ * QUAKED misc_bigviper (1 .5 0) (-176 -120 -24) (176 120 72) This is a
+ * large stationary viper as seen in Paul's intro
+ */
+ public static void SP_misc_bigviper(EDict ent) {
+ ent.movetype = Defines.MOVETYPE_NONE;
+ ent.solid = Defines.SOLID_BBOX;
+ Math3D.vectorSet(ent.mins, -176, -120, -24);
+ Math3D.vectorSet(ent.maxs, 176, 120, 72);
+ ent.s.modelindex = GameBase.gi
+ .modelindex("models/ships/bigviper/tris.md2");
+ GameBase.gi.linkentity(ent);
+ }
+
+ /*
+ * QUAKED monster_commander_body (1 .5 0) (-32 -32 0) (32 32 48) Not really
+ * a monster, this is the Tank Commander's decapitated body. There should be
+ * a item_commander_head that has this as it's target.
+ */
+
+ public static void SP_misc_viper_bomb(EDict self) {
+ self.movetype = Defines.MOVETYPE_NONE;
+ self.solid = Defines.SOLID_NOT;
+ Math3D.vectorSet(self.mins, -8, -8, -8);
+ Math3D.vectorSet(self.maxs, 8, 8, 8);
+
+ self.s.modelindex = GameBase.gi
+ .modelindex("models/objects/bomb/tris.md2");
+
+ if (self.dmg == 0)
+ self.dmg = 1000;
+
+ self.use = misc_viper_bomb_use;
+ self.svflags |= Defines.SVF_NOCLIENT;
+
+ GameBase.gi.linkentity(self);
+ }
+
+ public static void SP_misc_strogg_ship(EDict ent) {
+ if (null == ent.target) {
+ GameBase.gi.dprintf(ent.classname + " without a target at "
+ + Lib.vtos(ent.absmin) + "\n");
+ GameUtil.G_FreeEdict(ent);
+ return;
+ }
+
+ if (0 == ent.speed)
+ ent.speed = 300;
+
+ ent.movetype = Defines.MOVETYPE_PUSH;
+ ent.solid = Defines.SOLID_NOT;
+ ent.s.modelindex = GameBase.gi
+ .modelindex("models/ships/strogg1/tris.md2");
+ Math3D.vectorSet(ent.mins, -16, -16, 0);
+ Math3D.vectorSet(ent.maxs, 16, 16, 32);
+
+ ent.think = GameFunc.func_train_find;
+ ent.nextthink = GameBase.level.time + Defines.FRAMETIME;
+ ent.use = misc_strogg_ship_use;
+ ent.svflags |= Defines.SVF_NOCLIENT;
+ ent.moveinfo.accel = ent.moveinfo.decel = ent.moveinfo.speed = ent.speed;
+
+ GameBase.gi.linkentity(ent);
+ }
+
+ public static void SP_misc_satellite_dish(EDict ent) {
+ ent.movetype = Defines.MOVETYPE_NONE;
+ ent.solid = Defines.SOLID_BBOX;
+ Math3D.vectorSet(ent.mins, -64, -64, 0);
+ Math3D.vectorSet(ent.maxs, 64, 64, 128);
+ ent.s.modelindex = GameBase.gi
+ .modelindex("models/objects/satellite/tris.md2");
+ ent.use = misc_satellite_dish_use;
+ GameBase.gi.linkentity(ent);
+ }
+
+ /*
+ * QUAKED light_mine1 (0 1 0) (-2 -2 -12) (2 2 12)
+ */
+ public static void SP_light_mine1(EDict ent) {
+ ent.movetype = Defines.MOVETYPE_NONE;
+ ent.solid = Defines.SOLID_BBOX;
+ ent.s.modelindex = GameBase.gi
+ .modelindex("models/objects/minelite/light1/tris.md2");
+ GameBase.gi.linkentity(ent);
+ }
+
+ /*
+ * QUAKED light_mine2 (0 1 0) (-2 -2 -12) (2 2 12)
+ */
+ public static void SP_light_mine2(EDict ent) {
+ ent.movetype = Defines.MOVETYPE_NONE;
+ ent.solid = Defines.SOLID_BBOX;
+ ent.s.modelindex = GameBase.gi
+ .modelindex("models/objects/minelite/light2/tris.md2");
+ GameBase.gi.linkentity(ent);
+ }
+
+ /*
+ * QUAKED misc_viper (1 .5 0) (-16 -16 0) (16 16 32) This is the Viper for
+ * the flyby bombing. It is trigger_spawned, so you must have something use
+ * it for it to show up. There must be a path for it to follow once it is
+ * activated.
+ *
+ * "speed" How fast the Viper should fly
+ */
+
+
+ public static void SP_target_character(EDict self) {
+ self.movetype = Defines.MOVETYPE_PUSH;
+ GameBase.gi.setmodel(self, self.model);
+ self.solid = Defines.SOLID_BSP;
+ self.s.frame = 12;
+ GameBase.gi.linkentity(self);
+ }
+
+ /*
+ * QUAKED misc_strogg_ship (1 .5 0) (-16 -16 0) (16 16 32) This is a Storgg
+ * ship for the flybys. It is trigger_spawned, so you must have something
+ * use it for it to show up. There must be a path for it to follow once it
+ * is activated.
+ *
+ * "speed" How fast it should fly
+ */
+
+ public static void SP_target_string(EDict self) {
+ if (self.message == null)
+ self.message = "";
+ self.use = target_string_use;
+ }
+
+ public static void func_clock_reset(EDict self) {
+ self.activator = null;
+ if ((self.spawnflags & 1) != 0) {
+ self.health = 0;
+ self.wait = self.count;
+ } else if ((self.spawnflags & 2) != 0) {
+ self.health = self.count;
+ self.wait = 0;
+ }
+ }
+
+ public static void func_clock_format_countdown(EDict self) {
+ if (self.style == 0) {
+ self.message = "" + self.health;
+ //Com_sprintf(self.message, CLOCK_MESSAGE_SIZE, "%2i",
+ // self.health);
+ return;
+ }
+
+ if (self.style == 1) {
+ self.message = "" + self.health / 60 + ":" + self.health % 60;
+ //Com_sprintf(self.message, CLOCK_MESSAGE_SIZE, "%2i:%2i",
+ // self.health / 60, self.health % 60);
+ /*
+ * if (self.message.charAt(3) == ' ') self.message.charAt(3) = '0';
+ */
+ return;
+ }
+
+ if (self.style == 2) {
+ self.message = "" + self.health / 3600 + ":"
+ + (self.health - (self.health / 3600) * 3600) / 60 + ":"
+ + self.health % 60;
+ /*
+ * Com_sprintf( self.message, CLOCK_MESSAGE_SIZE, "%2i:%2i:%2i",
+ * self.health / 3600, (self.health - (self.health / 3600) * 3600) /
+ * 60, self.health % 60); if (self.message[3] == ' ')
+ * self.message[3] = '0'; if (self.message[6] == ' ')
+ * self.message[6] = '0';
+ */
+ }
+ }
+
+ /*
+ * QUAKED target_string (0 0 1) (-8 -8 -8) (8 8 8)
+ */
+
+ public static void SP_func_clock(EDict self) {
+ if (self.target == null) {
+ GameBase.gi.dprintf(self.classname + " with no target at "
+ + Lib.vtos(self.s.origin) + "\n");
+ GameUtil.G_FreeEdict(self);
+ return;
+ }
+
+ if ((self.spawnflags & 2) != 0 && (0 == self.count)) {
+ GameBase.gi.dprintf(self.classname + " with no count at "
+ + Lib.vtos(self.s.origin) + "\n");
+ GameUtil.G_FreeEdict(self);
+ return;
+ }
+
+ if ((self.spawnflags & 1) != 0 && (0 == self.count))
+ self.count = 60 * 60;
+
+ func_clock_reset(self);
+
+ self.message = "";
+
+ self.think = func_clock_think;
+
+ if ((self.spawnflags & 4) != 0)
+ self.use = func_clock_use;
+ else
+ self.nextthink = GameBase.level.time + 1;
+ }
+
+ /*
+ * QUAKED func_clock (0 0 1) (-8 -8 -8) (8 8 8) TIMER_UP TIMER_DOWN
+ * START_OFF MULTI_USE target a target_string with this
+ *
+ * The default is to be a time of day clock
+ *
+ * TIMER_UP and TIMER_DOWN run for "count" seconds and the fire "pathtarget"
+ * If START_OFF, this entity must be used before it starts
+ *
+ * "style" 0 "xx" 1 "xx:xx" 2 "xx:xx:xx"
+ */
+
+
+ public static void BecomeExplosion1(EDict self) {
+ GameBase.gi.WriteByte(Defines.svc_temp_entity);
+ GameBase.gi.WriteByte(Defines.TE_EXPLOSION1);
+ GameBase.gi.WritePosition(self.s.origin);
+ GameBase.gi.multicast(self.s.origin, Defines.MULTICAST_PVS);
+
+ GameUtil.G_FreeEdict(self);
+ }
+
+ //=================================================================================
+
+ public static void BecomeExplosion2(EDict self) {
+ GameBase.gi.WriteByte(Defines.svc_temp_entity);
+ GameBase.gi.WriteByte(Defines.TE_EXPLOSION2);
+ GameBase.gi.WritePosition(self.s.origin);
+ GameBase.gi.multicast(self.s.origin, Defines.MULTICAST_PVS);
+
+ GameUtil.G_FreeEdict(self);
+ }
+
+
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.game;
+
+import lwjake2.Defines;
+import lwjake2.qcommon.Com;
+import lwjake2.util.Lib;
+
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.nio.ByteBuffer;
+import java.util.StringTokenizer;
+
+public class GameSVCmds {
+
+ private static final int MAX_IPFILTERS = 1024;
+ private static final GameSVCmds.ipfilter_t[] ipfilters = new GameSVCmds.ipfilter_t[MAX_IPFILTERS];
+ private static int numipfilters;
+
+ static {
+ for (int n = 0; n < GameSVCmds.MAX_IPFILTERS; n++)
+ GameSVCmds.ipfilters[n] = new ipfilter_t();
+ }
+
+ private static void Svcmd_Test_f() {
+ GameBase.gi.cprintf(null, Defines.PRINT_HIGH, "Svcmd_Test_f()\n");
+ }
+
+ /**
+ * StringToFilter.
+ */
+ private static boolean StringToFilter(String s, GameSVCmds.ipfilter_t f) {
+
+ byte b[] = {0, 0, 0, 0};
+ byte m[] = {0, 0, 0, 0};
+
+ try {
+ StringTokenizer tk = new StringTokenizer(s, ". ");
+
+ for (int n = 0; n < 4; n++) {
+ b[n] = (byte) Lib.atoi(tk.nextToken());
+ if (b[n] != 0)
+ m[n] = -1;
+ }
+
+ f.mask = ByteBuffer.wrap(m).getInt();
+ f.compare = ByteBuffer.wrap(b).getInt();
+ } catch (Exception e) {
+ GameBase.gi.cprintf(null, Defines.PRINT_HIGH,
+ "Bad filter address: " + s + "\n");
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * SV_FilterPacket.
+ */
+ static boolean SV_FilterPacket(String from) {
+ int i;
+ int in;
+ int m[] = {0, 0, 0, 0};
+
+ int p = 0;
+ char c;
+
+ i = 0;
+
+ while (p < from.length() && i < 4) {
+ m[i] = 0;
+
+ c = from.charAt(p);
+ while (c >= '0' && c <= '9') {
+ m[i] = m[i] * 10 + (c - '0');
+ c = from.charAt(p++);
+ }
+ if (p == from.length() || c == ':')
+ break;
+
+ i++;
+ p++;
+ }
+
+ in = (m[0] & 0xff) | ((m[1] & 0xff) << 8) | ((m[2] & 0xff) << 16)
+ | ((m[3] & 0xff) << 24);
+
+ for (i = 0; i < numipfilters; i++)
+ if ((in & ipfilters[i].mask) == ipfilters[i].compare)
+ return ((int) GameBase.filterban.value) != 0;
+
+ return (1 - GameBase.filterban.value) != 0;
+ }
+
+ /**
+ * SV_AddIP_f.
+ */
+ private static void SVCmd_AddIP_f() {
+ int i;
+
+ if (GameBase.gi.argc() < 3) {
+ GameBase.gi.cprintf(null, Defines.PRINT_HIGH,
+ "Usage: addip <ip-mask>\n");
+ return;
+ }
+
+ for (i = 0; i < numipfilters; i++)
+ if (ipfilters[i].compare == 0xffffffff)
+ break; // free spot
+ if (i == numipfilters) {
+ if (numipfilters == MAX_IPFILTERS) {
+ GameBase.gi.cprintf(null, Defines.PRINT_HIGH,
+ "IP filter list is full\n");
+ return;
+ }
+ numipfilters++;
+ }
+
+ if (!StringToFilter(GameBase.gi.argv(2), ipfilters[i]))
+ ipfilters[i].compare = 0xffffffff;
+ }
+
+ /**
+ * SV_RemoveIP_f.
+ */
+ private static void SVCmd_RemoveIP_f() {
+ GameSVCmds.ipfilter_t f = new GameSVCmds.ipfilter_t();
+ int i, j;
+
+ if (GameBase.gi.argc() < 3) {
+ GameBase.gi.cprintf(null, Defines.PRINT_HIGH,
+ "Usage: sv removeip <ip-mask>\n");
+ return;
+ }
+
+ if (!StringToFilter(GameBase.gi.argv(2), f))
+ return;
+
+ for (i = 0; i < numipfilters; i++)
+ if (ipfilters[i].mask == f.mask
+ && ipfilters[i].compare == f.compare) {
+ for (j = i + 1; j < numipfilters; j++)
+ ipfilters[j - 1] = ipfilters[j];
+ numipfilters--;
+ GameBase.gi.cprintf(null, Defines.PRINT_HIGH, "Removed.\n");
+ return;
+ }
+ GameBase.gi.cprintf(null, Defines.PRINT_HIGH, "Didn't find "
+ + GameBase.gi.argv(2) + ".\n");
+ }
+
+ /**
+ * SV_ListIP_f.
+ */
+ private static void SVCmd_ListIP_f() {
+ int i;
+ byte b[];
+
+ GameBase.gi.cprintf(null, Defines.PRINT_HIGH, "Filter list:\n");
+ for (i = 0; i < numipfilters; i++) {
+ b = Lib.getIntBytes(ipfilters[i].compare);
+ GameBase.gi
+ .cprintf(null, Defines.PRINT_HIGH, (b[0] & 0xff) + "."
+ + (b[1] & 0xff) + "." + (b[2] & 0xff) + "."
+ + (b[3] & 0xff));
+ }
+ }
+
+ /**
+ * SV_WriteIP_f.
+ */
+ private static void SVCmd_WriteIP_f() {
+ RandomAccessFile f;
+ //char name[MAX_OSPATH];
+ String name;
+ byte b[];
+
+ int i;
+ CvarT game;
+
+ game = GameBase.gi.cvar("game", "", 0);
+
+ if (game.string == null)
+ name = Defines.GAMEVERSION + "/listip.cfg";
+ else
+ name = game.string + "/listip.cfg";
+
+ GameBase.gi.cprintf(null, Defines.PRINT_HIGH, "Writing " + name + ".\n");
+
+ f = Lib.fopen(name, "rw");
+ if (f == null) {
+ GameBase.gi.cprintf(null, Defines.PRINT_HIGH, "Couldn't open "
+ + name + "\n");
+ return;
+ }
+
+ try {
+ f.writeChars("set filterban " + (int) GameBase.filterban.value
+ + "\n");
+
+ for (i = 0; i < numipfilters; i++) {
+ b = Lib.getIntBytes(ipfilters[i].compare);
+ f.writeChars("sv addip " + (b[0] & 0xff) + "." + (b[1] & 0xff)
+ + "." + (b[2] & 0xff) + "." + (b[3] & 0xff) + "\n");
+ }
+
+ } catch (IOException e) {
+ Com.Printf("IOError in SVCmd_WriteIP_f:" + e);
+ }
+
+ Lib.fclose(f);
+ }
+
+ /**
+ * ServerCommand
+ * <p>
+ * ServerCommand will be called when an "sv" command is issued. The game can
+ * issue gi.argc() / gi.argv() commands to get the rest of the parameters
+ */
+ public static void ServerCommand() {
+ String cmd;
+
+ cmd = GameBase.gi.argv(1);
+ if (Lib.Q_stricmp(cmd, "test") == 0)
+ Svcmd_Test_f();
+ else if (Lib.Q_stricmp(cmd, "addip") == 0)
+ SVCmd_AddIP_f();
+ else if (Lib.Q_stricmp(cmd, "removeip") == 0)
+ SVCmd_RemoveIP_f();
+ else if (Lib.Q_stricmp(cmd, "listip") == 0)
+ SVCmd_ListIP_f();
+ else if (Lib.Q_stricmp(cmd, "writeip") == 0)
+ SVCmd_WriteIP_f();
+ else
+ GameBase.gi.cprintf(null, Defines.PRINT_HIGH,
+ "Unknown server command \"" + cmd + "\"\n");
+ }
+
+ /**
+ * PACKET FILTERING
+ * <p>
+ * <p>
+ * You can add or remove addresses from the filter list with:
+ * <p>
+ * addip <ip> removeip <ip>
+ * <p>
+ * The ip address is specified in dot format, and any unspecified digits
+ * will match any value, so you can specify an entire class C network with
+ * "addip 192.246.40".
+ * <p>
+ * Removeip will only remove an address specified exactly the same way. You
+ * cannot addip a subnet, then removeip a single host.
+ * <p>
+ * listip Prints the current list of filters.
+ * <p>
+ * writeip Dumps "addip <ip>" commands to listip.cfg so it can be execed at
+ * a later date. The filter lists are not saved and restored by default,
+ * because I beleive it would cause too much confusion.
+ * <p>
+ * filterban <0 or 1>
+ * <p>
+ * If 1 (the default), then ip addresses matching the current list will be
+ * prohibited from entering the game. This is the default setting.
+ * <p>
+ * If 0, then only addresses matching the list will be allowed. This lets
+ * you easily set up a private game, or a game that only allows players from
+ * your local network.
+ */
+
+ static class ipfilter_t {
+ int mask;
+
+ int compare;
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.game;
+
+import lwjake2.Defines;
+import lwjake2.Globals;
+import lwjake2.util.Lib;
+import lwjake2.util.QuakeFile;
+
+public class GameSave {
+
+ private static void CreateEdicts() {
+ GameBase.g_edicts = new EDict[GameBase.game.maxentities];
+ for (int i = 0; i < GameBase.game.maxentities; i++)
+ GameBase.g_edicts[i] = new EDict(i);
+ }
+
+ private static void CreateClients() {
+ GameBase.game.clients = new Client[GameBase.game.maxclients];
+ for (int i = 0; i < GameBase.game.maxclients; i++)
+ GameBase.game.clients[i] = new Client(i);
+
+ }
+
+ /**
+ * InitGame
+ * <p>
+ * This will be called when the dll is first loaded, which only happens when
+ * a new game is started or a save game is loaded.
+ */
+ public static void InitGame() {
+ GameBase.gi.dprintf("==== InitGame ====\n");
+
+
+ GameBase.gun_x = GameBase.gi.cvar("gun_x", "0", 0);
+ GameBase.gun_y = GameBase.gi.cvar("gun_y", "0", 0);
+ GameBase.gun_z = GameBase.gi.cvar("gun_z", "0", 0);
+
+ //FIXME: sv_ prefix are wrong names for these variables
+ GameBase.sv_rollspeed = GameBase.gi.cvar("sv_rollspeed", "200", 0);
+ GameBase.sv_rollangle = GameBase.gi.cvar("sv_rollangle", "2", 0);
+ GameBase.sv_maxvelocity = GameBase.gi.cvar("sv_maxvelocity", "2000", 0);
+ GameBase.sv_gravity = GameBase.gi.cvar("sv_gravity", "800", 0);
+
+ // noset vars
+ Globals.dedicated = GameBase.gi.cvar("dedicated", "0",
+ Defines.CVAR_NOSET);
+
+ // latched vars
+ GameBase.sv_cheats = GameBase.gi.cvar("cheats", "0",
+ Defines.CVAR_SERVERINFO | Defines.CVAR_LATCH);
+ GameBase.gi.cvar("gamename", Defines.GAMEVERSION,
+ Defines.CVAR_SERVERINFO | Defines.CVAR_LATCH);
+ GameBase.gi.cvar("gamedate", Globals.__DATE__, Defines.CVAR_SERVERINFO
+ | Defines.CVAR_LATCH);
+
+ GameBase.maxclients = GameBase.gi.cvar("maxclients", "4",
+ Defines.CVAR_SERVERINFO | Defines.CVAR_LATCH);
+ GameBase.maxspectators = GameBase.gi.cvar("maxspectators", "4",
+ Defines.CVAR_SERVERINFO);
+ GameBase.deathmatch = GameBase.gi.cvar("deathmatch", "0",
+ Defines.CVAR_LATCH);
+ GameBase.coop = GameBase.gi.cvar("coop", "0", Defines.CVAR_LATCH);
+ GameBase.maxentities = GameBase.gi.cvar("maxentities", "1024",
+ Defines.CVAR_LATCH);
+
+ // change anytime vars
+ GameBase.dmflags = GameBase.gi.cvar("dmflags", "0",
+ Defines.CVAR_SERVERINFO);
+ GameBase.fraglimit = GameBase.gi.cvar("fraglimit", "0",
+ Defines.CVAR_SERVERINFO);
+ GameBase.timelimit = GameBase.gi.cvar("timelimit", "0",
+ Defines.CVAR_SERVERINFO);
+ GameBase.password = GameBase.gi.cvar("password", "",
+ Defines.CVAR_USERINFO);
+ GameBase.spectator_password = GameBase.gi.cvar("spectator_password",
+ "", Defines.CVAR_USERINFO);
+ GameBase.needpass = GameBase.gi.cvar("needpass", "0",
+ Defines.CVAR_SERVERINFO);
+ GameBase.filterban = GameBase.gi.cvar("filterban", "1", 0);
+
+ GameBase.g_select_empty = GameBase.gi.cvar("g_select_empty", "0",
+ Defines.CVAR_ARCHIVE);
+
+ GameBase.run_pitch = GameBase.gi.cvar("run_pitch", "0.002", 0);
+ GameBase.run_roll = GameBase.gi.cvar("run_roll", "0.005", 0);
+ GameBase.bob_up = GameBase.gi.cvar("bob_up", "0.005", 0);
+ GameBase.bob_pitch = GameBase.gi.cvar("bob_pitch", "0.002", 0);
+ GameBase.bob_roll = GameBase.gi.cvar("bob_roll", "0.002", 0);
+
+ // flood control
+ GameBase.flood_msgs = GameBase.gi.cvar("flood_msgs", "4", 0);
+ GameBase.flood_persecond = GameBase.gi.cvar("flood_persecond", "4", 0);
+ GameBase.flood_waitdelay = GameBase.gi.cvar("flood_waitdelay", "10", 0);
+
+ // dm map list
+ GameBase.sv_maplist = GameBase.gi.cvar("sv_maplist", "", 0);
+
+ GameBase.game.helpmessage1 = "";
+ GameBase.game.helpmessage2 = "";
+
+ // initialize all entities for this game
+ GameBase.game.maxentities = (int) GameBase.maxentities.value;
+ CreateEdicts();
+
+ // initialize all clients for this game
+ GameBase.game.maxclients = (int) GameBase.maxclients.value;
+
+ CreateClients();
+
+ GameBase.num_edicts = GameBase.game.maxclients + 1;
+ }
+
+ /**
+ * WriteGame
+ * <p>
+ * This will be called whenever the game goes to a new level, and when the
+ * user explicitly saves the game.
+ * <p>
+ * Game information include cross level data, like multi level triggers,
+ * help computer info, and all client states.
+ * <p>
+ * A single player death will automatically restore from the last save
+ * position.
+ */
+ public static void WriteGame(String filename, boolean autosave) {
+ try {
+ QuakeFile f;
+
+ if (!autosave)
+ PlayerClient.SaveClientData();
+
+ f = new QuakeFile(filename, "rw");
+
+ GameBase.game.autosaved = autosave;
+ GameBase.game.write(f);
+ GameBase.game.autosaved = false;
+
+ for (int i = 0; i < GameBase.game.maxclients; i++)
+ GameBase.game.clients[i].write(f);
+
+ Lib.fclose(f);
+ } catch (Exception e) {
+ e.printStackTrace();
+ GameBase.gi.error("Couldn't write to " + filename);
+ }
+ }
+
+ public static void ReadGame(String filename) {
+
+ QuakeFile f = null;
+
+ try {
+
+ f = new QuakeFile(filename, "r");
+ CreateEdicts();
+
+ GameBase.game.load(f);
+
+ for (int i = 0; i < GameBase.game.maxclients; i++) {
+ GameBase.game.clients[i] = new Client(i);
+ GameBase.game.clients[i].read(f);
+ }
+
+ f.close();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * WriteLevel
+ */
+ public static void WriteLevel(String filename) {
+ try {
+ int i;
+ EDict ent;
+ QuakeFile f;
+
+ f = new QuakeFile(filename, "rw");
+
+ // write out level_locals_t
+ GameBase.level.write(f);
+
+ // write out all the entities
+ for (i = 0; i < GameBase.num_edicts; i++) {
+ ent = GameBase.g_edicts[i];
+ if (!ent.inuse)
+ continue;
+ f.writeInt(i);
+ ent.write(f);
+ }
+
+ i = -1;
+ f.writeInt(-1);
+
+ f.close();
+ } catch (Exception e) {
+ e.printStackTrace();
+ GameBase.gi.error("Couldn't open for writing: " + filename);
+ }
+ }
+
+ /**
+ * ReadLevel
+ * <p>
+ * SpawnEntities will allready have been called on the level the same way it
+ * was when the level was saved.
+ * <p>
+ * That is necessary to get the baselines set up identically.
+ * <p>
+ * The server will have cleared all of the world links before calling
+ * ReadLevel.
+ * <p>
+ * No clients are connected yet.
+ */
+ public static void ReadLevel(String filename) {
+ try {
+ EDict ent;
+
+ QuakeFile f = new QuakeFile(filename, "r");
+
+ // wipe all the entities
+ CreateEdicts();
+
+ GameBase.num_edicts = (int) GameBase.maxclients.value + 1;
+
+ // load the level locals
+ GameBase.level.read(f);
+
+ // load all the entities
+ while (true) {
+ int entnum = f.readInt();
+ if (entnum == -1)
+ break;
+
+ if (entnum >= GameBase.num_edicts)
+ GameBase.num_edicts = entnum + 1;
+
+ ent = GameBase.g_edicts[entnum];
+ ent.read(f);
+ ent.cleararealinks();
+ GameBase.gi.linkentity(ent);
+ }
+
+ Lib.fclose(f);
+
+ // mark all clients as unconnected
+ for (int i = 0; i < GameBase.maxclients.value; i++) {
+ ent = GameBase.g_edicts[i + 1];
+ ent.client = GameBase.game.clients[i];
+ ent.client.pers.connected = false;
+ }
+
+ // do any load time things at this point
+ for (int i = 0; i < GameBase.num_edicts; i++) {
+ ent = GameBase.g_edicts[i];
+
+ if (!ent.inuse)
+ continue;
+
+ // fire any cross-level triggers
+ if (ent.classname != null)
+ if (Lib.strcmp(ent.classname, "target_crosslevel_target") == 0)
+ ent.nextthink = GameBase.level.time + ent.delay;
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ GameBase.gi.error("Couldn't read level file " + filename);
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.game;
+
+import lwjake2.Defines;
+import lwjake2.qcommon.Com;
+import lwjake2.util.Lib;
+
+public class GameSpawn {
+
+
+ static final EntThinkAdapter SP_info_player_start = new EntThinkAdapter() {
+ public String getID() {
+ return "SP_info_player_start";
+ }
+
+ public void think(EDict ent) {
+ PlayerClient.SP_info_player_start(ent);
+ }
+ };
+
+ static final EntThinkAdapter SP_info_player_deathmatch = new EntThinkAdapter() {
+ public String getID() {
+ return "SP_info_player_deathmatch";
+ }
+
+ public void think(EDict ent) {
+ PlayerClient.SP_info_player_deathmatch(ent);
+ }
+ };
+
+ static final EntThinkAdapter SP_info_player_coop = new EntThinkAdapter() {
+ public String getID() {
+ return "SP_info_player_coop";
+ }
+
+ public void think(EDict ent) {
+ PlayerClient.SP_info_player_coop(ent);
+ }
+ };
+
+ static final EntThinkAdapter SP_info_player_intermission = new EntThinkAdapter() {
+ public String getID() {
+ return "SP_info_player_intermission";
+ }
+
+ public void think(EDict ent) {
+ PlayerClient.SP_info_player_intermission();
+ }
+ };
+
+ static final EntThinkAdapter SP_func_plat = new EntThinkAdapter() {
+ public String getID() {
+ return "SP_func_plat";
+ }
+
+ public void think(EDict ent) {
+ GameFunc.SP_func_plat(ent);
+ }
+ };
+
+
+ static final EntThinkAdapter SP_func_train = new EntThinkAdapter() {
+ public String getID() {
+ return "SP_func_train";
+ }
+
+ public void think(EDict ent) {
+ GameFunc.SP_func_train(ent);
+ }
+ };
+
+ static final EntThinkAdapter SP_func_clock = new EntThinkAdapter() {
+ public String getID() {
+ return "SP_func_clock";
+ }
+
+ public void think(EDict ent) {
+ GameMisc.SP_func_clock(ent);
+ }
+ };
+ static final String single_statusbar = "yb -24 " // health
+ + "xv 0 " + "hnum " + "xv 50 " + "pic 0 " // ammo
+ + "if 2 " + " xv 100 " + " anum " + " xv 150 " + " pic 2 "
+ + "endif " // armor
+ + "if 4 " + " xv 200 " + " rnum " + " xv 250 " + " pic 4 "
+ + "endif " // selected item
+ + "if 6 " + " xv 296 " + " pic 6 " + "endif " + "yb -50 " // picked
+ // up
+ // item
+ + "if 7 " + " xv 0 " + " pic 7 " + " xv 26 " + " yb -42 "
+ + " stat_string 8 " + " yb -50 " + "endif "
+ // timer
+ + "if 9 " + " xv 262 " + " num 2 10 " + " xv 296 " + " pic 9 "
+ + "endif "
+ // help / weapon icon
+ + "if 11 " + " xv 148 " + " pic 11 " + "endif ";
+ static final String dm_statusbar = "yb -24 " // health
+ + "xv 0 " + "hnum " + "xv 50 " + "pic 0 " // ammo
+ + "if 2 " + " xv 100 " + " anum " + " xv 150 " + " pic 2 "
+ + "endif " // armor
+ + "if 4 " + " xv 200 " + " rnum " + " xv 250 " + " pic 4 "
+ + "endif " // selected item
+ + "if 6 " + " xv 296 " + " pic 6 " + "endif " + "yb -50 " // picked
+ // up
+ // item
+ + "if 7 " + " xv 0 " + " pic 7 " + " xv 26 " + " yb -42 "
+ + " stat_string 8 " + " yb -50 " + "endif "
+ // timer
+ + "if 9 " + " xv 246 " + " num 2 10 " + " xv 296 " + " pic 9 "
+ + "endif "
+ // help / weapon icon
+ + "if 11 " + " xv 148 " + " pic 11 " + "endif " // frags
+ + "xr -50 " + "yt 2 " + "num 3 14 " // spectator
+ + "if 17 " + "xv 0 " + "yb -58 " + "string2 \"SPECTATOR MODE\" "
+ + "endif " // chase camera
+ + "if 16 " + "xv 0 " + "yb -68 " + "string \"Chasing\" " + "xv 64 "
+ + "stat_string 16 " + "endif ";
+ /**
+ * QUAKED worldspawn (0 0 0) ?
+ * <p>
+ * Only used for the world. "sky" environment map name "skyaxis" vector axis
+ * for rotating sky "skyrotate" speed of rotation in degrees/second "sounds"
+ * music cd track number "gravity" 800 is default gravity "message" text to
+ * print at user logon
+ */
+
+ static final EntThinkAdapter SP_worldspawn = new EntThinkAdapter() {
+ public String getID() {
+ return "SP_worldspawn";
+ }
+
+ public void think(EDict ent) {
+ ent.movetype = Defines.MOVETYPE_PUSH;
+ ent.solid = Defines.SOLID_BSP;
+ ent.inuse = true;
+ // since the world doesn't use G_Spawn()
+ ent.s.modelindex = 1;
+ // world model is always index 1
+ //---------------
+ // reserve some spots for dead player bodies for coop / deathmatch
+ PlayerClient.InitBodyQue();
+ // set configstrings for items
+ if (GameBase.st.nextmap != null)
+ GameBase.level.nextmap = GameBase.st.nextmap;
+ // make some data visible to the server
+ if (ent.message != null && ent.message.length() > 0) {
+ GameBase.gi.configstring(Defines.CS_NAME, ent.message);
+ GameBase.level.level_name = ent.message;
+ } else
+ GameBase.level.level_name = GameBase.level.mapname;
+ if (GameBase.st.sky != null && GameBase.st.sky.length() > 0)
+ GameBase.gi.configstring(Defines.CS_SKY, GameBase.st.sky);
+ else
+ GameBase.gi.configstring(Defines.CS_SKY, "unit1_");
+ GameBase.gi.configstring(Defines.CS_SKYROTATE, ""
+ + GameBase.st.skyrotate);
+ GameBase.gi.configstring(Defines.CS_SKYAXIS, Lib
+ .vtos(GameBase.st.skyaxis));
+ GameBase.gi.configstring(Defines.CS_CDTRACK, "" + ent.sounds);
+ GameBase.gi.configstring(Defines.CS_MAXCLIENTS, ""
+ + (int) (GameBase.maxclients.value));
+ // status bar program
+ if (GameBase.deathmatch.value != 0)
+ GameBase.gi.configstring(Defines.CS_STATUSBAR, "" + dm_statusbar);
+ else
+ GameBase.gi.configstring(Defines.CS_STATUSBAR, "" + single_statusbar);
+ //---------------
+ // help icon for statusbar
+ GameBase.gi.imageindex("i_help");
+ GameBase.level.pic_health = GameBase.gi.imageindex("i_health");
+ GameBase.gi.imageindex("help");
+ GameBase.gi.imageindex("field_3");
+ if ("".equals(GameBase.st.gravity))
+ GameBase.gi.cvar_set("sv_gravity", "800");
+ else
+ GameBase.gi.cvar_set("sv_gravity", GameBase.st.gravity);
+ GameBase.snd_fry = GameBase.gi.soundindex("player/fry.wav");
+ // gibs
+ GameBase.gi.soundindex("items/respawn1.wav");
+ // drowning damage
+ GameBase.gi.soundindex("*gurp2.wav");
+ GameBase.gi.soundindex("*jump1.wav");
+ // THIS ORDER MUST MATCH THE DEFINES IN g_local.h
+ //-------------------
+ GameBase.gi.soundindex("player/gasp1.wav");
+ // gasping for air
+ GameBase.gi.soundindex("player/gasp2.wav");
+ // bonus item pickup
+ GameBase.gi.soundindex("world/land.wav");
+ // landing thud
+ GameBase.gi.soundindex("misc/h2ohit1.wav");
+ // landing splash
+ //
+ // Setup light animation tables. 'a' is total darkness, 'z' is
+ // doublebright.
+ //
+ // 0 normal
+ GameBase.gi.configstring(Defines.CS_LIGHTS, "m");
+ // 1 FLICKER (first variety)
+ GameBase.gi.configstring(Defines.CS_LIGHTS + 1,
+ "mmnmmommommnonmmonqnmmo");
+ // 2 SLOW STRONG PULSE
+ GameBase.gi.configstring(Defines.CS_LIGHTS + 2,
+ "abcdefghijklmnopqrstuvwxyzyxwvutsrqponmlkjihgfedcba");
+ // 3 CANDLE (first variety)
+ GameBase.gi.configstring(Defines.CS_LIGHTS + 3,
+ "mmmmmaaaaammmmmaaaaaabcdefgabcdefg");
+ // 4 FAST STROBE
+ GameBase.gi.configstring(Defines.CS_LIGHTS + 4, "mamamamamama");
+ // 5 GENTLE PULSE 1
+ GameBase.gi.configstring(Defines.CS_LIGHTS + 5,
+ "jklmnopqrstuvwxyzyxwvutsrqponmlkj");
+ // 6 FLICKER (second variety)
+ GameBase.gi
+ .configstring(Defines.CS_LIGHTS + 6, "nmonqnmomnmomomno");
+ // 7 CANDLE (second variety)
+ GameBase.gi.configstring(Defines.CS_LIGHTS + 7,
+ "mmmaaaabcdefgmmmmaaaammmaamm");
+ // 8 CANDLE (third variety)
+ GameBase.gi.configstring(Defines.CS_LIGHTS + 8,
+ "mmmaaammmaaammmabcdefaaaammmmabcdefmmmaaaa");
+ // 9 SLOW STROBE (fourth variety)
+ GameBase.gi.configstring(Defines.CS_LIGHTS + 9, "aaaaaaaazzzzzzzz");
+ // 10 FLUORESCENT FLICKER
+ GameBase.gi.configstring(Defines.CS_LIGHTS + 10,
+ "mmamammmmammamamaaamammma");
+ // 11 SLOW PULSE NOT FADE TO BLACK
+ GameBase.gi.configstring(Defines.CS_LIGHTS + 11,
+ "abcdefghijklmnopqrrqponmlkjihgfedcba");
+ // styles 32-62 are assigned by the light program for switchable
+ // lights
+ // 63 testing
+ GameBase.gi.configstring(Defines.CS_LIGHTS + 63, "a");
+ }
+ };
+ static final spawn_t[] spawns = {
+ new spawn_t("info_player_start", SP_info_player_start),
+ new spawn_t("info_player_deathmatch", SP_info_player_deathmatch),
+ new spawn_t("info_player_coop", SP_info_player_coop),
+ new spawn_t("info_player_intermission", SP_info_player_intermission),
+ new spawn_t("func_plat", SP_func_plat),
+ new spawn_t("func_train", SP_func_train),
+ new spawn_t("func_conveyor", GameFunc.SP_func_conveyor),
+ new spawn_t("func_areaportal", GameMisc.SP_func_areaportal),
+ new spawn_t("func_clock", SP_func_clock),
+ new spawn_t("func_timer", new EntThinkAdapter() {
+ public String getID() {
+ return "SP_func_timer";
+ }
+
+ public void think(EDict ent) {
+ GameFunc.SP_func_timer(ent);
+ }
+ }),
+ new spawn_t("trigger_always", new EntThinkAdapter() {
+ public String getID() {
+ return "SP_trigger_always";
+ }
+
+ public void think(EDict ent) {
+ GameTrigger.SP_trigger_always(ent);
+ }
+ }),
+ new spawn_t("trigger_once", new EntThinkAdapter() {
+ public String getID() {
+ return "SP_trigger_once";
+ }
+
+ public void think(EDict ent) {
+ GameTrigger.SP_trigger_once(ent);
+ }
+ }),
+ new spawn_t("trigger_multiple", new EntThinkAdapter() {
+ public String getID() {
+ return "SP_trigger_multiple";
+ }
+
+ public void think(EDict ent) {
+ GameTrigger.SP_trigger_multiple(ent);
+ }
+ }),
+ new spawn_t("trigger_relay", new EntThinkAdapter() {
+ public String getID() {
+ return "SP_trigger_relay";
+ }
+
+ public void think(EDict ent) {
+ GameTrigger.SP_trigger_relay(ent);
+ }
+ }),
+ new spawn_t("trigger_push", new EntThinkAdapter() {
+ public String getID() {
+ return "SP_trigger_push";
+ }
+
+ public void think(EDict ent) {
+ GameTrigger.SP_trigger_push(ent);
+ }
+ }),
+ new spawn_t("trigger_key", new EntThinkAdapter() {
+ public String getID() {
+ return "SP_trigger_key";
+ }
+
+ public void think(EDict ent) {
+ GameTrigger.SP_trigger_key(ent);
+ }
+ }),
+ new spawn_t("trigger_counter", new EntThinkAdapter() {
+ public String getID() {
+ return "SP_trigger_counter";
+ }
+
+ public void think(EDict ent) {
+ GameTrigger.SP_trigger_counter(ent);
+ }
+ }),
+ new spawn_t("trigger_elevator", GameFunc.SP_trigger_elevator),
+ new spawn_t("trigger_gravity", new EntThinkAdapter() {
+ public String getID() {
+ return "SP_trigger_gravity";
+ }
+
+ public void think(EDict ent) {
+ GameTrigger.SP_trigger_gravity(ent);
+ }
+ }),
+ new spawn_t("trigger_monsterjump", new EntThinkAdapter() {
+ public String getID() {
+ return "SP_trigger_monsterjump";
+ }
+
+ public void think(EDict ent) {
+ GameTrigger.SP_trigger_monsterjump(ent);
+ }
+ }),
+ new spawn_t("target_temp_entity", new EntThinkAdapter() {
+ public String getID() {
+ return "SP_target_temp_entity";
+ }
+
+ public void think(EDict ent) {
+ GameTarget.SP_target_temp_entity(ent);
+ }
+ }),
+ new spawn_t("target_speaker", new EntThinkAdapter() {
+ public String getID() {
+ return "SP_target_speaker";
+ }
+
+ public void think(EDict ent) {
+ GameTarget.SP_target_speaker(ent);
+ }
+ }),
+ new spawn_t("target_explosion", new EntThinkAdapter() {
+ public String getID() {
+ return "SP_target_explosion";
+ }
+
+ public void think(EDict ent) {
+ GameTarget.SP_target_explosion(ent);
+ }
+ }),
+ new spawn_t("target_changelevel", new EntThinkAdapter() {
+ public String getID() {
+ return "SP_target_changelevel";
+ }
+
+ public void think(EDict ent) {
+ GameTarget.SP_target_changelevel(ent);
+ }
+ }),
+ new spawn_t("target_secret", new EntThinkAdapter() {
+ public String getID() {
+ return "SP_target_secret";
+ }
+
+ public void think(EDict ent) {
+ GameTarget.SP_target_secret(ent);
+ }
+ }),
+ new spawn_t("target_goal", new EntThinkAdapter() {
+ public String getID() {
+ return "SP_target_goal";
+ }
+
+ public void think(EDict ent) {
+ GameTarget.SP_target_goal(ent);
+ }
+ }),
+ new spawn_t("target_splash", new EntThinkAdapter() {
+ public String getID() {
+ return "SP_target_splash";
+ }
+
+ public void think(EDict ent) {
+ GameTarget.SP_target_splash(ent);
+ }
+ }),
+ new spawn_t("target_blaster", new EntThinkAdapter() {
+ public String getID() {
+ return "SP_target_blaster";
+ }
+
+ public void think(EDict ent) {
+ GameTarget.SP_target_blaster(ent);
+ }
+ }),
+ new spawn_t("target_crosslevel_trigger", new EntThinkAdapter() {
+ public String getID() {
+ return "SP_target_crosslevel_trigger";
+ }
+
+ public void think(EDict ent) {
+ GameTarget.SP_target_crosslevel_trigger(ent);
+ }
+ }),
+ new spawn_t("target_crosslevel_target", new EntThinkAdapter() {
+ public String getID() {
+ return "SP_target_crosslevel_target";
+ }
+
+ public void think(EDict ent) {
+ GameTarget.SP_target_crosslevel_target(ent);
+ }
+ }),
+ new spawn_t("target_laser", new EntThinkAdapter() {
+ public String getID() {
+ return "SP_target_laser";
+ }
+
+ public void think(EDict ent) {
+ GameTarget.SP_target_laser(ent);
+ }
+ }),
+ new spawn_t("target_help", new EntThinkAdapter() {
+ public String getID() {
+ return "SP_target_help";
+ }
+
+ public void think(EDict ent) {
+ GameTarget.SP_target_help(ent);
+ }
+ }),
+ new spawn_t("target_lightramp", new EntThinkAdapter() {
+ public String getID() {
+ return "SP_target_lightramp";
+ }
+
+ public void think(EDict ent) {
+ GameTarget.SP_target_lightramp(ent);
+ }
+ }),
+ new spawn_t("target_earthquake", new EntThinkAdapter() {
+ public String getID() {
+ return "SP_target_earthquake";
+ }
+
+ public void think(EDict ent) {
+ GameTarget.SP_target_earthquake(ent);
+ }
+ }),
+ new spawn_t("target_character", new EntThinkAdapter() {
+ public String getID() {
+ return "SP_target_character";
+ }
+
+ public void think(EDict ent) {
+ GameMisc.SP_target_character(ent);
+ }
+ }),
+ new spawn_t("target_string", new EntThinkAdapter() {
+ public String getID() {
+ return "SP_target_string";
+ }
+
+ public void think(EDict ent) {
+ GameMisc.SP_target_string(ent);
+ }
+ }),
+ new spawn_t("worldspawn", SP_worldspawn),
+ new spawn_t("viewthing", new EntThinkAdapter() {
+ public String getID() {
+ return "SP_viewthing";
+ }
+
+ public void think(EDict ent) {
+ GameMisc.SP_viewthing(ent);
+ }
+ }),
+ new spawn_t("light", new EntThinkAdapter() {
+ public String getID() {
+ return "SP_light";
+ }
+
+ public void think(EDict ent) {
+ GameMisc.SP_light(ent);
+ }
+ }),
+ new spawn_t("light_mine1", new EntThinkAdapter() {
+ public String getID() {
+ return "SP_light_mine1";
+ }
+
+ public void think(EDict ent) {
+ GameMisc.SP_light_mine1(ent);
+ }
+ }),
+ new spawn_t("light_mine2", new EntThinkAdapter() {
+ public String getID() {
+ return "SP_light_mine2";
+ }
+
+ public void think(EDict ent) {
+ GameMisc.SP_light_mine2(ent);
+ }
+ }),
+ new spawn_t("info_null", new EntThinkAdapter() {
+ public String getID() {
+ return "SP_info_null";
+ }
+
+ public void think(EDict ent) {
+ GameMisc.SP_info_null(ent);
+ }
+ }),
+ new spawn_t("func_group", new EntThinkAdapter() {
+ public String getID() {
+ return "SP_info_null";
+ }
+
+ public void think(EDict ent) {
+ GameMisc.SP_info_null(ent);
+ }
+ }),
+ new spawn_t("info_notnull", new EntThinkAdapter() {
+ public String getID() {
+ return "info_notnull";
+ }
+
+ public void think(EDict ent) {
+ GameMisc.SP_info_notnull(ent);
+ }
+ }),
+ new spawn_t("path_corner", new EntThinkAdapter() {
+ public String getID() {
+ return "SP_path_corner";
+ }
+
+ public void think(EDict ent) {
+ GameMisc.SP_path_corner(ent);
+ }
+ }),
+ new spawn_t("point_combat", new EntThinkAdapter() {
+ public String getID() {
+ return "SP_point_combat";
+ }
+
+ public void think(EDict ent) {
+ GameMisc.SP_point_combat(ent);
+ }
+ }),
+ new spawn_t("misc_banner", new EntThinkAdapter() {
+ public String getID() {
+ return "SP_misc_banner";
+ }
+
+ public void think(EDict ent) {
+ GameMisc.SP_misc_banner(ent);
+ }
+ }),
+ new spawn_t("misc_satellite_dish", new EntThinkAdapter() {
+ public String getID() {
+ return "SP_misc_satellite_dish";
+ }
+
+ public void think(EDict ent) {
+ GameMisc.SP_misc_satellite_dish(ent);
+ }
+ }),
+ new spawn_t("misc_viper", new EntThinkAdapter() {
+ public String getID() {
+ return "SP_misc_viper";
+ }
+
+ public void think(EDict ent) {
+ GameMisc.SP_misc_viper(ent);
+ }
+ }),
+ new spawn_t("misc_viper_bomb", new EntThinkAdapter() {
+ public String getID() {
+ return "SP_misc_viper_bomb";
+ }
+
+ public void think(EDict ent) {
+ GameMisc.SP_misc_viper_bomb(ent);
+ }
+ }),
+ new spawn_t("misc_bigviper", new EntThinkAdapter() {
+ public String getID() {
+ return "SP_misc_bigviper";
+ }
+
+ public void think(EDict ent) {
+ GameMisc.SP_misc_bigviper(ent);
+ }
+ }),
+ new spawn_t("misc_strogg_ship", new EntThinkAdapter() {
+ public String getID() {
+ return "SP_misc_strogg_ship";
+ }
+
+ public void think(EDict ent) {
+ GameMisc.SP_misc_strogg_ship(ent);
+ }
+ }),
+ new spawn_t("misc_eastertank", new EntThinkAdapter() {
+ public String getID() {
+ return "SP_misc_eastertank";
+ }
+
+ public void think(EDict ent) {
+ GameMisc.SP_misc_eastertank(ent);
+ }
+ }),
+ new spawn_t("misc_easterchick", new EntThinkAdapter() {
+ public String getID() {
+ return "SP_misc_easterchick";
+ }
+
+ public void think(EDict ent) {
+ GameMisc.SP_misc_easterchick(ent);
+ }
+ }),
+ new spawn_t("misc_easterchick2", new EntThinkAdapter() {
+ public String getID() {
+ return "SP_misc_easterchick2";
+ }
+
+ public void think(EDict ent) {
+ GameMisc.SP_misc_easterchick2(ent);
+ }
+ }), new spawn_t("turret_breach", new EntThinkAdapter() {
+ public String getID() {
+ return "SP_turret_breach";
+ }
+
+ public void think(EDict ent) {
+ }
+ }), new spawn_t("turret_base", new EntThinkAdapter() {
+ public String getID() {
+ return "SP_turret_base";
+ }
+
+ public void think(EDict ent) {
+ }
+ }), new spawn_t(null, null)};
+
+ /**
+ * ED_NewString.
+ */
+ static String ED_NewString(String string) {
+
+ int l = string.length();
+ StringBuilder newb = new StringBuilder(l);
+
+ for (int i = 0; i < l; i++) {
+ char c = string.charAt(i);
+ if (c == '\\' && i < l - 1) {
+ c = string.charAt(++i);
+ if (c == 'n')
+ newb.append('\n');
+ else
+ newb.append('\\');
+ } else
+ newb.append(c);
+ }
+
+ return newb.toString();
+ }
+
+ /**
+ * ED_ParseField
+ * <p>
+ * Takes a key/value pair and sets the binary values in an edict.
+ */
+ static void ED_ParseField(String key, String value, EDict ent) {
+
+ if (key.equals("nextmap"))
+ Com.Println("nextmap: " + value);
+ if (!GameBase.st.set(key, value))
+ if (!ent.setField(key, value))
+ GameBase.gi.dprintf("??? The key [" + key
+ + "] is not a field\n");
+
+ }
+
+ /**
+ * ED_ParseEdict
+ * <p>
+ * Parses an edict out of the given string, returning the new position ed
+ * should be a properly initialized empty edict.
+ */
+
+ static void ED_ParseEdict(Com.ParseHelp ph, EDict ent) {
+
+ boolean init;
+ String keyname;
+ String com_token;
+ init = false;
+
+ GameBase.st = new spawn_temp_t();
+ while (true) {
+
+ // parse key
+ com_token = Com.Parse(ph);
+ if (com_token.equals("}"))
+ break;
+
+ if (ph.isEof())
+ GameBase.gi.error("ED_ParseEntity: EOF without closing brace");
+
+ keyname = com_token;
+
+ // parse value
+ com_token = Com.Parse(ph);
+
+ if (ph.isEof())
+ GameBase.gi.error("ED_ParseEntity: EOF without closing brace");
+
+ if (com_token.equals("}"))
+ GameBase.gi.error("ED_ParseEntity: closing brace without data");
+
+ init = true;
+ // keynames with a leading underscore are used for utility comments,
+ // and are immediately discarded by quake
+ if (keyname.charAt(0) == '_')
+ continue;
+
+ ED_ParseField(keyname.toLowerCase(), com_token, ent);
+
+ }
+
+ if (!init) {
+ GameUtil.G_ClearEdict(ent);
+ }
+
+ }
+
+ /**
+ * G_FindTeams
+ * <p>
+ * Chain together all entities with a matching team field.
+ * <p>
+ * All but the first will have the FL_TEAMSLAVE flag set. All but the last
+ * will have the teamchain field set to the next one.
+ */
+
+ static void G_FindTeams() {
+ EDict e, e2, chain;
+ int i, j;
+ for (i = 1; i < GameBase.num_edicts; i++) {
+ e = GameBase.g_edicts[i];
+
+ if (!e.inuse)
+ continue;
+ if (e.team == null)
+ continue;
+ if ((e.flags & Defines.FL_TEAMSLAVE) != 0)
+ continue;
+ chain = e;
+ e.teammaster = e;
+
+ for (j = i + 1; j < GameBase.num_edicts; j++) {
+ e2 = GameBase.g_edicts[j];
+ if (!e2.inuse)
+ continue;
+ if (null == e2.team)
+ continue;
+ if ((e2.flags & Defines.FL_TEAMSLAVE) != 0)
+ continue;
+ if (0 == Lib.strcmp(e.team, e2.team)) {
+ chain.teamchain = e2;
+ e2.teammaster = e;
+ chain = e2;
+ e2.flags |= Defines.FL_TEAMSLAVE;
+
+ }
+ }
+ }
+ }
+
+ /**
+ * SpawnEntities
+ * <p>
+ * Creates a server's entity / program execution context by parsing textual
+ * entity definitions out of an ent file.
+ */
+
+ public static void SpawnEntities(String mapname, String entities,
+ String spawnpoint) {
+
+ Com.dprintln("SpawnEntities(), mapname=" + mapname);
+ EDict ent;
+ int inhibit;
+ String com_token;
+ int i;
+
+ PlayerClient.SaveClientData();
+
+ GameBase.level = new level_locals_t();
+ for (int n = 0; n < GameBase.game.maxentities; n++) {
+ GameBase.g_edicts[n] = new EDict(n);
+ }
+
+ GameBase.level.mapname = mapname;
+ GameBase.game.spawnpoint = spawnpoint;
+
+ // set client fields on player ents
+ for (i = 0; i < GameBase.game.maxclients; i++)
+ GameBase.g_edicts[i + 1].client = GameBase.game.clients[i];
+
+ ent = null;
+ inhibit = 0;
+
+ Com.ParseHelp ph = new Com.ParseHelp(entities);
+
+ while (true) { // parse the opening brace
+
+ com_token = Com.Parse(ph);
+ if (ph.isEof())
+ break;
+ if (!com_token.startsWith("{"))
+ GameBase.gi.error("ED_LoadFromFile: found " + com_token
+ + " when expecting {");
+
+ if (ent == null)
+ ent = GameBase.g_edicts[0];
+ else
+ ent = GameUtil.G_Spawn();
+
+ ED_ParseEdict(ph, ent);
+ Com.DPrintf("spawning ent[" + ent.index + "], classname=" +
+ ent.classname + ", flags= " + Integer.toHexString(ent.spawnflags));
+
+ // yet another map hack
+ if (0 == Lib.Q_stricmp(GameBase.level.mapname, "command")
+ && 0 == Lib.Q_stricmp(ent.classname, "trigger_once")
+ && 0 == Lib.Q_stricmp(ent.model, "*27"))
+ ent.spawnflags &= ~Defines.SPAWNFLAG_NOT_HARD;
+
+ // remove things (except the world) from different skill levels or
+ // deathmatch
+ if (ent != GameBase.g_edicts[0]) {
+ if (GameBase.deathmatch.value != 0) {
+ if ((ent.spawnflags & Defines.SPAWNFLAG_NOT_DEATHMATCH) != 0) {
+
+ Com.DPrintf("->inhibited.\n");
+ GameUtil.G_FreeEdict(ent);
+ inhibit++;
+ continue;
+ }
+ } else {
+ }
+
+ ent.spawnflags &= ~(Defines.SPAWNFLAG_NOT_EASY
+ | Defines.SPAWNFLAG_NOT_MEDIUM
+ | Defines.SPAWNFLAG_NOT_HARD
+ | Defines.SPAWNFLAG_NOT_COOP | Defines.SPAWNFLAG_NOT_DEATHMATCH);
+ }
+ ED_CallSpawn(ent);
+ Com.DPrintf("\n");
+ }
+ Com.DPrintf(inhibit + " entities inhibited.\n");
+ i = 1;
+ G_FindTeams();
+ PlayerTrail.Init();
+ }
+
+ /**
+ * ED_CallSpawn
+ * <p>
+ * Finds the spawn function for the entity and calls it.
+ */
+ public static void ED_CallSpawn(EDict ent) {
+
+ spawn_t s;
+ int i;
+ if (null == ent.classname) {
+ GameBase.gi.dprintf("ED_CallSpawn: null classname\n");
+ return;
+ } // check item spawn functions
+
+ for (i = 0; (s = spawns[i]) != null && s.name != null; i++) {
+ if (s.name.equalsIgnoreCase(ent.classname)) { // found it
+
+ if (s.spawn == null)
+ GameBase.gi.error("ED_CallSpawn: null-spawn on index=" + i);
+ s.spawn.think(ent);
+ return;
+ }
+ }
+ GameBase.gi.dprintf(ent.classname + " doesn't have a spawn function\n");
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.game;
+
+import lwjake2.Defines;
+import lwjake2.util.Lib;
+import lwjake2.util.Math3D;
+
+class GameTarget {
+
+ /**
+ * QUAKED target_temp_entity (1 0 0) (-8 -8 -8) (8 8 8) Fire an origin based
+ * temp entity event to the clients. "style" type byte
+ */
+ private static final EntUseAdapter Use_Target_Tent = new EntUseAdapter() {
+ public String getID() {
+ return "Use_Target_Tent";
+ }
+
+ public void use(EDict ent, EDict other, EDict activator) {
+ GameBase.gi.WriteByte(Defines.svc_temp_entity);
+ GameBase.gi.WriteByte(ent.style);
+ GameBase.gi.WritePosition(ent.s.origin);
+ GameBase.gi.multicast(ent.s.origin, Defines.MULTICAST_PVS);
+ }
+ };
+ /**
+ * QUAKED target_speaker (1 0 0) (-8 -8 -8) (8 8 8) looped-on looped-off
+ * reliable "noise" wav file to play "attenuation" -1 = none, send to whole
+ * level 1 = normal fighting sounds 2 = idle sound level 3 = ambient sound
+ * level "volume" 0.0 to 1.0
+ * <p>
+ * Normal sounds play each time the target is used. The reliable flag can be
+ * set for crucial voiceovers.
+ * <p>
+ * Looped sounds are always atten 3 / vol 1, and the use function toggles it
+ * on/off. Multiple identical looping sounds will just increase volume
+ * without any speed cost.
+ */
+ private static final EntUseAdapter Use_Target_Speaker = new EntUseAdapter() {
+ public String getID() {
+ return "Use_Target_Speaker";
+ }
+
+ public void use(EDict ent, EDict other, EDict activator) {
+ int chan;
+
+ if ((ent.spawnflags & 3) != 0) { // looping sound toggles
+ if (ent.s.sound != 0)
+ ent.s.sound = 0; // turn it off
+ else
+ ent.s.sound = ent.noise_index; // start it
+ } else { // normal sound
+ if ((ent.spawnflags & 4) != 0)
+ chan = Defines.CHAN_VOICE | Defines.CHAN_RELIABLE;
+ else
+ chan = Defines.CHAN_VOICE;
+ // use a positioned_sound, because this entity won't normally be
+ // sent to any clients because it is invisible
+ GameBase.gi.positioned_sound(ent.s.origin, ent, chan,
+ ent.noise_index, ent.volume, ent.attenuation, 0);
+ }
+
+ }
+ };
+ private static final EntUseAdapter Use_Target_Help = new EntUseAdapter() {
+ public String getID() {
+ return "Use_Target_Help";
+ }
+
+ public void use(EDict ent, EDict other, EDict activator) {
+
+ if ((ent.spawnflags & 1) != 0)
+ GameBase.game.helpmessage1 = ent.message;
+ else
+ GameBase.game.helpmessage2 = ent.message;
+
+ GameBase.game.helpchanged++;
+ }
+ };
+ /**
+ * QUAKED target_blaster (1 0 0) (-8 -8 -8) (8 8 8) NOTRAIL NOEFFECTS Fires
+ * a blaster bolt in the set direction when triggered.
+ * <p>
+ * dmg default is 15 speed default is 1000
+ */
+ private static final EntUseAdapter use_target_blaster = new EntUseAdapter() {
+ public String getID() {
+ return "use_target_blaster";
+ }
+
+ public void use(EDict self, EDict other, EDict activator) {
+
+ /* Wait what - flibit
+ int effect;
+
+ if ((self.spawnflags & 2) != 0)
+ effect = 0;
+ else if ((self.spawnflags & 1) != 0)
+ effect = Defines.EF_HYPERBLASTER;
+ else
+ effect = Defines.EF_BLASTER;
+ */
+
+ GameBase.gi.sound(self, Defines.CHAN_VOICE, self.noise_index, 1,
+ Defines.ATTN_NORM, 0);
+ }
+ };
+ /**
+ * QUAKED target_crosslevel_trigger (.5 .5 .5) (-8 -8 -8) (8 8 8) trigger1
+ * trigger2 trigger3 trigger4 trigger5 trigger6 trigger7 trigger8 Once this
+ * trigger is touched/used, any trigger_crosslevel_target with the same
+ * trigger number is automatically used when a level is started within the
+ * same unit. It is OK to check multiple triggers. Message, delay, target,
+ * and killtarget also work.
+ */
+ private static final EntUseAdapter trigger_crosslevel_trigger_use = new EntUseAdapter() {
+ public String getID() {
+ return "trigger_crosslevel_trigger_use";
+ }
+
+ public void use(EDict self, EDict other, EDict activator) {
+ GameBase.game.serverflags |= self.spawnflags;
+ GameUtil.G_FreeEdict(self);
+ }
+ };
+ /**
+ * QUAKED target_laser (0 .5 .8) (-8 -8 -8) (8 8 8) START_ON RED GREEN BLUE
+ * YELLOW ORANGE FAT When triggered, fires a laser. You can either set a
+ * target or a direction.
+ */
+ private static final EntThinkAdapter target_laser_think = new EntThinkAdapter() {
+ public String getID() {
+ return "target_laser_think";
+ }
+
+ public void think(EDict self) {
+
+ EDict ignore;
+ float[] start = {0, 0, 0};
+ float[] end = {0, 0, 0};
+ trace_t tr;
+ float[] point = {0, 0, 0};
+ float[] last_movedir = {0, 0, 0};
+ int count;
+
+ if ((self.spawnflags & 0x80000000) != 0)
+ count = 8;
+ else
+ count = 4;
+
+ if (self.enemy != null) {
+ Math3D.vectorCopy(self.movedir, last_movedir);
+ Math3D.vectorMA(self.enemy.absmin, 0.5f, self.enemy.size, point);
+ Math3D.vectorSubtract(point, self.s.origin, self.movedir);
+ Math3D.vectorNormalize(self.movedir);
+ if (!Math3D.vectorEquals(self.movedir, last_movedir))
+ self.spawnflags |= 0x80000000;
+ }
+
+ ignore = self;
+ Math3D.vectorCopy(self.s.origin, start);
+ Math3D.vectorMA(start, 2048, self.movedir, end);
+ while (true) {
+ tr = GameBase.gi.trace(start, null, null, end, ignore,
+ Defines.CONTENTS_SOLID | Defines.CONTENTS_MONSTER
+ | Defines.CONTENTS_DEADMONSTER);
+
+ if (tr.ent == null)
+ break;
+
+
+ // if we hit something that's not a monster or player or is
+ // immune to lasers, we're done
+ if (0 == (tr.ent.svflags & Defines.SVF_MONSTER)
+ && (null == tr.ent.client)) {
+ if ((self.spawnflags & 0x80000000) != 0) {
+ self.spawnflags &= ~0x80000000;
+ GameBase.gi.WriteByte(Defines.svc_temp_entity);
+ GameBase.gi.WriteByte(Defines.TE_LASER_SPARKS);
+ GameBase.gi.WriteByte(count);
+ GameBase.gi.WritePosition(tr.endpos);
+ GameBase.gi.WriteDir(tr.plane.normal);
+ GameBase.gi.WriteByte(self.s.skinnum);
+ GameBase.gi.multicast(tr.endpos, Defines.MULTICAST_PVS);
+ }
+ break;
+ }
+
+ ignore = tr.ent;
+ Math3D.vectorCopy(tr.endpos, start);
+ }
+
+ Math3D.vectorCopy(tr.endpos, self.s.old_origin);
+
+ self.nextthink = GameBase.level.time + Defines.FRAMETIME;
+ }
+ };
+ private static final EntUseAdapter target_laser_use = new EntUseAdapter() {
+ public String getID() {
+ return "target_laser_use";
+ }
+
+ public void use(EDict self, EDict other, EDict activator) {
+ self.activator = activator;
+ if ((self.spawnflags & 1) != 0)
+ target_laser_off(self);
+ else
+ target_laser_on(self);
+ }
+ };
+ /**
+ * QUAKED target_secret (1 0 1) (-8 -8 -8) (8 8 8) Counts a secret found.
+ * These are single use targets.
+ */
+ private static final EntUseAdapter use_target_secret = new EntUseAdapter() {
+ public String getID() {
+ return "use_target_secret";
+ }
+
+ public void use(EDict ent, EDict other, EDict activator) {
+ GameBase.gi.sound(ent, Defines.CHAN_VOICE, ent.noise_index, 1,
+ Defines.ATTN_NORM, 0);
+
+ GameBase.level.found_secrets++;
+
+ GameUtil.G_UseTargets(ent, activator);
+ GameUtil.G_FreeEdict(ent);
+ }
+ };
+ /**
+ * QUAKED target_goal (1 0 1) (-8 -8 -8) (8 8 8) Counts a goal completed.
+ * These are single use targets.
+ */
+ private static final EntUseAdapter use_target_goal = new EntUseAdapter() {
+ public String getID() {
+ return "use_target_goal";
+ }
+
+ public void use(EDict ent, EDict other, EDict activator) {
+ GameBase.gi.sound(ent, Defines.CHAN_VOICE, ent.noise_index, 1,
+ Defines.ATTN_NORM, 0);
+
+ GameBase.level.found_goals++;
+
+ if (GameBase.level.found_goals == GameBase.level.total_goals)
+ GameBase.gi.configstring(Defines.CS_CDTRACK, "0");
+
+ GameUtil.G_UseTargets(ent, activator);
+ GameUtil.G_FreeEdict(ent);
+ }
+ };
+ /**
+ * QUAKED target_explosion (1 0 0) (-8 -8 -8) (8 8 8) Spawns an explosion
+ * temporary entity when used.
+ * <p>
+ * "delay" wait this long before going off "dmg" how much radius damage
+ * should be done, defaults to 0
+ */
+ private static final EntThinkAdapter target_explosion_explode = new EntThinkAdapter() {
+ public String getID() {
+ return "target_explosion_explode";
+ }
+
+ public void think(EDict self) {
+
+ float save;
+
+ GameBase.gi.WriteByte(Defines.svc_temp_entity);
+ GameBase.gi.WriteByte(Defines.TE_EXPLOSION1);
+ GameBase.gi.WritePosition(self.s.origin);
+ GameBase.gi.multicast(self.s.origin, Defines.MULTICAST_PHS);
+
+ save = self.delay;
+ self.delay = 0;
+ GameUtil.G_UseTargets(self, self.activator);
+ self.delay = save;
+ }
+ };
+ private static final EntUseAdapter use_target_explosion = new EntUseAdapter() {
+ public String getID() {
+ return "use_target_explosion";
+ }
+
+ public void use(EDict self, EDict other, EDict activator) {
+ self.activator = activator;
+
+ if (0 == self.delay) {
+ target_explosion_explode.think(self);
+ return;
+ }
+
+ self.think = target_explosion_explode;
+ self.nextthink = GameBase.level.time + self.delay;
+ }
+ };
+ /**
+ * QUAKED target_changelevel (1 0 0) (-8 -8 -8) (8 8 8) Changes level to
+ * "map" when fired
+ */
+ private static final EntUseAdapter use_target_changelevel = new EntUseAdapter() {
+ public String getID() {
+ return "use_target_changelevel";
+ }
+
+ public void use(EDict self, EDict other, EDict activator) {
+ if (GameBase.level.intermissiontime != 0)
+ return; // already activated
+
+ if (0 == GameBase.deathmatch.value && 0 == GameBase.coop.value) {
+ if (GameBase.g_edicts[1].health <= 0)
+ return;
+ }
+
+ // if noexit, do a ton of damage to other
+ if (GameBase.deathmatch.value != 0
+ && 0 == ((int) GameBase.dmflags.value & Defines.DF_ALLOW_EXIT)
+ && other != GameBase.g_edicts[0] /* world */
+ ) {
+ return;
+ }
+
+ // if multiplayer, let everyone know who hit the exit
+ if (GameBase.deathmatch.value != 0) {
+ if (activator != null && activator.client != null)
+ GameBase.gi.bprintf(Defines.PRINT_HIGH,
+ activator.client.pers.netname
+ + " exited the level.\n");
+ }
+
+ // if going to a new unit, clear cross triggers
+ if (self.map.indexOf('*') > -1)
+ GameBase.game.serverflags &= ~(Defines.SFL_CROSS_TRIGGER_MASK);
+
+ PlayerHud.BeginIntermission(self);
+ }
+ };
+ /**
+ * QUAKED target_splash (1 0 0) (-8 -8 -8) (8 8 8) Creates a particle splash
+ * effect when used.
+ * <p>
+ * Set "sounds" to one of the following: 1) sparks 2) blue water 3) brown
+ * water 4) slime 5) lava 6) blood
+ * <p>
+ * "count" how many pixels in the splash "dmg" if set, does a radius damage
+ * at this location when it splashes useful for lava/sparks
+ */
+ private static final EntUseAdapter use_target_splash = new EntUseAdapter() {
+ public String getID() {
+ return "use_target_splash";
+ }
+
+ public void use(EDict self, EDict other, EDict activator) {
+ GameBase.gi.WriteByte(Defines.svc_temp_entity);
+ GameBase.gi.WriteByte(Defines.TE_SPLASH);
+ GameBase.gi.WriteByte(self.count);
+ GameBase.gi.WritePosition(self.s.origin);
+ GameBase.gi.WriteDir(self.movedir);
+ GameBase.gi.WriteByte(self.sounds);
+ GameBase.gi.multicast(self.s.origin, Defines.MULTICAST_PVS);
+
+ }
+ };
+ /**
+ * QUAKED target_spawner (1 0 0) (-8 -8 -8) (8 8 8) Set target to the type
+ * of entity you want spawned. Useful for spawning monsters and gibs in the
+ * factory levels.
+ * <p>
+ * For monsters: Set direction to the facing you want it to have.
+ * <p>
+ * For gibs: Set direction if you want it moving and speed how fast it
+ * should be moving otherwise it will just be dropped
+ */
+
+ /**
+ * QUAKED target_crosslevel_target (.5 .5 .5) (-8 -8 -8) (8 8 8) trigger1
+ * trigger2 trigger3 trigger4 trigger5 trigger6 trigger7 trigger8 Triggered
+ * by a trigger_crosslevel elsewhere within a unit. If multiple triggers are
+ * checked, all must be true. Delay, target and killtarget also work.
+ * <p>
+ * "delay" delay before using targets if the trigger has been activated
+ * (default 1)
+ */
+ private static final EntThinkAdapter target_crosslevel_target_think = new EntThinkAdapter() {
+ public String getID() {
+ return "target_crosslevel_target_think";
+ }
+
+ public void think(EDict self) {
+ if (self.spawnflags == (GameBase.game.serverflags
+ & Defines.SFL_CROSS_TRIGGER_MASK & self.spawnflags)) {
+ GameUtil.G_UseTargets(self, self);
+ GameUtil.G_FreeEdict(self);
+ }
+ }
+ };
+ private static final EntThinkAdapter target_laser_start = new EntThinkAdapter() {
+ public String getID() {
+ return "target_laser_start";
+ }
+
+ public void think(EDict self) {
+
+ self.movetype = Defines.MOVETYPE_NONE;
+ self.solid = Defines.SOLID_NOT;
+ self.s.modelindex = 1; // must be non-zero
+
+ // set the beam diameter
+ if ((self.spawnflags & 64) != 0)
+ self.s.frame = 16;
+ else
+ self.s.frame = 4;
+
+ // set the color
+ if ((self.spawnflags & 2) != 0)
+ self.s.skinnum = 0xf2f2f0f0;
+ else if ((self.spawnflags & 4) != 0)
+ self.s.skinnum = 0xd0d1d2d3;
+ else if ((self.spawnflags & 8) != 0)
+ self.s.skinnum = 0xf3f3f1f1;
+ else if ((self.spawnflags & 16) != 0)
+ self.s.skinnum = 0xdcdddedf;
+ else if ((self.spawnflags & 32) != 0)
+ self.s.skinnum = 0xe0e1e2e3;
+
+ if (null == self.enemy) {
+ if (self.target != null) {
+ EdictIterator edit = GameBase.G_Find(null, GameBase.findByTarget,
+ self.target);
+ if (edit == null)
+ GameBase.gi.dprintf(self.classname + " at "
+ + Lib.vtos(self.s.origin) + ": " + self.target
+ + " is a bad target\n");
+ self.enemy = edit.o;
+ } else {
+ GameBase.G_SetMovedir(self.s.angles, self.movedir);
+ }
+ }
+ self.use = target_laser_use;
+ self.think = target_laser_think;
+
+ if (0 == self.dmg)
+ self.dmg = 1;
+
+ Math3D.vectorSet(self.mins, -8, -8, -8);
+ Math3D.vectorSet(self.maxs, 8, 8, 8);
+ GameBase.gi.linkentity(self);
+
+ if ((self.spawnflags & 1) != 0)
+ target_laser_on(self);
+ else
+ target_laser_off(self);
+ }
+ };
+ /**
+ * QUAKED target_lightramp (0 .5 .8) (-8 -8 -8) (8 8 8) TOGGLE speed How
+ * many seconds the ramping will take message two letters; starting
+ * lightlevel and ending lightlevel
+ */
+
+ private static final EntThinkAdapter target_lightramp_think = new EntThinkAdapter() {
+ public String getID() {
+ return "target_lightramp_think";
+ }
+
+ public void think(EDict self) {
+
+ char tmp[] = {(char) ('a' + (int) (self.movedir[0] + (GameBase.level.time - self.timestamp)
+ / Defines.FRAMETIME * self.movedir[2]))};
+
+ GameBase.gi.configstring(Defines.CS_LIGHTS + self.enemy.style,
+ new String(tmp));
+
+ if ((GameBase.level.time - self.timestamp) < self.speed) {
+ self.nextthink = GameBase.level.time + Defines.FRAMETIME;
+ } else if ((self.spawnflags & 1) != 0) {
+ char temp;
+
+ temp = (char) self.movedir[0];
+ self.movedir[0] = self.movedir[1];
+ self.movedir[1] = temp;
+ self.movedir[2] *= -1;
+ }
+
+ }
+ };
+ private static final EntUseAdapter target_lightramp_use = new EntUseAdapter() {
+ public String getID() {
+ return "target_lightramp_use";
+ }
+
+ public void use(EDict self, EDict other, EDict activator) {
+ if (self.enemy == null) {
+ EDict e;
+
+ // check all the targets
+ e = null;
+ EdictIterator es = null;
+
+ while (true) {
+ es = GameBase
+ .G_Find(es, GameBase.findByTarget, self.target);
+
+ if (es == null)
+ break;
+
+ e = es.o;
+
+ if (Lib.strcmp(e.classname, "light") != 0) {
+ GameBase.gi.dprintf(self.classname + " at "
+ + Lib.vtos(self.s.origin));
+ GameBase.gi.dprintf("target " + self.target + " ("
+ + e.classname + " at " + Lib.vtos(e.s.origin)
+ + ") is not a light\n");
+ } else {
+ self.enemy = e;
+ }
+ }
+
+ if (null == self.enemy) {
+ GameBase.gi.dprintf(self.classname + " target "
+ + self.target + " not found at "
+ + Lib.vtos(self.s.origin) + "\n");
+ GameUtil.G_FreeEdict(self);
+ return;
+ }
+ }
+
+ self.timestamp = GameBase.level.time;
+ target_lightramp_think.think(self);
+ }
+ };
+ /**
+ * QUAKED target_earthquake (1 0 0) (-8 -8 -8) (8 8 8) When triggered, this
+ * initiates a level-wide earthquake. All players and monsters are affected.
+ * "speed" severity of the quake (default:200) "count" duration of the quake
+ * (default:5)
+ */
+
+ private static final EntThinkAdapter target_earthquake_think = new EntThinkAdapter() {
+ public String getID() {
+ return "target_earthquake_think";
+ }
+
+ public void think(EDict self) {
+
+ int i;
+ EDict e;
+
+ if (self.last_move_time < GameBase.level.time) {
+ GameBase.gi.positioned_sound(self.s.origin, self,
+ Defines.CHAN_AUTO, self.noise_index, 1.0f,
+ Defines.ATTN_NONE, 0);
+ self.last_move_time = GameBase.level.time + 0.5f;
+ }
+
+ for (i = 1; i < GameBase.num_edicts; i++) {
+ e = GameBase.g_edicts[i];
+
+ if (!e.inuse)
+ continue;
+ if (null == e.client)
+ continue;
+ if (null == e.groundentity)
+ continue;
+
+ e.groundentity = null;
+ e.velocity[0] += Lib.crandom() * 150;
+ e.velocity[1] += Lib.crandom() * 150;
+ e.velocity[2] = self.speed * (100.0f / e.mass);
+ }
+
+ if (GameBase.level.time < self.timestamp)
+ self.nextthink = GameBase.level.time + Defines.FRAMETIME;
+
+ }
+ };
+ private static final EntUseAdapter target_earthquake_use = new EntUseAdapter() {
+ public String getID() {
+ return "target_earthquake_use";
+ }
+
+ public void use(EDict self, EDict other, EDict activator) {
+ self.timestamp = GameBase.level.time + self.count;
+ self.nextthink = GameBase.level.time + Defines.FRAMETIME;
+ self.activator = activator;
+ self.last_move_time = 0;
+ }
+ };
+
+ public static void SP_target_temp_entity(EDict ent) {
+ ent.use = Use_Target_Tent;
+ }
+
+ public static void SP_target_speaker(EDict ent) {
+ //char buffer[MAX_QPATH];
+ String buffer;
+
+ if (GameBase.st.noise == null) {
+ GameBase.gi.dprintf("target_speaker with no noise set at "
+ + Lib.vtos(ent.s.origin) + "\n");
+ return;
+ }
+ if (!GameBase.st.noise.contains(".wav"))
+ buffer = "" + GameBase.st.noise + ".wav";
+ else
+ buffer = GameBase.st.noise;
+
+ ent.noise_index = GameBase.gi.soundindex(buffer);
+
+ if (ent.volume == 0)
+ ent.volume = 1.0f;
+
+ if (ent.attenuation == 0)
+ ent.attenuation = 1.0f;
+ else if (ent.attenuation == -1) // use -1 so 0 defaults to 1
+ ent.attenuation = 0;
+
+ // check for prestarted looping sound
+ if ((ent.spawnflags & 1) != 0)
+ ent.s.sound = ent.noise_index;
+
+ ent.use = Use_Target_Speaker;
+
+ // must link the entity so we get areas and clusters so
+ // the server can determine who to send updates to
+ GameBase.gi.linkentity(ent);
+ }
+
+ /**
+ * QUAKED target_help (1 0 1) (-16 -16 -24) (16 16 24) help1 When fired, the
+ * "message" key becomes the current personal computer string, and the
+ * message light will be set on all clients status bars.
+ */
+ public static void SP_target_help(EDict ent) {
+ if (GameBase.deathmatch.value != 0) { // auto-remove for deathmatch
+ GameUtil.G_FreeEdict(ent);
+ return;
+ }
+
+ if (ent.message == null) {
+ GameBase.gi.dprintf(ent.classname + " with no message at "
+ + Lib.vtos(ent.s.origin) + "\n");
+ GameUtil.G_FreeEdict(ent);
+ return;
+ }
+ ent.use = Use_Target_Help;
+ }
+
+ public static void SP_target_secret(EDict ent) {
+ if (GameBase.deathmatch.value != 0) { // auto-remove for deathmatch
+ GameUtil.G_FreeEdict(ent);
+ return;
+ }
+
+ ent.use = use_target_secret;
+ if (GameBase.st.noise == null)
+ GameBase.st.noise = "misc/secret.wav";
+ ent.noise_index = GameBase.gi.soundindex(GameBase.st.noise);
+ ent.svflags = Defines.SVF_NOCLIENT;
+ GameBase.level.total_secrets++;
+ // map bug hack
+ if (0 == Lib.Q_stricmp(GameBase.level.mapname, "mine3")
+ && ent.s.origin[0] == 280 && ent.s.origin[1] == -2048
+ && ent.s.origin[2] == -624)
+ ent.message = "You have found a secret area.";
+ }
+
+ public static void SP_target_goal(EDict ent) {
+ if (GameBase.deathmatch.value != 0) { // auto-remove for deathmatch
+ GameUtil.G_FreeEdict(ent);
+ return;
+ }
+
+ ent.use = use_target_goal;
+ if (GameBase.st.noise == null)
+ GameBase.st.noise = "misc/secret.wav";
+ ent.noise_index = GameBase.gi.soundindex(GameBase.st.noise);
+ ent.svflags = Defines.SVF_NOCLIENT;
+ GameBase.level.total_goals++;
+ }
+
+ public static void SP_target_explosion(EDict ent) {
+ ent.use = use_target_explosion;
+ ent.svflags = Defines.SVF_NOCLIENT;
+ }
+
+ public static void SP_target_changelevel(EDict ent) {
+ if (ent.map == null) {
+ GameBase.gi.dprintf("target_changelevel with no map at "
+ + Lib.vtos(ent.s.origin) + "\n");
+ GameUtil.G_FreeEdict(ent);
+ return;
+ }
+
+ // ugly hack because *SOMEBODY* screwed up their map
+ if ((Lib.Q_stricmp(GameBase.level.mapname, "fact1") == 0)
+ && (Lib.Q_stricmp(ent.map, "fact3") == 0))
+ ent.map = "fact3$secret1";
+
+ ent.use = use_target_changelevel;
+ ent.svflags = Defines.SVF_NOCLIENT;
+ }
+
+ public static void SP_target_splash(EDict self) {
+ self.use = use_target_splash;
+ GameBase.G_SetMovedir(self.s.angles, self.movedir);
+
+ if (0 == self.count)
+ self.count = 32;
+
+ self.svflags = Defines.SVF_NOCLIENT;
+ }
+
+
+ public static void SP_target_blaster(EDict self) {
+ self.use = use_target_blaster;
+ GameBase.G_SetMovedir(self.s.angles, self.movedir);
+ self.noise_index = GameBase.gi.soundindex("weapons/laser2.wav");
+
+ if (0 == self.dmg)
+ self.dmg = 15;
+ if (0 == self.speed)
+ self.speed = 1000;
+
+ self.svflags = Defines.SVF_NOCLIENT;
+ }
+
+ public static void SP_target_crosslevel_trigger(EDict self) {
+ self.svflags = Defines.SVF_NOCLIENT;
+ self.use = trigger_crosslevel_trigger_use;
+ }
+
+ public static void SP_target_crosslevel_target(EDict self) {
+ if (0 == self.delay)
+ self.delay = 1;
+ self.svflags = Defines.SVF_NOCLIENT;
+
+ self.think = target_crosslevel_target_think;
+ self.nextthink = GameBase.level.time + self.delay;
+ }
+
+ private static void target_laser_on(EDict self) {
+ if (null == self.activator)
+ self.activator = self;
+ self.spawnflags |= 0x80000001;
+ self.svflags &= ~Defines.SVF_NOCLIENT;
+ target_laser_think.think(self);
+ }
+
+ private static void target_laser_off(EDict self) {
+ self.spawnflags &= ~1;
+ self.svflags |= Defines.SVF_NOCLIENT;
+ self.nextthink = 0;
+ }
+
+ public static void SP_target_laser(EDict self) {
+ // let everything else get spawned before we start firing
+ self.think = target_laser_start;
+ self.nextthink = GameBase.level.time + 1;
+ }
+
+ public static void SP_target_lightramp(EDict self) {
+ if (self.message == null || self.message.length() != 2
+ || self.message.charAt(0) < 'a' || self.message.charAt(0) > 'z'
+ || self.message.charAt(1) < 'a' || self.message.charAt(1) > 'z'
+ || self.message.charAt(0) == self.message.charAt(1)) {
+ GameBase.gi.dprintf("target_lightramp has bad ramp ("
+ + self.message + ") at " + Lib.vtos(self.s.origin) + "\n");
+ GameUtil.G_FreeEdict(self);
+ return;
+ }
+
+ if (GameBase.deathmatch.value != 0) {
+ GameUtil.G_FreeEdict(self);
+ return;
+ }
+
+ if (self.target == null) {
+ GameBase.gi.dprintf(self.classname + " with no target at "
+ + Lib.vtos(self.s.origin) + "\n");
+ GameUtil.G_FreeEdict(self);
+ return;
+ }
+
+ self.svflags |= Defines.SVF_NOCLIENT;
+ self.use = target_lightramp_use;
+ self.think = target_lightramp_think;
+
+ self.movedir[0] = self.message.charAt(0) - 'a';
+ self.movedir[1] = self.message.charAt(1) - 'a';
+ self.movedir[2] = (self.movedir[1] - self.movedir[0])
+ / (self.speed / Defines.FRAMETIME);
+ }
+
+ public static void SP_target_earthquake(EDict self) {
+ if (null == self.targetname)
+ GameBase.gi.dprintf("untargeted " + self.classname + " at "
+ + Lib.vtos(self.s.origin) + "\n");
+
+ if (0 == self.count)
+ self.count = 5;
+
+ if (0 == self.speed)
+ self.speed = 200;
+
+ self.svflags |= Defines.SVF_NOCLIENT;
+ self.think = target_earthquake_think;
+ self.use = target_earthquake_use;
+
+ self.noise_index = GameBase.gi.soundindex("world/quake.wav");
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.game;
+
+import lwjake2.Defines;
+import lwjake2.Globals;
+import lwjake2.util.Lib;
+import lwjake2.util.Math3D;
+
+public class GameTrigger {
+
+ public static final int PUSH_ONCE = 1;
+ // the wait time has passed, so set back up for another activation
+ public static final EntThinkAdapter multi_wait = new EntThinkAdapter() {
+ public String getID() {
+ return "multi_wait";
+ }
+
+ public void think(EDict ent) {
+
+ ent.nextthink = 0;
+ }
+ };
+ /**
+ * QUAKED trigger_relay (.5 .5 .5) (-8 -8 -8) (8 8 8) This fixed size
+ * trigger cannot be touched, it can only be fired by other events.
+ */
+ public static final EntUseAdapter trigger_relay_use = new EntUseAdapter() {
+ public String getID() {
+ return "trigger_relay_use";
+ }
+
+ public void use(EDict self, EDict other, EDict activator) {
+ GameUtil.G_UseTargets(self, activator);
+ }
+ };
+ static final EntUseAdapter Use_Multi = new EntUseAdapter() {
+ public String getID() {
+ return "Use_Multi";
+ }
+
+ public void use(EDict ent, EDict other, EDict activator) {
+ ent.activator = activator;
+ multi_trigger(ent);
+ }
+ };
+ static final EntTouchAdapter Touch_Multi = new EntTouchAdapter() {
+ public String getID() {
+ return "Touch_Multi";
+ }
+
+ public void touch(EDict self, EDict other, cplane_t plane,
+ csurface_t surf) {
+ if (other.client != null) {
+ if ((self.spawnflags & 2) != 0)
+ return;
+ } else if ((other.svflags & Defines.SVF_MONSTER) != 0) {
+ if (0 == (self.spawnflags & 1))
+ return;
+ } else
+ return;
+
+ if (!Math3D.vectorEquals(self.movedir, Globals.vec3_origin)) {
+ float[] forward = {0, 0, 0};
+
+ Math3D.angleVectors(other.s.angles, forward, null, null);
+ if (Math3D.dotProduct(forward, self.movedir) < 0)
+ return;
+ }
+
+ self.activator = other;
+ multi_trigger(self);
+ }
+ };
+ /**
+ * QUAKED trigger_multiple (.5 .5 .5) ? MONSTER NOT_PLAYER TRIGGERED
+ * Variable sized repeatable trigger. Must be targeted at one or more
+ * entities. If "delay" is set, the trigger waits some time after activating
+ * before firing. "wait" : Seconds between triggerings. (.2 default) sounds
+ * 1) secret 2) beep beep 3) large switch 4) set "message" to text string
+ */
+ static final EntUseAdapter trigger_enable = new EntUseAdapter() {
+ public String getID() {
+ return "trigger_enable";
+ }
+
+ public void use(EDict self, EDict other, EDict activator) {
+ self.solid = Defines.SOLID_TRIGGER;
+ self.use = Use_Multi;
+ GameBase.gi.linkentity(self);
+ }
+ };
+ /**
+ * QUAKED trigger_counter (.5 .5 .5) ? nomessage Acts as an intermediary for
+ * an action that takes multiple inputs.
+ * <p>
+ * If nomessage is not set, t will print "1 more.. " etc when triggered and
+ * "sequence complete" when finished.
+ * <p>
+ * After the counter has been triggered "count" times (default 2), it will
+ * fire all of it's targets and remove itself.
+ */
+ static final EntUseAdapter trigger_counter_use = new EntUseAdapter() {
+ public String getID() {
+ return "trigger_counter_use";
+ }
+
+ public void use(EDict self, EDict other, EDict activator) {
+ if (self.count == 0)
+ return;
+
+ self.count--;
+
+ if (self.count != 0) {
+ if (0 == (self.spawnflags & 1)) {
+ GameBase.gi.centerprintf(activator, self.count
+ + " more to go...");
+ GameBase.gi.sound(activator, Defines.CHAN_AUTO, GameBase.gi
+ .soundindex("misc/talk1.wav"), 1,
+ Defines.ATTN_NORM, 0);
+ }
+ return;
+ }
+
+ if (0 == (self.spawnflags & 1)) {
+ GameBase.gi.centerprintf(activator, "Sequence completed!");
+ GameBase.gi.sound(activator, Defines.CHAN_AUTO, GameBase.gi
+ .soundindex("misc/talk1.wav"), 1, Defines.ATTN_NORM, 0);
+ }
+ self.activator = activator;
+ multi_trigger(self);
+ }
+ };
+
+ /*
+ * ==============================================================================
+ *
+ * trigger_always
+ *
+ * ==============================================================================
+ */
+ /**
+ * QUAKED trigger_gravity (.5 .5 .5) ? Changes the touching entites gravity
+ * to the value of "gravity". 1.0 is standard gravity for the level.
+ */
+
+ static final EntTouchAdapter trigger_gravity_touch = new EntTouchAdapter() {
+ public String getID() {
+ return "trigger_gravity_touch";
+ }
+
+ public void touch(EDict self, EDict other, cplane_t plane,
+ csurface_t surf) {
+ other.gravity = self.gravity;
+ }
+ };
+ /**
+ * QUAKED trigger_monsterjump (.5 .5 .5) ? Walking monsters that touch this
+ * will jump in the direction of the trigger's angle "speed" default to 200,
+ * the speed thrown forward "height" default to 200, the speed thrown
+ * upwards
+ */
+
+ static final EntTouchAdapter trigger_monsterjump_touch = new EntTouchAdapter() {
+ public String getID() {
+ return "trigger_monsterjump_touch";
+ }
+
+ public void touch(EDict self, EDict other, cplane_t plane,
+ csurface_t surf) {
+ if ((other.flags & (Defines.FL_FLY | Defines.FL_SWIM)) != 0)
+ return;
+ if ((other.svflags & Defines.SVF_DEADMONSTER) != 0)
+ return;
+ if (0 == (other.svflags & Defines.SVF_MONSTER))
+ return;
+
+ // set XY even if not on ground, so the jump will clear lips
+ other.velocity[0] = self.movedir[0] * self.speed;
+ other.velocity[1] = self.movedir[1] * self.speed;
+
+ if (other.groundentity != null)
+ return;
+
+ other.groundentity = null;
+ other.velocity[2] = self.movedir[2];
+ }
+ };
+ public static int windsound;
+ static final EntTouchAdapter trigger_push_touch = new EntTouchAdapter() {
+ public String getID() {
+ return "trigger_push_touch";
+ }
+
+ public void touch(EDict self, EDict other, cplane_t plane,
+ csurface_t surf) {
+ if (Lib.strcmp(other.classname, "grenade") == 0) {
+ Math3D.vectorScale(self.movedir, self.speed * 10,
+ other.velocity);
+ } else if (other.health > 0) {
+ Math3D.vectorScale(self.movedir, self.speed * 10,
+ other.velocity);
+
+ if (other.client != null) {
+ // don't take falling damage immediately from this
+ Math3D.vectorCopy(other.velocity, other.client.oldvelocity);
+ if (other.fly_sound_debounce_time < GameBase.level.time) {
+ other.fly_sound_debounce_time = GameBase.level.time + 1.5f;
+ GameBase.gi.sound(other, Defines.CHAN_AUTO, windsound,
+ 1, Defines.ATTN_NORM, 0);
+ }
+ }
+ }
+ if ((self.spawnflags & PUSH_ONCE) != 0)
+ GameUtil.G_FreeEdict(self);
+ }
+ };
+ /**
+ * QUAKED trigger_hurt (.5 .5 .5) ? START_OFF TOGGLE SILENT NO_PROTECTION
+ * SLOW Any entity that touches this will be hurt.
+ * <p>
+ * It does dmg points of damage each server frame
+ * <p>
+ * SILENT supresses playing the sound SLOW changes the damage rate to once
+ * per second NO_PROTECTION *nothing* stops the damage
+ * <p>
+ * "dmg" default 5 (whole numbers only)
+ */
+ static EntUseAdapter hurt_use = new EntUseAdapter() {
+ public String getID() {
+ return "hurt_use";
+ }
+
+ public void use(EDict self, EDict other, EDict activator) {
+ if (self.solid == Defines.SOLID_NOT)
+ self.solid = Defines.SOLID_TRIGGER;
+ else
+ self.solid = Defines.SOLID_NOT;
+ GameBase.gi.linkentity(self);
+
+ if (0 == (self.spawnflags & 2))
+ self.use = null;
+ }
+ };
+
+ public static void InitTrigger(EDict self) {
+ if (!Math3D.vectorEquals(self.s.angles, Globals.vec3_origin))
+ GameBase.G_SetMovedir(self.s.angles, self.movedir);
+
+ self.solid = Defines.SOLID_TRIGGER;
+ self.movetype = Defines.MOVETYPE_NONE;
+ GameBase.gi.setmodel(self, self.model);
+ self.svflags = Defines.SVF_NOCLIENT;
+ }
+
+ // the trigger was just activated
+ // ent.activator should be set to the activator so it can be held through a
+ // delay so wait for the delay time before firing
+ public static void multi_trigger(EDict ent) {
+ if (ent.nextthink != 0)
+ return; // already been triggered
+
+ GameUtil.G_UseTargets(ent, ent.activator);
+
+ if (ent.wait > 0) {
+ ent.think = multi_wait;
+ ent.nextthink = GameBase.level.time + ent.wait;
+ } else { // we can't just remove (self) here, because this is a touch
+ // function
+ // called while looping through area links...
+ ent.touch = null;
+ ent.nextthink = GameBase.level.time + Defines.FRAMETIME;
+ ent.think = GameUtil.G_FreeEdictA;
+ }
+ }
+
+ public static void SP_trigger_multiple(EDict ent) {
+ if (ent.sounds == 1)
+ ent.noise_index = GameBase.gi.soundindex("misc/secret.wav");
+ else if (ent.sounds == 2)
+ ent.noise_index = GameBase.gi.soundindex("misc/talk.wav");
+ else if (ent.sounds == 3)
+ ent.noise_index = GameBase.gi.soundindex("misc/trigger1.wav");
+
+ if (ent.wait == 0)
+ ent.wait = 0.2f;
+
+ ent.touch = Touch_Multi;
+ ent.movetype = Defines.MOVETYPE_NONE;
+ ent.svflags |= Defines.SVF_NOCLIENT;
+
+ if ((ent.spawnflags & 4) != 0) {
+ ent.solid = Defines.SOLID_NOT;
+ ent.use = trigger_enable;
+ } else {
+ ent.solid = Defines.SOLID_TRIGGER;
+ ent.use = Use_Multi;
+ }
+
+ if (!Math3D.vectorEquals(ent.s.angles, Globals.vec3_origin))
+ GameBase.G_SetMovedir(ent.s.angles, ent.movedir);
+
+ GameBase.gi.setmodel(ent, ent.model);
+ GameBase.gi.linkentity(ent);
+ }
+
+ /*
+ * ==============================================================================
+ *
+ * trigger_key
+ *
+ * ==============================================================================
+ */
+
+ /**
+ * QUAKED trigger_once (.5 .5 .5) ? x x TRIGGERED Triggers once, then
+ * removes itself. You must set the key "target" to the name of another
+ * object in the level that has a matching "targetname".
+ * <p>
+ * If TRIGGERED, this trigger must be triggered before it is live.
+ * <p>
+ * sounds 1) secret 2) beep beep 3) large switch 4)
+ * <p>
+ * "message" string to be displayed when triggered
+ */
+
+ public static void SP_trigger_once(EDict ent) {
+ // make old maps work because I messed up on flag assignments here
+ // triggered was on bit 1 when it should have been on bit 4
+ if ((ent.spawnflags & 1) != 0) {
+ float[] v = {0, 0, 0};
+
+ Math3D.vectorMA(ent.mins, 0.5f, ent.size, v);
+ ent.spawnflags &= ~1;
+ ent.spawnflags |= 4;
+ GameBase.gi.dprintf("fixed TRIGGERED flag on " + ent.classname
+ + " at " + Lib.vtos(v) + "\n");
+ }
+
+ ent.wait = -1;
+ SP_trigger_multiple(ent);
+ }
+
+ public static void SP_trigger_relay(EDict self) {
+ self.use = trigger_relay_use;
+ }
+
+ /*
+ * ==============================================================================
+ *
+ * trigger_push
+ *
+ * ==============================================================================
+ */
+
+ public static void SP_trigger_key(EDict self) {
+ if (GameBase.st.item == null) {
+ GameBase.gi.dprintf("no key item for trigger_key at "
+ + Lib.vtos(self.s.origin) + "\n");
+ }
+ }
+
+ public static void SP_trigger_counter(EDict self) {
+ self.wait = -1;
+ if (0 == self.count)
+ self.count = 2;
+
+ self.use = trigger_counter_use;
+ }
+
+ /*
+ * QUAKED trigger_always (.5 .5 .5) (-8 -8 -8) (8 8 8) This trigger will
+ * always fire. It is activated by the world.
+ */
+ public static void SP_trigger_always(EDict ent) {
+ // we must have some delay to make sure our use targets are present
+ if (ent.delay < 0.2f)
+ ent.delay = 0.2f;
+ GameUtil.G_UseTargets(ent, ent);
+ }
+
+ /*
+ * QUAKED trigger_push (.5 .5 .5) ? PUSH_ONCE Pushes the player "speed"
+ * defaults to 1000
+ */
+ public static void SP_trigger_push(EDict self) {
+ InitTrigger(self);
+ windsound = GameBase.gi.soundindex("misc/windfly.wav");
+ self.touch = trigger_push_touch;
+ if (0 == self.speed)
+ self.speed = 1000;
+ GameBase.gi.linkentity(self);
+ }
+
+
+ /*
+ * ==============================================================================
+ *
+ * trigger_gravity
+ *
+ * ==============================================================================
+ */
+
+ public static void SP_trigger_gravity(EDict self) {
+ if (GameBase.st.gravity == null) {
+ GameBase.gi.dprintf("trigger_gravity without gravity set at "
+ + Lib.vtos(self.s.origin) + "\n");
+ GameUtil.G_FreeEdict(self);
+ return;
+ }
+
+ InitTrigger(self);
+ self.gravity = Lib.atoi(GameBase.st.gravity);
+ self.touch = trigger_gravity_touch;
+ }
+
+ /*
+ * ==============================================================================
+ *
+ * trigger_monsterjump
+ *
+ * ==============================================================================
+ */
+
+ public static void SP_trigger_monsterjump(EDict self) {
+ if (0 == self.speed)
+ self.speed = 200;
+ if (0 == GameBase.st.height)
+ GameBase.st.height = 200;
+ if (self.s.angles[Defines.YAW] == 0)
+ self.s.angles[Defines.YAW] = 360;
+ InitTrigger(self);
+ self.touch = trigger_monsterjump_touch;
+ self.movedir[2] = GameBase.st.height;
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.game;
+
+import lwjake2.Defines;
+import lwjake2.Globals;
+import lwjake2.qcommon.Com;
+import lwjake2.util.Lib;
+import lwjake2.util.Math3D;
+
+class GameUtil {
+
+ public static final EntThinkAdapter G_FreeEdictA = new EntThinkAdapter() {
+ public String getID() {
+ return "G_FreeEdictA";
+ }
+
+ public void think(EDict ent) {
+ G_FreeEdict(ent);
+ }
+ };
+ private static final EntThinkAdapter Think_Delay = new EntThinkAdapter() {
+ public String getID() {
+ return "Think_Delay";
+ }
+
+ public void think(EDict ent) {
+ G_UseTargets(ent, ent.activator);
+ G_FreeEdict(ent);
+ }
+ };
+
+ private static void checkClassname(EDict ent) {
+
+ if (ent.classname == null) {
+ Com.Printf("edict with classname = null: " + ent.index);
+ }
+ }
+
+ /**
+ * Use the targets.
+ * <p>
+ * The global "activator" should be set to the entity that initiated the
+ * firing.
+ * <p>
+ * If self.delay is set, a DelayedUse entity will be created that will
+ * actually do the SUB_UseTargets after that many seconds have passed.
+ * <p>
+ * Centerprints any self.message to the activator.
+ * <p>
+ * Search for (string)targetname in all entities that match
+ * (string)self.target and call their .use function
+ */
+
+ public static void G_UseTargets(EDict ent, EDict activator) {
+ EDict t;
+
+ checkClassname(ent);
+
+ // check for a delay
+ if (ent.delay != 0) {
+ // create a temp object to fire at a later time
+ t = G_Spawn();
+ t.classname = "DelayedUse";
+ t.nextthink = GameBase.level.time + ent.delay;
+ t.think = Think_Delay;
+ t.activator = activator;
+ if (activator == null)
+ GameBase.gi.dprintf("Think_Delay with no activator\n");
+ t.message = ent.message;
+ t.target = ent.target;
+ return;
+ }
+
+
+ // print the message
+ if ((ent.message != null)
+ && (activator.svflags & Defines.SVF_MONSTER) == 0) {
+ GameBase.gi.centerprintf(activator, "" + ent.message);
+ if (ent.noise_index != 0)
+ GameBase.gi.sound(activator, Defines.CHAN_AUTO,
+ ent.noise_index, 1, Defines.ATTN_NORM, 0);
+ else
+ GameBase.gi.sound(activator, Defines.CHAN_AUTO, GameBase.gi
+ .soundindex("misc/talk1.wav"), 1, Defines.ATTN_NORM, 0);
+ }
+
+ // kill killtargets
+ EdictIterator edit = null;
+
+ // fire targets
+ if (ent.target != null) {
+ edit = null;
+ while ((edit = GameBase.G_Find(edit, GameBase.findByTarget,
+ ent.target)) != null) {
+ t = edit.o;
+ // doors fire area portals in a specific way
+ if (Lib.Q_stricmp("func_areaportal", t.classname) == 0
+ && (Lib.Q_stricmp("func_door", ent.classname) == 0 || Lib
+ .Q_stricmp("func_door_rotating", ent.classname) == 0))
+ continue;
+
+ if (t == ent) {
+ GameBase.gi.dprintf("WARNING: Entity used itself.\n");
+ } else {
+ if (t.use != null)
+ t.use.use(t, ent, activator);
+ }
+ if (!ent.inuse) {
+ GameBase.gi
+ .dprintf("entity was removed while using targets\n");
+ return;
+ }
+ }
+ }
+ }
+
+ public static void G_InitEdict(EDict e, int i) {
+ e.inuse = true;
+ e.classname = "noclass";
+ e.gravity = 1.0f;
+ //e.s.number= e - g_edicts;
+ e.s = new entity_state_t(e);
+ e.s.number = i;
+ e.index = i;
+ }
+
+ /**
+ * Either finds a free edict, or allocates a new one. Try to avoid reusing
+ * an entity that was recently freed, because it can cause the client to
+ * think the entity morphed into something else instead of being removed and
+ * recreated, which can cause interpolated angles and bad trails.
+ */
+ public static EDict G_Spawn() {
+ int i;
+ EDict e = null;
+
+ for (i = (int) GameBase.maxclients.value + 1; i < GameBase.num_edicts; i++) {
+ e = GameBase.g_edicts[i];
+ // the first couple seconds of server time can involve a lot of
+ // freeing and allocating, so relax the replacement policy
+ if (!e.inuse
+ && (e.freetime < 2 || GameBase.level.time - e.freetime > 0.5)) {
+ e = GameBase.g_edicts[i] = new EDict(i);
+ G_InitEdict(e, i);
+ return e;
+ }
+ }
+
+ if (i == GameBase.game.maxentities)
+ GameBase.gi.error("ED_Alloc: no free edicts");
+
+ e = GameBase.g_edicts[i] = new EDict(i);
+ GameBase.num_edicts++;
+ G_InitEdict(e, i);
+ return e;
+ }
+
+ /**
+ * Marks the edict as free
+ */
+ public static void G_FreeEdict(EDict ed) {
+ GameBase.gi.unlinkentity(ed); // unlink from world
+
+ //if ((ed - g_edicts) <= (maxclients.value + BODY_QUEUE_SIZE))
+ if (ed.index <= (GameBase.maxclients.value + Defines.BODY_QUEUE_SIZE)) {
+ // gi.dprintf("tried to free special edict\n");
+ return;
+ }
+
+ GameBase.g_edicts[ed.index] = new EDict(ed.index);
+ ed.classname = "freed";
+ ed.freetime = GameBase.level.time;
+ ed.inuse = false;
+ }
+
+ /**
+ * Call after linking a new trigger in during gameplay to force all entities
+ * it covers to immediately touch it.
+ */
+
+ public static void G_ClearEdict(EDict ent) {
+ int i = ent.index;
+ GameBase.g_edicts[i] = new EDict(i);
+ }
+
+ /**
+ * Returns true, if two edicts are on the same team.
+ */
+ public static boolean OnSameTeam(EDict ent1, EDict ent2) {
+ return 0 != ((int) (GameBase.dmflags.value) & (Defines.DF_MODELTEAMS | Defines.DF_SKINTEAMS)) && ClientTeam(ent1).equals(ClientTeam(ent2));
+
+ }
+
+ /**
+ * Returns the team string of an entity
+ * with respect to rteam_by_model and team_by_skin.
+ */
+ private static String ClientTeam(EDict ent) {
+ String value;
+
+ if (ent.client == null)
+ return "";
+
+ value = Info.Info_ValueForKey(ent.client.pers.userinfo, "skin");
+
+ int p = value.indexOf("/");
+
+ if (p == -1)
+ return value;
+
+ if (((int) (GameBase.dmflags.value) & Defines.DF_MODELTEAMS) != 0) {
+ return value.substring(0, p);
+ }
+
+ return value.substring(p + 1, value.length());
+ }
+
+ /**
+ * Returns 1 if the entity is visible to self, even if not infront().
+ */
+ public static boolean visible(EDict self, EDict other) {
+ float[] spot1 = {0, 0, 0};
+ float[] spot2 = {0, 0, 0};
+ trace_t trace;
+
+ Math3D.vectorCopy(self.s.origin, spot1);
+ spot1[2] += self.viewheight;
+ Math3D.vectorCopy(other.s.origin, spot2);
+ spot2[2] += other.viewheight;
+ trace = GameBase.gi.trace(spot1, Globals.vec3_origin,
+ Globals.vec3_origin, spot2, self, Defines.MASK_OPAQUE);
+
+ return trace.fraction == 1.0;
+ }
+
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.game;
+
+import lwjake2.Defines;
+import lwjake2.qcommon.Com;
+
+import java.util.StringTokenizer;
+
+public class Info {
+
+ /**
+ * Returns a value for a key from an info string.
+ */
+ public static String Info_ValueForKey(String s, String key) {
+
+ StringTokenizer tk = new StringTokenizer(s, "\\");
+
+ while (tk.hasMoreTokens()) {
+ String key1 = tk.nextToken();
+
+ if (!tk.hasMoreTokens()) {
+ Com.Printf("MISSING VALUE\n");
+ return s;
+ }
+ String value1 = tk.nextToken();
+
+ if (key.equals(key1))
+ return value1;
+ }
+
+ return "";
+ }
+
+ /**
+ * Sets a value for a key in the user info string.
+ */
+ public static String Info_SetValueForKey(String s, String key, String value) {
+
+ if (value == null || value.length() == 0)
+ return s;
+
+ if (key.indexOf('\\') != -1 || value.indexOf('\\') != -1) {
+ Com.Printf("Can't use keys or values with a \\\n");
+ return s;
+ }
+
+ if (key.indexOf(';') != -1) {
+ Com.Printf("Can't use keys or values with a semicolon\n");
+ return s;
+ }
+
+ if (key.indexOf('"') != -1 || value.indexOf('"') != -1) {
+ Com.Printf("Can't use keys or values with a \"\n");
+ return s;
+ }
+
+ if (key.length() > Defines.MAX_INFO_KEY - 1
+ || value.length() > Defines.MAX_INFO_KEY - 1) {
+ Com.Printf("Keys and values must be < 64 characters.\n");
+ return s;
+ }
+
+ StringBuilder sb = new StringBuilder(Info_RemoveKey(s, key));
+
+ if (sb.length() + 2 + key.length() + value.length() > Defines.MAX_INFO_STRING) {
+
+ Com.Printf("Info string length exceeded\n");
+ return s;
+ }
+
+ sb.append('\\').append(key).append('\\').append(value);
+
+ return sb.toString();
+ }
+
+ /**
+ * Removes a key and value from an info string.
+ */
+ private static String Info_RemoveKey(String s, String key) {
+
+ StringBuilder sb = new StringBuilder(512);
+
+ if (key.indexOf('\\') != -1) {
+ Com.Printf("Can't use a key with a \\\n");
+ return s;
+ }
+
+ StringTokenizer tk = new StringTokenizer(s, "\\");
+
+ while (tk.hasMoreTokens()) {
+ String key1 = tk.nextToken();
+
+ if (!tk.hasMoreTokens()) {
+ Com.Printf("MISSING VALUE\n");
+ return s;
+ }
+ String value1 = tk.nextToken();
+
+ if (!key.equals(key1))
+ sb.append('\\').append(key1).append('\\').append(value1);
+ }
+
+ return sb.toString();
+
+ }
+
+ /**
+ * Some characters are illegal in info strings because they can mess up the
+ * server's parsing.
+ */
+ public static boolean Info_Validate(String s) {
+ return !((s.indexOf('"') != -1) || (s.indexOf(';') != -1));
+ }
+
+ public static void Print(String s) {
+
+ StringBuilder sb = new StringBuilder(512);
+ StringTokenizer tk = new StringTokenizer(s, "\\");
+
+ while (tk.hasMoreTokens()) {
+
+ String key1 = tk.nextToken();
+
+ if (!tk.hasMoreTokens()) {
+ Com.Printf("MISSING VALUE\n");
+ return;
+ }
+
+ String value1 = tk.nextToken();
+
+ sb.append(key1);
+
+ int len = key1.length();
+
+ if (len < 20) {
+ String fillspaces = " ";
+ sb.append(fillspaces.substring(len));
+ }
+ sb.append('=').append(value1).append('\n');
+ }
+ Com.Printf(sb.toString());
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.game;
+
+abstract class ItemDropAdapter extends SuperAdapter {
+ public void drop(EDict ent, gitem_t item) {
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.game;
+
+abstract class ItemUseAdapter extends SuperAdapter {
+ public void use(EDict ent, gitem_t item) {
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.game;
+
+class M_Player {
+ // This file generated by qdata - Do NOT Modify
+
+ public final static int FRAME_stand01 = 0;
+
+ public final static int FRAME_stand40 = 39;
+
+ public final static int FRAME_run1 = 40;
+
+ public final static int FRAME_run6 = 45;
+
+ public final static int FRAME_pain101 = 54;
+
+ public final static int FRAME_pain104 = 57;
+
+ public final static int FRAME_pain201 = 58;
+
+ public final static int FRAME_pain204 = 61;
+
+ public final static int FRAME_pain301 = 62;
+
+ public final static int FRAME_pain304 = 65;
+
+ public final static int FRAME_jump1 = 66;
+
+ public final static int FRAME_jump2 = 67;
+
+ public final static int FRAME_jump3 = 68;
+
+ public final static int FRAME_jump6 = 71;
+
+ public final static int FRAME_flip01 = 72;
+
+ public final static int FRAME_flip12 = 83;
+
+ public final static int FRAME_salute01 = 84;
+
+ public final static int FRAME_salute11 = 94;
+
+ public final static int FRAME_taunt01 = 95;
+
+ public final static int FRAME_taunt17 = 111;
+
+ public final static int FRAME_wave01 = 112;
+
+ public final static int FRAME_wave11 = 122;
+
+ public final static int FRAME_point01 = 123;
+
+ public final static int FRAME_point12 = 134;
+
+ public final static int FRAME_crstnd01 = 135;
+
+ public final static int FRAME_crstnd19 = 153;
+
+ public final static int FRAME_crwalk1 = 154;
+
+ public final static int FRAME_crwalk6 = 159;
+
+ public final static int FRAME_crpain1 = 169;
+
+ public final static int FRAME_crpain4 = 172;
+
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.game;
+
+import lwjake2.Defines;
+import lwjake2.util.Math3D;
+
+import java.util.Arrays;
+
+public class Move {
+
+ // pmove->pm_flags
+ public final static int PMF_DUCKED = 1;
+ public final static int PMF_JUMP_HELD = 2;
+ public final static int PMF_ON_GROUND = 4;
+ public final static int PMF_TIME_WATERJUMP = 8; // pm_time is waterjump
+ public final static int PMF_TIME_LAND = 16; // pm_time is time before rejump
+ public final static int PMF_TIME_TELEPORT = 32; // pm_time is non-moving
+ public final static int PMF_NO_PREDICTION = 64; // temporarily disables
+ // state (in / out)
+ public final pmove_state_t s = new pmove_state_t();
+ // command (in)
+ public final usercmd_t cmd = new usercmd_t();
+ public final EDict[] touchents = new EDict[Defines.MAXTOUCH];
+ public final float[] viewangles = {0, 0, 0}; // clamped
+ public final float[] mins = {0, 0, 0};
+ public final float[] maxs = {0, 0, 0}; // bounding box size
+ public boolean snapinitial; // if s has been changed outside pmove
+ // results (out)
+ public int numtouch;
+ public float viewheight;
+ public EDict groundentity;
+ public int watertype;
+ public int waterlevel;
+ public TraceAdapter trace;
+ public PointContentsAdapter pointcontents;
+
+ public void clear() {
+ groundentity = null;
+ waterlevel = watertype = 0;
+ trace = null;
+ pointcontents = null;
+ Math3D.vectorClear(mins);
+ Math3D.vectorClear(maxs);
+ viewheight = 0;
+ Math3D.vectorClear(viewangles);
+ Arrays.fill(touchents, null);
+ numtouch = 0;
+ snapinitial = false;
+ cmd.clear();
+ s.clear();
+ }
+ // time
+
+ public static class TraceAdapter {
+ // callbacks to test the world
+ public trace_t trace(float[] start, float[] mins, float[] maxs,
+ float[] end) {
+ return null;
+ }
+ }
+ // prediction (used for
+ // grappling hook)
+
+ public static class PointContentsAdapter {
+ // callbacks to test the world
+ public int pointcontents(float[] point) {
+ return 0;
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.game;
+
+import lwjake2.Defines;
+import lwjake2.util.Lib;
+import lwjake2.util.Math3D;
+
+public class PlayerClient {
+
+ static final EntThinkAdapter SP_FixCoopSpots = new EntThinkAdapter() {
+ public String getID() {
+ return "SP_FixCoopSpots";
+ }
+
+ public void think(EDict self) {
+
+ EDict spot;
+ float[] d = {0, 0, 0};
+
+ spot = null;
+ EdictIterator es = null;
+
+ while (true) {
+ es = GameBase.G_Find(es, GameBase.findByClass,
+ "info_player_start");
+
+ if (es == null)
+ return;
+
+ spot = es.o;
+
+ if (spot.targetname == null)
+ continue;
+ Math3D.vectorSubtract(self.s.origin, spot.s.origin, d);
+ if (Math3D.vectorLength(d) < 384) {
+ if ((self.targetname == null)
+ || Lib.Q_stricmp(self.targetname, spot.targetname) != 0) {
+ // gi.dprintf("FixCoopSpots changed %s at %s targetname
+ // from %s to %s\n", self.classname,
+ // vtos(self.s.origin), self.targetname,
+ // spot.targetname);
+ self.targetname = spot.targetname;
+ }
+ return;
+ }
+ }
+ }
+ };
+ static final EntThinkAdapter SP_CreateCoopSpots = new EntThinkAdapter() {
+ public String getID() {
+ return "SP_CreateCoopSpots";
+ }
+
+ public void think(EDict self) {
+
+ EDict spot;
+
+ if (Lib.Q_stricmp(GameBase.level.mapname, "security") == 0) {
+ spot = GameUtil.G_Spawn();
+ spot.classname = "info_player_coop";
+ spot.s.origin[0] = 188 - 64;
+ spot.s.origin[1] = -164;
+ spot.s.origin[2] = 80;
+ spot.targetname = "jail3";
+ spot.s.angles[1] = 90;
+
+ spot = GameUtil.G_Spawn();
+ spot.classname = "info_player_coop";
+ spot.s.origin[0] = 188 + 64;
+ spot.s.origin[1] = -164;
+ spot.s.origin[2] = 80;
+ spot.targetname = "jail3";
+ spot.s.angles[1] = 90;
+
+ spot = GameUtil.G_Spawn();
+ spot.classname = "info_player_coop";
+ spot.s.origin[0] = 188 + 128;
+ spot.s.origin[1] = -164;
+ spot.s.origin[2] = 80;
+ spot.targetname = "jail3";
+ spot.s.angles[1] = 90;
+ }
+ }
+ };
+ static EDict pm_passent;
+ // pmove doesn't need to know about passent and contentmask
+ public static final Move.TraceAdapter PM_trace = new Move.TraceAdapter() {
+
+ public trace_t trace(float[] start, float[] mins, float[] maxs,
+ float[] end) {
+ if (pm_passent.health > 0)
+ return GameBase.gi.trace(start, mins, maxs, end, pm_passent,
+ Defines.MASK_PLAYERSOLID);
+ else
+ return GameBase.gi.trace(start, mins, maxs, end, pm_passent,
+ Defines.MASK_DEADSOLID);
+ }
+
+ };
+
+ /**
+ * QUAKED info_player_start (1 0 0) (-16 -16 -24) (16 16 32) The normal
+ * starting point for a level.
+ */
+ public static void SP_info_player_start(EDict self) {
+ if (GameBase.coop.value == 0)
+ return;
+ if (Lib.Q_stricmp(GameBase.level.mapname, "security") == 0) {
+ // invoke one of our gross, ugly, disgusting hacks
+ self.think = PlayerClient.SP_CreateCoopSpots;
+ self.nextthink = GameBase.level.time + Defines.FRAMETIME;
+ }
+ }
+
+ /**
+ * QUAKED info_player_deathmatch (1 0 1) (-16 -16 -24) (16 16 32) potential
+ * spawning position for deathmatch games.
+ */
+ public static void SP_info_player_deathmatch(EDict self) {
+ if (0 == GameBase.deathmatch.value) {
+ GameUtil.G_FreeEdict(self);
+ return;
+ }
+ GameMisc.SP_misc_teleporter_dest.think(self);
+ }
+
+ /**
+ * QUAKED info_player_coop (1 0 1) (-16 -16 -24) (16 16 32) potential
+ * spawning position for coop games.
+ */
+
+ public static void SP_info_player_coop(EDict self) {
+ if (0 == GameBase.coop.value) {
+ GameUtil.G_FreeEdict(self);
+ return;
+ }
+
+ if ((Lib.Q_stricmp(GameBase.level.mapname, "jail2") == 0)
+ || (Lib.Q_stricmp(GameBase.level.mapname, "jail4") == 0)
+ || (Lib.Q_stricmp(GameBase.level.mapname, "mine1") == 0)
+ || (Lib.Q_stricmp(GameBase.level.mapname, "mine2") == 0)
+ || (Lib.Q_stricmp(GameBase.level.mapname, "mine3") == 0)
+ || (Lib.Q_stricmp(GameBase.level.mapname, "mine4") == 0)
+ || (Lib.Q_stricmp(GameBase.level.mapname, "lab") == 0)
+ || (Lib.Q_stricmp(GameBase.level.mapname, "boss1") == 0)
+ || (Lib.Q_stricmp(GameBase.level.mapname, "fact3") == 0)
+ || (Lib.Q_stricmp(GameBase.level.mapname, "biggun") == 0)
+ || (Lib.Q_stricmp(GameBase.level.mapname, "space") == 0)
+ || (Lib.Q_stricmp(GameBase.level.mapname, "command") == 0)
+ || (Lib.Q_stricmp(GameBase.level.mapname, "power2") == 0)
+ || (Lib.Q_stricmp(GameBase.level.mapname, "strike") == 0)) {
+ // invoke one of our gross, ugly, disgusting hacks
+ self.think = PlayerClient.SP_FixCoopSpots;
+ self.nextthink = GameBase.level.time + Defines.FRAMETIME;
+ }
+ }
+
+ /**
+ * QUAKED info_player_intermission (1 0 1) (-16 -16 -24) (16 16 32) The
+ * deathmatch intermission point will be at one of these Use 'angles'
+ * instead of 'angle', so you can set pitch or roll as well as yaw. 'pitch
+ * yaw roll'
+ */
+ public static void SP_info_player_intermission() {
+ }
+
+ /**
+ * This is only called when the game first initializes in single player, but
+ * is called after each death and level change in deathmatch.
+ */
+ public static void InitClientPersistant(Client client) {
+
+ client.pers = new client_persistant_t();
+
+ client.pers.inventory[client.pers.selected_item] = 1;
+
+ /*
+ * Give shotgun. item = FindItem("Shotgun"); client.pers.selected_item =
+ * ITEM_INDEX(item); client.pers.inventory[client.pers.selected_item] =
+ * 1;
+ */
+
+
+ client.pers.health = 100;
+ client.pers.max_health = 100;
+ client.pers.max_slugs = 50;
+
+ client.pers.connected = true;
+ }
+
+ public static void InitClientResp(Client client) {
+ //memset(& client.resp, 0, sizeof(client.resp));
+ client.resp.clear(); // ok.
+ client.resp.enterframe = GameBase.level.framenum;
+ client.resp.coop_respawn.set(client.pers);
+ }
+
+ /**
+ * Some information that should be persistant, like health, is still stored
+ * in the edict structure, so it needs to be mirrored out to the client
+ * structure before all the edicts are wiped.
+ */
+ public static void SaveClientData() {
+ int i;
+ EDict ent;
+
+ for (i = 0; i < GameBase.game.maxclients; i++) {
+ ent = GameBase.g_edicts[1 + i];
+ if (!ent.inuse)
+ continue;
+
+ GameBase.game.clients[i].pers.health = ent.health;
+ GameBase.game.clients[i].pers.max_health = ent.max_health;
+ GameBase.game.clients[i].pers.savedFlags = (ent.flags & (Defines.FL_GODMODE
+ | Defines.FL_NOTARGET | Defines.FL_POWER_ARMOR));
+
+ }
+ }
+
+ public static void FetchClientEntData(EDict ent) {
+ ent.health = ent.client.pers.health;
+ ent.max_health = ent.client.pers.max_health;
+ ent.flags |= ent.client.pers.savedFlags;
+ }
+
+ /**
+ * Returns the distance to the nearest player from the given spot.
+ */
+ static float PlayersRangeFromSpot(EDict spot) {
+ EDict player;
+ float bestplayerdistance;
+ float[] v = {0, 0, 0};
+ int n;
+ float playerdistance;
+
+ bestplayerdistance = 9999999;
+
+ for (n = 1; n <= GameBase.maxclients.value; n++) {
+ player = GameBase.g_edicts[n];
+
+ if (!player.inuse)
+ continue;
+
+ if (player.health <= 0)
+ continue;
+
+ Math3D.vectorSubtract(spot.s.origin, player.s.origin, v);
+ playerdistance = Math3D.vectorLength(v);
+
+ if (playerdistance < bestplayerdistance)
+ bestplayerdistance = playerdistance;
+ }
+
+ return bestplayerdistance;
+ }
+
+ /**
+ * Go to a random point, but NOT the two points closest to other players.
+ */
+ public static EDict SelectRandomDeathmatchSpawnPoint() {
+ EDict spot, spot1, spot2;
+ int count = 0;
+ int selection;
+ float range, range1, range2;
+
+ spot = null;
+ range1 = range2 = 99999;
+ spot1 = spot2 = null;
+
+ EdictIterator es = null;
+
+ while ((es = GameBase.G_Find(es, GameBase.findByClass,
+ "info_player_deathmatch")) != null) {
+ spot = es.o;
+ count++;
+ range = PlayersRangeFromSpot(spot);
+ if (range < range1) {
+ range1 = range;
+ spot1 = spot;
+ } else if (range < range2) {
+ range2 = range;
+ spot2 = spot;
+ }
+ }
+
+ if (count == 0)
+ return null;
+
+ if (count <= 2) {
+ spot1 = spot2 = null;
+ } else
+ count -= 2;
+
+ selection = Lib.rand() % count;
+
+ spot = null;
+ es = null;
+ do {
+ es = GameBase.G_Find(es, GameBase.findByClass,
+ "info_player_deathmatch");
+
+ if (es == null)
+ break;
+
+ spot = es.o;
+ if (spot == spot1 || spot == spot2)
+ selection++;
+ } while (selection-- > 0);
+
+ return spot;
+ }
+
+ /**
+ * If turned on in the dmflags, select a spawn point far away from other players.
+ */
+ static EDict SelectFarthestDeathmatchSpawnPoint() {
+ EDict bestspot;
+ float bestdistance, bestplayerdistance;
+ EDict spot;
+
+ spot = null;
+ bestspot = null;
+ bestdistance = 0;
+
+ EdictIterator es = null;
+ while ((es = GameBase.G_Find(es, GameBase.findByClass,
+ "info_player_deathmatch")) != null) {
+ spot = es.o;
+ bestplayerdistance = PlayersRangeFromSpot(spot);
+
+ if (bestplayerdistance > bestdistance) {
+ bestspot = spot;
+ bestdistance = bestplayerdistance;
+ }
+ }
+
+ if (bestspot != null) {
+ return bestspot;
+ }
+
+ // if there is a player just spawned on each and every start spot
+ // we have no choice to turn one into a telefrag meltdown
+ EdictIterator edit = GameBase.G_Find(null, GameBase.findByClass,
+ "info_player_deathmatch");
+ if (edit == null)
+ return null;
+
+ return edit.o;
+ }
+
+
+ public static EDict SelectDeathmatchSpawnPoint() {
+ if (0 != ((int) (GameBase.dmflags.value) & Defines.DF_SPAWN_FARTHEST))
+ return SelectFarthestDeathmatchSpawnPoint();
+ else
+ return SelectRandomDeathmatchSpawnPoint();
+ }
+
+ public static EDict SelectCoopSpawnPoint(EDict ent) {
+ int index;
+ EDict spot = null;
+ String target;
+
+ //index = ent.client - game.clients;
+ index = ent.client.index;
+
+ // player 0 starts in normal player spawn point
+ if (index == 0)
+ return null;
+
+ spot = null;
+ EdictIterator es = null;
+
+ // assume there are four coop spots at each spawnpoint
+ while (true) {
+
+ es = GameBase.G_Find(es, GameBase.findByClass,
+ "info_player_coop");
+
+ if (es == null)
+ return null;
+
+ spot = es.o;
+
+ if (spot == null)
+ return null; // we didn't have enough...
+
+ target = spot.targetname;
+ if (target == null)
+ target = "";
+ if (Lib.Q_stricmp(GameBase.game.spawnpoint, target) == 0) {
+ // this is a coop spawn point for one of the clients here
+ index--;
+ if (0 == index)
+ return spot; // this is it
+ }
+ }
+
+ }
+
+ /**
+ * Chooses a player start, deathmatch start, coop start, etc.
+ */
+ public static void SelectSpawnPoint(EDict ent, float[] origin,
+ float[] angles) {
+ EDict spot = null;
+
+ if (GameBase.deathmatch.value != 0)
+ spot = SelectDeathmatchSpawnPoint();
+ else if (GameBase.coop.value != 0)
+ spot = SelectCoopSpawnPoint(ent);
+
+ EdictIterator es = null;
+ // find a single player start spot
+ if (null == spot) {
+ while ((es = GameBase.G_Find(es, GameBase.findByClass,
+ "info_player_start")) != null) {
+ spot = es.o;
+
+ if (GameBase.game.spawnpoint.length() == 0
+ && spot.targetname == null)
+ break;
+
+ if (GameBase.game.spawnpoint.length() == 0
+ || spot.targetname == null)
+ continue;
+
+ if (Lib.Q_stricmp(GameBase.game.spawnpoint, spot.targetname) == 0)
+ break;
+ }
+
+ if (null == spot) {
+ if (GameBase.game.spawnpoint.length() == 0) {
+ // there wasn't a spawnpoint without a
+ // target, so use any
+ es = GameBase.G_Find(es, GameBase.findByClass,
+ "info_player_start");
+
+ if (es != null)
+ spot = es.o;
+ }
+ if (null == spot) {
+ GameBase.gi.error("Couldn't find spawn point "
+ + GameBase.game.spawnpoint + "\n");
+ return;
+ }
+ }
+ }
+
+ Math3D.vectorCopy(spot.s.origin, origin);
+ origin[2] += 9;
+ Math3D.vectorCopy(spot.s.angles, angles);
+ }
+
+
+ public static void InitBodyQue() {
+ int i;
+ EDict ent;
+
+ GameBase.level.body_que = 0;
+ for (i = 0; i < Defines.BODY_QUEUE_SIZE; i++) {
+ ent = GameUtil.G_Spawn();
+ ent.classname = "bodyque";
+ }
+ }
+
+ public static void CopyToBodyQue(EDict ent) {
+ EDict body;
+
+ // grab a body que and cycle to the next one
+ int i = (int) GameBase.maxclients.value + GameBase.level.body_que + 1;
+ body = GameBase.g_edicts[i];
+ GameBase.level.body_que = (GameBase.level.body_que + 1)
+ % Defines.BODY_QUEUE_SIZE;
+
+ // FIXME: send an effect on the removed body
+
+ GameBase.gi.unlinkentity(ent);
+
+ GameBase.gi.unlinkentity(body);
+ body.s = ent.s.getClone();
+
+ body.s.number = body.index;
+
+ body.svflags = ent.svflags;
+ Math3D.vectorCopy(ent.mins, body.mins);
+ Math3D.vectorCopy(ent.maxs, body.maxs);
+ Math3D.vectorCopy(ent.absmin, body.absmin);
+ Math3D.vectorCopy(ent.absmax, body.absmax);
+ Math3D.vectorCopy(ent.size, body.size);
+ body.solid = ent.solid;
+ body.clipmask = ent.clipmask;
+ body.owner = ent.owner;
+ body.movetype = ent.movetype;
+
+
+ GameBase.gi.linkentity(body);
+ }
+
+ public static void respawn(EDict self) {
+ if (GameBase.deathmatch.value != 0 || GameBase.coop.value != 0) {
+ // spectator's don't leave bodies
+ if (self.movetype != Defines.MOVETYPE_NOCLIP)
+ CopyToBodyQue(self);
+ self.svflags &= ~Defines.SVF_NOCLIENT;
+ PutClientInServer(self);
+
+ // add a teleportation effect
+ self.s.event = Defines.EV_PLAYER_TELEPORT;
+
+ // hold in place briefly
+ self.client.ps.pmove.pm_flags = Move.PMF_TIME_TELEPORT;
+ self.client.ps.pmove.pm_time = 14;
+
+ self.client.respawn_time = GameBase.level.time;
+
+ return;
+ }
+
+ // restart the entire server
+ GameBase.gi.AddCommandString("menu_loadgame\n");
+ }
+
+ private static boolean passwdOK(String i1, String i2) {
+ return !(i1.length() != 0 && !i1.equals("none") && !i1.equals(i2));
+ }
+
+ /**
+ * Only called when pers.spectator changes note that resp.spectator should
+ * be the opposite of pers.spectator here
+ */
+ public static void spectator_respawn(EDict ent) {
+ int i, numspec;
+
+ // if the user wants to become a spectator, make sure he doesn't
+ // exceed max_spectators
+
+ if (ent.client.pers.spectator) {
+ String value = Info.Info_ValueForKey(ent.client.pers.userinfo,
+ "spectator");
+
+ if (!passwdOK(GameBase.spectator_password.string, value)) {
+ GameBase.gi.cprintf(ent, Defines.PRINT_HIGH,
+ "Spectator password incorrect.\n");
+ ent.client.pers.spectator = false;
+ GameBase.gi.WriteByte(Defines.svc_stufftext);
+ GameBase.gi.WriteString("spectator 0\n");
+ GameBase.gi.unicast(ent, true);
+ return;
+ }
+
+ // count spectators
+ for (i = 1, numspec = 0; i <= GameBase.maxclients.value; i++)
+ if (GameBase.g_edicts[i].inuse
+ && GameBase.g_edicts[i].client.pers.spectator)
+ numspec++;
+
+ if (numspec >= GameBase.maxspectators.value) {
+ GameBase.gi.cprintf(ent, Defines.PRINT_HIGH,
+ "Server spectator limit is full.");
+ ent.client.pers.spectator = false;
+ // reset his spectator var
+ GameBase.gi.WriteByte(Defines.svc_stufftext);
+ GameBase.gi.WriteString("spectator 0\n");
+ GameBase.gi.unicast(ent, true);
+ return;
+ }
+ } else {
+ // he was a spectator and wants to join the game
+ // he must have the right password
+ String value = Info.Info_ValueForKey(ent.client.pers.userinfo,
+ "password");
+ if (!passwdOK(GameBase.spectator_password.string, value)) {
+ GameBase.gi.cprintf(ent, Defines.PRINT_HIGH,
+ "Password incorrect.\n");
+ ent.client.pers.spectator = true;
+ GameBase.gi.WriteByte(Defines.svc_stufftext);
+ GameBase.gi.WriteString("spectator 1\n");
+ GameBase.gi.unicast(ent, true);
+ return;
+ }
+ }
+
+
+ ent.svflags &= ~Defines.SVF_NOCLIENT;
+ PutClientInServer(ent);
+
+ // add a teleportation effect
+ if (!ent.client.pers.spectator) {
+ //gi.WriteShort(ent - g_edicts);
+ GameBase.gi.WriteShort(ent.index);
+
+ GameBase.gi.WriteByte(Defines.MZ_LOGIN);
+ GameBase.gi.multicast(ent.s.origin, Defines.MULTICAST_PVS);
+
+ // hold in place briefly
+ ent.client.ps.pmove.pm_flags = Move.PMF_TIME_TELEPORT;
+ ent.client.ps.pmove.pm_time = 14;
+ }
+
+ ent.client.respawn_time = GameBase.level.time;
+
+ if (ent.client.pers.spectator)
+ GameBase.gi.bprintf(Defines.PRINT_HIGH, ent.client.pers.netname
+ + " has moved to the sidelines\n");
+ else
+ GameBase.gi.bprintf(Defines.PRINT_HIGH, ent.client.pers.netname
+ + " joined the game\n");
+ }
+
+ /**
+ * Called when a player connects to a server or respawns in a deathmatch.
+ */
+ public static void PutClientInServer(EDict ent) {
+ float[] mins = {-16, -16, -24};
+ float[] maxs = {16, 16, 32};
+ int index;
+ float[] spawn_origin = {0, 0, 0}, spawn_angles = {0, 0, 0};
+ Client client;
+ int i;
+ client_persistant_t saved = new client_persistant_t();
+ client_respawn_t resp = new client_respawn_t();
+
+ // find a spawn point
+ // do it before setting health back up, so farthest
+ // ranging doesn't count this client
+ SelectSpawnPoint(ent, spawn_origin, spawn_angles);
+
+ index = ent.index - 1;
+ client = ent.client;
+
+ // deathmatch wipes most client data every spawn
+ if (GameBase.deathmatch.value != 0) {
+
+ resp.set(client.resp);
+ String userinfo = client.pers.userinfo;
+ InitClientPersistant(client);
+
+ userinfo = ClientUserinfoChanged(ent, userinfo);
+
+ } else if (GameBase.coop.value != 0) {
+
+ resp.set(client.resp);
+
+ String userinfo = client.pers.userinfo;
+
+ resp.coop_respawn.game_helpchanged = client.pers.game_helpchanged;
+ resp.coop_respawn.helpchanged = client.pers.helpchanged;
+ client.pers.set(resp.coop_respawn);
+ ClientUserinfoChanged(ent, userinfo);
+ } else {
+ resp.clear();
+ }
+
+ // clear everything but the persistant data
+ saved.set(client.pers);
+ client.clear();
+ client.pers.set(saved);
+ if (client.pers.health <= 0)
+ InitClientPersistant(client);
+
+ client.resp.set(resp);
+
+ // copy some data from the client to the entity
+ FetchClientEntData(ent);
+
+ // clear entity values
+ ent.groundentity = null;
+ ent.client = GameBase.game.clients[index];
+ ent.movetype = Defines.MOVETYPE_WALK;
+ ent.viewheight = 22;
+ ent.inuse = true;
+ ent.classname = "player";
+ ent.mass = 200;
+ ent.solid = Defines.SOLID_BBOX;
+ ent.air_finished = GameBase.level.time + 12;
+ ent.clipmask = Defines.MASK_PLAYERSOLID;
+ ent.model = "players/male/tris.md2";
+ ent.waterlevel = 0;
+ ent.watertype = 0;
+ ent.flags &= ~Defines.FL_NO_KNOCKBACK;
+ ent.svflags &= ~Defines.SVF_DEADMONSTER;
+
+ Math3D.vectorCopy(mins, ent.mins);
+ Math3D.vectorCopy(maxs, ent.maxs);
+ Math3D.vectorClear(ent.velocity);
+
+ // clear playerstate values
+ ent.client.ps.clear();
+
+ client.ps.pmove.origin[0] = (short) (spawn_origin[0] * 8);
+ client.ps.pmove.origin[1] = (short) (spawn_origin[1] * 8);
+ client.ps.pmove.origin[2] = (short) (spawn_origin[2] * 8);
+
+ if (GameBase.deathmatch.value != 0
+ && 0 != ((int) GameBase.dmflags.value & Defines.DF_FIXED_FOV)) {
+ client.ps.fov = 90;
+ } else {
+ client.ps.fov = Lib.atoi(Info.Info_ValueForKey(
+ client.pers.userinfo, "fov"));
+ if (client.ps.fov < 1)
+ client.ps.fov = 90;
+ else if (client.ps.fov > 160)
+ client.ps.fov = 160;
+ }
+
+ // clear entity state values
+ ent.s.effects = 0;
+ ent.s.modelindex = 255; // will use the skin specified model
+ ent.s.modelindex2 = 255; // custom gun model
+ // sknum is player num and weapon number
+ // weapon number will be added in changeweapon
+ ent.s.skinnum = ent.index - 1;
+
+ ent.s.frame = 0;
+ Math3D.vectorCopy(spawn_origin, ent.s.origin);
+ ent.s.origin[2] += 1; // make sure off ground
+ Math3D.vectorCopy(ent.s.origin, ent.s.old_origin);
+
+ // set the delta angle
+ for (i = 0; i < 3; i++) {
+ client.ps.pmove.delta_angles[i] = (short) Math3D
+ .angle2Short(spawn_angles[i] - client.resp.cmd_angles[i]);
+ }
+
+ ent.s.angles[Defines.PITCH] = 0;
+ ent.s.angles[Defines.YAW] = spawn_angles[Defines.YAW];
+ ent.s.angles[Defines.ROLL] = 0;
+ Math3D.vectorCopy(ent.s.angles, client.ps.viewangles);
+ Math3D.vectorCopy(ent.s.angles, client.v_angle);
+
+ // spawn a spectator
+ if (client.pers.spectator) {
+ client.chase_target = null;
+
+ client.resp.spectator = true;
+
+ ent.movetype = Defines.MOVETYPE_NOCLIP;
+ ent.solid = Defines.SOLID_NOT;
+ ent.svflags |= Defines.SVF_NOCLIENT;
+ GameBase.gi.linkentity(ent);
+ return;
+ } else
+ client.resp.spectator = false;
+
+
+ GameBase.gi.linkentity(ent);
+
+ }
+
+ /**
+ * A client has just connected to the server in deathmatch mode, so clear
+ * everything out before starting them.
+ */
+ public static void ClientBeginDeathmatch(EDict ent) {
+ GameUtil.G_InitEdict(ent, ent.index);
+
+ InitClientResp(ent.client);
+
+ // locate ent at a spawn point
+ PutClientInServer(ent);
+
+ if (GameBase.level.intermissiontime != 0) {
+ PlayerHud.MoveClientToIntermission(ent);
+ } else {
+ //gi.WriteShort(ent - g_edicts);
+ GameBase.gi.WriteShort(ent.index);
+ GameBase.gi.WriteByte(Defines.MZ_LOGIN);
+ GameBase.gi.multicast(ent.s.origin, Defines.MULTICAST_PVS);
+ }
+
+ GameBase.gi.bprintf(Defines.PRINT_HIGH, ent.client.pers.netname
+ + " entered the game\n");
+
+ // make sure all view stuff is valid
+ PlayerView.ClientEndServerFrame(ent);
+ }
+
+ /**
+ * Called when a client has finished connecting, and is ready to be placed
+ * into the game. This will happen every level load.
+ */
+ public static void ClientBegin(EDict ent) {
+ int i;
+
+ //ent.client = game.clients + (ent - g_edicts - 1);
+ ent.client = GameBase.game.clients[ent.index - 1];
+
+ if (GameBase.deathmatch.value != 0) {
+ ClientBeginDeathmatch(ent);
+ return;
+ }
+
+ // if there is already a body waiting for us (a loadgame), just
+ // take it, otherwise spawn one from scratch
+ if (ent.inuse) {
+ // the client has cleared the client side viewangles upon
+ // connecting to the server, which is different than the
+ // state when the game is saved, so we need to compensate
+ // with deltaangles
+ for (i = 0; i < 3; i++)
+ ent.client.ps.pmove.delta_angles[i] = (short) Math3D
+ .angle2Short(ent.client.ps.viewangles[i]);
+ } else {
+ // a spawn point will completely reinitialize the entity
+ // except for the persistant data that was initialized at
+ // ClientConnect() time
+ GameUtil.G_InitEdict(ent, ent.index);
+ ent.classname = "player";
+ InitClientResp(ent.client);
+ PutClientInServer(ent);
+ }
+
+ if (GameBase.level.intermissiontime != 0) {
+ PlayerHud.MoveClientToIntermission(ent);
+ }
+
+ // make sure all view stuff is valid
+ PlayerView.ClientEndServerFrame(ent);
+ }
+
+ /**
+ * Called whenever the player updates a userinfo variable.
+ * <p>
+ * The game can override any of the settings in place (forcing skins or
+ * names, etc) before copying it off.
+ */
+ public static String ClientUserinfoChanged(EDict ent, String userinfo) {
+ String s;
+ int playernum;
+
+ // check for malformed or illegal info strings
+ if (!Info.Info_Validate(userinfo)) {
+ return "\\name\\badinfo\\skin\\male/grunt";
+ }
+
+ // set name
+ s = Info.Info_ValueForKey(userinfo, "name");
+
+ ent.client.pers.netname = s;
+
+ // set spectator
+ s = Info.Info_ValueForKey(userinfo, "spectator");
+ // spectators are only supported in deathmatch
+ ent.client.pers.spectator = GameBase.deathmatch.value != 0 && !s.equals("0");
+
+ // set skin
+ s = Info.Info_ValueForKey(userinfo, "skin");
+
+ playernum = ent.index - 1;
+
+ // combine name and skin into a configstring
+ GameBase.gi.configstring(Defines.CS_PLAYERSKINS + playernum,
+ ent.client.pers.netname + "\\" + s);
+
+ // fov
+ if (GameBase.deathmatch.value != 0
+ && 0 != ((int) GameBase.dmflags.value & Defines.DF_FIXED_FOV)) {
+ ent.client.ps.fov = 90;
+ } else {
+ ent.client.ps.fov = Lib
+ .atoi(Info.Info_ValueForKey(userinfo, "fov"));
+ if (ent.client.ps.fov < 1)
+ ent.client.ps.fov = 90;
+ else if (ent.client.ps.fov > 160)
+ ent.client.ps.fov = 160;
+ }
+
+ // handedness
+ s = Info.Info_ValueForKey(userinfo, "hand");
+ if (s.length() > 0) {
+ ent.client.pers.hand = Lib.atoi(s);
+ }
+
+ // save off the userinfo in case we want to check something later
+ ent.client.pers.userinfo = userinfo;
+
+ return userinfo;
+ }
+
+ /**
+ * Called when a player begins connecting to the server. The game can refuse
+ * entrance to a client by returning false. If the client is allowed, the
+ * connection process will continue and eventually get to ClientBegin()
+ * Changing levels will NOT cause this to be called again, but loadgames
+ * will.
+ */
+ public static boolean ClientConnect(EDict ent, String userinfo) {
+ String value;
+
+ // check to see if they are on the banned IP list
+ value = Info.Info_ValueForKey(userinfo, "ip");
+ if (GameSVCmds.SV_FilterPacket(value)) {
+ userinfo = Info.Info_SetValueForKey(userinfo, "rejmsg", "Banned.");
+ return false;
+ }
+
+ // check for a spectator
+ value = Info.Info_ValueForKey(userinfo, "spectator");
+ if (GameBase.deathmatch.value != 0 && value.length() != 0
+ && 0 != Lib.strcmp(value, "0")) {
+ int i, numspec;
+
+ if (!passwdOK(GameBase.spectator_password.string, value)) {
+ userinfo = Info.Info_SetValueForKey(userinfo, "rejmsg",
+ "Spectator password required or incorrect.");
+ return false;
+ }
+
+ // count spectators
+ for (i = numspec = 0; i < GameBase.maxclients.value; i++)
+ if (GameBase.g_edicts[i + 1].inuse
+ && GameBase.g_edicts[i + 1].client.pers.spectator)
+ numspec++;
+
+ if (numspec >= GameBase.maxspectators.value) {
+ userinfo = Info.Info_SetValueForKey(userinfo, "rejmsg",
+ "Server spectator limit is full.");
+ return false;
+ }
+ } else {
+ // check for a password
+ value = Info.Info_ValueForKey(userinfo, "password");
+ if (!passwdOK(GameBase.spectator_password.string, value)) {
+ userinfo = Info.Info_SetValueForKey(userinfo, "rejmsg",
+ "Password required or incorrect.");
+ return false;
+ }
+ }
+
+ // they can connect
+ ent.client = GameBase.game.clients[ent.index - 1];
+
+ // if there is already a body waiting for us (a loadgame), just
+ // take it, otherwise spawn one from scratch
+ if (!ent.inuse) {
+ // clear the respawning variables
+ InitClientResp(ent.client);
+ }
+
+ ClientUserinfoChanged(ent, userinfo);
+
+ if (GameBase.game.maxclients > 1)
+ GameBase.gi.dprintf(ent.client.pers.netname + " connected\n");
+
+ ent.svflags = 0; // make sure we start with known default
+ ent.client.pers.connected = true;
+ return true;
+ }
+
+ /**
+ * Called when a player drops from the server. Will not be called between levels.
+ */
+ public static void ClientDisconnect(EDict ent) {
+ int playernum;
+
+ if (ent.client == null)
+ return;
+
+ GameBase.gi.bprintf(Defines.PRINT_HIGH, ent.client.pers.netname
+ + " disconnected\n");
+
+
+ GameBase.gi.unlinkentity(ent);
+ ent.s.modelindex = 0;
+ ent.solid = Defines.SOLID_NOT;
+ ent.inuse = false;
+ ent.classname = "disconnected";
+ ent.client.pers.connected = false;
+
+ playernum = ent.index - 1;
+ GameBase.gi.configstring(Defines.CS_PLAYERSKINS + playernum, "");
+ }
+
+ /*
+ * static int CheckBlock(int c)
+ * {
+ * int v, i;
+ * v = 0;
+ * for (i = 0; i < c; i++)
+ * v += ((byte *) b)[i];
+ * return v;
+ * }
+ *
+ * public static void PrintPmove(pmove_t * pm)
+ * {
+ * unsigned c1, c2;
+ *
+ * c1 = CheckBlock(&pm.s, sizeof(pm.s));
+ * c2 = CheckBlock(&pm.cmd, sizeof(pm.cmd));
+ * Com_Printf("sv %3i:%i %i\n", pm.cmd.impulse, c1, c2);
+ * }
+ */
+
+ /**
+ * This will be called once for each client frame, which will usually be a
+ * couple times for each server frame.
+ */
+ public static void ClientThink(EDict ent, usercmd_t ucmd) {
+ Client client;
+ EDict other;
+ int i, j;
+ Move pm = null;
+
+ GameBase.level.current_entity = ent;
+ client = ent.client;
+
+ if (GameBase.level.intermissiontime != 0) {
+ client.ps.pmove.pm_type = Defines.PM_FREEZE;
+ // can exit intermission after five seconds
+ if (GameBase.level.time > GameBase.level.intermissiontime + 5.0f
+ && 0 != (ucmd.buttons & Defines.BUTTON_ANY))
+ GameBase.level.exitintermission = true;
+ return;
+ }
+
+ PlayerClient.pm_passent = ent;
+
+ if (ent.client.chase_target != null) {
+
+ client.resp.cmd_angles[0] = Math3D.short2Angle(ucmd.angles[0]);
+ client.resp.cmd_angles[1] = Math3D.short2Angle(ucmd.angles[1]);
+ client.resp.cmd_angles[2] = Math3D.short2Angle(ucmd.angles[2]);
+
+ } else {
+
+ // set up for pmove
+ pm = new Move();
+
+ if (ent.movetype == Defines.MOVETYPE_NOCLIP)
+ client.ps.pmove.pm_type = Defines.PM_SPECTATOR;
+ else if (ent.s.modelindex != 255)
+ client.ps.pmove.pm_type = Defines.PM_GIB;
+ else
+ client.ps.pmove.pm_type = Defines.PM_NORMAL;
+
+ client.ps.pmove.gravity = (short) GameBase.sv_gravity.value;
+ pm.s.set(client.ps.pmove);
+
+ for (i = 0; i < 3; i++) {
+ pm.s.origin[i] = (short) (ent.s.origin[i] * 8);
+ pm.s.velocity[i] = (short) (ent.velocity[i] * 8);
+ }
+
+ if (client.old_pmove.equals(pm.s)) {
+ pm.snapinitial = true;
+ // gi.dprintf ("pmove changed!\n");
+ }
+
+ // this should be a copy
+ pm.cmd.set(ucmd);
+
+ pm.trace = PlayerClient.PM_trace; // adds default parms
+ pm.pointcontents = GameBase.gi.pointcontents;
+
+ // perform a pmove
+ GameBase.gi.Pmove(pm);
+
+ // save results of pmove
+ client.ps.pmove.set(pm.s);
+ client.old_pmove.set(pm.s);
+
+ for (i = 0; i < 3; i++) {
+ ent.s.origin[i] = pm.s.origin[i] * 0.125f;
+ ent.velocity[i] = pm.s.velocity[i] * 0.125f;
+ }
+
+ Math3D.vectorCopy(pm.mins, ent.mins);
+ Math3D.vectorCopy(pm.maxs, ent.maxs);
+
+ client.resp.cmd_angles[0] = Math3D.short2Angle(ucmd.angles[0]);
+ client.resp.cmd_angles[1] = Math3D.short2Angle(ucmd.angles[1]);
+ client.resp.cmd_angles[2] = Math3D.short2Angle(ucmd.angles[2]);
+
+ if (ent.groundentity != null && null == pm.groundentity
+ && (pm.cmd.upmove >= 10) && (pm.waterlevel == 0)) {
+ GameBase.gi.sound(ent, Defines.CHAN_VOICE, GameBase.gi
+ .soundindex("*jump1.wav"), 1, Defines.ATTN_NORM, 0);
+ }
+
+ ent.viewheight = (int) pm.viewheight;
+ ent.waterlevel = pm.waterlevel;
+ ent.watertype = pm.watertype;
+ ent.groundentity = pm.groundentity;
+ if (pm.groundentity != null)
+ ent.groundentity_linkcount = pm.groundentity.linkcount;
+
+ Math3D.vectorCopy(pm.viewangles, client.v_angle);
+ Math3D.vectorCopy(pm.viewangles, client.ps.viewangles);
+
+ GameBase.gi.linkentity(ent);
+
+ if (ent.movetype != Defines.MOVETYPE_NOCLIP)
+ GameBase.G_TouchTriggers(ent);
+
+ // touch other objects
+ for (i = 0; i < pm.numtouch; i++) {
+ other = pm.touchents[i];
+ for (j = 0; j < i; j++)
+ if (pm.touchents[j] == other)
+ break;
+ if (j != i)
+ continue; // duplicated
+ if (other.touch == null)
+ continue;
+ other.touch.touch(other, ent, GameBase.dummyplane, null);
+ }
+
+ }
+
+ client.oldbuttons = client.buttons;
+ client.buttons = ucmd.buttons;
+ client.latched_buttons |= client.buttons & ~client.oldbuttons;
+
+ // save light level the player is standing on for
+ // monster sighting AI
+ ent.light_level = ucmd.lightlevel;
+
+ // fire weapon from final position if needed
+ if ((client.latched_buttons & Defines.BUTTON_ATTACK) != 0) {
+ if (client.resp.spectator) {
+
+ client.latched_buttons = 0;
+
+ if (client.chase_target != null) {
+ client.chase_target = null;
+ client.ps.pmove.pm_flags &= ~Move.PMF_NO_PREDICTION;
+ }
+
+ }
+ }
+
+ }
+
+ /**
+ * This will be called once for each server frame, before running any other
+ * entities in the world.
+ */
+ public static void clientBeginServerFrame(EDict ent) {
+ Client client;
+ int buttonMask;
+
+ if (GameBase.level.intermissiontime != 0)
+ return;
+
+ client = ent.client;
+
+ if (GameBase.deathmatch.value != 0
+ && client.pers.spectator != client.resp.spectator
+ && (GameBase.level.time - client.respawn_time) >= 5) {
+ spectator_respawn(ent);
+ return;
+ }
+
+
+ // add player trail so monsters can follow
+ if (GameBase.deathmatch.value != 0)
+ if (!GameUtil.visible(ent, PlayerTrail.LastSpot()))
+ PlayerTrail.Add(ent.s.old_origin);
+
+ client.latched_buttons = 0;
+ }
+
+
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.game;
+
+import lwjake2.Defines;
+import lwjake2.qcommon.Com;
+import lwjake2.util.Lib;
+import lwjake2.util.Math3D;
+import lwjake2.util.Vargs;
+
+class PlayerHud {
+
+ /*
+ * ======================================================================
+ *
+ * INTERMISSION
+ *
+ * ======================================================================
+ */
+
+ public static void MoveClientToIntermission(EDict ent) {
+ if (GameBase.deathmatch.value != 0 || GameBase.coop.value != 0)
+ ent.client.showscores = true;
+ Math3D.vectorCopy(GameBase.level.intermission_origin, ent.s.origin);
+ ent.client.ps.pmove.origin[0] = (short) (GameBase.level.intermission_origin[0] * 8);
+ ent.client.ps.pmove.origin[1] = (short) (GameBase.level.intermission_origin[1] * 8);
+ ent.client.ps.pmove.origin[2] = (short) (GameBase.level.intermission_origin[2] * 8);
+ Math3D.vectorCopy(GameBase.level.intermission_angle,
+ ent.client.ps.viewangles);
+ ent.client.ps.pmove.pm_type = Defines.PM_FREEZE;
+ ent.client.ps.blend[3] = 0;
+ ent.client.ps.rdflags &= ~Defines.RDF_UNDERWATER;
+
+ // clean up powerup info
+ ent.client.quad_framenum = 0;
+ ent.client.invincible_framenum = 0;
+ ent.client.enviro_framenum = 0;
+
+ ent.viewheight = 0;
+ ent.s.modelindex = 0;
+ ent.s.modelindex2 = 0;
+ ent.s.modelindex3 = 0;
+ ent.s.modelindex = 0;
+ ent.s.effects = 0;
+ ent.s.sound = 0;
+ ent.solid = Defines.SOLID_NOT;
+
+ // add the layout
+
+ if (GameBase.deathmatch.value != 0 || GameBase.coop.value != 0) {
+ DeathmatchScoreboardMessage(ent, null);
+ GameBase.gi.unicast(ent, true);
+ }
+
+ }
+
+ public static void BeginIntermission(EDict targ) {
+ int i, n;
+ EDict ent, client;
+
+ if (GameBase.level.intermissiontime != 0)
+ return; // already activated
+
+ GameBase.game.autosaved = false;
+
+ // respawn any dead clients
+ for (i = 0; i < GameBase.maxclients.value; i++) {
+ client = GameBase.g_edicts[1 + i];
+ if (!client.inuse)
+ continue;
+ if (client.health <= 0)
+ PlayerClient.respawn(client);
+ }
+
+ GameBase.level.intermissiontime = GameBase.level.time;
+ GameBase.level.changemap = targ.map;
+
+ if (GameBase.level.changemap.indexOf('*') > -1) {
+ if (GameBase.coop.value != 0) {
+ for (i = 0; i < GameBase.maxclients.value; i++) {
+ client = GameBase.g_edicts[1 + i];
+ if (!client.inuse) {
+ }
+ }
+ }
+ } else {
+ if (0 == GameBase.deathmatch.value) {
+ GameBase.level.exitintermission = true; // go immediately to the
+ // next level
+ return;
+ }
+ }
+
+ GameBase.level.exitintermission = false;
+
+ // find an intermission spot
+ ent = GameBase.G_FindEdict(null, GameBase.findByClass,
+ "info_player_intermission");
+ if (ent == null) { // the map creator forgot to put in an intermission
+ // point...
+ ent = GameBase.G_FindEdict(null, GameBase.findByClass,
+ "info_player_start");
+ if (ent == null)
+ ent = GameBase.G_FindEdict(null, GameBase.findByClass,
+ "info_player_deathmatch");
+ } else { // chose one of four spots
+ i = Lib.rand() & 3;
+ EdictIterator es = null;
+
+ while (i-- > 0) {
+ es = GameBase.G_Find(es, GameBase.findByClass,
+ "info_player_intermission");
+
+ if (es == null) // wrap around the list
+ continue;
+ ent = es.o;
+ }
+ }
+
+ Math3D.vectorCopy(ent.s.origin, GameBase.level.intermission_origin);
+ Math3D.vectorCopy(ent.s.angles, GameBase.level.intermission_angle);
+
+ // move all clients to the intermission point
+ for (i = 0; i < GameBase.maxclients.value; i++) {
+ client = GameBase.g_edicts[1 + i];
+ if (!client.inuse)
+ continue;
+ MoveClientToIntermission(client);
+ }
+ }
+
+ /*
+ * ==================
+ * DeathmatchScoreboardMessage
+ * ==================
+ */
+ public static void DeathmatchScoreboardMessage(EDict ent, EDict killer) {
+ StringBuilder string = new StringBuilder(1400);
+
+ int i, j, k;
+ int sorted[] = new int[Defines.MAX_CLIENTS];
+ int sortedscores[] = new int[Defines.MAX_CLIENTS];
+ int score, total;
+ int x, y;
+ Client cl;
+ EDict cl_ent;
+ String tag;
+
+ // sort the clients by score
+ total = 0;
+ for (i = 0; i < GameBase.game.maxclients; i++) {
+ cl_ent = GameBase.g_edicts[1 + i];
+ if (!cl_ent.inuse || GameBase.game.clients[i].resp.spectator)
+ continue;
+ score = GameBase.game.clients[i].resp.score;
+ for (j = 0; j < total; j++) {
+ if (score > sortedscores[j])
+ break;
+ }
+ for (k = total; k > j; k--) {
+ sorted[k] = sorted[k - 1];
+ sortedscores[k] = sortedscores[k - 1];
+ }
+ sorted[j] = i;
+ sortedscores[j] = score;
+ total++;
+ }
+
+ // print level name and exit rules
+
+ // add the clients in sorted order
+ if (total > 12)
+ total = 12;
+
+ for (i = 0; i < total; i++) {
+ cl = GameBase.game.clients[sorted[i]];
+ cl_ent = GameBase.g_edicts[1 + sorted[i]];
+
+ GameBase.gi.imageindex("i_fixme");
+ x = (i >= 6) ? 160 : 0;
+ y = 32 + 32 * (i % 6);
+
+ // add a dogtag
+ if (cl_ent == ent)
+ tag = "tag1";
+ else if (cl_ent == killer)
+ tag = "tag2";
+ else
+ tag = null;
+
+ if (tag != null) {
+ string.append("xv ").append(x + 32).append(" yv ").append(y)
+ .append(" picn ").append(tag);
+ }
+
+ // send the layout
+ string
+ .append(" client ")
+ .append(x)
+ .append(" ")
+ .append(y)
+ .append(" ")
+ .append(sorted[i])
+ .append(" ")
+ .append(cl.resp.score)
+ .append(" ")
+ .append(cl.ping)
+ .append(" ")
+ .append(
+ (GameBase.level.framenum - cl.resp.enterframe) / 600);
+ }
+
+ GameBase.gi.WriteByte(Defines.svc_layout);
+ GameBase.gi.WriteString(string.toString());
+ }
+
+ /*
+ * ==================
+ * DeathmatchScoreboard
+ *
+ * Draw instead of help message. Note that it isn't that hard to overflow
+ * the 1400 byte message limit!
+ * ==================
+ */
+ public static void DeathmatchScoreboard(EDict ent) {
+ DeathmatchScoreboardMessage(ent, ent.enemy);
+ GameBase.gi.unicast(ent, true);
+ }
+
+ /*
+ * ==================
+ * Cmd_Score_f
+ *
+ * Display the scoreboard
+ * ==================
+ */
+ public static void Cmd_Score_f(EDict ent) {
+ ent.client.showinventory = false;
+ ent.client.showhelp = false;
+
+ if (0 == GameBase.deathmatch.value && 0 == GameBase.coop.value)
+ return;
+
+ if (ent.client.showscores) {
+ ent.client.showscores = false;
+ return;
+ }
+
+ ent.client.showscores = true;
+ DeathmatchScoreboard(ent);
+ }
+
+ //=======================================================================
+
+ /*
+ * ===============
+ * G_SetStats
+ * ===============
+ */
+ public static void G_SetStats(EDict ent) {
+ int index, cells = 0;
+ int power_armor_type;
+
+ //
+ // health
+ //
+ ent.client.ps.stats[Defines.STAT_HEALTH_ICON] = (short) GameBase.level.pic_health;
+ ent.client.ps.stats[Defines.STAT_HEALTH] = (short) ent.health;
+
+
+ //
+ // pickup message
+ //
+ if (GameBase.level.time > ent.client.pickup_msg_time) {
+ ent.client.ps.stats[Defines.STAT_PICKUP_ICON] = 0;
+ ent.client.ps.stats[Defines.STAT_PICKUP_STRING] = 0;
+ }
+
+ //
+ // timers
+ //
+ if (ent.client.quad_framenum > GameBase.level.framenum) {
+ ent.client.ps.stats[Defines.STAT_TIMER_ICON] = (short) GameBase.gi
+ .imageindex("p_quad");
+ ent.client.ps.stats[Defines.STAT_TIMER] = (short) ((ent.client.quad_framenum - GameBase.level.framenum) / 10);
+ } else if (ent.client.invincible_framenum > GameBase.level.framenum) {
+ ent.client.ps.stats[Defines.STAT_TIMER_ICON] = (short) GameBase.gi
+ .imageindex("p_invulnerability");
+ ent.client.ps.stats[Defines.STAT_TIMER] = (short) ((ent.client.invincible_framenum - GameBase.level.framenum) / 10);
+ } else if (ent.client.enviro_framenum > GameBase.level.framenum) {
+ ent.client.ps.stats[Defines.STAT_TIMER_ICON] = (short) GameBase.gi
+ .imageindex("p_envirosuit");
+ ent.client.ps.stats[Defines.STAT_TIMER] = (short) ((ent.client.enviro_framenum - GameBase.level.framenum) / 10);
+ } else {
+ ent.client.ps.stats[Defines.STAT_TIMER_ICON] = 0;
+ ent.client.ps.stats[Defines.STAT_TIMER] = 0;
+ }
+
+ //
+ // layouts
+ //
+ ent.client.ps.stats[Defines.STAT_LAYOUTS] = 0;
+
+ if (GameBase.deathmatch.value != 0) {
+ if (ent.client.pers.health <= 0
+ || GameBase.level.intermissiontime != 0
+ || ent.client.showscores)
+ ent.client.ps.stats[Defines.STAT_LAYOUTS] |= 1;
+ if (ent.client.showinventory && ent.client.pers.health > 0)
+ ent.client.ps.stats[Defines.STAT_LAYOUTS] |= 2;
+ } else {
+ if (ent.client.showscores || ent.client.showhelp)
+ ent.client.ps.stats[Defines.STAT_LAYOUTS] |= 1;
+ if (ent.client.showinventory && ent.client.pers.health > 0)
+ ent.client.ps.stats[Defines.STAT_LAYOUTS] |= 2;
+ }
+
+ //
+ // frags
+ //
+ ent.client.ps.stats[Defines.STAT_FRAGS] = (short) ent.client.resp.score;
+
+ //
+ // help icon / current weapon if not shown
+ //
+ if (ent.client.pers.helpchanged != 0
+ && (GameBase.level.framenum & 8) != 0)
+ ent.client.ps.stats[Defines.STAT_HELPICON] = (short) GameBase.gi
+ .imageindex("i_help");
+ else
+ ent.client.ps.stats[Defines.STAT_HELPICON] = 0;
+
+ ent.client.ps.stats[Defines.STAT_SPECTATOR] = 0;
+ }
+
+ /*
+ * ===============
+ * G_CheckChaseStats
+ * ===============
+ */
+ public static void G_CheckChaseStats(EDict ent) {
+ int i;
+ Client cl;
+
+ for (i = 1; i <= GameBase.maxclients.value; i++) {
+ cl = GameBase.g_edicts[i].client;
+ if (!GameBase.g_edicts[i].inuse || cl.chase_target != ent)
+ continue;
+ //memcpy(cl.ps.stats, ent.client.ps.stats, sizeof(cl.ps.stats));
+ System.arraycopy(ent.client.ps.stats, 0, cl.ps.stats, 0,
+ Defines.MAX_STATS);
+
+ G_SetSpectatorStats(GameBase.g_edicts[i]);
+ }
+ }
+
+ /*
+ * ===============
+ * G_SetSpectatorStats
+ * ===============
+ */
+ public static void G_SetSpectatorStats(EDict ent) {
+ Client cl = ent.client;
+
+ if (null == cl.chase_target)
+ G_SetStats(ent);
+
+ cl.ps.stats[Defines.STAT_SPECTATOR] = 1;
+
+ // layouts are independant in spectator
+ cl.ps.stats[Defines.STAT_LAYOUTS] = 0;
+ if (cl.pers.health <= 0 || GameBase.level.intermissiontime != 0
+ || cl.showscores)
+ cl.ps.stats[Defines.STAT_LAYOUTS] |= 1;
+ if (cl.showinventory && cl.pers.health > 0)
+ cl.ps.stats[Defines.STAT_LAYOUTS] |= 2;
+
+ if (cl.chase_target != null && cl.chase_target.inuse)
+ //cl.ps.stats[STAT_CHASE] = (short) (CS_PLAYERSKINS +
+ // (cl.chase_target - g_edicts) - 1);
+ cl.ps.stats[Defines.STAT_CHASE] = (short) (Defines.CS_PLAYERSKINS
+ + cl.chase_target.index - 1);
+ else
+ cl.ps.stats[Defines.STAT_CHASE] = 0;
+ }
+
+ /**
+ * HelpComputer. Draws the help computer.
+ */
+ public static void HelpComputer(EDict ent) {
+ String sb = "xv 32 yv 8 picn help " + // background
+ "xv 0 yv 24 cstring2 \"" + GameBase.level.level_name +
+ "\" " + // level name
+ "xv 0 yv 54 cstring2 \"" + GameBase.game.helpmessage1 +
+ "\" " + // help 1
+ "xv 0 yv 110 cstring2 \"" + GameBase.game.helpmessage2 +
+ "\" " + // help 2
+ "xv 50 yv 164 string2 \" kills goals secrets\" " +
+ "xv 50 yv 172 string2 \"" +
+ Com.sprintf("%3i/%3i %i/%i %i/%i\" ", new Vargs(6)
+ .add(
+ GameBase.level.found_goals).add(
+ GameBase.level.total_goals).add(
+ GameBase.level.found_secrets).add(
+ GameBase.level.total_secrets));
+
+ // send the layout
+
+ GameBase.gi.WriteByte(Defines.svc_layout);
+ GameBase.gi.WriteString(sb);
+ GameBase.gi.unicast(ent, true);
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.game;
+
+import lwjake2.util.Math3D;
+
+class PlayerTrail {
+
+ /*
+ * ==============================================================================
+ *
+ * PLAYER TRAIL
+ *
+ * ==============================================================================
+ *
+ * This is a circular list containing the a list of points of where the
+ * player has been recently. It is used by monsters for pursuit.
+ *
+ * .origin the spot .owner forward link .aiment backward link
+ */
+
+ private static final int TRAIL_LENGTH = 8;
+
+ private static final EDict[] trail = new EDict[TRAIL_LENGTH];
+
+ private static int trail_head;
+
+ private static boolean trail_active = false;
+
+ static {
+ //TODO: potential error
+ for (int n = 0; n < TRAIL_LENGTH; n++)
+ trail[n] = new EDict(n);
+ }
+
+ private static int NEXT(int n) {
+ return (n + 1) % PlayerTrail.TRAIL_LENGTH;
+ }
+
+ private static int PREV(int n) {
+ return (n + PlayerTrail.TRAIL_LENGTH - 1) % PlayerTrail.TRAIL_LENGTH;
+ }
+
+ static void Init() {
+
+ // FIXME || coop
+ if (GameBase.deathmatch.value != 0)
+ return;
+
+ for (int n = 0; n < PlayerTrail.TRAIL_LENGTH; n++) {
+ PlayerTrail.trail[n] = GameUtil.G_Spawn();
+ PlayerTrail.trail[n].classname = "player_trail";
+ }
+
+ trail_head = 0;
+ trail_active = true;
+ }
+
+ static void Add(float[] spot) {
+ float[] temp = {0, 0, 0};
+
+ if (!trail_active)
+ return;
+
+ Math3D.vectorCopy(spot, PlayerTrail.trail[trail_head].s.origin);
+
+ PlayerTrail.trail[trail_head].timestamp = GameBase.level.time;
+
+ Math3D.vectorSubtract(spot,
+ PlayerTrail.trail[PREV(trail_head)].s.origin, temp);
+ PlayerTrail.trail[trail_head].s.angles[1] = Math3D.vectoyaw(temp);
+
+ trail_head = NEXT(trail_head);
+ }
+
+
+ static EDict LastSpot() {
+ return PlayerTrail.trail[PREV(trail_head)];
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.game;
+
+import lwjake2.Defines;
+import lwjake2.util.Lib;
+import lwjake2.util.Math3D;
+
+public class PlayerView {
+
+ public static final float[] forward = {0, 0, 0};
+ public static final float[] right = {0, 0, 0};
+ public static final float[] up = {0, 0, 0};
+ public static EDict current_player;
+ public static Client current_client;
+ public static float xyspeed;
+
+ /*
+ * ===============
+ * P_DamageFeedback
+ *
+ * Handles color blends and view kicks
+ * ===============
+ */
+ public static float bobmove;
+ public static int bobcycle; // odd cycles are right foot going forward
+ public static float bobfracsin; // sin(bobfrac*M_PI)}
+ private static int xxxi = 0;
+
+ /**
+ * SV_CalcRoll.
+ */
+ public static float SV_CalcRoll(float[] angles, float[] velocity) {
+ float sign;
+ float side;
+ float value;
+
+ side = Math3D.dotProduct(velocity, right);
+ sign = side < 0 ? -1 : 1;
+ side = Math.abs(side);
+
+ value = GameBase.sv_rollangle.value;
+
+ if (side < GameBase.sv_rollspeed.value)
+ side = side * value / GameBase.sv_rollspeed.value;
+ else
+ side = value;
+
+ return side * sign;
+ }
+
+ public static void P_DamageFeedback(EDict player) {
+ Client client;
+ float side;
+ float realcount, kick;
+ float[] v = {0, 0, 0};
+ int r, l;
+ float[] power_color = {0.0f, 1.0f, 0.0f};
+ float[] acolor = {1.0f, 1.0f, 1.0f};
+ float[] bcolor = {1.0f, 0.0f, 0.0f};
+
+ client = player.client;
+
+ // flash the backgrounds behind the status numbers
+ client.ps.stats[Defines.STAT_FLASHES] = 0;
+ // total points of damage shot at the player this frame
+ }
+
+ /**
+ * fall from 128: 400 = 160000
+ * fall from 256: 580 = 336400
+ * fall from 384: 720 = 518400
+ * fall from 512: 800 = 640000
+ * fall from 640: 960 =
+ * damage = deltavelocity*deltavelocity * 0.0001
+ */
+ public static void SV_CalcViewOffset(EDict ent) {
+ float angles[] = {0, 0, 0};
+ float bob;
+ float ratio;
+ float delta;
+ float[] v = {0, 0, 0};
+
+ // base angles
+ angles = ent.client.ps.kick_angles;
+
+ // if dead, fix the angle and don't add any kick
+
+ Math3D.vectorClear(angles);
+
+ // add angles based on damage kick
+ ratio = (ent.client.v_dmg_time - GameBase.level.time)
+ / Defines.DAMAGE_TIME;
+ if (ratio < 0) {
+ ratio = 0;
+ ent.client.v_dmg_pitch = 0;
+ ent.client.v_dmg_roll = 0;
+ }
+ angles[Defines.PITCH] += ratio * ent.client.v_dmg_pitch;
+ angles[Defines.ROLL] += ratio * ent.client.v_dmg_roll;
+
+ // add pitch based on fall kick
+ ratio = (ent.client.fall_time - GameBase.level.time)
+ / Defines.FALL_TIME;
+ if (ratio < 0)
+ ratio = 0;
+ angles[Defines.PITCH] += ratio * ent.client.fall_value;
+
+ // add angles based on velocity
+ delta = Math3D.dotProduct(ent.velocity, forward);
+ angles[Defines.PITCH] += delta * GameBase.run_pitch.value;
+
+ delta = Math3D.dotProduct(ent.velocity, right);
+ angles[Defines.ROLL] += delta * GameBase.run_roll.value;
+
+ // add angles based on bob
+ delta = bobfracsin * GameBase.bob_pitch.value * xyspeed;
+ if ((ent.client.ps.pmove.pm_flags & Move.PMF_DUCKED) != 0)
+ delta *= 6; // crouching
+ angles[Defines.PITCH] += delta;
+ delta = bobfracsin * GameBase.bob_roll.value * xyspeed;
+ if ((ent.client.ps.pmove.pm_flags & Move.PMF_DUCKED) != 0)
+ delta *= 6; // crouching
+ if ((bobcycle & 1) != 0)
+ delta = -delta;
+ angles[Defines.ROLL] += delta;
+
+ // base origin
+ Math3D.vectorClear(v);
+
+ // add view height
+ v[2] += ent.viewheight;
+
+ // add fall height
+ ratio = (ent.client.fall_time - GameBase.level.time)
+ / Defines.FALL_TIME;
+ if (ratio < 0)
+ ratio = 0;
+ v[2] -= ratio * ent.client.fall_value * 0.4;
+
+ // add bob height
+ bob = bobfracsin * xyspeed * GameBase.bob_up.value;
+ if (bob > 6)
+ bob = 6;
+
+ //gi.DebugGraph (bob *2, 255);
+ v[2] += bob;
+
+ // add kick offset
+
+ // absolutely bound offsets
+ // so the view can never be outside the player box
+
+ if (v[0] < -14)
+ v[0] = -14;
+ else if (v[0] > 14)
+ v[0] = 14;
+ if (v[1] < -14)
+ v[1] = -14;
+ else if (v[1] > 14)
+ v[1] = 14;
+ if (v[2] < -22)
+ v[2] = -22;
+ else if (v[2] > 30)
+ v[2] = 30;
+
+ Math3D.vectorCopy(v, ent.client.ps.viewoffset);
+ }
+
+ /**
+ * Calculates where to draw the gun.
+ */
+ public static void SV_CalcGunOffset(EDict ent) {
+
+ }
+
+ /**
+ * Adds a blending effect to the clients view.
+ */
+ public static void SV_AddBlend(float r, float g, float b, float a,
+ float v_blend[]) {
+ float a2, a3;
+
+ if (a <= 0)
+ return;
+ a2 = v_blend[3] + (1 - v_blend[3]) * a; // new total alpha
+ a3 = v_blend[3] / a2; // fraction of color from old
+
+ v_blend[0] = v_blend[0] * a3 + r * (1 - a3);
+ v_blend[1] = v_blend[1] * a3 + g * (1 - a3);
+ v_blend[2] = v_blend[2] * a3 + b * (1 - a3);
+ v_blend[3] = a2;
+ }
+
+ /**
+ * Calculates the blending color according to the players environment.
+ */
+ public static void SV_CalcBlend(EDict ent) {
+ int contents;
+ float[] vieworg = {0, 0, 0};
+ int remaining;
+
+ ent.client.ps.blend[0] = ent.client.ps.blend[1] = ent.client.ps.blend[2] = ent.client.ps.blend[3] = 0;
+
+ // add for contents
+ Math3D.vectorAdd(ent.s.origin, ent.client.ps.viewoffset, vieworg);
+ contents = GameBase.gi.pointcontents.pointcontents(vieworg);
+ if ((contents & (Defines.CONTENTS_LAVA | Defines.CONTENTS_SLIME | Defines.CONTENTS_WATER)) != 0)
+ ent.client.ps.rdflags |= Defines.RDF_UNDERWATER;
+ else
+ ent.client.ps.rdflags &= ~Defines.RDF_UNDERWATER;
+
+ if ((contents & (Defines.CONTENTS_SOLID | Defines.CONTENTS_LAVA)) != 0)
+ SV_AddBlend(1.0f, 0.3f, 0.0f, 0.6f, ent.client.ps.blend);
+ else if ((contents & Defines.CONTENTS_SLIME) != 0)
+ SV_AddBlend(0.0f, 0.1f, 0.05f, 0.6f, ent.client.ps.blend);
+ else if ((contents & Defines.CONTENTS_WATER) != 0)
+ SV_AddBlend(0.5f, 0.3f, 0.2f, 0.4f, ent.client.ps.blend);
+
+ // add for powerups
+ if (ent.client.quad_framenum > GameBase.level.framenum) {
+ remaining = (int) (ent.client.quad_framenum - GameBase.level.framenum);
+ if (remaining == 30) // beginning to fade
+ GameBase.gi.sound(ent, Defines.CHAN_ITEM,
+ GameBase.gi.soundindex("items/damage2.wav"), 1, Defines.ATTN_NORM, 0);
+ if (remaining > 30 || (remaining & 4) != 0)
+ SV_AddBlend(0, 0, 1, 0.08f, ent.client.ps.blend);
+ } else if (ent.client.invincible_framenum > GameBase.level.framenum) {
+ remaining = (int) ent.client.invincible_framenum - GameBase.level.framenum;
+ if (remaining == 30) // beginning to fade
+ GameBase.gi.sound(ent, Defines.CHAN_ITEM,
+ GameBase.gi.soundindex("items/protect2.wav"), 1, Defines.ATTN_NORM, 0);
+ if (remaining > 30 || (remaining & 4) != 0)
+ SV_AddBlend(1, 1, 0, 0.08f, ent.client.ps.blend);
+ } else if (ent.client.enviro_framenum > GameBase.level.framenum) {
+ remaining = (int) ent.client.enviro_framenum
+ - GameBase.level.framenum;
+ if (remaining == 30) // beginning to fade
+ GameBase.gi.sound(ent, Defines.CHAN_ITEM,
+ GameBase.gi.soundindex("items/airout.wav"), 1, Defines.ATTN_NORM, 0);
+ if (remaining > 30 || (remaining & 4) != 0)
+ SV_AddBlend(0, 1, 0, 0.08f, ent.client.ps.blend);
+ }
+
+ }
+
+
+ /**
+ * General effect handling for a player.
+ */
+ public static void P_WorldEffects() {
+ boolean envirosuit;
+ int waterlevel, old_waterlevel;
+
+ if (current_player.movetype == Defines.MOVETYPE_NOCLIP) {
+ current_player.air_finished = GameBase.level.time + 12; // don't
+ // need air
+ return;
+ }
+
+ waterlevel = current_player.waterlevel;
+ old_waterlevel = current_client.old_waterlevel;
+ current_client.old_waterlevel = waterlevel;
+
+ envirosuit = current_client.enviro_framenum > GameBase.level.framenum;
+
+ //
+ // if just entered a water volume, play a sound
+ //
+ if (old_waterlevel == 0 && waterlevel != 0) {
+ if ((current_player.watertype & Defines.CONTENTS_LAVA) != 0)
+ GameBase.gi.sound(current_player, Defines.CHAN_BODY,
+ GameBase.gi.soundindex("player/lava_in.wav"), 1,
+ Defines.ATTN_NORM, 0);
+ else if ((current_player.watertype & Defines.CONTENTS_SLIME) != 0)
+ GameBase.gi.sound(current_player, Defines.CHAN_BODY,
+ GameBase.gi.soundindex("player/watr_in.wav"), 1,
+ Defines.ATTN_NORM, 0);
+ else if ((current_player.watertype & Defines.CONTENTS_WATER) != 0)
+ GameBase.gi.sound(current_player, Defines.CHAN_BODY,
+ GameBase.gi.soundindex("player/watr_in.wav"), 1,
+ Defines.ATTN_NORM, 0);
+ current_player.flags |= Defines.FL_INWATER;
+
+ // clear damage_debounce, so the pain sound will play immediately
+ current_player.damage_debounce_time = GameBase.level.time - 1;
+ }
+
+ //
+ // if just completely exited a water volume, play a sound
+ //
+ if (old_waterlevel != 0 && waterlevel == 0) {
+
+ GameBase.gi
+ .sound(current_player, Defines.CHAN_BODY, GameBase.gi
+ .soundindex("player/watr_out.wav"), 1,
+ Defines.ATTN_NORM, 0);
+ current_player.flags &= ~Defines.FL_INWATER;
+ }
+
+ //
+ // check for head just going under water
+ //
+ if (old_waterlevel != 3 && waterlevel == 3) {
+ GameBase.gi.sound(current_player, Defines.CHAN_BODY, GameBase.gi
+ .soundindex("player/watr_un.wav"), 1, Defines.ATTN_NORM, 0);
+ }
+
+ //
+ // check for head just coming out of water
+ //
+ if (old_waterlevel == 3 && waterlevel != 3) {
+ if (current_player.air_finished < GameBase.level.time) { // gasp for
+ // air
+ GameBase.gi.sound(current_player, Defines.CHAN_VOICE,
+ GameBase.gi.soundindex("player/gasp1.wav"), 1,
+ Defines.ATTN_NORM, 0);
+ } else if (current_player.air_finished < GameBase.level.time + 11) { // just
+ // break
+ // surface
+ GameBase.gi.sound(current_player, Defines.CHAN_VOICE,
+ GameBase.gi.soundindex("player/gasp2.wav"), 1,
+ Defines.ATTN_NORM, 0);
+ }
+ }
+
+
+ //
+ // check for sizzle damage
+ //
+ if (waterlevel != 0
+ && 0 != (current_player.watertype & (Defines.CONTENTS_LAVA | Defines.CONTENTS_SLIME))) {
+ if ((current_player.watertype & Defines.CONTENTS_LAVA) != 0) {
+ if (current_player.health > 0
+ && current_player.pain_debounce_time <= GameBase.level.time
+ && current_client.invincible_framenum < GameBase.level.framenum) {
+ if ((Lib.rand() & 1) != 0)
+ GameBase.gi.sound(current_player, Defines.CHAN_VOICE,
+ GameBase.gi.soundindex("player/burn1.wav"), 1,
+ Defines.ATTN_NORM, 0);
+ else
+ GameBase.gi.sound(current_player, Defines.CHAN_VOICE,
+ GameBase.gi.soundindex("player/burn2.wav"), 1,
+ Defines.ATTN_NORM, 0);
+ current_player.pain_debounce_time = GameBase.level.time + 1;
+ }
+
+ }
+
+ }
+ }
+
+ /*
+ * ===============
+ * G_SetClientEffects
+ * ===============
+ */
+ public static void G_SetClientEffects(EDict ent) {
+ int pa_type;
+ int remaining;
+
+ ent.s.effects = 0;
+ ent.s.renderfx = 0;
+
+ if (ent.health <= 0 || GameBase.level.intermissiontime != 0)
+ return;
+
+ if (ent.client.quad_framenum > GameBase.level.framenum) {
+ remaining = (int) ent.client.quad_framenum
+ - GameBase.level.framenum;
+ if (remaining > 30 || 0 != (remaining & 4))
+ ent.s.effects |= Defines.EF_QUAD;
+ }
+
+ if (ent.client.invincible_framenum > GameBase.level.framenum) {
+ remaining = (int) ent.client.invincible_framenum
+ - GameBase.level.framenum;
+ if (remaining > 30 || 0 != (remaining & 4))
+ ent.s.effects |= Defines.EF_PENT;
+ }
+
+ // show cheaters!!!
+ if ((ent.flags & Defines.FL_GODMODE) != 0) {
+ ent.s.effects |= Defines.EF_COLOR_SHELL;
+ ent.s.renderfx |= (Defines.RF_SHELL_RED | Defines.RF_SHELL_GREEN | Defines.RF_SHELL_BLUE);
+ }
+ }
+
+ /*
+ * ===============
+ * G_SetClientEvent
+ * ===============
+ */
+ public static void G_SetClientEvent(EDict ent) {
+ if (ent.s.event != 0)
+ return;
+
+ if (ent.groundentity != null && xyspeed > 225) {
+ if ((int) (current_client.bobtime + bobmove) != bobcycle)
+ ent.s.event = Defines.EV_FOOTSTEP;
+ }
+ }
+
+ /*
+ * ===============
+ * G_SetClientSound
+ * ===============
+ */
+ public static void G_SetClientSound(EDict ent) {
+ String weap;
+
+ if (ent.client.pers.game_helpchanged != GameBase.game.helpchanged) {
+ ent.client.pers.game_helpchanged = GameBase.game.helpchanged;
+ ent.client.pers.helpchanged = 1;
+ }
+
+ // help beep (no more than three times)
+ if (ent.client.pers.helpchanged != 0
+ && ent.client.pers.helpchanged <= 3
+ && 0 == (GameBase.level.framenum & 63)) {
+ ent.client.pers.helpchanged++;
+ GameBase.gi.sound(ent, Defines.CHAN_VOICE, GameBase.gi
+ .soundindex("misc/pc_up.wav"), 1, Defines.ATTN_STATIC, 0);
+ }
+
+
+ ent.s.sound = 0;
+ }
+
+ /*
+ * ===============
+ * G_SetClientFrame
+ * ===============
+ */
+ public static void G_SetClientFrame(EDict ent) {
+ Client client;
+ boolean duck, run;
+
+ if (ent.s.modelindex != 255)
+ return; // not in the player model
+
+ client = ent.client;
+
+ duck = (client.ps.pmove.pm_flags & Move.PMF_DUCKED) != 0;
+ run = xyspeed != 0;
+
+ boolean skip = false;
+ // check for stand/duck and stop/go transitions
+ if (duck != client.anim_duck
+ && client.anim_priority < Defines.ANIM_DEATH)
+ skip = true;
+
+ if (run != client.anim_run
+ && client.anim_priority == Defines.ANIM_BASIC)
+ skip = true;
+
+ if (null == ent.groundentity
+ && client.anim_priority <= Defines.ANIM_WAVE)
+ skip = true;
+
+ if (!skip) {
+ if (client.anim_priority == Defines.ANIM_REVERSE) {
+ if (ent.s.frame > client.anim_end) {
+ ent.s.frame--;
+ return;
+ }
+ } else if (ent.s.frame < client.anim_end) { // continue an animation
+ ent.s.frame++;
+ return;
+ }
+
+ if (client.anim_priority == Defines.ANIM_DEATH)
+ return; // stay there
+ if (client.anim_priority == Defines.ANIM_JUMP) {
+ if (null == ent.groundentity)
+ return; // stay there
+ ent.client.anim_priority = Defines.ANIM_WAVE;
+ ent.s.frame = M_Player.FRAME_jump3;
+ ent.client.anim_end = M_Player.FRAME_jump6;
+ return;
+ }
+ }
+
+ // return to either a running or standing frame
+ client.anim_priority = Defines.ANIM_BASIC;
+ client.anim_duck = duck;
+ client.anim_run = run;
+
+ if (null == ent.groundentity) {
+ client.anim_priority = Defines.ANIM_JUMP;
+ if (ent.s.frame != M_Player.FRAME_jump2)
+ ent.s.frame = M_Player.FRAME_jump1;
+ client.anim_end = M_Player.FRAME_jump2;
+ } else if (run) { // running
+ if (duck) {
+ ent.s.frame = M_Player.FRAME_crwalk1;
+ client.anim_end = M_Player.FRAME_crwalk6;
+ } else {
+ ent.s.frame = M_Player.FRAME_run1;
+ client.anim_end = M_Player.FRAME_run6;
+ }
+ } else { // standing
+ if (duck) {
+ ent.s.frame = M_Player.FRAME_crstnd01;
+ client.anim_end = M_Player.FRAME_crstnd19;
+ } else {
+ ent.s.frame = M_Player.FRAME_stand01;
+ client.anim_end = M_Player.FRAME_stand40;
+ }
+ }
+ }
+
+ /**
+ * Called for each player at the end of the server frame and right after
+ * spawning.
+ */
+ public static void ClientEndServerFrame(EDict ent) {
+ float bobtime;
+ int i;
+
+ current_player = ent;
+ current_client = ent.client;
+
+ //
+ // If the origin or velocity have changed since ClientThink(),
+ // update the pmove values. This will happen when the client
+ // is pushed by a bmodel or kicked by an explosion.
+ //
+ // If it wasn't updated here, the view position would lag a frame
+ // behind the body position when pushed -- "sinking into plats"
+ //
+ for (i = 0; i < 3; i++) {
+ current_client.ps.pmove.origin[i] = (short) (ent.s.origin[i] * 8.0);
+ current_client.ps.pmove.velocity[i] = (short) (ent.velocity[i] * 8.0);
+ }
+
+ //
+ // If the end of unit layout is displayed, don't give
+ // the player any normal movement attributes
+ //
+ if (GameBase.level.intermissiontime != 0) {
+ // FIXME: add view drifting here?
+ current_client.ps.blend[3] = 0;
+ current_client.ps.fov = 90;
+ PlayerHud.G_SetStats(ent);
+ return;
+ }
+
+ Math3D.angleVectors(ent.client.v_angle, forward, right, up);
+
+ // burn from lava, etc
+ P_WorldEffects();
+
+ //
+ // set model angles from view angles so other things in
+ // the world can tell which direction you are looking
+ //
+ if (ent.client.v_angle[Defines.PITCH] > 180)
+ ent.s.angles[Defines.PITCH] = (-360 + ent.client.v_angle[Defines.PITCH]) / 3;
+ else
+ ent.s.angles[Defines.PITCH] = ent.client.v_angle[Defines.PITCH] / 3;
+ ent.s.angles[Defines.YAW] = ent.client.v_angle[Defines.YAW];
+ ent.s.angles[Defines.ROLL] = 0;
+ ent.s.angles[Defines.ROLL] = SV_CalcRoll(ent.s.angles, ent.velocity) * 4;
+
+ //
+ // calculate speed and cycle to be used for
+ // all cyclic walking effects
+ //
+ xyspeed = (float) Math.sqrt(ent.velocity[0] * ent.velocity[0]
+ + ent.velocity[1] * ent.velocity[1]);
+
+ if (xyspeed < 5) {
+ bobmove = 0;
+ current_client.bobtime = 0; // start at beginning of cycle again
+ } else if (ent.groundentity != null) { // so bobbing only cycles when on
+ // ground
+ if (xyspeed > 210)
+ bobmove = 0.25f;
+ else if (xyspeed > 100)
+ bobmove = 0.125f;
+ else
+ bobmove = 0.0625f;
+ }
+
+ bobtime = (current_client.bobtime += bobmove);
+
+ if ((current_client.ps.pmove.pm_flags & Move.PMF_DUCKED) != 0)
+ bobtime *= 4;
+
+ bobcycle = (int) bobtime;
+ bobfracsin = (float) Math.abs(Math.sin(bobtime * Math.PI));
+
+
+ // apply all the damage taken this frame
+ P_DamageFeedback(ent);
+
+ // determine the view offsets
+ SV_CalcViewOffset(ent);
+
+ // determine the gun offsets
+ SV_CalcGunOffset(ent);
+
+ // determine the full screen color blend
+ // must be after viewoffset, so eye contents can be
+ // accurately determined
+ // FIXME: with client prediction, the contents
+ // should be determined by the client
+ SV_CalcBlend(ent);
+
+ // chase cam stuff
+ if (ent.client.resp.spectator)
+ PlayerHud.G_SetSpectatorStats(ent);
+ else
+ PlayerHud.G_SetStats(ent);
+ PlayerHud.G_CheckChaseStats(ent);
+
+ G_SetClientEvent(ent);
+
+ G_SetClientEffects(ent);
+
+ G_SetClientSound(ent);
+
+ G_SetClientFrame(ent);
+
+ Math3D.vectorCopy(ent.velocity, ent.client.oldvelocity);
+ Math3D.vectorCopy(ent.client.ps.viewangles, ent.client.oldviewangles);
+
+ // clear weapon kicks
+
+ // if the scoreboard is up, update it
+ if (ent.client.showscores && 0 == (GameBase.level.framenum & 31)) {
+ PlayerHud.DeathmatchScoreboardMessage(ent, ent.enemy);
+ GameBase.gi.unicast(ent, false);
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.game;
+
+import lwjake2.qcommon.Com;
+
+import java.util.Hashtable;
+
+public abstract class SuperAdapter {
+
+ /**
+ * Adapter repository.
+ */
+ private static final Hashtable<String, SuperAdapter> adapters = new Hashtable<>();
+
+ /**
+ * Constructor, does the adapter registration.
+ */
+ SuperAdapter() {
+ register(this, getID());
+ }
+
+ /**
+ * Adapter registration.
+ */
+ private static void register(SuperAdapter sa, String id) {
+ adapters.put(id, sa);
+ }
+
+ /**
+ * Returns the adapter from the repository given by its ID.
+ */
+ public static SuperAdapter getFromID(String key) {
+ SuperAdapter sa = adapters.get(key);
+
+ // try to create the adapter
+ if (sa == null) {
+ Com.DPrintf("SuperAdapter.getFromID():adapter not found->" + key + "\n");
+ }
+
+ return sa;
+ }
+
+ /**
+ * Returns the Adapter-ID.
+ */
+ public abstract String getID();
+}
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.game;
+
+import lwjake2.Defines;
+import lwjake2.util.QuakeFile;
+
+import java.io.IOException;
+
+public class client_persistant_t {
+
+ final int[] inventory = new int[Defines.MAX_ITEMS];
+ // just don't have a connection yet
+ public int max_slugs;
+ // client data that stays across multiple level loads
+ String userinfo = "";
+ String netname = "";
+ int hand;
+ boolean connected; // a loadgame will leave valid entities that
+ // values saved and restored from edicts when changing levels
+ int health;
+ int max_health;
+ int savedFlags;
+ int selected_item;
+ int game_helpchanged;
+ int helpchanged;
+ boolean spectator; // client is a spectator
+
+ public void set(client_persistant_t from) {
+
+ userinfo = from.userinfo;
+ netname = from.netname;
+ hand = from.hand;
+ connected = from.connected;
+ health = from.health;
+ max_health = from.max_health;
+ savedFlags = from.savedFlags;
+ selected_item = from.selected_item;
+ System.arraycopy(from.inventory, 0, inventory, 0, inventory.length);
+ max_slugs = from.max_slugs;
+ game_helpchanged = from.game_helpchanged;
+ helpchanged = from.helpchanged;
+ spectator = from.spectator;
+ }
+
+ /**
+ * Reads a client_persistant structure from a file.
+ */
+ public void read(QuakeFile f) throws IOException {
+
+ userinfo = f.readString();
+ netname = f.readString();
+
+ hand = f.readInt();
+
+ connected = f.readInt() != 0;
+ health = f.readInt();
+
+ max_health = f.readInt();
+ savedFlags = f.readInt();
+ selected_item = f.readInt();
+
+ for (int n = 0; n < Defines.MAX_ITEMS; n++)
+ inventory[n] = f.readInt();
+
+ max_slugs = f.readInt();
+
+ game_helpchanged = f.readInt();
+ helpchanged = f.readInt();
+ spectator = f.readInt() != 0;
+ }
+
+ /**
+ * Writes a client_persistant structure to a file.
+ */
+ public void write(QuakeFile f) throws IOException {
+ // client persistant_t
+ f.writeString(userinfo);
+ f.writeString(netname);
+
+ f.writeInt(hand);
+
+ f.writeInt(connected ? 1 : 0);
+ f.writeInt(health);
+
+ f.writeInt(max_health);
+ f.writeInt(savedFlags);
+ f.writeInt(selected_item);
+
+ for (int n = 0; n < Defines.MAX_ITEMS; n++)
+ f.writeInt(inventory[n]);
+
+ f.writeInt(max_slugs);
+
+ f.writeInt(game_helpchanged);
+ f.writeInt(helpchanged);
+ f.writeInt(spectator ? 1 : 0);
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.game;
+
+import lwjake2.util.Math3D;
+import lwjake2.util.QuakeFile;
+
+import java.io.IOException;
+
+/**
+ * Client data that stays across deathmatch respawns.
+ */
+public class client_respawn_t {
+ /**
+ * angles sent over in the last command.
+ */
+ protected final float[] cmd_angles = {0, 0, 0};
+ /**
+ * What to set client->pers to on a respawn
+ */
+ protected client_persistant_t coop_respawn = new client_persistant_t();
+ /**
+ * Level.framenum the client entered the game.
+ */
+ protected int enterframe;
+ /**
+ * frags, etc.
+ */
+ protected int score;
+ /**
+ * client is a spectator.
+ */
+ protected boolean spectator;
+
+
+ /**
+ * Copies the client respawn data.
+ */
+ public void set(client_respawn_t from) {
+ coop_respawn.set(from.coop_respawn);
+ enterframe = from.enterframe;
+ score = from.score;
+ Math3D.vectorCopy(from.cmd_angles, cmd_angles);
+ spectator = from.spectator;
+ }
+
+ /**
+ * Clears the client reaspawn informations.
+ */
+ public void clear() {
+ coop_respawn = new client_persistant_t();
+ enterframe = 0;
+ score = 0;
+ Math3D.vectorClear(cmd_angles);
+ spectator = false;
+ }
+
+ /**
+ * Reads a client_respawn from a file.
+ */
+ public void read(QuakeFile f) throws IOException {
+ coop_respawn.read(f);
+ enterframe = f.readInt();
+ score = f.readInt();
+ cmd_angles[0] = f.readFloat();
+ cmd_angles[1] = f.readFloat();
+ cmd_angles[2] = f.readFloat();
+ spectator = f.readInt() != 0;
+ }
+
+ /**
+ * Writes a client_respawn to a file.
+ */
+ public void write(QuakeFile f) throws IOException {
+ coop_respawn.write(f);
+ f.writeInt(enterframe);
+ f.writeInt(score);
+ f.writeFloat(cmd_angles[0]);
+ f.writeFloat(cmd_angles[1]);
+ f.writeFloat(cmd_angles[2]);
+ f.writeInt(spectator ? 1 : 0);
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.game;
+
+public final class cmdalias_t {
+ public cmdalias_t next;
+ public String name = "";
+ public String value;
+}
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.game;
+
+public class cmodel_t {
+ public final float[] mins = {0, 0, 0};
+ public final float[] maxs = {0, 0, 0};
+ public final float[] origin = {0, 0, 0}; // for sounds or lights
+ public int headnode;
+}
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.game;
+
+import lwjake2.util.Math3D;
+
+public class cplane_t {
+ public final float[] normal = new float[3];
+ public final byte[] pad = {0, 0};
+ public float dist;
+ /**
+ * This is for fast side tests, 0=xplane, 1=yplane, 2=zplane and 3=arbitrary.
+ */
+ public byte type;
+ /**
+ * This represents signx + (signy<<1) + (signz << 1).
+ */
+ public byte signbits; // signx + (signy<<1) + (signz<<1)
+
+ public void set(cplane_t c) {
+ Math3D.set(normal, c.normal);
+ dist = c.dist;
+ type = c.type;
+ signbits = c.signbits;
+ pad[0] = c.pad[0];
+ pad[1] = c.pad[1];
+ }
+
+ public void clear() {
+ Math3D.vectorClear(normal);
+ dist = 0;
+ type = 0;
+ signbits = 0;
+ pad[0] = 0;
+ pad[1] = 0;
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.game;
+
+public class csurface_t {
+ public String name = "";
+ public int flags;
+ public int value;
+}
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.game;
+
+import lwjake2.util.Math3D;
+import lwjake2.util.QuakeFile;
+
+import java.io.IOException;
+
+public class entity_state_t implements Cloneable {
+ /**
+ * edict index. TODO: this is critical. The index has to be proper managed.
+ */
+ public int number = 0;
+ // TODO: why was this introduced?
+ public EDict surrounding_ent = null;
+ public float[] origin = {0, 0, 0};
+ public float[] angles = {0, 0, 0};
+ /**
+ * for lerping.
+ */
+ public float[] old_origin = {0, 0, 0};
+ public int modelindex;
+ /**
+ * weapons, CTF flags, etc.
+ */
+ public int modelindex2, modelindex3, modelindex4;
+ public int frame;
+ public int skinnum;
+ /**
+ * PGM - we're filling it, so it needs to be unsigned.
+ */
+ public int effects;
+ public int renderfx;
+ public int solid;
+ // for client side prediction, 8*(bits 0-4) is x/y radius
+ // 8*(bits 5-9) is z down distance, 8(bits10-15) is z up
+ // gi.linkentity sets this properly
+ public int sound; // for looping sounds, to guarantee shutoff
+ public int event; // impulse events -- muzzle flashes, footsteps, etc
+
+ /**
+ * entity_state_t is the information conveyed from the server
+ * in an update message about entities that the client will
+ * need to render in some way.
+ */
+ public entity_state_t(EDict ent) {
+ this.surrounding_ent = ent;
+ if (ent != null)
+ number = ent.index;
+ }
+ // events only go out for a single frame, they
+ // are automatically cleared each frame
+
+ /**
+ * Writes the entity state to the file.
+ */
+ public void write(QuakeFile f) throws IOException {
+ f.writeEdictRef(surrounding_ent);
+ f.writeVector(origin);
+ f.writeVector(angles);
+ f.writeVector(old_origin);
+
+ f.writeInt(modelindex);
+
+ f.writeInt(modelindex2);
+ f.writeInt(modelindex3);
+ f.writeInt(modelindex4);
+
+ f.writeInt(frame);
+ f.writeInt(skinnum);
+
+ f.writeInt(effects);
+ f.writeInt(renderfx);
+ f.writeInt(solid);
+
+ f.writeInt(sound);
+ f.writeInt(event);
+
+ }
+
+ /**
+ * Reads the entity state from the file.
+ */
+ public void read(QuakeFile f) throws IOException {
+ surrounding_ent = f.readEdictRef();
+ origin = f.readVector();
+ angles = f.readVector();
+ old_origin = f.readVector();
+
+ modelindex = f.readInt();
+
+ modelindex2 = f.readInt();
+ modelindex3 = f.readInt();
+ modelindex4 = f.readInt();
+
+ frame = f.readInt();
+ skinnum = f.readInt();
+
+ effects = f.readInt();
+ renderfx = f.readInt();
+ solid = f.readInt();
+
+ sound = f.readInt();
+ event = f.readInt();
+
+
+ }
+
+
+ public entity_state_t getClone() {
+ entity_state_t out = new entity_state_t(this.surrounding_ent);
+ out.set(this);
+ return out;
+ }
+
+ public void set(entity_state_t from) {
+ number = from.number;
+ Math3D.vectorCopy(from.origin, origin);
+ Math3D.vectorCopy(from.angles, angles);
+ Math3D.vectorCopy(from.old_origin, old_origin);
+
+ modelindex = from.modelindex;
+ modelindex2 = from.modelindex2;
+ modelindex3 = from.modelindex3;
+ modelindex4 = from.modelindex4;
+
+ frame = from.frame;
+ skinnum = from.skinnum;
+ effects = from.effects;
+ renderfx = from.renderfx;
+ solid = from.solid;
+ sound = from.sound;
+ event = from.event;
+ }
+
+ public void clear() {
+ //TODO: this is critical. The index has to be proper managed.
+ number = 0;
+ surrounding_ent = null;
+ Math3D.vectorClear(origin);
+ Math3D.vectorClear(angles);
+ Math3D.vectorClear(old_origin);
+ modelindex = 0;
+ modelindex2 = modelindex3 = modelindex4 = 0;
+ frame = 0;
+ skinnum = 0;
+ effects = 0;
+ renderfx = 0;
+ solid = 0;
+ sound = 0;
+ event = 0;
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.game;
+
+import lwjake2.Defines;
+import lwjake2.qcommon.*;
+import lwjake2.server.SV_GAME;
+import lwjake2.server.SV_INIT;
+import lwjake2.server.SV_SEND;
+import lwjake2.server.SV_WORLD;
+
+//
+// collection of functions provided by the main engine
+//
+public class game_import_t {
+ public Move.PointContentsAdapter pointcontents = new Move.PointContentsAdapter() {
+ public int pointcontents(float[] o) {
+ return 0;
+ }
+ };
+
+ // special messages
+ public void bprintf(int printlevel, String s) {
+ SV_SEND.SV_BroadcastPrintf(printlevel, s);
+ }
+
+ public void dprintf(String s) {
+ SV_GAME.PF_dprintf(s);
+ }
+
+ public void cprintf(EDict ent, int printlevel, String s) {
+ SV_GAME.PF_cprintf(ent, printlevel, s);
+ }
+
+ public void centerprintf(EDict ent, String s) {
+ SV_GAME.PF_centerprintf(ent, s);
+ }
+
+ public void sound(EDict ent, int channel, int soundindex, float volume,
+ float attenuation, float timeofs) {
+ SV_GAME.PF_StartSound(ent, channel, soundindex, volume, attenuation,
+ timeofs);
+ }
+
+ public void positioned_sound(float[] origin, EDict ent, int channel,
+ int soundinedex, float volume, float attenuation, float timeofs) {
+
+ SV_SEND.SV_StartSound(origin, ent, channel, soundinedex, volume,
+ attenuation, timeofs);
+ }
+
+ // config strings hold all the index strings, the lightstyles,
+ // and misc data like the sky definition and cdtrack.
+ // All of the current configstrings are sent to clients when
+ // they connect, and changes are sent to all connected clients.
+ public void configstring(int num, String string) {
+ SV_GAME.PF_Configstring(num, string);
+ }
+
+ public void error(String err) {
+ Com.Error(Defines.ERR_FATAL, err);
+ }
+
+ // the *index functions create configstrings and some internal server state
+ public int modelindex(String name) {
+ return SV_INIT.SV_ModelIndex(name);
+ }
+
+ public int soundindex(String name) {
+ return SV_INIT.SV_SoundIndex(name);
+ }
+
+ public int imageindex(String name) {
+ return SV_INIT.SV_ImageIndex(name);
+ }
+
+ public void setmodel(EDict ent, String name) {
+ SV_GAME.PF_setmodel(ent, name);
+ }
+
+ // collision detection
+ public trace_t trace(float[] start, float[] mins, float[] maxs,
+ float[] end, EDict passent, int contentmask) {
+ return SV_WORLD.SV_Trace(start, mins, maxs, end, passent, contentmask);
+ }
+
+ public void SetAreaPortalState(int portalnum, boolean open) {
+ CM.CM_SetAreaPortalState(portalnum, open);
+ }
+
+ // an entity will never be sent to a client or used for collision
+ // if it is not passed to linkentity. If the size, position, or
+ // solidity changes, it must be relinked.
+ public void linkentity(EDict ent) {
+ SV_WORLD.SV_LinkEdict(ent);
+ }
+
+ public void unlinkentity(EDict ent) {
+ SV_WORLD.SV_UnlinkEdict(ent);
+ }
+
+ // call before removing an interactive edict
+ public int BoxEdicts(float[] mins, float[] maxs, EDict list[],
+ int maxcount, int areatype) {
+ return SV_WORLD.SV_AreaEdicts(mins, maxs, list, maxcount, areatype);
+ }
+
+ public void Pmove(Move move) {
+ PMove.Pmove(move);
+ }
+
+ // player movement code common with client prediction
+ // network messaging
+ public void multicast(float[] origin, int to) {
+ SV_SEND.SV_Multicast(origin, to);
+ }
+
+ public void unicast(EDict ent, boolean reliable) {
+ SV_GAME.PF_Unicast(ent, reliable);
+ }
+
+
+ public void WriteByte(int c) {
+ SV_GAME.PF_WriteByte(c);
+ }
+
+ public void WriteShort(int c) {
+ SV_GAME.PF_WriteShort(c);
+ }
+
+ public void WriteString(String s) {
+ SV_GAME.PF_WriteString(s);
+ }
+
+ public void WritePosition(float[] pos) {
+ SV_GAME.PF_WritePos(pos);
+ }
+
+ // some fractional bits
+ public void WriteDir(float[] pos) {
+ SV_GAME.PF_WriteDir(pos);
+ }
+
+ // console variable interaction
+ public CvarT cvar(String var_name, String value, int flags) {
+ return Cvar.get(var_name, value, flags);
+ }
+
+ // console variable interaction
+ public void cvar_set(String var_name, String value) {
+ Cvar.set(var_name, value);
+ }
+
+ // console variable interaction
+ public CvarT cvar_forceset(String var_name, String value) {
+ return Cvar.forceSet(var_name, value);
+ }
+
+ // ClientCommand and ServerCommand parameter access
+ public int argc() {
+ return Cmd.Argc();
+ }
+
+
+ public String argv(int n) {
+ return Cmd.Argv(n);
+ }
+
+ // add commands to the server console as if they were typed in
+ // for map changing, etc
+ public void AddCommandString(String text) {
+ CommandBuffer.AddText(text);
+ }
+
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.game;
+
+import lwjake2.Defines;
+import lwjake2.qcommon.Com;
+import lwjake2.util.QuakeFile;
+
+import java.io.IOException;
+import java.util.Date;
+
+public class game_locals_t {
+ //
+ // this structure is left intact through an entire game
+ // it should be initialized at dll load time, and read/written to
+ // the server.ssv file for savegames
+ //
+
+ public String helpmessage1 = "";
+
+ public String helpmessage2 = "";
+
+ public int helpchanged; // flash F1 icon if non 0, play sound
+
+ // and increment only if 1, 2, or 3
+
+ public Client clients[] = new Client[Defines.MAX_CLIENTS];
+
+ // can't store spawnpoint in level, because
+ // it would get overwritten by the savegame restore
+ public String spawnpoint = ""; // needed for coop respawns
+
+ // store latched cvars here that we want to get at often
+ public int maxclients;
+
+ public int maxentities;
+
+ // cross level triggers
+ public int serverflags;
+ public boolean autosaved;
+ // items
+ private int num_items;
+
+ /**
+ * Reads the game locals from a file.
+ */
+ public void load(QuakeFile f) throws IOException {
+ f.readString(); // Reads date?
+
+ helpmessage1 = f.readString();
+ helpmessage2 = f.readString();
+
+ helpchanged = f.readInt();
+ // gclient_t*
+
+ spawnpoint = f.readString();
+ maxclients = f.readInt();
+ maxentities = f.readInt();
+ serverflags = f.readInt();
+ num_items = f.readInt();
+ autosaved = f.readInt() != 0;
+
+ // rst's checker :-)
+ if (f.readInt() != 1928)
+ Com.DPrintf("error in loading game_locals, 1928\n");
+
+ }
+
+ /**
+ * Writes the game locals to a file.
+ */
+ public void write(QuakeFile f) throws IOException {
+ f.writeString(new Date().toString());
+
+ f.writeString(helpmessage1);
+ f.writeString(helpmessage2);
+
+ f.writeInt(helpchanged);
+ // gclient_t*
+
+ f.writeString(spawnpoint);
+ f.writeInt(maxclients);
+ f.writeInt(maxentities);
+ f.writeInt(serverflags);
+ f.writeInt(num_items);
+ f.writeInt(autosaved ? 1 : 0);
+ // rst's checker :-)
+ f.writeInt(1928);
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.game;
+
+public class gitem_armor_t {
+
+}
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.game;
+
+public class gitem_t {
+ public int index;
+ String classname; // spawning name
+ ItemUseAdapter use;
+ int flags; // IT_* flags
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.game;
+
+import lwjake2.util.QuakeFile;
+
+import java.io.IOException;
+
+public class level_locals_t {
+
+ // this structure is cleared as each map is entered
+ // it is read/written to the level.sav file for savegames
+ //
+ public int framenum;
+ public float time;
+
+ public String level_name = ""; // the descriptive name (Outer Base, etc)
+ public String mapname = ""; // the server name (base1, etc)
+ public String nextmap = ""; // go here when fraglimit is hit
+
+ // intermission state
+ public float intermissiontime; // time the intermission was started
+ public String changemap;
+ public boolean exitintermission;
+ public float[] intermission_origin = {0, 0, 0};
+ public float[] intermission_angle = {0, 0, 0};
+
+ public EDict sight_client; // changed once each frame for coop games
+ public int pic_health;
+ public int total_secrets;
+ public int found_secrets;
+ public int total_goals;
+ public int found_goals;
+ public EDict current_entity; // entity running from G_RunFrame
+ public int body_que; // dead bodies
+ private EDict sight_entity;
+ private int sight_entity_framenum;
+ private EDict sound_entity;
+ private int sound_entity_framenum;
+ private EDict sound2_entity;
+ private int sound2_entity_framenum;
+ private int power_cubes; // ugly necessity for coop
+
+ /**
+ * Writes the levellocales to the file.
+ */
+ public void write(QuakeFile f) throws IOException {
+ f.writeInt(framenum);
+ f.writeFloat(time);
+ f.writeString(level_name);
+ f.writeString(mapname);
+ f.writeString(nextmap);
+ f.writeFloat(intermissiontime);
+ f.writeString(changemap);
+ f.writeBoolean(exitintermission);
+ f.writeVector(intermission_origin);
+ f.writeVector(intermission_angle);
+ f.writeEdictRef(sight_client);
+
+ f.writeEdictRef(sight_entity);
+ f.writeInt(sight_entity_framenum);
+
+ f.writeEdictRef(sound_entity);
+ f.writeInt(sound_entity_framenum);
+ f.writeEdictRef(sound2_entity);
+ f.writeInt(sound2_entity_framenum);
+
+ f.writeInt(pic_health);
+
+ f.writeInt(total_secrets);
+ f.writeInt(found_secrets);
+
+ f.writeInt(total_goals);
+ f.writeInt(found_goals);
+
+ f.writeEdictRef(current_entity);
+ f.writeInt(body_que); // dead bodies
+ f.writeInt(power_cubes); // ugly necessity for coop
+
+ // rst's checker :-)
+ f.writeInt(4711);
+ }
+
+ /**
+ * Reads the level locals from the file.
+ */
+ public void read(QuakeFile f) throws IOException {
+ framenum = f.readInt();
+ time = f.readFloat();
+ level_name = f.readString();
+ mapname = f.readString();
+ nextmap = f.readString();
+ intermissiontime = f.readFloat();
+ changemap = f.readString();
+ exitintermission = f.readBoolean();
+ intermission_origin = f.readVector();
+ intermission_angle = f.readVector();
+ sight_client = f.readEdictRef();
+
+ sight_entity = f.readEdictRef();
+ sight_entity_framenum = f.readInt();
+
+ sound_entity = f.readEdictRef();
+ sound_entity_framenum = f.readInt();
+ sound2_entity = f.readEdictRef();
+ sound2_entity_framenum = f.readInt();
+
+ pic_health = f.readInt();
+
+ total_secrets = f.readInt();
+ found_secrets = f.readInt();
+
+ total_goals = f.readInt();
+ found_goals = f.readInt();
+
+ current_entity = f.readEdictRef();
+ body_que = f.readInt(); // dead bodies
+ power_cubes = f.readInt(); // ugly necessity for coop
+
+ // rst's checker :-)
+ if (f.readInt() != 4711)
+ System.out.println("error in reading level_locals.");
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.game;
+
+public class link_t {
+ public final Object o;
+ public link_t prev, next;
+
+ public link_t(Object o) {
+ this.o = o;
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.game;
+
+public class mapsurface_t {
+ public final csurface_t c = new csurface_t();
+ public String rname;
+}
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.game;
+
+import lwjake2.util.QuakeFile;
+
+import java.io.IOException;
+
+public class mframe_t {
+ public float dist;
+ public EntThinkAdapter think;
+
+ public mframe_t(float dist, EntThinkAdapter think) {
+ this.dist = dist;
+ this.think = think;
+ }
+
+ /**
+ * Empty constructor.
+ */
+ public mframe_t() {
+ }
+
+ public void write(QuakeFile f) throws IOException {
+ f.writeFloat(dist);
+ f.writeAdapter(think);
+ }
+
+ public void read(QuakeFile f) throws IOException {
+ dist = f.readFloat();
+ think = (EntThinkAdapter) f.readAdapter();
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.game;
+
+import lwjake2.util.QuakeFile;
+
+import java.io.IOException;
+
+public class mmove_t {
+ private int firstframe;
+ private int lastframe;
+ private mframe_t[] frame; //ptr
+ private EntThinkAdapter endfunc;
+
+ public mmove_t() {
+ }
+
+ /**
+ * Writes the structure to a random acccess file.
+ */
+ public void write(QuakeFile f) throws IOException {
+ f.writeInt(firstframe);
+ f.writeInt(lastframe);
+ if (frame == null)
+ f.writeInt(-1);
+ else {
+ f.writeInt(frame.length);
+ for (mframe_t aFrame : frame) aFrame.write(f);
+ }
+ f.writeAdapter(endfunc);
+ }
+
+ /**
+ * Read the mmove_t from the RandomAccessFile.
+ */
+ public void read(QuakeFile f) throws IOException {
+ firstframe = f.readInt();
+ lastframe = f.readInt();
+
+ int len = f.readInt();
+
+ frame = new mframe_t[len];
+ for (int n = 0; n < len; n++) {
+ frame[n] = new mframe_t();
+ frame[n].read(f);
+ }
+ endfunc = (EntThinkAdapter) f.readAdapter();
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.game;
+
+import lwjake2.util.QuakeFile;
+
+import java.io.IOException;
+
+public class monsterinfo_t {
+
+ private mmove_t currentmove;
+ private int aiflags;
+ private int nextframe;
+ private float scale;
+
+ private EntThinkAdapter stand;
+ private EntThinkAdapter idle;
+ private EntThinkAdapter search;
+ private EntThinkAdapter walk;
+ private EntThinkAdapter run;
+
+ private EntDodgeAdapter dodge;
+
+ private EntThinkAdapter attack;
+ private EntThinkAdapter melee;
+
+ private EntInteractAdapter sight;
+
+ private EntThinkAdapter checkattack;
+
+ private float pausetime;
+ private float attack_finished;
+
+ private float[] saved_goal = {0, 0, 0};
+ private float search_time;
+ private float trail_time;
+ private float[] last_sighting = {0, 0, 0};
+ private int attack_state;
+ private int lefty;
+ private float idle_time;
+ private int linkcount;
+
+ private int power_armor_type;
+ private int power_armor_power;
+
+ /**
+ * Writes the monsterinfo to the file.
+ */
+ public void write(QuakeFile f) throws IOException {
+ f.writeBoolean(currentmove != null);
+ if (currentmove != null)
+ currentmove.write(f);
+ f.writeInt(aiflags);
+ f.writeInt(nextframe);
+ f.writeFloat(scale);
+ f.writeAdapter(stand);
+ f.writeAdapter(idle);
+ f.writeAdapter(search);
+ f.writeAdapter(walk);
+ f.writeAdapter(run);
+
+ f.writeAdapter(dodge);
+
+ f.writeAdapter(attack);
+ f.writeAdapter(melee);
+
+ f.writeAdapter(sight);
+
+ f.writeAdapter(checkattack);
+
+ f.writeFloat(pausetime);
+ f.writeFloat(attack_finished);
+
+ f.writeVector(saved_goal);
+
+ f.writeFloat(search_time);
+ f.writeFloat(trail_time);
+
+ f.writeVector(last_sighting);
+
+ f.writeInt(attack_state);
+ f.writeInt(lefty);
+
+ f.writeFloat(idle_time);
+ f.writeInt(linkcount);
+
+ f.writeInt(power_armor_power);
+ f.writeInt(power_armor_type);
+ }
+
+ /**
+ * Writes the monsterinfo to the file.
+ */
+ public void read(QuakeFile f) throws IOException {
+ if (f.readBoolean()) {
+ currentmove = new mmove_t();
+ currentmove.read(f);
+ } else
+ currentmove = null;
+ aiflags = f.readInt();
+ nextframe = f.readInt();
+ scale = f.readFloat();
+ stand = (EntThinkAdapter) f.readAdapter();
+ idle = (EntThinkAdapter) f.readAdapter();
+ search = (EntThinkAdapter) f.readAdapter();
+ walk = (EntThinkAdapter) f.readAdapter();
+ run = (EntThinkAdapter) f.readAdapter();
+
+ dodge = (EntDodgeAdapter) f.readAdapter();
+
+ attack = (EntThinkAdapter) f.readAdapter();
+ melee = (EntThinkAdapter) f.readAdapter();
+
+ sight = (EntInteractAdapter) f.readAdapter();
+
+ checkattack = (EntThinkAdapter) f.readAdapter();
+
+ pausetime = f.readFloat();
+ attack_finished = f.readFloat();
+
+ saved_goal = f.readVector();
+
+ search_time = f.readFloat();
+ trail_time = f.readFloat();
+
+ last_sighting = f.readVector();
+
+ attack_state = f.readInt();
+ lefty = f.readInt();
+
+ idle_time = f.readFloat();
+ linkcount = f.readInt();
+
+ power_armor_power = f.readInt();
+ power_armor_type = f.readInt();
+
+ }
+
+
+}
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.game;
+
+import lwjake2.util.QuakeFile;
+
+import java.io.IOException;
+
+public class moveinfo_t {
+ // fixed data
+ float[] start_origin = {0, 0, 0};
+ float[] start_angles = {0, 0, 0};
+ float[] end_origin = {0, 0, 0};
+ float[] end_angles = {0, 0, 0};
+
+ int sound_start;
+ int sound_middle;
+ int sound_end;
+
+ float accel;
+ float speed;
+ float decel;
+ float distance;
+
+ float wait;
+
+ // state data
+ int state;
+ float[] dir = {0, 0, 0};
+
+ float current_speed;
+ float move_speed;
+ float next_speed;
+ float remaining_distance;
+ float decel_distance;
+ EntThinkAdapter endfunc;
+
+ /**
+ * saves the moveinfo to the file.
+ */
+ public void write(QuakeFile f) throws IOException {
+ f.writeVector(start_origin);
+ f.writeVector(start_angles);
+ f.writeVector(end_origin);
+ f.writeVector(end_angles);
+
+ f.writeInt(sound_start);
+ f.writeInt(sound_middle);
+ f.writeInt(sound_end);
+
+ f.writeFloat(accel);
+ f.writeFloat(speed);
+ f.writeFloat(decel);
+ f.writeFloat(distance);
+
+ f.writeFloat(wait);
+
+ f.writeInt(state);
+ f.writeVector(dir);
+
+ f.writeFloat(current_speed);
+ f.writeFloat(move_speed);
+ f.writeFloat(next_speed);
+ f.writeFloat(remaining_distance);
+ f.writeFloat(decel_distance);
+ f.writeAdapter(endfunc);
+ }
+
+ /**
+ * Reads the moveinfo from a file.
+ */
+ public void read(QuakeFile f) throws IOException {
+ start_origin = f.readVector();
+ start_angles = f.readVector();
+ end_origin = f.readVector();
+ end_angles = f.readVector();
+
+ sound_start = f.readInt();
+ sound_middle = f.readInt();
+ sound_end = f.readInt();
+
+ accel = f.readFloat();
+ speed = f.readFloat();
+ decel = f.readFloat();
+ distance = f.readFloat();
+
+ wait = f.readFloat();
+
+ state = f.readInt();
+ dir = f.readVector();
+
+ current_speed = f.readFloat();
+ move_speed = f.readFloat();
+ next_speed = f.readFloat();
+ remaining_distance = f.readFloat();
+ decel_distance = f.readFloat();
+ endfunc = (EntThinkAdapter) f.readAdapter();
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.game;
+
+import lwjake2.Defines;
+import lwjake2.util.Math3D;
+
+import java.io.IOException;
+import java.io.RandomAccessFile;
+
+/**
+ * Player_state_t is the information needed in addition to pmove_state_t
+ * to rendered a view. There will only be 10 player_state_t sent each second,
+ * but the number of pmove_state_t changes will be relative to client
+ * frame rates.
+ */
+
+public class player_state_t {
+
+ /**
+ * Lets cleverly reset the structure.
+ */
+ private static final player_state_t prototype = new player_state_t();
+ public final pmove_state_t pmove = new pmove_state_t(); // for prediction
+ // these fields do not need to be communicated bit-precise
+ public final float[] viewangles = {0, 0, 0}; // for fixed views
+ public final float[] viewoffset = {0, 0, 0}; // add to pmovestate->origin
+ public final float[] kick_angles = {0, 0, 0}; // add to view direction to get render angles
+ // set by weapon kicks, pain effects, etc
+ public final float[] blend = new float[4]; // rgba full screen effect
+ public final short[] stats = new short[Defines.MAX_STATS];
+ public float fov; // horizontal field of view
+ public int rdflags; // refdef flags
+
+ /**
+ * Clears the player_state.
+ */
+ public void clear() {
+ this.set(prototype);
+ }
+
+ /**
+ * Clones the object.
+ */
+ public player_state_t getClone() {
+ return new player_state_t().set(this);
+ }
+
+ /**
+ * Copies the player state data.
+ */
+ public player_state_t set(player_state_t from) {
+ pmove.set(from.pmove);
+ Math3D.vectorCopy(from.viewangles, viewangles);
+ Math3D.vectorCopy(from.viewoffset, viewoffset);
+ Math3D.vectorCopy(from.kick_angles, kick_angles);
+
+ blend[0] = from.blend[0];
+ blend[1] = from.blend[1];
+ blend[2] = from.blend[2];
+ blend[3] = from.blend[3];
+
+ fov = from.fov;
+ rdflags = from.rdflags;
+
+ System.arraycopy(from.stats, 0, stats, 0, Defines.MAX_STATS);
+
+ return this;
+ }
+
+ /**
+ * Reads a player_state from a file.
+ */
+ public void load(RandomAccessFile f) throws IOException {
+ pmove.load(f);
+
+ viewangles[0] = f.readFloat();
+ viewangles[1] = f.readFloat();
+ viewangles[2] = f.readFloat();
+
+ viewoffset[0] = f.readFloat();
+ viewoffset[1] = f.readFloat();
+ viewoffset[2] = f.readFloat();
+
+ kick_angles[0] = f.readFloat();
+ kick_angles[1] = f.readFloat();
+ kick_angles[2] = f.readFloat();
+
+ blend[0] = f.readFloat();
+ blend[1] = f.readFloat();
+ blend[2] = f.readFloat();
+ blend[3] = f.readFloat();
+
+ fov = f.readFloat();
+
+ rdflags = f.readInt();
+
+ for (int n = 0; n < Defines.MAX_STATS; n++)
+ stats[n] = f.readShort();
+ }
+
+ /**
+ * Writes a player_state to a file.
+ */
+ public void write(RandomAccessFile f) throws IOException {
+ pmove.write(f);
+
+ f.writeFloat(viewangles[0]);
+ f.writeFloat(viewangles[1]);
+ f.writeFloat(viewangles[2]);
+
+ f.writeFloat(viewoffset[0]);
+ f.writeFloat(viewoffset[1]);
+ f.writeFloat(viewoffset[2]);
+
+ f.writeFloat(kick_angles[0]);
+ f.writeFloat(kick_angles[1]);
+ f.writeFloat(kick_angles[2]);
+
+ f.writeFloat(blend[0]);
+ f.writeFloat(blend[1]);
+ f.writeFloat(blend[2]);
+ f.writeFloat(blend[3]);
+
+ f.writeFloat(fov);
+
+ f.writeInt(rdflags);
+
+ for (int n = 0; n < Defines.MAX_STATS; n++)
+ f.writeShort(stats[n]);
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.game;
+
+import lwjake2.qcommon.Com;
+import lwjake2.util.Math3D;
+
+import java.io.IOException;
+import java.io.RandomAccessFile;
+
+public class pmove_state_t {
+ // this structure needs to be communicated bit-accurate
+ // from the server to the client to guarantee that
+ // prediction stays in sync, so no floats are used.
+ // if any part of the game code modifies this struct, it
+ // will result in a prediction error of some degree.
+
+ /**
+ * changed by spawns, rotating objects, and teleporters.
+ */
+
+ private static final pmove_state_t prototype = new pmove_state_t();
+ public final short[] origin = {0, 0, 0}; // 12.3
+ public final short[] velocity = {0, 0, 0}; // 12.3
+ /**
+ * add to command angles to get view direction.
+ */
+ public final short[] delta_angles = {0, 0, 0};
+ public int pm_type;
+ /**
+ * ducked, jump_held, etc.
+ */
+ public byte pm_flags;
+ /**
+ * each unit = 8 ms.
+ */
+ public byte pm_time;
+ public short gravity;
+
+ public void clear() {
+ this.set(prototype);
+ }
+
+ public void set(pmove_state_t from) {
+ pm_type = from.pm_type;
+ Math3D.vectorCopy(from.origin, origin);
+ Math3D.vectorCopy(from.velocity, velocity);
+ pm_flags = from.pm_flags;
+ pm_time = from.pm_time;
+ gravity = from.gravity;
+ Math3D.vectorCopy(from.delta_angles, delta_angles);
+ }
+
+ public boolean equals(pmove_state_t p2) {
+ return pm_type == p2.pm_type
+ && origin[0] == p2.origin[0]
+ && origin[1] == p2.origin[1]
+ && origin[2] == p2.origin[2]
+ && velocity[0] == p2.velocity[0]
+ && velocity[1] == p2.velocity[1]
+ && velocity[2] == p2.origin[2]
+ && pm_flags == p2.pm_flags
+ && pm_time == p2.pm_time
+ && delta_angles[0] == p2.delta_angles[0]
+ && delta_angles[1] == p2.delta_angles[1]
+ && delta_angles[2] == p2.origin[2];
+
+ }
+
+ /**
+ * Reads the playermove from the file.
+ */
+ public void load(RandomAccessFile f) throws IOException {
+
+ pm_type = f.readInt();
+
+ origin[0] = f.readShort();
+ origin[1] = f.readShort();
+ origin[2] = f.readShort();
+
+ velocity[0] = f.readShort();
+ velocity[1] = f.readShort();
+ velocity[2] = f.readShort();
+
+ pm_flags = f.readByte();
+ pm_time = f.readByte();
+ gravity = f.readShort();
+
+ f.readShort();
+
+ delta_angles[0] = f.readShort();
+ delta_angles[1] = f.readShort();
+ delta_angles[2] = f.readShort();
+
+ }
+
+ /**
+ * Writes the playermove to the file.
+ */
+ public void write(RandomAccessFile f) throws IOException {
+
+ f.writeInt(pm_type);
+
+ f.writeShort(origin[0]);
+ f.writeShort(origin[1]);
+ f.writeShort(origin[2]);
+
+ f.writeShort(velocity[0]);
+ f.writeShort(velocity[1]);
+ f.writeShort(velocity[2]);
+
+ f.writeByte(pm_flags);
+ f.writeByte(pm_time);
+ f.writeShort(gravity);
+
+ f.writeShort(0);
+
+ f.writeShort(delta_angles[0]);
+ f.writeShort(delta_angles[1]);
+ f.writeShort(delta_angles[2]);
+ }
+
+ public void dump() {
+ Com.Println("pm_type: " + pm_type);
+
+ Com.Println("origin[0]: " + origin[0]);
+ Com.Println("origin[1]: " + origin[0]);
+ Com.Println("origin[2]: " + origin[0]);
+
+ Com.Println("velocity[0]: " + velocity[0]);
+ Com.Println("velocity[1]: " + velocity[1]);
+ Com.Println("velocity[2]: " + velocity[2]);
+
+ Com.Println("pmflags: " + pm_flags);
+ Com.Println("pmtime: " + pm_time);
+ Com.Println("gravity: " + gravity);
+
+ Com.Println("delta-angle[0]: " + delta_angles[0]);
+ Com.Println("delta-angle[1]: " + delta_angles[0]);
+ Com.Println("delta-angle[2]: " + delta_angles[0]);
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.game;
+
+public class pushed_t {
+ public final float[] origin = {0.0f, 0.0f, 0.0f};
+ public final float[] angles = {0.0f, 0.0f, 0.0f};
+ public EDict ent;
+ public float deltayaw;
+}
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.game;
+
+public class spawn_t {
+ final String name;
+ final EntThinkAdapter spawn;
+
+ public spawn_t(String name, EntThinkAdapter spawn) {
+ this.name = name;
+ this.spawn = spawn;
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.game;
+
+import lwjake2.util.Lib;
+
+public class spawn_temp_t {
+ // world vars
+ public String sky = "";
+ public float skyrotate;
+ public float[] skyaxis = {0, 0, 0};
+
+ public String nextmap = "";
+
+ public int lip;
+ public int distance;
+ public int height;
+
+ public String noise = "";
+ public float pausetime;
+
+ public String item = "";
+ public String gravity = "";
+
+ public float minyaw;
+ public float maxyaw;
+ public float minpitch;
+ public float maxpitch;
+
+ public boolean set(String key, String value) {
+ if (key.equals("lip")) {
+ lip = Lib.atoi(value);
+ return true;
+ } // F_INT, FFL_SPAWNTEMP),
+
+ if (key.equals("distance")) {
+ distance = Lib.atoi(value);
+ return true;
+ } // F_INT, FFL_SPAWNTEMP),
+
+ if (key.equals("height")) {
+ height = Lib.atoi(value);
+ return true;
+ } // F_INT, FFL_SPAWNTEMP),
+
+ if (key.equals("noise")) {
+ noise = GameSpawn.ED_NewString(value);
+ return true;
+ } // F_LSTRING, FFL_SPAWNTEMP),
+
+ if (key.equals("pausetime")) {
+ pausetime = Lib.atof(value);
+ return true;
+ } // F_FLOAT, FFL_SPAWNTEMP),
+
+ if (key.equals("item")) {
+ item = GameSpawn.ED_NewString(value);
+ return true;
+ } // F_LSTRING, FFL_SPAWNTEMP),
+
+ if (key.equals("gravity")) {
+ gravity = GameSpawn.ED_NewString(value);
+ return true;
+ } // F_LSTRING, FFL_SPAWNTEMP),
+
+ if (key.equals("sky")) {
+ sky = GameSpawn.ED_NewString(value);
+ return true;
+ } // F_LSTRING, FFL_SPAWNTEMP),
+
+ if (key.equals("skyrotate")) {
+ skyrotate = Lib.atof(value);
+ return true;
+ } // F_FLOAT, FFL_SPAWNTEMP),
+
+ if (key.equals("skyaxis")) {
+ skyaxis = Lib.atov(value);
+ return true;
+ } // F_VECTOR, FFL_SPAWNTEMP),
+
+ if (key.equals("minyaw")) {
+ minyaw = Lib.atof(value);
+ return true;
+ } // F_FLOAT, FFL_SPAWNTEMP),
+
+ if (key.equals("maxyaw")) {
+ maxyaw = Lib.atof(value);
+ return true;
+ } // F_FLOAT, FFL_SPAWNTEMP),
+
+ if (key.equals("minpitch")) {
+ minpitch = Lib.atof(value);
+ return true;
+ } // F_FLOAT, FFL_SPAWNTEMP),
+
+ if (key.equals("maxpitch")) {
+ maxpitch = Lib.atof(value);
+ return true;
+ } // F_FLOAT, FFL_SPAWNTEMP),
+
+ if (key.equals("nextmap")) {
+ nextmap = GameSpawn.ED_NewString(value);
+ return true;
+ } // F_LSTRING, FFL_SPAWNTEMP),
+
+ return false;
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.game;
+
+import lwjake2.util.Math3D;
+
+//a trace is returned when a box is swept through the world
+public class trace_t {
+ public final float[] endpos = {0, 0, 0}; // final position
+ // memory
+ public final cplane_t plane = new cplane_t(); // surface normal at impact
+ public boolean allsolid; // if true, plane is not valid
+ public boolean startsolid; // if true, the initial point was in a solid area
+ public float fraction; // time completed, 1.0 = didn't hit anything
+ // pointer
+ public csurface_t surface; // surface hit
+ public int contents; // contents on other side of surface hit
+ // pointer
+ public EDict ent; // not set by CM_*() functions
+
+ public void set(trace_t from) {
+ allsolid = from.allsolid;
+ startsolid = from.allsolid;
+ fraction = from.fraction;
+ Math3D.vectorCopy(from.endpos, endpos);
+ plane.set(from.plane);
+ surface = from.surface;
+ contents = from.contents;
+ ent = from.ent;
+ }
+
+ public void clear() {
+ allsolid = false;
+ startsolid = false;
+ fraction = 0;
+ Math3D.vectorClear(endpos);
+ plane.clear();
+ surface = null;
+ contents = 0;
+ ent = null;
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.game;
+
+public class usercmd_t implements Cloneable {
+ public final short[] angles = new short[3];
+ public byte msec;
+ public byte buttons;
+ public short forwardmove, sidemove, upmove;
+ public byte impulse; // remove?
+ public byte lightlevel; // light level the player is standing on
+
+ public usercmd_t() {
+ }
+
+ public usercmd_t(usercmd_t from) {
+ msec = from.msec;
+ buttons = from.buttons;
+ angles[0] = from.angles[0];
+ angles[1] = from.angles[1];
+ angles[2] = from.angles[2];
+ forwardmove = from.forwardmove;
+ sidemove = from.sidemove;
+ upmove = from.upmove;
+ impulse = from.impulse;
+ lightlevel = from.lightlevel;
+ }
+
+ public void clear() {
+ forwardmove = sidemove = upmove = msec = buttons = impulse = lightlevel = 0;
+ angles[0] = angles[1] = angles[2] = 0;
+ }
+
+ public void set(usercmd_t from) {
+ msec = from.msec;
+ buttons = from.buttons;
+ angles[0] = from.angles[0];
+ angles[1] = from.angles[1];
+ angles[2] = from.angles[2];
+ forwardmove = from.forwardmove;
+ sidemove = from.sidemove;
+ upmove = from.upmove;
+ impulse = from.impulse;
+ lightlevel = from.lightlevel;
+
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.qcommon;
+
+import lwjake2.Defines;
+import lwjake2.Globals;
+import lwjake2.game.*;
+import lwjake2.util.Lib;
+import lwjake2.util.Math3D;
+import lwjake2.util.Vargs;
+import lwjake2.util.Vec3Cache;
+
+import java.io.RandomAccessFile;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.IntBuffer;
+import java.util.Arrays;
+
+public class CM {
+
+ public static final mapsurface_t[] map_surfaces = new mapsurface_t[Defines.MAX_MAP_TEXINFO];
+ public static final int[] map_leafbrushes = new int[Defines.MAX_MAP_LEAFBRUSHES];
+ public static final cmodel_t[] map_cmodels = new cmodel_t[Defines.MAX_MAP_MODELS];
+ public static final cbrush_t[] map_brushes = new cbrush_t[Defines.MAX_MAP_BRUSHES];
+ public static final byte[] map_visibility = new byte[Defines.MAX_MAP_VISIBILITY];
+ public static final carea_t[] map_areas = new carea_t[Defines.MAX_MAP_AREAS];
+ public static final qfiles.dareaportal_t[] map_areaportals = new qfiles.dareaportal_t[Defines.MAX_MAP_AREAPORTALS];
+ public static final mapsurface_t nullsurface = new mapsurface_t();
+ public static final boolean[] portalopen = new boolean[Defines.MAX_MAP_AREAPORTALS];
+ public static final byte[] pvsrow = new byte[Defines.MAX_MAP_LEAFS / 8];
+ public static final byte[] phsrow = new byte[Defines.MAX_MAP_LEAFS / 8];
+ static final cbrushside_t[] map_brushsides = new cbrushside_t[Defines.MAX_MAP_BRUSHSIDES];
+ /**
+ * Extra for box hull ( +6)
+ */
+ static final cplane_t[] map_planes = new cplane_t[Defines.MAX_MAP_PLANES + 6];
+ /**
+ * Extra for box hull ( +6)
+ */
+ static final cnode_t[] map_nodes = new cnode_t[Defines.MAX_MAP_NODES + 6];
+ static final cleaf_t[] map_leafs = new cleaf_t[Defines.MAX_MAP_LEAFS];
+ static final boolean debugloadmap = false;
+ // 1/32 epsilon to keep floating point happy
+ private static final float DIST_EPSILON = 0.03125f;
+ private static final float[] trace_start = {0, 0, 0};
+ private static final float[] trace_end = {0, 0, 0};
+ private static final float[] trace_mins = {0, 0, 0};
+ private static final float[] trace_maxs = {0, 0, 0};
+ private static final float[] trace_extents = {0, 0, 0};
+ public static int numtexinfo;
+ public static int numcmodels;
+ public static int numbrushes;
+ public static int numvisibility;
+ /**
+ * Main visibility data.
+ */
+ public static qfiles.dvis_t map_vis = new qfiles.dvis_t(ByteBuffer
+ .wrap(map_visibility));
+ public static String map_entitystring;
+ public static int numareas = 1;
+ public static int numareaportals;
+ public static int numclusters = 1;
+ public static int floodvalid;
+ public static CvarT map_noareas;
+ public static byte cmod_base[];
+ public static int last_checksum;
+ static int checkcount;
+ static String map_name = "";
+ static int numbrushsides;
+ static int numplanes;
+ static int numnodes;
+ static int numleafs = 1; // allow leaf funcs to be called without a map
+ static int emptyleaf;
+ static int numleafbrushes;
+ static cplane_t box_planes[];
+ static int box_headnode;
+ static cbrush_t box_brush;
+ static cleaf_t box_leaf;
+ private static int leaf_count, leaf_maxcount;
+ private static int leaf_list[];
+ private static float leaf_mins[], leaf_maxs[];
+ private static int leaf_topnode;
+ private static trace_t trace_trace = new trace_t();
+ private static int trace_contents;
+ private static boolean trace_ispoint; // optimized case
+
+ static {
+ for (int n = 0; n < Defines.MAX_MAP_BRUSHSIDES; n++)
+ map_brushsides[n] = new cbrushside_t();
+ }
+
+ static {
+ for (int n = 0; n < Defines.MAX_MAP_TEXINFO; n++)
+ map_surfaces[n] = new mapsurface_t();
+ }
+
+ static {
+ for (int n = 0; n < Defines.MAX_MAP_PLANES + 6; n++)
+ map_planes[n] = new cplane_t();
+ }
+
+ static {
+ for (int n = 0; n < Defines.MAX_MAP_NODES + 6; n++)
+ map_nodes[n] = new cnode_t();
+ }
+
+ static {
+ for (int n = 0; n < Defines.MAX_MAP_LEAFS; n++)
+ map_leafs[n] = new cleaf_t();
+ }
+
+ static {
+ for (int n = 0; n < Defines.MAX_MAP_MODELS; n++)
+ map_cmodels[n] = new cmodel_t();
+
+ }
+
+ static {
+ for (int n = 0; n < Defines.MAX_MAP_BRUSHES; n++)
+ map_brushes[n] = new cbrush_t();
+
+ }
+
+ static {
+ for (int n = 0; n < Defines.MAX_MAP_AREAS; n++)
+ map_areas[n] = new carea_t();
+
+ }
+
+ static {
+ for (int n = 0; n < Defines.MAX_MAP_AREAPORTALS; n++)
+ map_areaportals[n] = new qfiles.dareaportal_t();
+
+ }
+
+ /**
+ * Loads in the map and all submodels.
+ */
+ public static cmodel_t CM_LoadMap(String name, boolean clientload,
+ int checksum[]) {
+ Com.DPrintf("CM_LoadMap(" + name + ")...\n");
+ byte buf[];
+ qfiles.dheader_t header;
+ int length;
+
+ map_noareas = Cvar.get("map_noareas", "0", 0);
+
+ if (map_name.equals(name)
+ && (clientload || 0 == Cvar.variableValue("flushmap"))) {
+
+ checksum[0] = last_checksum;
+
+ if (!clientload) {
+ Arrays.fill(portalopen, false);
+ FloodAreaConnections();
+ }
+ return map_cmodels[0]; // still have the right version
+ }
+
+ // free old stuff
+ numnodes = 0;
+ numleafs = 0;
+ numcmodels = 0;
+ numvisibility = 0;
+ map_entitystring = "";
+ map_name = "";
+
+ if (name == null || name.length() == 0) {
+ numleafs = 1;
+ numclusters = 1;
+ numareas = 1;
+ checksum[0] = 0;
+ return map_cmodels[0];
+ // cinematic servers won't have anything at all
+ }
+
+ //
+ // load the file
+ //
+ buf = FS.LoadFile(name);
+
+ if (buf == null)
+ Com.Error(Defines.ERR_DROP, "Couldn't load " + name);
+
+ length = buf.length;
+
+ ByteBuffer bbuf = ByteBuffer.wrap(buf);
+
+ last_checksum = MD4.Com_BlockChecksum(buf, length);
+ checksum[0] = last_checksum;
+
+ header = new qfiles.dheader_t(bbuf.slice());
+
+ if (header.version != Defines.BSPVERSION)
+ Com.Error(Defines.ERR_DROP, "CMod_LoadBrushModel: " + name
+ + " has wrong version number (" + header.version
+ + " should be " + Defines.BSPVERSION + ")");
+
+ cmod_base = buf;
+
+ // load into heap
+ CMod_LoadSurfaces(header.lumps[Defines.LUMP_TEXINFO]); // ok
+ CMod_LoadLeafs(header.lumps[Defines.LUMP_LEAFS]);
+ CMod_LoadLeafBrushes(header.lumps[Defines.LUMP_LEAFBRUSHES]);
+ CMod_LoadPlanes(header.lumps[Defines.LUMP_PLANES]);
+ CMod_LoadBrushes(header.lumps[Defines.LUMP_BRUSHES]);
+ CMod_LoadBrushSides(header.lumps[Defines.LUMP_BRUSHSIDES]);
+ CMod_LoadSubmodels(header.lumps[Defines.LUMP_MODELS]);
+
+ CMod_LoadNodes(header.lumps[Defines.LUMP_NODES]);
+ CMod_LoadAreas(header.lumps[Defines.LUMP_AREAS]);
+ CMod_LoadAreaPortals(header.lumps[Defines.LUMP_AREAPORTALS]);
+ CMod_LoadVisibility(header.lumps[Defines.LUMP_VISIBILITY]);
+ CMod_LoadEntityString(header.lumps[Defines.LUMP_ENTITIES]);
+
+ FS.FreeFile();
+
+ CM_InitBoxHull();
+
+ Arrays.fill(portalopen, false);
+
+ FloodAreaConnections();
+
+ map_name = name;
+
+ return map_cmodels[0];
+ }
+
+ /**
+ * Loads Submodels.
+ */
+ public static void CMod_LoadSubmodels(lump_t l) {
+ Com.DPrintf("CMod_LoadSubmodels()\n");
+ qfiles.dmodel_t in;
+ cmodel_t out;
+ int i, j, count;
+
+ if ((l.filelen % qfiles.dmodel_t.SIZE) != 0)
+ Com.Error(Defines.ERR_DROP, "CMod_LoadBmodel: funny lump size");
+
+ count = l.filelen / qfiles.dmodel_t.SIZE;
+
+ if (count < 1)
+ Com.Error(Defines.ERR_DROP, "Map with no models");
+ if (count > Defines.MAX_MAP_MODELS)
+ Com.Error(Defines.ERR_DROP, "Map has too many models");
+
+ Com.DPrintf(" numcmodels=" + count + "\n");
+ numcmodels = count;
+
+ if (debugloadmap) {
+ Com.DPrintf("submodles(headnode, <origin>, <mins>, <maxs>)\n");
+ }
+ for (i = 0; i < count; i++) {
+ in = new qfiles.dmodel_t(ByteBuffer.wrap(cmod_base, i
+ * qfiles.dmodel_t.SIZE + l.fileofs, qfiles.dmodel_t.SIZE));
+ out = map_cmodels[i];
+
+ for (j = 0; j < 3; j++) { // spread the mins / maxs by a pixel
+ out.mins[j] = in.mins[j] - 1;
+ out.maxs[j] = in.maxs[j] + 1;
+ out.origin[j] = in.origin[j];
+ }
+ out.headnode = in.headnode;
+ if (debugloadmap) {
+ Com
+ .DPrintf(
+ "|%6i|%8.2f|%8.2f|%8.2f| %8.2f|%8.2f|%8.2f| %8.2f|%8.2f|%8.2f|\n",
+ new Vargs().add(out.headnode)
+ .add(out.origin[0]).add(out.origin[1])
+ .add(out.origin[2]).add(out.mins[0])
+ .add(out.mins[1]).add(out.mins[2]).add(
+ out.maxs[0]).add(out.maxs[1])
+ .add(out.maxs[2]));
+ }
+ }
+ }
+
+ /**
+ * Loads surfaces.
+ */
+ public static void CMod_LoadSurfaces(lump_t l) {
+ Com.DPrintf("CMod_LoadSurfaces()\n");
+ texinfo_t in;
+ mapsurface_t out;
+ int i, count;
+
+ if ((l.filelen % texinfo_t.SIZE) != 0)
+ Com.Error(Defines.ERR_DROP, "MOD_LoadBmodel: funny lump size");
+
+ count = l.filelen / texinfo_t.SIZE;
+ if (count < 1)
+ Com.Error(Defines.ERR_DROP, "Map with no surfaces");
+ if (count > Defines.MAX_MAP_TEXINFO)
+ Com.Error(Defines.ERR_DROP, "Map has too many surfaces");
+
+ numtexinfo = count;
+ Com.DPrintf(" numtexinfo=" + count + "\n");
+ if (debugloadmap)
+ Com.DPrintf("surfaces:\n");
+
+ for (i = 0; i < count; i++) {
+ out = map_surfaces[i] = new mapsurface_t();
+ in = new texinfo_t(cmod_base, l.fileofs + i * texinfo_t.SIZE,
+ texinfo_t.SIZE);
+
+ out.c.name = in.texture;
+ out.rname = in.texture;
+ out.c.flags = in.flags;
+ out.c.value = in.value;
+
+ if (debugloadmap) {
+ Com.DPrintf("|%20s|%20s|%6i|%6i|\n", new Vargs()
+ .add(out.c.name).add(out.rname).add(out.c.value).add(
+ out.c.flags));
+ }
+
+ }
+ }
+
+ /**
+ * Loads nodes.
+ */
+ public static void CMod_LoadNodes(lump_t l) {
+ Com.DPrintf("CMod_LoadNodes()\n");
+ qfiles.dnode_t in;
+ int child;
+ cnode_t out;
+ int i, j, count;
+
+ if ((l.filelen % qfiles.dnode_t.SIZE) != 0)
+ Com.Error(Defines.ERR_DROP, "MOD_LoadBmodel: funny lump size:"
+ + l.fileofs + "," + qfiles.dnode_t.SIZE);
+ count = l.filelen / qfiles.dnode_t.SIZE;
+
+ if (count < 1)
+ Com.Error(Defines.ERR_DROP, "Map has no nodes");
+ if (count > Defines.MAX_MAP_NODES)
+ Com.Error(Defines.ERR_DROP, "Map has too many nodes");
+
+ numnodes = count;
+ Com.DPrintf(" numnodes=" + count + "\n");
+
+ if (debugloadmap) {
+ Com.DPrintf("nodes(planenum, child[0], child[1])\n");
+ }
+
+ for (i = 0; i < count; i++) {
+ in = new qfiles.dnode_t(ByteBuffer.wrap(cmod_base,
+ qfiles.dnode_t.SIZE * i + l.fileofs, qfiles.dnode_t.SIZE));
+ out = map_nodes[i];
+
+ out.plane = map_planes[in.planenum];
+ for (j = 0; j < 2; j++) {
+ child = in.children[j];
+ out.children[j] = child;
+ }
+ if (debugloadmap) {
+ Com.DPrintf("|%6i| %6i| %6i|\n", new Vargs().add(in.planenum)
+ .add(out.children[0]).add(out.children[1]));
+ }
+ }
+ }
+
+ /**
+ * Loads brushes.
+ */
+ public static void CMod_LoadBrushes(lump_t l) {
+ Com.DPrintf("CMod_LoadBrushes()\n");
+ qfiles.dbrush_t in;
+ cbrush_t out;
+ int i, count;
+
+ if ((l.filelen % qfiles.dbrush_t.SIZE) != 0)
+ Com.Error(Defines.ERR_DROP, "MOD_LoadBmodel: funny lump size");
+
+ count = l.filelen / qfiles.dbrush_t.SIZE;
+
+ if (count > Defines.MAX_MAP_BRUSHES)
+ Com.Error(Defines.ERR_DROP, "Map has too many brushes");
+
+ numbrushes = count;
+ Com.DPrintf(" numbrushes=" + count + "\n");
+ if (debugloadmap) {
+ Com.DPrintf("brushes:(firstbrushside, numsides, contents)\n");
+ }
+ for (i = 0; i < count; i++) {
+ in = new qfiles.dbrush_t(ByteBuffer.wrap(cmod_base, i
+ * qfiles.dbrush_t.SIZE + l.fileofs, qfiles.dbrush_t.SIZE));
+ out = map_brushes[i];
+ out.firstbrushside = in.firstside;
+ out.numsides = in.numsides;
+ out.contents = in.contents;
+
+ if (debugloadmap) {
+ Com
+ .DPrintf("| %6i| %6i| %8X|\n", new Vargs().add(
+ out.firstbrushside).add(out.numsides).add(
+ out.contents));
+ }
+ }
+ }
+
+ /**
+ * Loads leafs.
+ */
+ public static void CMod_LoadLeafs(lump_t l) {
+ Com.DPrintf("CMod_LoadLeafs()\n");
+ int i;
+ cleaf_t out;
+ qfiles.dleaf_t in;
+ int count;
+
+ if ((l.filelen % qfiles.dleaf_t.SIZE) != 0)
+ Com.Error(Defines.ERR_DROP, "MOD_LoadBmodel: funny lump size");
+
+ count = l.filelen / qfiles.dleaf_t.SIZE;
+
+ if (count < 1)
+ Com.Error(Defines.ERR_DROP, "Map with no leafs");
+
+ // need to save space for box planes
+ if (count > Defines.MAX_MAP_PLANES)
+ Com.Error(Defines.ERR_DROP, "Map has too many planes");
+
+ Com.DPrintf(" numleafes=" + count + "\n");
+
+ numleafs = count;
+ numclusters = 0;
+ if (debugloadmap)
+ Com.DPrintf("cleaf-list:(contents, cluster, area, firstleafbrush, numleafbrushes)\n");
+ for (i = 0; i < count; i++) {
+ in = new qfiles.dleaf_t(cmod_base, i * qfiles.dleaf_t.SIZE
+ + l.fileofs, qfiles.dleaf_t.SIZE);
+
+ out = map_leafs[i];
+
+ out.contents = in.contents;
+ out.cluster = in.cluster;
+ out.area = in.area;
+ out.firstleafbrush = (short) in.firstleafbrush;
+ out.numleafbrushes = (short) in.numleafbrushes;
+
+ if (out.cluster >= numclusters)
+ numclusters = out.cluster + 1;
+
+ if (debugloadmap) {
+ Com.DPrintf("|%8x|%6i|%6i|%6i|\n", new Vargs()
+ .add(out.contents).add(out.cluster).add(out.area).add(
+ out.firstleafbrush).add(out.numleafbrushes));
+ }
+
+ }
+
+ Com.DPrintf(" numclusters=" + numclusters + "\n");
+
+ if (map_leafs[0].contents != Defines.CONTENTS_SOLID)
+ Com.Error(Defines.ERR_DROP, "Map leaf 0 is not CONTENTS_SOLID");
+
+ emptyleaf = -1;
+
+ for (i = 1; i < numleafs; i++) {
+ if (map_leafs[i].contents == 0) {
+ emptyleaf = i;
+ break;
+ }
+ }
+
+ if (emptyleaf == -1)
+ Com.Error(Defines.ERR_DROP, "Map does not have an empty leaf");
+ }
+
+ /**
+ * Loads planes.
+ */
+ public static void CMod_LoadPlanes(lump_t l) {
+ Com.DPrintf("CMod_LoadPlanes()\n");
+ int i, j;
+ cplane_t out;
+ qfiles.dplane_t in;
+ int count;
+ int bits;
+
+ if ((l.filelen % qfiles.dplane_t.SIZE) != 0)
+ Com.Error(Defines.ERR_DROP, "MOD_LoadBmodel: funny lump size");
+
+ count = l.filelen / qfiles.dplane_t.SIZE;
+
+ if (count < 1)
+ Com.Error(Defines.ERR_DROP, "Map with no planes");
+
+ // need to save space for box planes
+ if (count > Defines.MAX_MAP_PLANES)
+ Com.Error(Defines.ERR_DROP, "Map has too many planes");
+
+ Com.DPrintf(" numplanes=" + count + "\n");
+
+ numplanes = count;
+ if (debugloadmap) {
+ Com
+ .DPrintf("cplanes(normal[0],normal[1],normal[2], dist, type, signbits)\n");
+ }
+
+ for (i = 0; i < count; i++) {
+ in = new qfiles.dplane_t(ByteBuffer.wrap(cmod_base, i
+ * qfiles.dplane_t.SIZE + l.fileofs, qfiles.dplane_t.SIZE));
+
+ out = map_planes[i];
+
+ bits = 0;
+ for (j = 0; j < 3; j++) {
+ out.normal[j] = in.normal[j];
+
+ if (out.normal[j] < 0)
+ bits |= 1 << j;
+ }
+
+ out.dist = in.dist;
+ out.type = (byte) in.type;
+ out.signbits = (byte) bits;
+
+ if (debugloadmap) {
+ Com.DPrintf("|%6.2f|%6.2f|%6.2f| %10.2f|%3i| %1i|\n",
+ new Vargs().add(out.normal[0]).add(out.normal[1]).add(
+ out.normal[2]).add(out.dist).add(out.type).add(
+ out.signbits));
+ }
+ }
+ }
+
+ /**
+ * Loads leaf brushes.
+ */
+ public static void CMod_LoadLeafBrushes(lump_t l) {
+ Com.DPrintf("CMod_LoadLeafBrushes()\n");
+ int i;
+ int out[];
+ int count;
+
+ if ((l.filelen % 2) != 0)
+ Com.Error(Defines.ERR_DROP, "MOD_LoadBmodel: funny lump size");
+
+ count = l.filelen / 2;
+
+ Com.DPrintf(" numbrushes=" + count + "\n");
+
+ if (count < 1)
+ Com.Error(Defines.ERR_DROP, "Map with no planes");
+
+ // need to save space for box planes
+ if (count > Defines.MAX_MAP_LEAFBRUSHES)
+ Com.Error(Defines.ERR_DROP, "Map has too many leafbrushes");
+
+ out = map_leafbrushes;
+ numleafbrushes = count;
+
+ ByteBuffer bb = ByteBuffer.wrap(cmod_base, l.fileofs, count * 2).order(
+ ByteOrder.LITTLE_ENDIAN);
+
+ if (debugloadmap) {
+ Com.DPrintf("map_brushes:\n");
+ }
+
+ for (i = 0; i < count; i++) {
+ out[i] = bb.getShort();
+ if (debugloadmap) {
+ Com.DPrintf("|%6i|%6i|\n", new Vargs().add(i).add(out[i]));
+ }
+ }
+ }
+
+ /**
+ * Loads brush sides.
+ */
+ public static void CMod_LoadBrushSides(lump_t l) {
+ Com.DPrintf("CMod_LoadBrushSides()\n");
+ int i, j;
+ cbrushside_t out;
+ qfiles.dbrushside_t in;
+ int count;
+ int num;
+
+ if ((l.filelen % qfiles.dbrushside_t.SIZE) != 0)
+ Com.Error(Defines.ERR_DROP, "MOD_LoadBmodel: funny lump size");
+ count = l.filelen / qfiles.dbrushside_t.SIZE;
+
+ // need to save space for box planes
+ if (count > Defines.MAX_MAP_BRUSHSIDES)
+ Com.Error(Defines.ERR_DROP, "Map has too many planes");
+
+ numbrushsides = count;
+
+ Com.DPrintf(" numbrushsides=" + count + "\n");
+
+ if (debugloadmap) {
+ Com.DPrintf("brushside(planenum, surfacenum):\n");
+ }
+ for (i = 0; i < count; i++) {
+
+ in = new qfiles.dbrushside_t(ByteBuffer.wrap(cmod_base, i
+ * qfiles.dbrushside_t.SIZE + l.fileofs,
+ qfiles.dbrushside_t.SIZE));
+
+ out = map_brushsides[i];
+
+ num = in.planenum;
+
+ out.plane = map_planes[num]; // pointer
+
+ j = in.texinfo;
+
+ if (j >= numtexinfo)
+ Com.Error(Defines.ERR_DROP, "Bad brushside texinfo");
+
+ // java specific handling of -1
+ if (j == -1)
+ out.surface = new mapsurface_t(); // just for safety
+ else
+ out.surface = map_surfaces[j];
+
+ if (debugloadmap) {
+ Com.DPrintf("| %6i| %6i|\n", new Vargs().add(num).add(j));
+ }
+ }
+ }
+
+ /**
+ * Loads areas.
+ */
+ public static void CMod_LoadAreas(lump_t l) {
+ Com.DPrintf("CMod_LoadAreas()\n");
+ int i;
+ carea_t out;
+ qfiles.darea_t in;
+ int count;
+
+ if ((l.filelen % qfiles.darea_t.SIZE) != 0)
+ Com.Error(Defines.ERR_DROP, "MOD_LoadBmodel: funny lump size");
+
+ count = l.filelen / qfiles.darea_t.SIZE;
+
+ if (count > Defines.MAX_MAP_AREAS)
+ Com.Error(Defines.ERR_DROP, "Map has too many areas");
+
+ Com.DPrintf(" numareas=" + count + "\n");
+ numareas = count;
+
+ if (debugloadmap) {
+ Com.DPrintf("areas(numportals, firstportal)\n");
+ }
+
+ for (i = 0; i < count; i++) {
+
+ in = new qfiles.darea_t(ByteBuffer.wrap(cmod_base, i
+ * qfiles.darea_t.SIZE + l.fileofs, qfiles.darea_t.SIZE));
+ out = map_areas[i];
+
+ out.numareaportals = in.numareaportals;
+ out.firstareaportal = in.firstareaportal;
+ out.floodvalid = 0;
+ out.floodnum = 0;
+ if (debugloadmap) {
+ Com.DPrintf("| %6i| %6i|\n", new Vargs()
+ .add(out.numareaportals).add(out.firstareaportal));
+ }
+ }
+ }
+
+ /**
+ * Loads area portals.
+ */
+ public static void CMod_LoadAreaPortals(lump_t l) {
+ Com.DPrintf("CMod_LoadAreaPortals()\n");
+ int i;
+ qfiles.dareaportal_t out;
+ qfiles.dareaportal_t in;
+ int count;
+
+ if ((l.filelen % qfiles.dareaportal_t.SIZE) != 0)
+ Com.Error(Defines.ERR_DROP, "MOD_LoadBmodel: funny lump size");
+ count = l.filelen / qfiles.dareaportal_t.SIZE;
+
+ if (count > Defines.MAX_MAP_AREAS)
+ Com.Error(Defines.ERR_DROP, "Map has too many areas");
+
+ numareaportals = count;
+ Com.DPrintf(" numareaportals=" + count + "\n");
+ if (debugloadmap) {
+ Com.DPrintf("areaportals(portalnum, otherarea)\n");
+ }
+ for (i = 0; i < count; i++) {
+ in = new qfiles.dareaportal_t(ByteBuffer.wrap(cmod_base, i
+ * qfiles.dareaportal_t.SIZE + l.fileofs,
+ qfiles.dareaportal_t.SIZE));
+
+ out = map_areaportals[i];
+
+ out.portalnum = in.portalnum;
+ out.otherarea = in.otherarea;
+
+ if (debugloadmap) {
+ Com.DPrintf("|%6i|%6i|\n", new Vargs().add(out.portalnum).add(
+ out.otherarea));
+ }
+ }
+ }
+
+ /**
+ * Loads visibility data.
+ */
+ public static void CMod_LoadVisibility(lump_t l) {
+ Com.DPrintf("CMod_LoadVisibility()\n");
+
+ numvisibility = l.filelen;
+
+ Com.DPrintf(" numvisibility=" + numvisibility + "\n");
+
+ if (l.filelen > Defines.MAX_MAP_VISIBILITY)
+ Com.Error(Defines.ERR_DROP, "Map has too large visibility lump");
+
+ System.arraycopy(cmod_base, l.fileofs, map_visibility, 0, l.filelen);
+
+ ByteBuffer bb = ByteBuffer.wrap(map_visibility, 0, l.filelen);
+ bb.order(ByteOrder.LITTLE_ENDIAN);
+
+ map_vis = new qfiles.dvis_t(bb);
+
+ }
+
+ /**
+ * Loads entity strings.
+ */
+ public static void CMod_LoadEntityString(lump_t l) {
+ Com.DPrintf("CMod_LoadEntityString()\n");
+
+ if (l.filelen > Defines.MAX_MAP_ENTSTRING)
+ Com.Error(Defines.ERR_DROP, "Map has too large entity lump");
+
+ int x = 0;
+ for (; x < l.filelen && cmod_base[x + l.fileofs] != 0; x++) ;
+
+ map_entitystring = new String(cmod_base, l.fileofs, x).trim();
+ Com.dprintln("entitystring=" + map_entitystring.length() +
+ " bytes, [" + map_entitystring.substring(0, Math.min(
+ map_entitystring.length(), 15)) + "...]");
+ }
+
+ /**
+ * Returns the model with a given id "*" + <number>
+ */
+ public static cmodel_t InlineModel(String name) {
+ int num;
+
+ if (name == null || name.charAt(0) != '*')
+ Com.Error(Defines.ERR_DROP, "CM_InlineModel: bad name");
+
+ num = Lib.atoi(name.substring(1));
+
+ if (num < 1 || num >= numcmodels)
+ Com.Error(Defines.ERR_DROP, "CM_InlineModel: bad number");
+
+ return map_cmodels[num];
+ }
+
+ public static int CM_NumClusters() {
+ return numclusters;
+ }
+
+ public static int CM_NumInlineModels() {
+ return numcmodels;
+ }
+
+ public static String CM_EntityString() {
+ return map_entitystring;
+ }
+
+ public static int CM_LeafCluster(int leafnum) {
+ if (leafnum < 0 || leafnum >= numleafs)
+ Com.Error(Defines.ERR_DROP, "CM_LeafCluster: bad number");
+ return map_leafs[leafnum].cluster;
+ }
+
+ public static int CM_LeafArea(int leafnum) {
+ if (leafnum < 0 || leafnum >= numleafs)
+ Com.Error(Defines.ERR_DROP, "CM_LeafArea: bad number");
+ return map_leafs[leafnum].area;
+ }
+
+ /**
+ * Set up the planes and nodes so that the six floats of a bounding box can
+ * just be stored out and get a proper clipping hull structure.
+ */
+ public static void CM_InitBoxHull() {
+ int i;
+ int side;
+ cnode_t c;
+ cplane_t p;
+ cbrushside_t s;
+
+ box_headnode = numnodes; //rst: still room for 6 brushes left?
+
+ box_planes = new cplane_t[]{map_planes[numplanes],
+ map_planes[numplanes + 1], map_planes[numplanes + 2],
+ map_planes[numplanes + 3], map_planes[numplanes + 4],
+ map_planes[numplanes + 5], map_planes[numplanes + 6],
+ map_planes[numplanes + 7], map_planes[numplanes + 8],
+ map_planes[numplanes + 9], map_planes[numplanes + 10],
+ map_planes[numplanes + 11], map_planes[numplanes + 12]};
+
+ if (numnodes + 6 > Defines.MAX_MAP_NODES
+ || numbrushes + 1 > Defines.MAX_MAP_BRUSHES
+ || numleafbrushes + 1 > Defines.MAX_MAP_LEAFBRUSHES
+ || numbrushsides + 6 > Defines.MAX_MAP_BRUSHSIDES
+ || numplanes + 12 > Defines.MAX_MAP_PLANES)
+ Com.Error(Defines.ERR_DROP, "Not enough room for box tree");
+
+ box_brush = map_brushes[numbrushes];
+ box_brush.numsides = 6;
+ box_brush.firstbrushside = numbrushsides;
+ box_brush.contents = Defines.CONTENTS_MONSTER;
+
+ box_leaf = map_leafs[numleafs];
+ box_leaf.contents = Defines.CONTENTS_MONSTER;
+ box_leaf.firstleafbrush = (short) numleafbrushes;
+ box_leaf.numleafbrushes = 1;
+
+ map_leafbrushes[numleafbrushes] = numbrushes;
+
+ for (i = 0; i < 6; i++) {
+ side = i & 1;
+
+ // brush sides
+ s = map_brushsides[numbrushsides + i];
+ s.plane = map_planes[(numplanes + i * 2 + side)];
+ s.surface = nullsurface;
+
+ // nodes
+ c = map_nodes[box_headnode + i];
+ c.plane = map_planes[(numplanes + i * 2)];
+ c.children[side] = -1 - emptyleaf;
+ if (i != 5)
+ c.children[side ^ 1] = box_headnode + i + 1;
+ else
+ c.children[side ^ 1] = -1 - numleafs;
+
+ // planes
+ p = box_planes[i * 2];
+ p.type = (byte) (i >> 1);
+ p.signbits = 0;
+ Math3D.vectorClear(p.normal);
+ p.normal[i >> 1] = 1;
+
+ p = box_planes[i * 2 + 1];
+ p.type = (byte) (3 + (i >> 1));
+ p.signbits = 0;
+ Math3D.vectorClear(p.normal);
+ p.normal[i >> 1] = -1;
+ }
+ }
+
+ /**
+ * To keep everything totally uniform, bounding boxes are turned into small
+ * BSP trees instead of being compared directly.
+ */
+ public static int HeadnodeForBox(float[] mins, float[] maxs) {
+ box_planes[0].dist = maxs[0];
+ box_planes[1].dist = -maxs[0];
+ box_planes[2].dist = mins[0];
+ box_planes[3].dist = -mins[0];
+ box_planes[4].dist = maxs[1];
+ box_planes[5].dist = -maxs[1];
+ box_planes[6].dist = mins[1];
+ box_planes[7].dist = -mins[1];
+ box_planes[8].dist = maxs[2];
+ box_planes[9].dist = -maxs[2];
+ box_planes[10].dist = mins[2];
+ box_planes[11].dist = -mins[2];
+
+ return box_headnode;
+ }
+
+ /**
+ * Recursively searches the leaf number that contains the 3d point.
+ */
+ private static int CM_PointLeafnum_r(float[] p, int num) {
+ float d;
+ cnode_t node;
+ cplane_t plane;
+
+ while (num >= 0) {
+ node = map_nodes[num];
+ plane = node.plane;
+
+ if (plane.type < 3)
+ d = p[plane.type] - plane.dist;
+ else
+ d = Math3D.dotProduct(plane.normal, p) - plane.dist;
+ if (d < 0)
+ num = node.children[1];
+ else
+ num = node.children[0];
+ }
+
+ Globals.c_pointcontents++; // optimize counter
+
+ return -1 - num;
+ }
+
+ /**
+ * Searches the leaf number that contains the 3d point.
+ */
+ public static int CM_PointLeafnum(float[] p) {
+ // sound may call this without map loaded
+ if (numplanes == 0)
+ return 0;
+ return CM_PointLeafnum_r(p, 0);
+ }
+
+ /**
+ * Recursively fills in a list of all the leafs touched.
+ */
+ private static void CM_BoxLeafnums_r(int nodenum) {
+ cplane_t plane;
+ cnode_t node;
+ int s;
+
+ while (true) {
+ if (nodenum < 0) {
+ if (leaf_count >= leaf_maxcount) {
+ Com.DPrintf("CM_BoxLeafnums_r: overflow\n");
+ return;
+ }
+ leaf_list[leaf_count++] = -1 - nodenum;
+ return;
+ }
+
+ node = map_nodes[nodenum];
+ plane = node.plane;
+
+ s = Math3D.boxOnPlaneSide(leaf_mins, leaf_maxs, plane);
+
+ if (s == 1)
+ nodenum = node.children[0];
+ else if (s == 2)
+ nodenum = node.children[1];
+ else {
+ // go down both
+ if (leaf_topnode == -1)
+ leaf_topnode = nodenum;
+ CM_BoxLeafnums_r(node.children[0]);
+ nodenum = node.children[1];
+ }
+ }
+ }
+
+ /*
+ * ===============================================================================
+ *
+ * BOX TRACING
+ *
+ * ===============================================================================
+ */
+
+ /**
+ * Fills in a list of all the leafs touched and starts with the head node.
+ */
+ private static int CM_BoxLeafnums_headnode(float[] mins, float[] maxs,
+ int list[], int listsize, int headnode, int topnode[]) {
+ leaf_list = list;
+ leaf_count = 0;
+ leaf_maxcount = listsize;
+ leaf_mins = mins;
+ leaf_maxs = maxs;
+
+ leaf_topnode = -1;
+
+ CM_BoxLeafnums_r(headnode);
+
+ if (topnode != null)
+ topnode[0] = leaf_topnode;
+
+ return leaf_count;
+ }
+
+ /**
+ * Fills in a list of all the leafs touched.
+ */
+ public static int CM_BoxLeafnums(float[] mins, float[] maxs, int list[],
+ int listsize, int topnode[]) {
+ return CM_BoxLeafnums_headnode(mins, maxs, list, listsize,
+ map_cmodels[0].headnode, topnode);
+ }
+
+ /**
+ * Returns a tag that describes the content of the point.
+ */
+ public static int PointContents(float[] p, int headnode) {
+ int l;
+
+ if (numnodes == 0) // map not loaded
+ return 0;
+
+ l = CM_PointLeafnum_r(p, headnode);
+
+ return map_leafs[l].contents;
+ }
+
+ /*
+ * ================== CM_TransformedPointContents
+ *
+ * Handles offseting and rotation of the end points for moving and rotating
+ * entities ==================
+ */
+ public static int TransformedPointContents(float[] p, int headnode,
+ float[] origin, float[] angles) {
+ float[] p_l = {0, 0, 0};
+ float[] temp = {0, 0, 0};
+ float[] forward = {0, 0, 0}, right = {0, 0, 0}, up = {0, 0, 0};
+ int l;
+
+ // subtract origin offset
+ Math3D.vectorSubtract(p, origin, p_l);
+
+ // rotate start and end into the models frame of reference
+ if (headnode != box_headnode
+ && (angles[0] != 0 || angles[1] != 0 || angles[2] != 0)) {
+ Math3D.angleVectors(angles, forward, right, up);
+
+ Math3D.vectorCopy(p_l, temp);
+ p_l[0] = Math3D.dotProduct(temp, forward);
+ p_l[1] = -Math3D.dotProduct(temp, right);
+ p_l[2] = Math3D.dotProduct(temp, up);
+ }
+
+ l = CM_PointLeafnum_r(p_l, headnode);
+
+ return map_leafs[l].contents;
+ }
+
+ /*
+ * ================ CM_ClipBoxToBrush ================
+ */
+ public static void CM_ClipBoxToBrush(float[] mins, float[] maxs,
+ float[] p1, float[] p2, trace_t trace, cbrush_t brush) {
+ int i, j;
+ cplane_t plane, clipplane;
+ float dist;
+ float enterfrac, leavefrac;
+ float[] ofs = {0, 0, 0};
+ float d1, d2;
+ boolean getout, startout;
+ float f;
+ cbrushside_t side, leadside;
+
+ enterfrac = -1;
+ leavefrac = 1;
+ clipplane = null;
+
+ if (brush.numsides == 0)
+ return;
+
+ Globals.c_brush_traces++;
+
+ getout = false;
+ startout = false;
+ leadside = null;
+
+ for (i = 0; i < brush.numsides; i++) {
+ side = map_brushsides[brush.firstbrushside + i];
+ plane = side.plane;
+
+ // FIXME: special case for axial
+
+ if (!trace_ispoint) { // general box case
+
+ // push the plane out apropriately for mins/maxs
+
+ // FIXME: use signbits into 8 way lookup for each mins/maxs
+ for (j = 0; j < 3; j++) {
+ if (plane.normal[j] < 0)
+ ofs[j] = maxs[j];
+ else
+ ofs[j] = mins[j];
+ }
+ dist = Math3D.dotProduct(ofs, plane.normal);
+ dist = plane.dist - dist;
+ } else { // special point case
+ dist = plane.dist;
+ }
+
+ d1 = Math3D.dotProduct(p1, plane.normal) - dist;
+ d2 = Math3D.dotProduct(p2, plane.normal) - dist;
+
+ if (d2 > 0)
+ getout = true; // endpoint is not in solid
+ if (d1 > 0)
+ startout = true;
+
+ // if completely in front of face, no intersection
+ if (d1 > 0 && d2 >= d1)
+ return;
+
+ if (d1 <= 0 && d2 <= 0)
+ continue;
+
+ // crosses face
+ if (d1 > d2) { // enter
+ f = (d1 - DIST_EPSILON) / (d1 - d2);
+ if (f > enterfrac) {
+ enterfrac = f;
+ clipplane = plane;
+ leadside = side;
+ }
+ } else { // leave
+ f = (d1 + DIST_EPSILON) / (d1 - d2);
+ if (f < leavefrac)
+ leavefrac = f;
+ }
+ }
+
+ if (!startout) { // original point was inside brush
+ trace.startsolid = true;
+ if (!getout)
+ trace.allsolid = true;
+ return;
+ }
+ if (enterfrac < leavefrac) {
+ if (enterfrac > -1 && enterfrac < trace.fraction) {
+ if (enterfrac < 0)
+ enterfrac = 0;
+ trace.fraction = enterfrac;
+ // copy
+ trace.plane.set(clipplane);
+ trace.surface = leadside.surface.c;
+ trace.contents = brush.contents;
+ }
+ }
+ }
+
+ /*
+ * ================ CM_TestBoxInBrush ================
+ */
+ public static void CM_TestBoxInBrush(float[] mins, float[] maxs,
+ float[] p1, trace_t trace, cbrush_t brush) {
+ int i, j;
+ cplane_t plane;
+ float dist;
+ float[] ofs = {0, 0, 0};
+ float d1;
+ cbrushside_t side;
+
+ if (brush.numsides == 0)
+ return;
+
+ for (i = 0; i < brush.numsides; i++) {
+ side = map_brushsides[brush.firstbrushside + i];
+ plane = side.plane;
+
+ // FIXME: special case for axial
+ // general box case
+ // push the plane out apropriately for mins/maxs
+ // FIXME: use signbits into 8 way lookup for each mins/maxs
+
+ for (j = 0; j < 3; j++) {
+ if (plane.normal[j] < 0)
+ ofs[j] = maxs[j];
+ else
+ ofs[j] = mins[j];
+ }
+ dist = Math3D.dotProduct(ofs, plane.normal);
+ dist = plane.dist - dist;
+
+ d1 = Math3D.dotProduct(p1, plane.normal) - dist;
+
+ // if completely in front of face, no intersection
+ if (d1 > 0)
+ return;
+
+ }
+
+ // inside this brush
+ trace.startsolid = trace.allsolid = true;
+ trace.fraction = 0;
+ trace.contents = brush.contents;
+ }
+
+ /*
+ * ================ CM_TraceToLeaf ================
+ */
+ public static void CM_TraceToLeaf(int leafnum) {
+ int k;
+ int brushnum;
+ cleaf_t leaf;
+ cbrush_t b;
+
+ leaf = map_leafs[leafnum];
+ if (0 == (leaf.contents & trace_contents))
+ return;
+
+ // trace line against all brushes in the leaf
+ for (k = 0; k < leaf.numleafbrushes; k++) {
+
+ brushnum = map_leafbrushes[leaf.firstleafbrush + k];
+ b = map_brushes[brushnum];
+ if (b.checkcount == checkcount)
+ continue; // already checked this brush in another leaf
+ b.checkcount = checkcount;
+
+ if (0 == (b.contents & trace_contents))
+ continue;
+ CM_ClipBoxToBrush(trace_mins, trace_maxs, trace_start, trace_end,
+ trace_trace, b);
+ if (0 == trace_trace.fraction)
+ return;
+ }
+
+ }
+
+ /*
+ * ================ CM_TestInLeaf ================
+ */
+ public static void CM_TestInLeaf(int leafnum) {
+ int k;
+ int brushnum;
+ cleaf_t leaf;
+ cbrush_t b;
+
+ leaf = map_leafs[leafnum];
+ if (0 == (leaf.contents & trace_contents))
+ return;
+ // trace line against all brushes in the leaf
+ for (k = 0; k < leaf.numleafbrushes; k++) {
+ brushnum = map_leafbrushes[leaf.firstleafbrush + k];
+ b = map_brushes[brushnum];
+ if (b.checkcount == checkcount)
+ continue; // already checked this brush in another leaf
+ b.checkcount = checkcount;
+
+ if (0 == (b.contents & trace_contents))
+ continue;
+ CM_TestBoxInBrush(trace_mins, trace_maxs, trace_start, trace_trace,
+ b);
+ if (0 == trace_trace.fraction)
+ return;
+ }
+
+ }
+
+ /*
+ * ================== CM_RecursiveHullCheck ==================
+ */
+ public static void CM_RecursiveHullCheck(int num, float p1f, float p2f,
+ float[] p1, float[] p2) {
+ cnode_t node;
+ cplane_t plane;
+ float t1, t2, offset;
+ float frac, frac2;
+ float idist;
+ int i;
+ int side;
+ float midf;
+
+ if (trace_trace.fraction <= p1f)
+ return; // already hit something nearer
+
+ // if < 0, we are in a leaf node
+ if (num < 0) {
+ CM_TraceToLeaf(-1 - num);
+ return;
+ }
+
+ //
+ // find the point distances to the seperating plane
+ // and the offset for the size of the box
+ //
+ node = map_nodes[num];
+ plane = node.plane;
+
+ if (plane.type < 3) {
+ t1 = p1[plane.type] - plane.dist;
+ t2 = p2[plane.type] - plane.dist;
+ offset = trace_extents[plane.type];
+ } else {
+ t1 = Math3D.dotProduct(plane.normal, p1) - plane.dist;
+ t2 = Math3D.dotProduct(plane.normal, p2) - plane.dist;
+ if (trace_ispoint)
+ offset = 0;
+ else
+ offset = Math.abs(trace_extents[0] * plane.normal[0])
+ + Math.abs(trace_extents[1] * plane.normal[1])
+ + Math.abs(trace_extents[2] * plane.normal[2]);
+ }
+
+ // see which sides we need to consider
+ if (t1 >= offset && t2 >= offset) {
+ CM_RecursiveHullCheck(node.children[0], p1f, p2f, p1, p2);
+ return;
+ }
+ if (t1 < -offset && t2 < -offset) {
+ CM_RecursiveHullCheck(node.children[1], p1f, p2f, p1, p2);
+ return;
+ }
+
+ // put the crosspoint DIST_EPSILON pixels on the near side
+ if (t1 < t2) {
+ idist = 1.0f / (t1 - t2);
+ side = 1;
+ frac2 = (t1 + offset + DIST_EPSILON) * idist;
+ frac = (t1 - offset + DIST_EPSILON) * idist;
+ } else if (t1 > t2) {
+ idist = 1.0f / (t1 - t2);
+ side = 0;
+ frac2 = (t1 - offset - DIST_EPSILON) * idist;
+ frac = (t1 + offset + DIST_EPSILON) * idist;
+ } else {
+ side = 0;
+ frac = 1;
+ frac2 = 0;
+ }
+
+ // move up to the node
+ if (frac < 0)
+ frac = 0;
+ if (frac > 1)
+ frac = 1;
+
+ midf = p1f + (p2f - p1f) * frac;
+ float[] mid = Vec3Cache.get();
+
+ for (i = 0; i < 3; i++)
+ mid[i] = p1[i] + frac * (p2[i] - p1[i]);
+
+ CM_RecursiveHullCheck(node.children[side], p1f, midf, p1, mid);
+
+ // go past the node
+ if (frac2 < 0)
+ frac2 = 0;
+ if (frac2 > 1)
+ frac2 = 1;
+
+ midf = p1f + (p2f - p1f) * frac2;
+ for (i = 0; i < 3; i++)
+ mid[i] = p1[i] + frac2 * (p2[i] - p1[i]);
+
+ CM_RecursiveHullCheck(node.children[side ^ 1], midf, p2f, mid, p2);
+ Vec3Cache.release();
+ }
+
+ /*
+ * ================== CM_BoxTrace ==================
+ */
+ public static trace_t boxTrace(float[] start, float[] end, float[] mins,
+ float[] maxs, int headnode, int brushmask) {
+
+ // for multi-check avoidance
+ checkcount++;
+
+ // for statistics, may be zeroed
+ Globals.c_traces++;
+
+ // fill in a default trace
+ //was: memset(& trace_trace, 0, sizeof(trace_trace));
+ trace_trace = new trace_t();
+
+ trace_trace.fraction = 1;
+ trace_trace.surface = nullsurface.c;
+
+ if (numnodes == 0) {
+ // map not loaded
+ return trace_trace;
+ }
+
+ trace_contents = brushmask;
+ Math3D.vectorCopy(start, trace_start);
+ Math3D.vectorCopy(end, trace_end);
+ Math3D.vectorCopy(mins, trace_mins);
+ Math3D.vectorCopy(maxs, trace_maxs);
+
+ //
+ // check for position test special case
+ //
+ if (start[0] == end[0] && start[1] == end[1] && start[2] == end[2]) {
+
+ int leafs[] = new int[1024];
+ int i, numleafs;
+ float[] c1 = {0, 0, 0}, c2 = {0, 0, 0};
+ int topnode = 0;
+
+ Math3D.vectorAdd(start, mins, c1);
+ Math3D.vectorAdd(start, maxs, c2);
+
+ for (i = 0; i < 3; i++) {
+ c1[i] -= 1;
+ c2[i] += 1;
+ }
+
+ int tn[] = {topnode};
+
+ numleafs = CM_BoxLeafnums_headnode(c1, c2, leafs, 1024, headnode,
+ tn);
+ topnode = tn[0];
+ for (i = 0; i < numleafs; i++) {
+ CM_TestInLeaf(leafs[i]);
+ if (trace_trace.allsolid)
+ break;
+ }
+ Math3D.vectorCopy(start, trace_trace.endpos);
+ return trace_trace;
+ }
+
+ //
+ // check for point special case
+ //
+ if (mins[0] == 0 && mins[1] == 0 && mins[2] == 0 && maxs[0] == 0
+ && maxs[1] == 0 && maxs[2] == 0) {
+ trace_ispoint = true;
+ Math3D.vectorClear(trace_extents);
+ } else {
+ trace_ispoint = false;
+ trace_extents[0] = -mins[0] > maxs[0] ? -mins[0] : maxs[0];
+ trace_extents[1] = -mins[1] > maxs[1] ? -mins[1] : maxs[1];
+ trace_extents[2] = -mins[2] > maxs[2] ? -mins[2] : maxs[2];
+ }
+
+ //
+ // general sweeping through world
+ //
+ CM_RecursiveHullCheck(headnode, 0, 1, start, end);
+
+ if (trace_trace.fraction == 1) {
+ Math3D.vectorCopy(end, trace_trace.endpos);
+ } else {
+ for (int i = 0; i < 3; i++)
+ trace_trace.endpos[i] = start[i] + trace_trace.fraction
+ * (end[i] - start[i]);
+ }
+ return trace_trace;
+ }
+
+ /*
+ * ================== CM_TransformedBoxTrace
+ *
+ * Handles offseting and rotation of the end points for moving and rotating
+ * entities ==================
+ */
+ public static trace_t TransformedBoxTrace(float[] start, float[] end,
+ float[] mins, float[] maxs, int headnode, int brushmask,
+ float[] origin, float[] angles) {
+ trace_t trace;
+ float[] start_l = {0, 0, 0}, end_l = {0, 0, 0};
+ float[] a = {0, 0, 0};
+ float[] forward = {0, 0, 0}, right = {0, 0, 0}, up = {0, 0, 0};
+ float[] temp = {0, 0, 0};
+ boolean rotated;
+
+ // subtract origin offset
+ Math3D.vectorSubtract(start, origin, start_l);
+ Math3D.vectorSubtract(end, origin, end_l);
+
+ // rotate start and end into the models frame of reference
+ rotated = headnode != box_headnode
+ && (angles[0] != 0 || angles[1] != 0 || angles[2] != 0);
+
+ if (rotated) {
+ Math3D.angleVectors(angles, forward, right, up);
+
+ Math3D.vectorCopy(start_l, temp);
+ start_l[0] = Math3D.dotProduct(temp, forward);
+ start_l[1] = -Math3D.dotProduct(temp, right);
+ start_l[2] = Math3D.dotProduct(temp, up);
+
+ Math3D.vectorCopy(end_l, temp);
+ end_l[0] = Math3D.dotProduct(temp, forward);
+ end_l[1] = -Math3D.dotProduct(temp, right);
+ end_l[2] = Math3D.dotProduct(temp, up);
+ }
+
+ // sweep the box through the model
+ trace = boxTrace(start_l, end_l, mins, maxs, headnode, brushmask);
+
+ if (rotated && trace.fraction != 1.0) {
+ // FIXME: figure out how to do this with existing angles
+ Math3D.vectorNegate(angles, a);
+ Math3D.angleVectors(a, forward, right, up);
+
+ Math3D.vectorCopy(trace.plane.normal, temp);
+ trace.plane.normal[0] = Math3D.dotProduct(temp, forward);
+ trace.plane.normal[1] = -Math3D.dotProduct(temp, right);
+ trace.plane.normal[2] = Math3D.dotProduct(temp, up);
+ }
+
+ trace.endpos[0] = start[0] + trace.fraction * (end[0] - start[0]);
+ trace.endpos[1] = start[1] + trace.fraction * (end[1] - start[1]);
+ trace.endpos[2] = start[2] + trace.fraction * (end[2] - start[2]);
+
+ return trace;
+ }
+
+ /*
+ * =================== CM_DecompressVis ===================
+ */
+ public static void CM_DecompressVis(byte in[], int offset, byte out[]) {
+ int c;
+
+ int row;
+
+ row = (numclusters + 7) >> 3;
+ int outp = 0;
+ int inp = offset;
+
+ if (in == null || numvisibility == 0) { // no vis info, so make all
+ // visible
+ while (row != 0) {
+ out[outp++] = (byte) 0xFF;
+ row--;
+ }
+ return;
+ }
+
+ do {
+ if (in[inp] != 0) {
+ out[outp++] = in[inp++];
+ continue;
+ }
+
+ c = in[inp + 1] & 0xFF;
+ inp += 2;
+ if (outp + c > row) {
+ c = row - (outp);
+ Com.DPrintf("warning: Vis decompression overrun\n");
+ }
+ while (c != 0) {
+ out[outp++] = 0;
+ c--;
+ }
+ } while (outp < row);
+ }
+
+ //======================================================================
+
+ public static byte[] CM_ClusterPVS(int cluster) {
+ if (cluster == -1)
+ Arrays.fill(pvsrow, 0, (numclusters + 7) >> 3, (byte) 0);
+ else
+ CM_DecompressVis(map_visibility,
+ map_vis.bitofs[cluster][Defines.DVIS_PVS], pvsrow);
+ return pvsrow;
+ }
+
+ public static byte[] CM_ClusterPHS(int cluster) {
+ if (cluster == -1)
+ Arrays.fill(phsrow, 0, (numclusters + 7) >> 3, (byte) 0);
+ else
+ CM_DecompressVis(map_visibility,
+ map_vis.bitofs[cluster][Defines.DVIS_PHS], phsrow);
+ return phsrow;
+ }
+
+ /*
+ * ===============================================================================
+ * PVS / PHS
+ * ===============================================================================
+ */
+
+ public static void FloodArea_r(carea_t area, int floodnum) {
+ //Com.Printf("FloodArea_r(" + floodnum + ")...\n");
+ int i;
+ qfiles.dareaportal_t p;
+
+ if (area.floodvalid == floodvalid) {
+ if (area.floodnum == floodnum)
+ return;
+ Com.Error(Defines.ERR_DROP, "FloodArea_r: reflooded");
+ }
+
+ area.floodnum = floodnum;
+ area.floodvalid = floodvalid;
+
+ for (i = 0; i < area.numareaportals; i++) {
+ p = map_areaportals[area.firstareaportal + i];
+ if (portalopen[p.portalnum])
+ FloodArea_r(map_areas[p.otherarea], floodnum);
+ }
+ }
+
+ /*
+ * ==================== FloodAreaConnections ====================
+ */
+ public static void FloodAreaConnections() {
+ Com.DPrintf("FloodAreaConnections...\n");
+
+ int i;
+ carea_t area;
+ int floodnum;
+
+ // all current floods are now invalid
+ floodvalid++;
+ floodnum = 0;
+
+ // area 0 is not used
+ for (i = 1; i < numareas; i++) {
+
+ area = map_areas[i];
+
+ if (area.floodvalid == floodvalid)
+ continue; // already flooded into
+ floodnum++;
+ FloodArea_r(area, floodnum);
+ }
+ }
+
+ /*
+ * ================= CM_SetAreaPortalState =================
+ */
+ public static void CM_SetAreaPortalState(int portalnum, boolean open) {
+ if (portalnum > numareaportals)
+ Com.Error(Defines.ERR_DROP, "areaportal > numareaportals");
+
+ portalopen[portalnum] = open;
+ FloodAreaConnections();
+ }
+
+ public static boolean CM_AreasConnected(int area1, int area2) {
+ if (map_noareas.value != 0)
+ return true;
+
+ if (area1 > numareas || area2 > numareas)
+ Com.Error(Defines.ERR_DROP, "area > numareas");
+
+ return map_areas[area1].floodnum == map_areas[area2].floodnum;
+
+ }
+
+ /*
+ * ================= CM_WriteAreaBits
+ *
+ * Writes a length byte followed by a bit vector of all the areas that area
+ * in the same flood as the area parameter
+ *
+ * This is used by the client refreshes to cull visibility =================
+ */
+ public static int CM_WriteAreaBits(byte buffer[], int area) {
+ int i;
+ int floodnum;
+ int bytes;
+
+ bytes = (numareas + 7) >> 3;
+
+ if (map_noareas.value != 0) { // for debugging, send everything
+ Arrays.fill(buffer, 0, bytes, (byte) 255);
+ } else {
+ Arrays.fill(buffer, 0, bytes, (byte) 0);
+ floodnum = map_areas[area].floodnum;
+ for (i = 0; i < numareas; i++) {
+ if (map_areas[i].floodnum == floodnum || area == 0)
+ buffer[i >> 3] |= 1 << (i & 7);
+ }
+ }
+
+ return bytes;
+ }
+
+ /*
+ * ===============================================================================
+ * AREAPORTALS
+ * ===============================================================================
+ */
+
+ public static void CM_WritePortalState(RandomAccessFile os) {
+
+ //was: fwrite(portalopen, sizeof(portalopen), 1, f);
+ try {
+
+ for (boolean aPortalopen : portalopen)
+ if (aPortalopen)
+ os.writeInt(1);
+ else
+ os.writeInt(0);
+ } catch (Exception e) {
+ Com.Printf("ERROR:" + e);
+ e.printStackTrace();
+ }
+ }
+
+ /*
+ * =================== CM_ReadPortalState
+ *
+ * Reads the portal state from a savegame file and recalculates the area
+ * connections ===================
+ */
+ public static void CM_ReadPortalState(RandomAccessFile f) {
+
+ //was: FS_Read(portalopen, sizeof(portalopen), f);
+ int len = portalopen.length * 4;
+
+ byte buf[] = new byte[len];
+
+ FS.Read(buf, len, f);
+
+ ByteBuffer bb = ByteBuffer.wrap(buf);
+ IntBuffer ib = bb.asIntBuffer();
+
+ for (int n = 0; n < portalopen.length; n++)
+ portalopen[n] = ib.get() != 0;
+
+ FloodAreaConnections();
+ }
+
+ /*
+ * ================= CM_AreasConnected =================
+ */
+
+ public static class cnode_t {
+ final int[] children = {0, 0}; // negative numbers are leafs
+ cplane_t plane; // ptr
+ }
+
+ public static class cbrushside_t {
+ cplane_t plane; // ptr
+
+ mapsurface_t surface; // ptr
+ }
+
+ /*
+ * =================== CM_WritePortalState
+ *
+ * Writes the portal state to a savegame file ===================
+ */
+
+ public static class cleaf_t {
+ int contents;
+
+ int cluster;
+
+ int area;
+
+ // was unsigned short, but is ok (rst)
+ short firstleafbrush;
+
+ // was unsigned short, but is ok (rst)
+ short numleafbrushes;
+ }
+
+ public static class cbrush_t {
+ int contents;
+
+ int numsides;
+
+ int firstbrushside;
+
+ int checkcount; // to avoid repeated testings
+ }
+
+ public static class carea_t {
+ int numareaportals;
+
+ int firstareaportal;
+
+ int floodnum; // if two areas have equal floodnums, they are connected
+
+ int floodvalid;
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.qcommon;
+
+public class CRC {
+
+ public final static short CRC_INIT_VALUE = (short) 0xffff;
+ public final static short CRC_XOR_VALUE = (short) 0x0000;
+
+ private static final int[] crctable = {0x0000, 0x1021, 0x2042, 0x3063, 0x4084,
+ 0x50a5, 0x60c6, 0x70e7, 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c,
+ 0xd1ad, 0xe1ce, 0xf1ef, 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5,
+ 0x4294, 0x72f7, 0x62d6, 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd,
+ 0xc39c, 0xf3ff, 0xe3de, 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6,
+ 0x74c7, 0x44a4, 0x5485, 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee,
+ 0xf5cf, 0xc5ac, 0xd58d, 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7,
+ 0x66f6, 0x5695, 0x46b4, 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df,
+ 0xe7fe, 0xd79d, 0xc7bc, 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840,
+ 0x1861, 0x2802, 0x3823, 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948,
+ 0x9969, 0xa90a, 0xb92b, 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71,
+ 0x0a50, 0x3a33, 0x2a12, 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79,
+ 0x8b58, 0xbb3b, 0xab1a, 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22,
+ 0x3c03, 0x0c60, 0x1c41, 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a,
+ 0xbd0b, 0x8d68, 0x9d49, 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13,
+ 0x2e32, 0x1e51, 0x0e70, 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b,
+ 0xaf3a, 0x9f59, 0x8f78, 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c,
+ 0xc12d, 0xf14e, 0xe16f, 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004,
+ 0x4025, 0x7046, 0x6067, 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d,
+ 0xd31c, 0xe37f, 0xf35e, 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235,
+ 0x5214, 0x6277, 0x7256, 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e,
+ 0xe54f, 0xd52c, 0xc50d, 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466,
+ 0x6447, 0x5424, 0x4405, 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f,
+ 0xf77e, 0xc71d, 0xd73c, 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657,
+ 0x7676, 0x4615, 0x5634, 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8,
+ 0x89e9, 0xb98a, 0xa9ab, 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0,
+ 0x08e1, 0x3882, 0x28a3, 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9,
+ 0x9bd8, 0xabbb, 0xbb9a, 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1,
+ 0x1ad0, 0x2ab3, 0x3a92, 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa,
+ 0xad8b, 0x9de8, 0x8dc9, 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2,
+ 0x2c83, 0x1ce0, 0x0cc1, 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b,
+ 0xbfba, 0x8fd9, 0x9ff8, 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93,
+ 0x3eb2, 0x0ed1, 0x1ef0};
+
+ static int CRC_Block(byte start[], int count) {
+ short crc = CRC_INIT_VALUE;
+
+ int ndx = 0;
+
+ while (count-- > 0)
+ crc = (short) ((crc << 8) ^ crctable[0xff & ((crc >> 8) ^ start[ndx++])]);
+
+ // unsigned short
+ return crc & 0xFFFF;
+ }
+
+ public static void main(String[] args) {
+ byte data[] =
+ {
+ (byte) 0x71,
+ (byte) 0xa9,
+ (byte) 0x05,
+ (byte) 0xce,
+ (byte) 0x8d,
+ (byte) 0x75,
+ (byte) 0x28,
+ (byte) 0xc8,
+ (byte) 0xba,
+ (byte) 0x97,
+
+ (byte) 0x45,
+ (byte) 0xe9,
+ (byte) 0x8a,
+ (byte) 0xe0,
+ (byte) 0x37,
+ (byte) 0xbd,
+ (byte) 0x6c,
+ (byte) 0x6d,
+ (byte) 0x67,
+ (byte) 0x4a,
+ (byte) 0x21};
+ System.out.println("crc:" + (CRC_Block(data, 21) & 0xffff));
+ System.out.println("----");
+ for (int n = 0; n < 5; n++)
+ System.out.println("seq:" + (Com.BlockSequenceCRCByte(data, 0, 21, n * 10) & 0xff));
+
+ }
+
+/* c test:
+ *
+ * D:\Rene\gamesrc\quake2-3.21\qcommon>crc
+ * crc=-12353
+ * ----
+ * seq:215
+ * seq:252
+ * seq:164
+ * seq:202
+ * seq:201
+ *
+int main()
+{
+ byte data[21] =
+ {
+ 0x71,
+ 0xa9,
+ 0x05,
+ 0xce,
+ 0x8d,
+ 0x75,
+ 0x28,
+ 0xc8,
+ 0xba,
+ 0x97,
+
+ 0x45,
+ 0xe9,
+ 0x8a,
+ 0xe0,
+ 0x37,
+ 0xbd,
+ 0x6c,
+ 0x6d,
+ 0x67,
+ 0x4a, 0x21 };
+ int n=0;
+
+ printf("crc=%d\n", (short) CRC_Block(&data, 21));
+
+ printf("----\n");
+ for (n=0; n < 5; n++)
+ printf("seq:%d\n", COM_BlockSequenceCRCByte( &data,21, n*10) );
+}
+ */
+
+}
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.qcommon;
+
+import lwjake2.Defines;
+import lwjake2.Globals;
+import lwjake2.client.Client;
+import lwjake2.client.Console;
+import lwjake2.game.Cmd;
+import lwjake2.server.Server;
+import lwjake2.sys.Sys;
+import lwjake2.util.PrintfFormat;
+import lwjake2.util.Vargs;
+
+import java.io.IOException;
+
+/**
+ * Com
+ */
+public final class Com {
+
+ public static final char[] com_token = new char[Defines.MAX_TOKEN_CHARS];
+ static final String[] com_argv = new String[Defines.MAX_NUM_ARGVS];
+ /**
+ * CRC table.
+ */
+ static final byte[] chktbl = {(byte) 0x84, (byte) 0x47, (byte) 0x51, (byte) 0xc1,
+ (byte) 0x93, (byte) 0x22, (byte) 0x21, (byte) 0x24, (byte) 0x2f,
+ (byte) 0x66, (byte) 0x60, (byte) 0x4d, (byte) 0xb0, (byte) 0x7c,
+ (byte) 0xda, (byte) 0x88, (byte) 0x54, (byte) 0x15, (byte) 0x2b,
+ (byte) 0xc6, (byte) 0x6c, (byte) 0x89, (byte) 0xc5, (byte) 0x9d,
+ (byte) 0x48, (byte) 0xee, (byte) 0xe6, (byte) 0x8a, (byte) 0xb5,
+ (byte) 0xf4, (byte) 0xcb, (byte) 0xfb, (byte) 0xf1, (byte) 0x0c,
+ (byte) 0x2e, (byte) 0xa0, (byte) 0xd7, (byte) 0xc9, (byte) 0x1f,
+ (byte) 0xd6, (byte) 0x06, (byte) 0x9a, (byte) 0x09, (byte) 0x41,
+ (byte) 0x54, (byte) 0x67, (byte) 0x46, (byte) 0xc7, (byte) 0x74,
+ (byte) 0xe3, (byte) 0xc8, (byte) 0xb6, (byte) 0x5d, (byte) 0xa6,
+ (byte) 0x36, (byte) 0xc4, (byte) 0xab, (byte) 0x2c, (byte) 0x7e,
+ (byte) 0x85, (byte) 0xa8, (byte) 0xa4, (byte) 0xa6, (byte) 0x4d,
+ (byte) 0x96, (byte) 0x19, (byte) 0x19, (byte) 0x9a, (byte) 0xcc,
+ (byte) 0xd8, (byte) 0xac, (byte) 0x39, (byte) 0x5e, (byte) 0x3c,
+ (byte) 0xf2, (byte) 0xf5, (byte) 0x5a, (byte) 0x72, (byte) 0xe5,
+ (byte) 0xa9, (byte) 0xd1, (byte) 0xb3, (byte) 0x23, (byte) 0x82,
+ (byte) 0x6f, (byte) 0x29, (byte) 0xcb, (byte) 0xd1, (byte) 0xcc,
+ (byte) 0x71, (byte) 0xfb, (byte) 0xea, (byte) 0x92, (byte) 0xeb,
+ (byte) 0x1c, (byte) 0xca, (byte) 0x4c, (byte) 0x70, (byte) 0xfe,
+ (byte) 0x4d, (byte) 0xc9, (byte) 0x67, (byte) 0x43, (byte) 0x47,
+ (byte) 0x94, (byte) 0xb9, (byte) 0x47, (byte) 0xbc, (byte) 0x3f,
+ (byte) 0x01, (byte) 0xab, (byte) 0x7b, (byte) 0xa6, (byte) 0xe2,
+ (byte) 0x76, (byte) 0xef, (byte) 0x5a, (byte) 0x7a, (byte) 0x29,
+ (byte) 0x0b, (byte) 0x51, (byte) 0x54, (byte) 0x67, (byte) 0xd8,
+ (byte) 0x1c, (byte) 0x14, (byte) 0x3e, (byte) 0x29, (byte) 0xec,
+ (byte) 0xe9, (byte) 0x2d, (byte) 0x48, (byte) 0x67, (byte) 0xff,
+ (byte) 0xed, (byte) 0x54, (byte) 0x4f, (byte) 0x48, (byte) 0xc0,
+ (byte) 0xaa, (byte) 0x61, (byte) 0xf7, (byte) 0x78, (byte) 0x12,
+ (byte) 0x03, (byte) 0x7a, (byte) 0x9e, (byte) 0x8b, (byte) 0xcf,
+ (byte) 0x83, (byte) 0x7b, (byte) 0xae, (byte) 0xca, (byte) 0x7b,
+ (byte) 0xd9, (byte) 0xe9, (byte) 0x53, (byte) 0x2a, (byte) 0xeb,
+ (byte) 0xd2, (byte) 0xd8, (byte) 0xcd, (byte) 0xa3, (byte) 0x10,
+ (byte) 0x25, (byte) 0x78, (byte) 0x5a, (byte) 0xb5, (byte) 0x23,
+ (byte) 0x06, (byte) 0x93, (byte) 0xb7, (byte) 0x84, (byte) 0xd2,
+ (byte) 0xbd, (byte) 0x96, (byte) 0x75, (byte) 0xa5, (byte) 0x5e,
+ (byte) 0xcf, (byte) 0x4e, (byte) 0xe9, (byte) 0x50, (byte) 0xa1,
+ (byte) 0xe6, (byte) 0x9d, (byte) 0xb1, (byte) 0xe3, (byte) 0x85,
+ (byte) 0x66, (byte) 0x28, (byte) 0x4e, (byte) 0x43, (byte) 0xdc,
+ (byte) 0x6e, (byte) 0xbb, (byte) 0x33, (byte) 0x9e, (byte) 0xf3,
+ (byte) 0x0d, (byte) 0x00, (byte) 0xc1, (byte) 0xcf, (byte) 0x67,
+ (byte) 0x34, (byte) 0x06, (byte) 0x7c, (byte) 0x71, (byte) 0xe3,
+ (byte) 0x63, (byte) 0xb7, (byte) 0xb7, (byte) 0xdf, (byte) 0x92,
+ (byte) 0xc4, (byte) 0xc2, (byte) 0x25, (byte) 0x5c, (byte) 0xff,
+ (byte) 0xc3, (byte) 0x6e, (byte) 0xfc, (byte) 0xaa, (byte) 0x1e,
+ (byte) 0x2a, (byte) 0x48, (byte) 0x11, (byte) 0x1c, (byte) 0x36,
+ (byte) 0x68, (byte) 0x78, (byte) 0x86, (byte) 0x79, (byte) 0x30,
+ (byte) 0xc3, (byte) 0xd6, (byte) 0xde, (byte) 0xbc, (byte) 0x3a,
+ (byte) 0x2a, (byte) 0x6d, (byte) 0x1e, (byte) 0x46, (byte) 0xdd,
+ (byte) 0xe0, (byte) 0x80, (byte) 0x1e, (byte) 0x44, (byte) 0x3b,
+ (byte) 0x6f, (byte) 0xaf, (byte) 0x31, (byte) 0xda, (byte) 0xa2,
+ (byte) 0xbd, (byte) 0x77, (byte) 0x06, (byte) 0x56, (byte) 0xc0,
+ (byte) 0xb7, (byte) 0x92, (byte) 0x4b, (byte) 0x37, (byte) 0xc0,
+ (byte) 0xfc, (byte) 0xc2, (byte) 0xd5, (byte) 0xfb, (byte) 0xa8,
+ (byte) 0xda, (byte) 0xf5, (byte) 0x57, (byte) 0xa8, (byte) 0x18,
+ (byte) 0xc0, (byte) 0xdf, (byte) 0xe7, (byte) 0xaa, (byte) 0x2a,
+ (byte) 0xe0, (byte) 0x7c, (byte) 0x6f, (byte) 0x77, (byte) 0xb1,
+ (byte) 0x26, (byte) 0xba, (byte) 0xf9, (byte) 0x2e, (byte) 0x1d,
+ (byte) 0x16, (byte) 0xcb, (byte) 0xb8, (byte) 0xa2, (byte) 0x44,
+ (byte) 0xd5, (byte) 0x2f, (byte) 0x1a, (byte) 0x79, (byte) 0x74,
+ (byte) 0x87, (byte) 0x4b, (byte) 0x00, (byte) 0xc9, (byte) 0x4a,
+ (byte) 0x3a, (byte) 0x65, (byte) 0x8f, (byte) 0xe6, (byte) 0x5d,
+ (byte) 0xe5, (byte) 0x0a, (byte) 0x77, (byte) 0xd8, (byte) 0x1a,
+ (byte) 0x14, (byte) 0x41, (byte) 0x75, (byte) 0xb1, (byte) 0xe2,
+ (byte) 0x50, (byte) 0x2c, (byte) 0x93, (byte) 0x38, (byte) 0x2b,
+ (byte) 0x6d, (byte) 0xf3, (byte) 0xf6, (byte) 0xdb, (byte) 0x1f,
+ (byte) 0xcd, (byte) 0xff, (byte) 0x14, (byte) 0x70, (byte) 0xe7,
+ (byte) 0x16, (byte) 0xe8, (byte) 0x3d, (byte) 0xf0, (byte) 0xe3,
+ (byte) 0xbc, (byte) 0x5e, (byte) 0xb6, (byte) 0x3f, (byte) 0xcc,
+ (byte) 0x81, (byte) 0x24, (byte) 0x67, (byte) 0xf3, (byte) 0x97,
+ (byte) 0x3b, (byte) 0xfe, (byte) 0x3a, (byte) 0x96, (byte) 0x85,
+ (byte) 0xdf, (byte) 0xe4, (byte) 0x6e, (byte) 0x3c, (byte) 0x85,
+ (byte) 0x05, (byte) 0x0e, (byte) 0xa3, (byte) 0x2b, (byte) 0x07,
+ (byte) 0xc8, (byte) 0xbf, (byte) 0xe5, (byte) 0x13, (byte) 0x82,
+ (byte) 0x62, (byte) 0x08, (byte) 0x61, (byte) 0x69, (byte) 0x4b,
+ (byte) 0x47, (byte) 0x62, (byte) 0x73, (byte) 0x44, (byte) 0x64,
+ (byte) 0x8e, (byte) 0xe2, (byte) 0x91, (byte) 0xa6, (byte) 0x9a,
+ (byte) 0xb7, (byte) 0xe9, (byte) 0x04, (byte) 0xb6, (byte) 0x54,
+ (byte) 0x0c, (byte) 0xc5, (byte) 0xa9, (byte) 0x47, (byte) 0xa6,
+ (byte) 0xc9, (byte) 0x08, (byte) 0xfe, (byte) 0x4e, (byte) 0xa6,
+ (byte) 0xcc, (byte) 0x8a, (byte) 0x5b, (byte) 0x90, (byte) 0x6f,
+ (byte) 0x2b, (byte) 0x3f, (byte) 0xb6, (byte) 0x0a, (byte) 0x96,
+ (byte) 0xc0, (byte) 0x78, (byte) 0x58, (byte) 0x3c, (byte) 0x76,
+ (byte) 0x6d, (byte) 0x94, (byte) 0x1a, (byte) 0xe4, (byte) 0x4e,
+ (byte) 0xb8, (byte) 0x38, (byte) 0xbb, (byte) 0xf5, (byte) 0xeb,
+ (byte) 0x29, (byte) 0xd8, (byte) 0xb0, (byte) 0xf3, (byte) 0x15,
+ (byte) 0x1e, (byte) 0x99, (byte) 0x96, (byte) 0x3c, (byte) 0x5d,
+ (byte) 0x63, (byte) 0xd5, (byte) 0xb1, (byte) 0xad, (byte) 0x52,
+ (byte) 0xb8, (byte) 0x55, (byte) 0x70, (byte) 0x75, (byte) 0x3e,
+ (byte) 0x1a, (byte) 0xd5, (byte) 0xda, (byte) 0xf6, (byte) 0x7a,
+ (byte) 0x48, (byte) 0x7d, (byte) 0x44, (byte) 0x41, (byte) 0xf9,
+ (byte) 0x11, (byte) 0xce, (byte) 0xd7, (byte) 0xca, (byte) 0xa5,
+ (byte) 0x3d, (byte) 0x7a, (byte) 0x79, (byte) 0x7e, (byte) 0x7d,
+ (byte) 0x25, (byte) 0x1b, (byte) 0x77, (byte) 0xbc, (byte) 0xf7,
+ (byte) 0xc7, (byte) 0x0f, (byte) 0x84, (byte) 0x95, (byte) 0x10,
+ (byte) 0x92, (byte) 0x67, (byte) 0x15, (byte) 0x11, (byte) 0x5a,
+ (byte) 0x5e, (byte) 0x41, (byte) 0x66, (byte) 0x0f, (byte) 0x38,
+ (byte) 0x03, (byte) 0xb2, (byte) 0xf1, (byte) 0x5d, (byte) 0xf8,
+ (byte) 0xab, (byte) 0xc0, (byte) 0x02, (byte) 0x76, (byte) 0x84,
+ (byte) 0x28, (byte) 0xf4, (byte) 0x9d, (byte) 0x56, (byte) 0x46,
+ (byte) 0x60, (byte) 0x20, (byte) 0xdb, (byte) 0x68, (byte) 0xa7,
+ (byte) 0xbb, (byte) 0xee, (byte) 0xac, (byte) 0x15, (byte) 0x01,
+ (byte) 0x2f, (byte) 0x20, (byte) 0x09, (byte) 0xdb, (byte) 0xc0,
+ (byte) 0x16, (byte) 0xa1, (byte) 0x89, (byte) 0xf9, (byte) 0x94,
+ (byte) 0x59, (byte) 0x00, (byte) 0xc1, (byte) 0x76, (byte) 0xbf,
+ (byte) 0xc1, (byte) 0x4d, (byte) 0x5d, (byte) 0x2d, (byte) 0xa9,
+ (byte) 0x85, (byte) 0x2c, (byte) 0xd6, (byte) 0xd3, (byte) 0x14,
+ (byte) 0xcc, (byte) 0x02, (byte) 0xc3, (byte) 0xc2, (byte) 0xfa,
+ (byte) 0x6b, (byte) 0xb7, (byte) 0xa6, (byte) 0xef, (byte) 0xdd,
+ (byte) 0x12, (byte) 0x26, (byte) 0xa4, (byte) 0x63, (byte) 0xe3,
+ (byte) 0x62, (byte) 0xbd, (byte) 0x56, (byte) 0x8a, (byte) 0x52,
+ (byte) 0x2b, (byte) 0xb9, (byte) 0xdf, (byte) 0x09, (byte) 0xbc,
+ (byte) 0x0e, (byte) 0x97, (byte) 0xa9, (byte) 0xb0, (byte) 0x82,
+ (byte) 0x46, (byte) 0x08, (byte) 0xd5, (byte) 0x1a, (byte) 0x8e,
+ (byte) 0x1b, (byte) 0xa7, (byte) 0x90, (byte) 0x98, (byte) 0xb9,
+ (byte) 0xbb, (byte) 0x3c, (byte) 0x17, (byte) 0x9a, (byte) 0xf2,
+ (byte) 0x82, (byte) 0xba, (byte) 0x64, (byte) 0x0a, (byte) 0x7f,
+ (byte) 0xca, (byte) 0x5a, (byte) 0x8c, (byte) 0x7c, (byte) 0xd3,
+ (byte) 0x79, (byte) 0x09, (byte) 0x5b, (byte) 0x26, (byte) 0xbb,
+ (byte) 0xbd, (byte) 0x25, (byte) 0xdf, (byte) 0x3d, (byte) 0x6f,
+ (byte) 0x9a, (byte) 0x8f, (byte) 0xee, (byte) 0x21, (byte) 0x66,
+ (byte) 0xb0, (byte) 0x8d, (byte) 0x84, (byte) 0x4c, (byte) 0x91,
+ (byte) 0x45, (byte) 0xd4, (byte) 0x77, (byte) 0x4f, (byte) 0xb3,
+ (byte) 0x8c, (byte) 0xbc, (byte) 0xa8, (byte) 0x99, (byte) 0xaa,
+ (byte) 0x19, (byte) 0x53, (byte) 0x7c, (byte) 0x02, (byte) 0x87,
+ (byte) 0xbb, (byte) 0x0b, (byte) 0x7c, (byte) 0x1a, (byte) 0x2d,
+ (byte) 0xdf, (byte) 0x48, (byte) 0x44, (byte) 0x06, (byte) 0xd6,
+ (byte) 0x7d, (byte) 0x0c, (byte) 0x2d, (byte) 0x35, (byte) 0x76,
+ (byte) 0xae, (byte) 0xc4, (byte) 0x5f, (byte) 0x71, (byte) 0x85,
+ (byte) 0x97, (byte) 0xc4, (byte) 0x3d, (byte) 0xef, (byte) 0x52,
+ (byte) 0xbe, (byte) 0x00, (byte) 0xe4, (byte) 0xcd, (byte) 0x49,
+ (byte) 0xd1, (byte) 0xd1, (byte) 0x1c, (byte) 0x3c, (byte) 0xd0,
+ (byte) 0x1c, (byte) 0x42, (byte) 0xaf, (byte) 0xd4, (byte) 0xbd,
+ (byte) 0x58, (byte) 0x34, (byte) 0x07, (byte) 0x32, (byte) 0xee,
+ (byte) 0xb9, (byte) 0xb5, (byte) 0xea, (byte) 0xff, (byte) 0xd7,
+ (byte) 0x8c, (byte) 0x0d, (byte) 0x2e, (byte) 0x2f, (byte) 0xaf,
+ (byte) 0x87, (byte) 0xbb, (byte) 0xe6, (byte) 0x52, (byte) 0x71,
+ (byte) 0x22, (byte) 0xf5, (byte) 0x25, (byte) 0x17, (byte) 0xa1,
+ (byte) 0x82, (byte) 0x04, (byte) 0xc2, (byte) 0x4a, (byte) 0xbd,
+ (byte) 0x57, (byte) 0xc6, (byte) 0xab, (byte) 0xc8, (byte) 0x35,
+ (byte) 0x0c, (byte) 0x3c, (byte) 0xd9, (byte) 0xc2, (byte) 0x43,
+ (byte) 0xdb, (byte) 0x27, (byte) 0x92, (byte) 0xcf, (byte) 0xb8,
+ (byte) 0x25, (byte) 0x60, (byte) 0xfa, (byte) 0x21, (byte) 0x3b,
+ (byte) 0x04, (byte) 0x52, (byte) 0xc8, (byte) 0x96, (byte) 0xba,
+ (byte) 0x74, (byte) 0xe3, (byte) 0x67, (byte) 0x3e, (byte) 0x8e,
+ (byte) 0x8d, (byte) 0x61, (byte) 0x90, (byte) 0x92, (byte) 0x59,
+ (byte) 0xb6, (byte) 0x1a, (byte) 0x1c, (byte) 0x5e, (byte) 0x21,
+ (byte) 0xc1, (byte) 0x65, (byte) 0xe5, (byte) 0xa6, (byte) 0x34,
+ (byte) 0x05, (byte) 0x6f, (byte) 0xc5, (byte) 0x60, (byte) 0xb1,
+ (byte) 0x83, (byte) 0xc1, (byte) 0xd5, (byte) 0xd5, (byte) 0xed,
+ (byte) 0xd9, (byte) 0xc7, (byte) 0x11, (byte) 0x7b, (byte) 0x49,
+ (byte) 0x7a, (byte) 0xf9, (byte) 0xf9, (byte) 0x84, (byte) 0x47,
+ (byte) 0x9b, (byte) 0xe2, (byte) 0xa5, (byte) 0x82, (byte) 0xe0,
+ (byte) 0xc2, (byte) 0x88, (byte) 0xd0, (byte) 0xb2, (byte) 0x58,
+ (byte) 0x88, (byte) 0x7f, (byte) 0x45, (byte) 0x09, (byte) 0x67,
+ (byte) 0x74, (byte) 0x61, (byte) 0xbf, (byte) 0xe6, (byte) 0x40,
+ (byte) 0xe2, (byte) 0x9d, (byte) 0xc2, (byte) 0x47, (byte) 0x05,
+ (byte) 0x89, (byte) 0xed, (byte) 0xcb, (byte) 0xbb, (byte) 0xb7,
+ (byte) 0x27, (byte) 0xe7, (byte) 0xdc, (byte) 0x7a, (byte) 0xfd,
+ (byte) 0xbf, (byte) 0xa8, (byte) 0xd0, (byte) 0xaa, (byte) 0x10,
+ (byte) 0x39, (byte) 0x3c, (byte) 0x20, (byte) 0xf0, (byte) 0xd3,
+ (byte) 0x6e, (byte) 0xb1, (byte) 0x72, (byte) 0xf8, (byte) 0xe6,
+ (byte) 0x0f, (byte) 0xef, (byte) 0x37, (byte) 0xe5, (byte) 0x09,
+ (byte) 0x33, (byte) 0x5a, (byte) 0x83, (byte) 0x43, (byte) 0x80,
+ (byte) 0x4f, (byte) 0x65, (byte) 0x2f, (byte) 0x7c, (byte) 0x8c,
+ (byte) 0x6a, (byte) 0xa0, (byte) 0x82, (byte) 0x0c, (byte) 0xd4,
+ (byte) 0xd4, (byte) 0xfa, (byte) 0x81, (byte) 0x60, (byte) 0x3d,
+ (byte) 0xdf, (byte) 0x06, (byte) 0xf1, (byte) 0x5f, (byte) 0x08,
+ (byte) 0x0d, (byte) 0x6d, (byte) 0x43, (byte) 0xf2, (byte) 0xe3,
+ (byte) 0x11, (byte) 0x7d, (byte) 0x80, (byte) 0x32, (byte) 0xc5,
+ (byte) 0xfb, (byte) 0xc5, (byte) 0xd9, (byte) 0x27, (byte) 0xec,
+ (byte) 0xc6, (byte) 0x4e, (byte) 0x65, (byte) 0x27, (byte) 0x76,
+ (byte) 0x87, (byte) 0xa6, (byte) 0xee, (byte) 0xee, (byte) 0xd7,
+ (byte) 0x8b, (byte) 0xd1, (byte) 0xa0, (byte) 0x5c, (byte) 0xb0,
+ (byte) 0x42, (byte) 0x13, (byte) 0x0e, (byte) 0x95, (byte) 0x4a,
+ (byte) 0xf2, (byte) 0x06, (byte) 0xc6, (byte) 0x43, (byte) 0x33,
+ (byte) 0xf4, (byte) 0xc7, (byte) 0xf8, (byte) 0xe7, (byte) 0x1f,
+ (byte) 0xdd, (byte) 0xe4, (byte) 0x46, (byte) 0x4a, (byte) 0x70,
+ (byte) 0x39, (byte) 0x6c, (byte) 0xd0, (byte) 0xed, (byte) 0xca,
+ (byte) 0xbe, (byte) 0x60, (byte) 0x3b, (byte) 0xd1, (byte) 0x7b,
+ (byte) 0x57, (byte) 0x48, (byte) 0xe5, (byte) 0x3a, (byte) 0x79,
+ (byte) 0xc1, (byte) 0x69, (byte) 0x33, (byte) 0x53, (byte) 0x1b,
+ (byte) 0x80, (byte) 0xb8, (byte) 0x91, (byte) 0x7d, (byte) 0xb4,
+ (byte) 0xf6, (byte) 0x17, (byte) 0x1a, (byte) 0x1d, (byte) 0x5a,
+ (byte) 0x32, (byte) 0xd6, (byte) 0xcc, (byte) 0x71, (byte) 0x29,
+ (byte) 0x3f, (byte) 0x28, (byte) 0xbb, (byte) 0xf3, (byte) 0x5e,
+ (byte) 0x71, (byte) 0xb8, (byte) 0x43, (byte) 0xaf, (byte) 0xf8,
+ (byte) 0xb9, (byte) 0x64, (byte) 0xef, (byte) 0xc4, (byte) 0xa5,
+ (byte) 0x6c, (byte) 0x08, (byte) 0x53, (byte) 0xc7, (byte) 0x00,
+ (byte) 0x10, (byte) 0x39, (byte) 0x4f, (byte) 0xdd, (byte) 0xe4,
+ (byte) 0xb6, (byte) 0x19, (byte) 0x27, (byte) 0xfb, (byte) 0xb8,
+ (byte) 0xf5, (byte) 0x32, (byte) 0x73, (byte) 0xe5, (byte) 0xcb,
+ (byte) 0x32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0};
+ static final byte[] chkb = new byte[60 + 4];
+ static String debugContext = "";
+ static String _debugContext = "";
+ static int com_argc;
+ static int rd_target;
+ static StringBuffer rd_buffer;
+ static int rd_buffersize;
+ static RD_Flusher rd_flusher;
+ static boolean recursive = false;
+ static String msg = "";
+ public static final xcommand_t Error_f = new xcommand_t() {
+ public void execute() throws longjmpException {
+ Error(Defines.ERR_FATAL, Cmd.Argv(1));
+ }
+ };
+
+ public static void BeginRedirect(int target, StringBuffer buffer, int buffersize, RD_Flusher flush) {
+ if (0 == target || null == buffer || 0 == buffersize || null == flush)
+ return;
+
+ rd_target = target;
+ rd_buffer = buffer;
+ rd_buffersize = buffersize;
+ rd_flusher = flush;
+
+ rd_buffer.setLength(0);
+ }
+
+ public static void EndRedirect() {
+ rd_flusher.rd_flush(rd_target, rd_buffer);
+
+ rd_target = 0;
+ rd_buffer = null;
+ rd_buffersize = 0;
+ rd_flusher = null;
+ }
+
+ // See GameSpanw.ED_ParseEdict() to see how to use it now.
+ public static String Parse(ParseHelp hlp) {
+ int c;
+ int len = 0;
+
+ if (hlp.data == null) {
+ return "";
+ }
+
+ while (true) {
+ // skip whitespace
+ hlp.skipwhites();
+ if (hlp.isEof()) {
+ hlp.data = null;
+ return "";
+ }
+
+ // skip // comments
+ if (hlp.getchar() == '/') {
+ if (hlp.nextchar() == '/') {
+ hlp.skiptoeol();
+ // goto skip whitespace
+ } else {
+ hlp.prevchar();
+ break;
+ }
+ } else
+ break;
+ }
+
+ // handle quoted strings specially
+ if (hlp.getchar() == '\"') {
+ hlp.nextchar();
+ while (true) {
+ c = hlp.getchar();
+ hlp.nextchar();
+ if (c == '\"' || c == 0) {
+ return new String(com_token, 0, len);
+ }
+ if (len < Defines.MAX_TOKEN_CHARS) {
+ com_token[len] = (char) c;
+ len++;
+ }
+ }
+ }
+
+ // parse a regular word
+ c = hlp.getchar();
+ do {
+ if (len < Defines.MAX_TOKEN_CHARS) {
+ com_token[len] = (char) c;
+ len++;
+ }
+ c = hlp.nextchar();
+ } while (c > 32);
+
+ if (len == Defines.MAX_TOKEN_CHARS) {
+ Com.Printf("Token exceeded " + Defines.MAX_TOKEN_CHARS + " chars, discarded.\n");
+ len = 0;
+ }
+
+ return new String(com_token, 0, len);
+ }
+
+ public static void Error(int code, String fmt) throws longjmpException {
+ Error(code, fmt, null);
+ }
+
+ public static void Error(int code, String fmt, Vargs vargs) throws longjmpException {
+ // va_list argptr;
+ // static char msg[MAXPRINTMSG];
+
+ if (recursive) {
+ Sys.Error("recursive error after: " + msg);
+ }
+ recursive = true;
+
+ msg = sprintf(fmt, vargs);
+
+ if (code == Defines.ERR_DISCONNECT) {
+ Client.Drop();
+ recursive = false;
+ throw new longjmpException();
+ } else if (code == Defines.ERR_DROP) {
+ Com.Printf("********************\nERROR: " + msg + "\n********************\n");
+ Server.SV_Shutdown("Server crashed: " + msg + "\n", false);
+ Client.Drop();
+ recursive = false;
+ throw new longjmpException();
+ } else {
+ Server.SV_Shutdown("Server fatal crashed: %s" + msg + "\n", false);
+ Client.Shutdown();
+ }
+
+ Sys.Error(msg);
+ }
+
+ /**
+ * Com_InitArgv checks the number of command line arguments
+ * and copies all arguments with valid length into com_argv.
+ */
+ static void InitArgv(String[] args) throws longjmpException {
+
+ if (args.length > Defines.MAX_NUM_ARGVS) {
+ Com.Error(Defines.ERR_FATAL, "argc > MAX_NUM_ARGVS");
+ }
+
+ Com.com_argc = args.length;
+ for (int i = 0; i < Com.com_argc; i++) {
+ if (args[i].length() >= Defines.MAX_TOKEN_CHARS)
+ Com.com_argv[i] = "";
+ else
+ Com.com_argv[i] = args[i];
+ }
+ }
+
+ public static void DPrintf(String fmt) {
+ _debugContext = debugContext;
+ DPrintf(fmt, null);
+ _debugContext = "";
+ }
+
+ public static void dprintln(String fmt) {
+ DPrintf(_debugContext + fmt + "\n", null);
+ }
+
+ public static void Printf(String fmt) {
+ Printf(_debugContext + fmt, null);
+ }
+
+ public static void DPrintf(String fmt, Vargs vargs) {
+ if (Globals.developer == null || Globals.developer.value == 0)
+ return; // don't confuse non-developers with techie stuff...
+ _debugContext = debugContext;
+ Printf(fmt, vargs);
+ _debugContext = "";
+ }
+
+ /**
+ * Prints out messages, which can also be redirected to a remote client.
+ */
+ public static void Printf(String fmt, Vargs vargs) {
+ String msg = sprintf(_debugContext + fmt, vargs);
+ if (rd_target != 0) {
+ if ((msg.length() + rd_buffer.length()) > (rd_buffersize - 1)) {
+ rd_flusher.rd_flush(rd_target, rd_buffer);
+ rd_buffer.setLength(0);
+ }
+ rd_buffer.append(msg);
+ return;
+ }
+
+ Console.Print(msg);
+
+ // also echo to debugging console
+ Sys.ConsoleOutput(msg);
+
+ }
+
+ public static void Println(String fmt) {
+ Printf(_debugContext + fmt + "\n");
+ }
+
+ public static String sprintf(String fmt, Vargs vargs) {
+ String msg = "";
+ if (vargs == null || vargs.size() == 0) {
+ msg = fmt;
+ } else {
+ msg = new PrintfFormat(fmt).sprintf(vargs.toArray());
+ }
+ return msg;
+ }
+
+ public static int Argc() {
+ return Com.com_argc;
+ }
+
+ public static String Argv(int arg) {
+ if (arg < 0 || arg >= Com.com_argc || Com.com_argv[arg].length() < 1)
+ return "";
+ return Com.com_argv[arg];
+ }
+
+ public static void ClearArgv(int arg) {
+ if (arg < 0 || arg >= Com.com_argc || Com.com_argv[arg].length() < 1)
+ return;
+ Com.com_argv[arg] = "";
+ }
+
+ public static void Quit() {
+ Server.SV_Shutdown("Server quit\n", false);
+ Client.Shutdown();
+
+ if (Globals.logfile != null) {
+ try {
+ Globals.logfile.close();
+ } catch (IOException ignored) {
+ }
+ Globals.logfile = null;
+ }
+
+ Sys.Quit();
+ }
+
+ public static String StripExtension(String string) {
+ int i = string.lastIndexOf('.');
+ if (i < 0)
+ return string;
+ return string.substring(0, i);
+ }
+
+ /**
+ * Calculates a crc checksum-sequence over an array.
+ */
+ public static byte BlockSequenceCRCByte(byte base[], int offset, int length, int sequence) {
+ if (sequence < 0)
+ Sys.Error("sequence < 0, this shouldn't happen\n");
+
+ //p_ndx = (sequence % (sizeof(chktbl) - 4));
+ int p_ndx = (sequence % (1024 - 4));
+
+ //memcpy(chkb, base, length);
+ length = Math.min(60, length);
+ System.arraycopy(base, offset, chkb, 0, length);
+
+ chkb[length] = chktbl[p_ndx];
+ chkb[length + 1] = chktbl[p_ndx + 1];
+ chkb[length + 2] = chktbl[p_ndx + 2];
+ chkb[length + 3] = chktbl[p_ndx + 3];
+
+ length += 4;
+
+ // unsigned short
+ int crc = CRC.CRC_Block(chkb, length);
+
+ int x = 0;
+ for (int n = 0; n < length; n++)
+ x += chkb[n] & 0xFF;
+
+ crc ^= x;
+
+ return (byte) (crc & 0xFF);
+ }
+
+ public abstract static class RD_Flusher {
+ public abstract void rd_flush(int target, StringBuffer buffer);
+ }
+
+ // helper class to replace the pointer-pointer
+ public static class ParseHelp {
+ private final int length;
+ public int index;
+ public char data[];
+
+ public ParseHelp(String in) {
+ if (in == null) {
+ data = null;
+ length = 0;
+ } else {
+ data = in.toCharArray();
+ length = data.length;
+ }
+ index = 0;
+ }
+
+ public ParseHelp(char in[]) {
+ this(in, 0);
+ }
+
+ public ParseHelp(char in[], int offset) {
+ data = in;
+ index = offset;
+ if (data != null) length = data.length;
+ else length = 0;
+ }
+
+ public char getchar() {
+ if (index < length) {
+ return data[index];
+ }
+ return 0;
+ }
+
+ public char nextchar() {
+ // faster than if
+ index++;
+ if (index < length) {
+ return data[index];
+ }
+ return 0;
+ }
+
+ public void prevchar() {
+ if (index > 0) {
+ index--;
+ }
+ }
+
+ public boolean isEof() {
+ return index >= length;
+ }
+
+ public void skipwhites() {
+ char c = 0;
+ while (index < length && ((c = data[index]) <= ' ') && c != 0)
+ index++;
+ }
+
+ public char skipwhitestoeol() {
+ char c = 0;
+ while (index < length && ((c = data[index]) <= ' ') && c != '\n' && c != 0)
+ index++;
+ return c;
+ }
+
+ public void skiptoeol() {
+ char c = 0;
+ while (index < length && (c = data[index]) != '\n' && c != 0)
+ index++;
+ }
+ }
+
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.qcommon;
+
+import lwjake2.Defines;
+import lwjake2.Globals;
+import lwjake2.game.Cmd;
+import lwjake2.util.Lib;
+
+public final class CommandBuffer {
+
+ private static final byte[] line = new byte[1024];
+ private static final byte[] tmp = new byte[8192];
+
+
+ public static void Init() {
+ SZ.Init(Globals.cmd_text, Globals.cmd_text_buf,
+ Globals.cmd_text_buf.length);
+ }
+
+ public static void InsertText(String text) {
+
+ int templen = 0;
+
+ // copy off any commands still remaining in the exec buffer
+ templen = Globals.cmd_text.cursize;
+ if (templen != 0) {
+ System.arraycopy(Globals.cmd_text.data, 0, tmp, 0, templen);
+ SZ.Clear(Globals.cmd_text);
+ }
+
+ // add the entire text of the file
+ CommandBuffer.AddText(text);
+
+ // add the copied off data
+ if (templen != 0) {
+ SZ.Write(Globals.cmd_text, tmp, templen);
+ }
+ }
+
+ /**
+ * @param clear
+ */
+ static void addEarlyCommands(boolean clear) {
+
+ for (int i = 0; i < Com.Argc(); i++) {
+ String s = Com.Argv(i);
+ if (!s.equals("+set"))
+ continue;
+ CommandBuffer.AddText("set " + Com.Argv(i + 1) + " " + Com.Argv(i + 2)
+ + "\n");
+ if (clear) {
+ Com.ClearArgv(i);
+ Com.ClearArgv(i + 1);
+ Com.ClearArgv(i + 2);
+ }
+ i += 2;
+ }
+ }
+
+ /**
+ * @return
+ */
+ static boolean AddLateCommands() {
+ int i;
+ int j;
+ boolean ret = false;
+
+ // build the combined string to parse from
+ int s = 0;
+ int argc = Com.Argc();
+ for (i = 1; i < argc; i++) {
+ s += Com.Argv(i).length();
+ }
+ if (s == 0)
+ return false;
+
+ StringBuilder text = new StringBuilder();
+ for (i = 1; i < argc; i++) {
+ text.append(Com.Argv(i));
+ if (i != argc - 1)
+ text.append(" ");
+ }
+
+ // pull out the commands
+ StringBuilder build = new StringBuilder();
+ for (i = 0; i < text.length(); i++) {
+ if (text.charAt(i) == '+') {
+ i++;
+
+ for (j = i; j < text.length() && (text.charAt(j) != '+') && (text.charAt(j) != '-'); j++) ;
+
+ build.append(text.substring(i, j));
+ build.append("\n");
+
+ i = j - 1;
+ }
+ }
+
+ ret = (build.length() != 0);
+ if (ret)
+ CommandBuffer.AddText(build.toString());
+
+ text = null;
+ build = null;
+
+ return ret;
+ }
+
+ /**
+ * @param text
+ */
+ public static void AddText(String text) {
+ int l = text.length();
+
+ if (Globals.cmd_text.cursize + l >= Globals.cmd_text.maxsize) {
+ Com.Printf("Cbuf_AddText: overflow\n");
+ return;
+ }
+ SZ.Write(Globals.cmd_text, Lib.stringToBytes(text), l);
+ }
+
+ public static void execute() {
+
+ byte[] text = null;
+
+ Globals.alias_count = 0; // don't allow infinite alias loops
+
+ while (Globals.cmd_text.cursize != 0) {
+ // find a \n or ; line break
+ text = Globals.cmd_text.data;
+
+ int quotes = 0;
+ int i;
+
+ for (i = 0; i < Globals.cmd_text.cursize; i++) {
+ if (text[i] == '"')
+ quotes++;
+ if (quotes % 2 == 0 && text[i] == ';')
+ break; // don't break if inside a quoted string
+ if (text[i] == '\n')
+ break;
+ }
+
+ System.arraycopy(text, 0, line, 0, i);
+ line[i] = 0;
+
+ // delete the text from the command buffer and move remaining
+ // commands down
+ // this is necessary because commands (exec, alias) can insert data
+ // at the
+ // beginning of the text buffer
+
+ if (i == Globals.cmd_text.cursize)
+ Globals.cmd_text.cursize = 0;
+ else {
+ i++;
+ Globals.cmd_text.cursize -= i;
+ //byte[] tmp = new byte[Globals.cmd_text.cursize];
+
+ System.arraycopy(text, i, tmp, 0, Globals.cmd_text.cursize);
+ System.arraycopy(tmp, 0, text, 0, Globals.cmd_text.cursize);
+ text[Globals.cmd_text.cursize] = '\0';
+
+ }
+
+ // execute the command line
+ int len = Lib.strlen(line);
+
+ String cmd = new String(line, 0, len);
+ Cmd.ExecuteString(cmd);
+
+ if (Globals.cmd_wait) {
+ // skip out while text still remains in buffer, leaving it
+ // for next frame
+ Globals.cmd_wait = false;
+ break;
+ }
+ }
+ }
+
+ public static void ExecuteText(int exec_when, String text) {
+ switch (exec_when) {
+ case Defines.EXEC_NOW:
+ Cmd.ExecuteString(text);
+ break;
+ case Defines.EXEC_INSERT:
+ CommandBuffer.InsertText(text);
+ break;
+ case Defines.EXEC_APPEND:
+ CommandBuffer.AddText(text);
+ break;
+ default:
+ Com.Error(Defines.ERR_FATAL, "Cbuf_ExecuteText: bad exec_when");
+ }
+ }
+
+ /*
+ * ============ Cbuf_CopyToDefer ============
+ */
+ public static void CopyToDefer() {
+ System.arraycopy(Globals.cmd_text_buf, 0, Globals.defer_text_buf, 0,
+ Globals.cmd_text.cursize);
+ Globals.defer_text_buf[Globals.cmd_text.cursize] = 0;
+ Globals.cmd_text.cursize = 0;
+ }
+
+ /*
+ * ============ Cbuf_InsertFromDefer ============
+ */
+ public static void InsertFromDefer() {
+ InsertText(new String(Globals.defer_text_buf).trim());
+ Globals.defer_text_buf[0] = 0;
+ }
+
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.qcommon;
+
+import lwjake2.Defines;
+import lwjake2.Globals;
+import lwjake2.game.Cmd;
+import lwjake2.game.CvarT;
+import lwjake2.game.Info;
+import lwjake2.util.Lib;
+
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.util.Vector;
+
+/**
+ * Cvar implements console variables. The original code is located in cvar.c
+ */
+public class Cvar extends Globals {
+
+
+ /**
+ * Set command, sets variables.
+ */
+
+ static final xcommand_t Set_f = new xcommand_t() {
+ public void execute() {
+ int c;
+ int flags;
+
+ c = Cmd.Argc();
+ if (c != 3 && c != 4) {
+ Com.Printf("usage: set <variable> <value> [u / s]\n");
+ return;
+ }
+
+ if (c == 4) {
+ switch (Cmd.Argv(3)) {
+ case "u":
+ flags = CVAR_USERINFO;
+ break;
+ case "s":
+ flags = CVAR_SERVERINFO;
+ break;
+ default:
+ Com.Printf("flags can only be 'u' or 's'\n");
+ return;
+ }
+ Cvar.fullSet(Cmd.Argv(1), Cmd.Argv(2), flags);
+ } else
+ Cvar.set(Cmd.Argv(1), Cmd.Argv(2));
+
+ }
+
+ };
+ /**
+ * List command, lists all available commands.
+ */
+ static final xcommand_t List_f = new xcommand_t() {
+ public void execute() {
+ CvarT var;
+ int i;
+
+ i = 0;
+ for (var = Globals.cvar_vars; var != null; var = var.next, i++) {
+ if ((var.flags & CVAR_ARCHIVE) != 0)
+ Com.Printf("*");
+ else
+ Com.Printf(" ");
+ if ((var.flags & CVAR_USERINFO) != 0)
+ Com.Printf("U");
+ else
+ Com.Printf(" ");
+ if ((var.flags & CVAR_SERVERINFO) != 0)
+ Com.Printf("S");
+ else
+ Com.Printf(" ");
+ if ((var.flags & CVAR_NOSET) != 0)
+ Com.Printf("-");
+ else if ((var.flags & CVAR_LATCH) != 0)
+ Com.Printf("L");
+ else
+ Com.Printf(" ");
+ Com.Printf(" " + var.name + " \"" + var.string + "\"\n");
+ }
+ Com.Printf(i + " cvars\n");
+ }
+ };
+
+ public static CvarT get(String var_name, String var_value, int flags) {
+ CvarT var;
+
+ if ((flags & (CVAR_USERINFO | CVAR_SERVERINFO)) != 0) {
+ if (!infoValidate(var_name)) {
+ Com.Printf("invalid info cvar name\n");
+ return null;
+ }
+ }
+
+ var = Cvar.FindVar(var_name);
+ if (var != null) {
+ var.flags |= flags;
+ return var;
+ }
+
+ if (var_value == null)
+ return null;
+
+ if ((flags & (CVAR_USERINFO | CVAR_SERVERINFO)) != 0) {
+ if (!infoValidate(var_value)) {
+ Com.Printf("invalid info cvar value\n");
+ return null;
+ }
+ }
+ var = new CvarT();
+ var.name = var_name;
+ var.string = var_value;
+ var.modified = true;
+ // handles atof(var.string)
+ try {
+ var.value = Float.parseFloat(var.string);
+ } catch (NumberFormatException e) {
+ var.value = 0.0f;
+ }
+ // link the variable in
+ var.next = Globals.cvar_vars;
+ Globals.cvar_vars = var;
+
+ var.flags = flags;
+
+ return var;
+ }
+
+ static void Init() {
+ Cmd.AddCommand("set", Set_f);
+ Cmd.AddCommand("cvarlist", List_f);
+ }
+
+ public static String variableString(String var_name) {
+ CvarT var;
+ var = FindVar(var_name);
+ return (var == null) ? "" : var.string;
+ }
+
+ static CvarT FindVar(String var_name) {
+ CvarT var;
+
+ for (var = Globals.cvar_vars; var != null; var = var.next) {
+ if (var_name.equals(var.name))
+ return var;
+ }
+
+ return null;
+ }
+
+ /**
+ * Creates a variable if not found and sets their value, the parsed float value and their flags.
+ */
+ public static void fullSet(String var_name, String value, int flags) {
+ CvarT var;
+
+ var = Cvar.FindVar(var_name);
+ if (null == var) { // create it
+ Cvar.get(var_name, value, flags);
+ return;
+ }
+
+ var.modified = true;
+
+ if ((var.flags & CVAR_USERINFO) != 0)
+ Globals.userinfo_modified = true; // transmit at next oportunity
+
+ var.string = value;
+ try {
+ var.value = Float.parseFloat(var.string);
+ } catch (Exception e) {
+ var.value = 0.0f;
+ }
+
+ var.flags = flags;
+
+ }
+
+ /**
+ * Sets the value of the variable without forcing.
+ */
+ public static void set(String var_name, String value) {
+ set2(var_name, value, false);
+ }
+
+ /**
+ * Sets the value of the variable with forcing.
+ */
+ public static CvarT forceSet(String var_name, String value) {
+ return Cvar.set2(var_name, value, true);
+ }
+
+ /**
+ * Gereric set function, sets the value of the variable, with forcing its even possible to
+ * override the variables write protection.
+ */
+ static CvarT set2(String var_name, String value, boolean force) {
+
+ CvarT var = Cvar.FindVar(var_name);
+ if (var == null) {
+ // create it
+ return Cvar.get(var_name, value, 0);
+ }
+
+ if ((var.flags & (CVAR_USERINFO | CVAR_SERVERINFO)) != 0) {
+ if (!infoValidate(value)) {
+ Com.Printf("invalid info cvar value\n");
+ return var;
+ }
+ }
+
+ if (!force) {
+ if ((var.flags & CVAR_NOSET) != 0) {
+ Com.Printf(var_name + " is write protected.\n");
+ return var;
+ }
+
+ if ((var.flags & CVAR_LATCH) != 0) {
+ if (var.latched_string != null) {
+ if (value.equals(var.latched_string))
+ return var;
+ var.latched_string = null;
+ } else {
+ if (value.equals(var.string))
+ return var;
+ }
+
+ if (Globals.server_state != 0) {
+ Com.Printf(var_name + " will be changed for next game.\n");
+ var.latched_string = value;
+ } else {
+ var.string = value;
+ try {
+ var.value = Float.parseFloat(var.string);
+ } catch (Exception e) {
+ var.value = 0.0f;
+ }
+ if (var.name.equals("game")) {
+ FS.SetGamedir(var.string);
+ FS.ExecAutoexec();
+ }
+ }
+ return var;
+ }
+ } else {
+ if (var.latched_string != null) {
+ var.latched_string = null;
+ }
+ }
+
+ if (value.equals(var.string))
+ return var; // not changed
+
+ var.modified = true;
+
+ if ((var.flags & CVAR_USERINFO) != 0)
+ Globals.userinfo_modified = true; // transmit at next oportunity
+
+ var.string = value;
+ try {
+ var.value = Float.parseFloat(var.string);
+ } catch (Exception e) {
+ var.value = 0.0f;
+ }
+
+ return var;
+ }
+
+ /**
+ * Sets a float value of a variable.
+ * <p>
+ * The overloading is very important, there was a problem with
+ * networt "rate" string --> 10000 became "10000.0" and that wasn't right.
+ */
+ public static void setValue(String var_name, int value) {
+ Cvar.set(var_name, "" + value);
+ }
+
+ public static void setValue(String var_name, float value) {
+ if (value == (int) value) {
+ Cvar.set(var_name, "" + (int) value);
+ } else {
+ Cvar.set(var_name, "" + value);
+ }
+ }
+
+ /**
+ * Returns the float value of a variable.
+ */
+ public static float variableValue(String var_name) {
+ CvarT var = Cvar.FindVar(var_name);
+ if (var == null)
+ return 0;
+ float val = 0.0f;
+ try {
+ val = Float.parseFloat(var.string);
+ } catch (Exception ignored) {
+ }
+ return val;
+ }
+
+ /**
+ * Handles variable inspection and changing from the console.
+ */
+ public static boolean command() {
+ CvarT v;
+
+ // check variables
+ v = Cvar.FindVar(Cmd.Argv(0));
+ if (v == null)
+ return false;
+
+ // perform a variable print or set
+ if (Cmd.Argc() == 1) {
+ Com.Printf("\"" + v.name + "\" is \"" + v.string + "\"\n");
+ return true;
+ }
+
+ Cvar.set(v.name, Cmd.Argv(1));
+ return true;
+ }
+
+ public static String bitInfo(int bit) {
+ String info;
+ CvarT var;
+
+ info = "";
+
+ for (var = Globals.cvar_vars; var != null; var = var.next) {
+ if ((var.flags & bit) != 0)
+ info = Info.Info_SetValueForKey(info, var.name, var.string);
+ }
+ return info;
+ }
+
+ /**
+ * Returns an info string containing all the CVAR_SERVERINFO cvars.
+ */
+ public static String serverinfo() {
+ return bitInfo(Defines.CVAR_SERVERINFO);
+ }
+
+
+ /**
+ * Any variables with latched values will be updated.
+ */
+ public static void getLatchedVars() {
+ CvarT var;
+
+ for (var = Globals.cvar_vars; var != null; var = var.next) {
+ if (var.latched_string == null || var.latched_string.length() == 0)
+ continue;
+ var.string = var.latched_string;
+ var.latched_string = null;
+ try {
+ var.value = Float.parseFloat(var.string);
+ } catch (NumberFormatException e) {
+ var.value = 0.0f;
+ }
+ if (var.name.equals("game")) {
+ FS.SetGamedir(var.string);
+ FS.ExecAutoexec();
+ }
+ }
+ }
+
+ /**
+ * Returns an info string containing all the CVAR_USERINFO cvars.
+ */
+ public static String userinfo() {
+ return bitInfo(CVAR_USERINFO);
+ }
+
+ /**
+ * Appends lines containing \"set vaqriable value\" for all variables
+ * with the archive flag set true.
+ */
+
+ public static void writeVariables(String path) {
+ CvarT var;
+ RandomAccessFile f;
+ String buffer;
+
+ f = Lib.fopen(path, "rw");
+ if (f == null)
+ return;
+
+ try {
+ f.seek(f.length());
+ } catch (IOException e1) {
+ Lib.fclose(f);
+ return;
+ }
+ for (var = cvar_vars; var != null; var = var.next) {
+ if ((var.flags & CVAR_ARCHIVE) != 0) {
+ buffer = "set " + var.name + " \"" + var.string + "\"\n";
+ try {
+ f.writeBytes(buffer);
+ } catch (IOException ignored) {
+ }
+ }
+ }
+ Lib.fclose(f);
+ }
+
+ /**
+ * Variable typing auto completition.
+ */
+ public static Vector<String> completeVariable(String partial) {
+
+ Vector<String> vars = new Vector<>();
+
+ // check match
+ for (CvarT cvar = Globals.cvar_vars; cvar != null; cvar = cvar.next)
+ if (cvar.name.startsWith(partial))
+ vars.add(cvar.name);
+
+ return vars;
+ }
+
+ /**
+ * Some characters are invalid for info strings.
+ */
+ static boolean infoValidate(String s) {
+ if (s.contains("\\"))
+ return false;
+ return !s.contains("\"") && !s.contains(";");
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.qcommon;
+
+import lwjake2.Defines;
+import lwjake2.Globals;
+import lwjake2.game.Cmd;
+import lwjake2.game.CvarT;
+import lwjake2.sys.Sys;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.channels.FileChannel;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * FS
+ *
+ * @author cwei
+ */
+public final class FS extends Globals {
+
+ /*
+ * ==================================================
+ *
+ * QUAKE FILESYSTEM
+ *
+ * ==================================================
+ */
+
+ // read in blocks of 64k
+ public static final int MAX_READ = 0x10000;
+ // with filelink_t entries
+ public static final List<filelink_t> fs_links = new LinkedList<>();
+ static final int IDPAKHEADER = (('K' << 24) + ('C' << 16) + ('A' << 8) + 'P');
+ static final int MAX_FILES_IN_PACK = 4096;
+ // buffer for C-Strings char[56]
+ static final byte[] tmpText = new byte[packfile_t.NAME_SIZE];
+ public static String fs_gamedir;
+ public static CvarT fs_basedir;
+ public static CvarT fs_cddir;
+ public static CvarT fs_gamedirvar;
+ public static searchpath_t fs_searchpaths;
+ // without gamedirs
+ public static searchpath_t fs_base_searchpaths;
+ public static int file_from_pak = 0;
+ /*
+ * All of Quake's data access is through a hierchal file system, but the
+ * contents of the file system can be transparently merged from several
+ * sources.
+ *
+ * The "base directory" is the path to the directory holding the quake.exe
+ * and all game directories. The sys_* files pass this to host_init in
+ * quakeparms_t->basedir. This can be overridden with the "-basedir" command
+ * line parm to allow code debugging in a different directory. The base
+ * directory is only used during filesystem initialization.
+ *
+ * The "game directory" is the first tree on the search path and directory
+ * that all generated files (savegames, screenshots, demos, config files)
+ * will be saved to. This can be overridden with the "-game" command line
+ * parameter. The game directory can never be changed while quake is
+ * executing. This is a precacution against having a malicious server
+ * instruct clients to write files over areas they shouldn't.
+ *
+ */
+ private static String fs_userdir;
+
+ /*
+ * CreatePath
+ *
+ * Creates any directories needed to store the given filename.
+ */
+ public static void CreatePath(String path) {
+ int index = path.lastIndexOf('/');
+ // -1 if not found and 0 means write to root
+ if (index > 0) {
+ File f = new File(path.substring(0, index));
+ if (!f.mkdirs() && !f.isDirectory()) {
+ Com.Printf("can't create path \"" + path + '"' + "\n");
+ }
+ }
+ }
+
+ public static int FileLength(String filename) {
+ searchpath_t search;
+ String netpath;
+ pack_t pak;
+ filelink_t link;
+
+ file_from_pak = 0;
+
+ // check for links first
+ for (filelink_t fs_link : fs_links) {
+ link = fs_link;
+
+ if (filename.regionMatches(0, link.from, 0, link.fromlength)) {
+ netpath = link.to + filename.substring(link.fromlength);
+ File file = new File(netpath);
+ if (file.canRead()) {
+ Com.DPrintf("link file: " + netpath + '\n');
+ return (int) file.length();
+ }
+ return -1;
+ }
+ }
+
+ // search through the path, one element at a time
+
+ for (search = fs_searchpaths; search != null; search = search.next) {
+ // is the element a pak file?
+ if (search.pack != null) {
+ // look through all the pak file elements
+ pak = search.pack;
+ filename = filename.toLowerCase();
+ packfile_t entry = pak.files.get(filename);
+
+ if (entry != null) {
+ // found it!
+ file_from_pak = 1;
+ Com.DPrintf("PackFile: " + pak.filename + " : " + filename
+ + '\n');
+ // open a new file on the pakfile
+ File file = new File(pak.filename);
+ if (!file.canRead()) {
+ Com.Error(Defines.ERR_FATAL, "Couldn't reopen "
+ + pak.filename);
+ }
+ return entry.filelen;
+ }
+ } else {
+ // check a file in the directory tree
+ netpath = search.filename + '/' + filename;
+
+ File file = new File(netpath);
+ if (!file.canRead())
+ continue;
+
+ Com.DPrintf("FindFile: " + netpath + '\n');
+
+ return (int) file.length();
+ }
+ }
+ Com.DPrintf("FindFile: can't find " + filename + '\n');
+ return -1;
+ }
+
+ /*
+ * FOpenFile
+ *
+ * Finds the file in the search path. returns a RadomAccesFile. Used for
+ * streaming data out of either a pak file or a seperate file.
+ */
+ public static RandomAccessFile FOpenFile(String filename)
+ throws IOException {
+ searchpath_t search;
+ String netpath;
+ pack_t pak;
+ filelink_t link;
+ File file = null;
+
+ file_from_pak = 0;
+
+ // check for links first
+ for (filelink_t fs_link : fs_links) {
+ link = fs_link;
+
+ // if (!strncmp (filename, link->from, link->fromlength))
+ if (filename.regionMatches(0, link.from, 0, link.fromlength)) {
+ netpath = link.to + filename.substring(link.fromlength);
+ file = new File(netpath);
+ if (file.canRead()) {
+ //Com.DPrintf ("link file: " + netpath +'\n');
+ return new RandomAccessFile(file, "r");
+ }
+ return null;
+ }
+ }
+
+ //
+ // search through the path, one element at a time
+ //
+ for (search = fs_searchpaths; search != null; search = search.next) {
+ // is the element a pak file?
+ if (search.pack != null) {
+ // look through all the pak file elements
+ pak = search.pack;
+ filename = filename.toLowerCase();
+ packfile_t entry = pak.files.get(filename);
+
+ if (entry != null) {
+ // found it!
+ file_from_pak = 1;
+ //Com.DPrintf ("PackFile: " + pak.filename + " : " +
+ // filename + '\n');
+ file = new File(pak.filename);
+ if (!file.canRead())
+ Com.Error(Defines.ERR_FATAL, "Couldn't reopen "
+ + pak.filename);
+ if (pak.handle == null || !pak.handle.getFD().valid()) {
+ // hold the pakfile handle open
+ pak.handle = new RandomAccessFile(pak.filename, "r");
+ }
+ // open a new file on the pakfile
+
+ RandomAccessFile raf = new RandomAccessFile(file, "r");
+ raf.seek(entry.filepos);
+
+ return raf;
+ }
+ } else {
+ // check a file in the directory tree
+ netpath = search.filename + '/' + filename;
+
+ file = new File(netpath);
+ if (!file.canRead())
+ continue;
+
+ //Com.DPrintf("FindFile: " + netpath +'\n');
+
+ return new RandomAccessFile(file, "r");
+ }
+ }
+ //Com.DPrintf ("FindFile: can't find " + filename + '\n');
+ return null;
+ }
+
+ /**
+ * Read
+ * <p>
+ * Properly handles partial reads
+ */
+ public static void Read(byte[] buffer, int len, RandomAccessFile f) {
+
+ int block, remaining;
+ int offset = 0;
+ int read = 0;
+
+ // read in chunks for progress bar
+ remaining = len;
+
+ while (remaining != 0) {
+ block = Math.min(remaining, MAX_READ);
+ try {
+ read = f.read(buffer, offset, block);
+ } catch (IOException e) {
+ Com.Error(Defines.ERR_FATAL, e.toString());
+ }
+
+ if (read == 0) {
+ Com.Error(Defines.ERR_FATAL, "FS_Read: 0 bytes read");
+ } else if (read == -1) {
+ Com.Error(Defines.ERR_FATAL, "FS_Read: -1 bytes read");
+ }
+ //
+ // do some progress bar thing here...
+ //
+ remaining -= read;
+ offset += read;
+ }
+ }
+
+ /*
+ * LoadFile
+ *
+ * Filename are reletive to the quake search path a null buffer will just
+ * return the file content as byte[]
+ */
+ public static byte[] LoadFile(String path) {
+ RandomAccessFile file;
+
+ byte[] buf = null;
+ int len = 0;
+
+ // TODO hack for bad strings (fuck \0)
+ int index = path.indexOf('\0');
+ if (index != -1)
+ path = path.substring(0, index);
+
+ // look for it in the filesystem or pack files
+ len = FileLength(path);
+
+ if (len < 1)
+ return null;
+
+ try {
+ file = FOpenFile(path);
+ //Read(buf = new byte[len], len, h);
+ buf = new byte[len];
+ file.readFully(buf);
+ file.close();
+ } catch (IOException e) {
+ Com.Error(Defines.ERR_FATAL, e.toString());
+ }
+ return buf;
+ }
+
+ /*
+ * LoadMappedFile
+ *
+ * Filename are reletive to the quake search path a null buffer will just
+ * return the file content as ByteBuffer (memory mapped)
+ */
+ public static ByteBuffer LoadMappedFile(String filename) {
+ searchpath_t search;
+ String netpath;
+ pack_t pak;
+ filelink_t link;
+ File file = null;
+
+ int fileLength = 0;
+ FileChannel channel = null;
+ FileInputStream input = null;
+ ByteBuffer buffer = null;
+
+ file_from_pak = 0;
+
+ try {
+ // check for links first
+ for (filelink_t fs_link : fs_links) {
+ link = fs_link;
+
+ if (filename.regionMatches(0, link.from, 0, link.fromlength)) {
+ netpath = link.to + filename.substring(link.fromlength);
+ file = new File(netpath);
+ if (file.canRead()) {
+ input = new FileInputStream(file);
+ channel = input.getChannel();
+ fileLength = (int) channel.size();
+ buffer = channel.map(FileChannel.MapMode.READ_ONLY, 0,
+ fileLength);
+ input.close();
+ return buffer;
+ }
+ return null;
+ }
+ }
+
+ //
+ // search through the path, one element at a time
+ //
+ for (search = fs_searchpaths; search != null; search = search.next) {
+ // is the element a pak file?
+ if (search.pack != null) {
+ // look through all the pak file elements
+ pak = search.pack;
+ filename = filename.toLowerCase();
+ packfile_t entry = pak.files.get(filename);
+
+ if (entry != null) {
+ // found it!
+ file_from_pak = 1;
+ //Com.DPrintf ("PackFile: " + pak.filename + " : " +
+ // filename + '\n');
+ file = new File(pak.filename);
+ if (!file.canRead())
+ Com.Error(Defines.ERR_FATAL, "Couldn't reopen "
+ + pak.filename);
+ if (pak.handle == null || !pak.handle.getFD().valid()) {
+ // hold the pakfile handle open
+ pak.handle = new RandomAccessFile(pak.filename, "r");
+ }
+ // open a new file on the pakfile
+ if (pak.backbuffer == null) {
+ channel = pak.handle.getChannel();
+ pak.backbuffer = channel.map(
+ FileChannel.MapMode.READ_ONLY, 0,
+ pak.handle.length());
+ channel.close();
+ }
+ pak.backbuffer.position(entry.filepos);
+ buffer = pak.backbuffer.slice();
+ buffer.limit(entry.filelen);
+ return buffer;
+ }
+ } else {
+ // check a file in the directory tree
+ netpath = search.filename + '/' + filename;
+
+ file = new File(netpath);
+ if (!file.canRead())
+ continue;
+
+ //Com.DPrintf("FindFile: " + netpath +'\n');
+ input = new FileInputStream(file);
+ channel = input.getChannel();
+ fileLength = (int) channel.size();
+ buffer = channel.map(FileChannel.MapMode.READ_ONLY, 0,
+ fileLength);
+ input.close();
+ return buffer;
+ }
+ }
+ } catch (Exception ignored) {
+ }
+ try {
+ if (input != null)
+ input.close();
+ else if (channel != null && channel.isOpen())
+ channel.close();
+ } catch (IOException ignored) {
+ }
+ return null;
+ }
+
+ /*
+ * FreeFile
+ */
+ public static void FreeFile() {
+ byte[] buffer = null;
+ }
+
+ /*
+ * LoadPackFile
+ *
+ * Takes an explicit (not game tree related) path to a pak file.
+ *
+ * Loads the header and directory, adding the files at the beginning of the
+ * list so they override previous pack files.
+ */
+ static pack_t LoadPackFile(String packfile) {
+
+ dpackheader_t header;
+ Hashtable<String, packfile_t> newfiles;
+ RandomAccessFile file;
+ int numpackfiles = 0;
+ pack_t pack = null;
+ // unsigned checksum;
+ //
+ try {
+ file = new RandomAccessFile(packfile, "r");
+ FileChannel fc = file.getChannel();
+ ByteBuffer packhandle = fc.map(FileChannel.MapMode.READ_ONLY, 0, file.length());
+ packhandle.order(ByteOrder.LITTLE_ENDIAN);
+
+ fc.close();
+
+ if (packhandle == null || packhandle.limit() < 1)
+ return null;
+ //
+ header = new dpackheader_t();
+ header.ident = packhandle.getInt();
+ header.dirofs = packhandle.getInt();
+ header.dirlen = packhandle.getInt();
+
+ if (header.ident != IDPAKHEADER)
+ Com.Error(Defines.ERR_FATAL, packfile + " is not a packfile");
+
+ numpackfiles = header.dirlen / packfile_t.SIZE;
+
+ if (numpackfiles > MAX_FILES_IN_PACK)
+ Com.Error(Defines.ERR_FATAL, packfile + " has " + numpackfiles
+ + " files");
+
+ newfiles = new Hashtable<>(numpackfiles);
+
+ packhandle.position(header.dirofs);
+
+ // parse the directory
+ packfile_t entry = null;
+
+ for (int i = 0; i < numpackfiles; i++) {
+ packhandle.get(tmpText);
+
+ entry = new packfile_t();
+ entry.name = new String(tmpText).trim();
+ entry.filepos = packhandle.getInt();
+ entry.filelen = packhandle.getInt();
+
+ newfiles.put(entry.name.toLowerCase(), entry);
+ }
+
+ } catch (IOException e) {
+ Com.DPrintf(e.getMessage() + '\n');
+ return null;
+ }
+
+ pack = new pack_t();
+ pack.filename = packfile;
+ pack.handle = file;
+ pack.numfiles = numpackfiles;
+ pack.files = newfiles;
+
+ Com.Printf("Added packfile " + packfile + " (" + numpackfiles
+ + " files)\n");
+
+ return pack;
+ }
+
+ /*
+ * AddGameDirectory
+ *
+ * Sets fs_gamedir, adds the directory to the head of the path, then loads
+ * and adds pak1.pak pak2.pak ...
+ */
+ static void AddGameDirectory(String dir) {
+ int i;
+ searchpath_t search;
+ pack_t pak;
+ String pakfile;
+
+ fs_gamedir = dir;
+
+ //
+ // add the directory to the search path
+ // ensure fs_userdir is first in searchpath
+ search = new searchpath_t();
+ search.filename = dir;
+ if (fs_searchpaths != null) {
+ search.next = fs_searchpaths.next;
+ fs_searchpaths.next = search;
+ } else {
+ fs_searchpaths = search;
+ }
+
+ //
+ // add any pak files in the format pak0.pak pak1.pak, ...
+ //
+ for (i = 0; i < 10; i++) {
+ pakfile = dir + "/pak" + i + ".pak";
+ if (!(new File(pakfile).canRead()))
+ continue;
+
+ pak = LoadPackFile(pakfile);
+ if (pak == null)
+ continue;
+
+ search = new searchpath_t();
+ search.pack = pak;
+ search.filename = "";
+ search.next = fs_searchpaths;
+ fs_searchpaths = search;
+ }
+ }
+
+ /*
+ * Gamedir
+ *
+ * Called to find where to write a file (demos, savegames, etc)
+ * this is modified to <user.home>/.lwjake2
+ */
+ public static String Gamedir() {
+ return (fs_userdir != null) ? fs_userdir : Globals.BASEDIRNAME;
+ }
+
+ /*
+ * BaseGamedir
+ *
+ * Called to find where to write a downloaded file
+ */
+ public static String BaseGamedir() {
+ return (fs_gamedir != null) ? fs_gamedir : Globals.BASEDIRNAME;
+ }
+
+ /*
+ * ExecAutoexec
+ */
+ public static void ExecAutoexec() {
+ String dir = fs_userdir;
+
+ String name;
+ if (dir != null && dir.length() > 0) {
+ name = dir + "/autoexec.cfg";
+ } else {
+ name = fs_basedir.string + '/' + Globals.BASEDIRNAME
+ + "/autoexec.cfg";
+ }
+
+ int canthave = Defines.SFF_SUBDIR | Defines.SFF_HIDDEN
+ | Defines.SFF_SYSTEM;
+
+ if (Sys.FindAll(name, 0, canthave) != null) {
+ CommandBuffer.AddText("exec autoexec.cfg\n");
+ }
+ }
+
+ /*
+ * SetGamedir
+ *
+ * Sets the gamedir and path to a different directory.
+ */
+ public static void SetGamedir(String dir) {
+ searchpath_t next;
+
+ if (dir.contains("..") || dir.contains("/")
+ || dir.contains("\\") || dir.contains(":")) {
+ Com.Printf("Gamedir should be a single filename, not a path\n");
+ return;
+ }
+
+ //
+ // free up any current game dir info
+ //
+ while (fs_searchpaths != fs_base_searchpaths) {
+ if (fs_searchpaths.pack != null) {
+ try {
+ fs_searchpaths.pack.handle.close();
+ } catch (IOException e) {
+ Com.DPrintf(e.getMessage() + '\n');
+ }
+ // clear the hashtable
+ fs_searchpaths.pack.files.clear();
+ fs_searchpaths.pack.files = null;
+ fs_searchpaths.pack = null;
+ }
+ next = fs_searchpaths.next;
+ fs_searchpaths = null;
+ fs_searchpaths = next;
+ }
+
+ //
+ // flush all data, so it will be forced to reload
+ //
+ if ((Globals.dedicated != null) && (Globals.dedicated.value == 0.0f))
+ CommandBuffer.AddText("vid_restart\nsnd_restart\n");
+
+ fs_gamedir = fs_basedir.string + '/' + dir;
+
+ if (dir.equals(Globals.BASEDIRNAME) || (dir.length() == 0)) {
+ Cvar.fullSet("gamedir", "", CVAR_SERVERINFO | CVAR_NOSET);
+ Cvar.fullSet("game", "", CVAR_LATCH | CVAR_SERVERINFO);
+ } else {
+ Cvar.fullSet("gamedir", dir, CVAR_SERVERINFO | CVAR_NOSET);
+ if (fs_cddir.string != null && fs_cddir.string.length() > 0)
+ AddGameDirectory(fs_cddir.string + '/' + dir);
+
+ AddGameDirectory(fs_basedir.string + '/' + dir);
+ }
+ }
+
+ /*
+ * Link_f
+ *
+ * Creates a filelink_t
+ */
+ public static void Link_f() {
+ filelink_t entry = null;
+
+ if (Cmd.Argc() != 3) {
+ Com.Printf("USAGE: link <from> <to>\n");
+ return;
+ }
+
+ // see if the link already exists
+ for (Iterator<filelink_t> it = fs_links.iterator(); it.hasNext(); ) {
+ entry = it.next();
+
+ if (entry.from.equals(Cmd.Argv(1))) {
+ if (Cmd.Argv(2).length() < 1) {
+ // delete it
+ it.remove();
+ return;
+ }
+ entry.to = Cmd.Argv(2);
+ return;
+ }
+ }
+
+ // create a new link if the <to> is not empty
+ if (Cmd.Argv(2).length() > 0) {
+ entry = new filelink_t();
+ entry.from = Cmd.Argv(1);
+ entry.fromlength = entry.from.length();
+ entry.to = Cmd.Argv(2);
+ fs_links.add(entry);
+ }
+ }
+
+ /*
+ * ListFiles
+ */
+ public static String[] ListFiles(String findname, int musthave, int canthave) {
+ String[] list = new String[0];
+
+ File[] files = Sys.FindAll(findname, musthave, canthave);
+
+ if (files != null) {
+ list = new String[files.length];
+ for (int i = 0; i < files.length; i++) {
+ list[i] = files[i].getPath();
+ }
+ }
+
+ return list;
+ }
+
+ /*
+ * Dir_f
+ */
+ public static void Dir_f() {
+ String path = null;
+ String findname = null;
+ String wildcard = "*.*";
+ String[] dirnames;
+
+ if (Cmd.Argc() != 1) {
+ wildcard = Cmd.Argv(1);
+ }
+
+ while ((path = NextPath(path)) != null) {
+ String tmp = findname;
+
+ findname = path + '/' + wildcard;
+
+ if (tmp != null)
+ tmp.replaceAll("\\\\", "/");
+
+ Com.Printf("Directory of " + findname + '\n');
+ Com.Printf("----\n");
+
+ dirnames = ListFiles(findname, 0, 0);
+
+ if (dirnames.length != 0) {
+ int index = 0;
+ for (String dirname : dirnames) {
+ if ((index = dirname.lastIndexOf('/')) > 0) {
+ Com.Printf(dirname.substring(index + 1, dirname
+ .length()) + '\n');
+ } else {
+ Com.Printf(dirname + '\n');
+ }
+ }
+ }
+
+ Com.Printf("\n");
+ }
+ }
+
+ /*
+ * Path_f
+ */
+ public static void Path_f() {
+
+ searchpath_t s;
+ filelink_t link;
+
+ Com.Printf("Current search path:\n");
+ for (s = fs_searchpaths; s != null; s = s.next) {
+ if (s == fs_base_searchpaths)
+ Com.Printf("----------\n");
+ if (s.pack != null)
+ Com.Printf(s.pack.filename + " (" + s.pack.numfiles
+ + " files)\n");
+ else
+ Com.Printf(s.filename + '\n');
+ }
+
+ Com.Printf("\nLinks:\n");
+ for (filelink_t fs_link : fs_links) {
+ link = fs_link;
+ Com.Printf(link.from + " : " + link.to + '\n');
+ }
+ }
+
+ /*
+ * NextPath
+ *
+ * Allows enumerating all of the directories in the search path
+ */
+ public static String NextPath(String prevpath) {
+ searchpath_t s;
+ String prev;
+
+ if (prevpath == null || prevpath.length() == 0)
+ return fs_gamedir;
+
+ prev = fs_gamedir;
+ for (s = fs_searchpaths; s != null; s = s.next) {
+ if (s.pack != null)
+ continue;
+
+ if (prevpath == prev)
+ return s.filename;
+
+ prev = s.filename;
+ }
+
+ return null;
+ }
+
+ /*
+ * initFilesystem
+ */
+ public static void initFilesystem() {
+ Cmd.AddCommand("path", new xcommand_t() {
+ public void execute() {
+ Path_f();
+ }
+ });
+ Cmd.AddCommand("link", new xcommand_t() {
+ public void execute() {
+ Link_f();
+ }
+ });
+ Cmd.AddCommand("dir", new xcommand_t() {
+ public void execute() {
+ Dir_f();
+ }
+ });
+
+ fs_userdir = System.getProperty("user.home") + "/.lwjake2";
+ FS.CreatePath(fs_userdir + "/");
+ FS.AddGameDirectory(fs_userdir);
+
+ //
+ // basedir <path>
+ // allows the game to run from outside the data tree
+ //
+ fs_basedir = Cvar.get("basedir", ".", CVAR_NOSET);
+
+ //
+ // cddir <path>
+ // Logically concatenates the cddir after the basedir for
+ // allows the game to run from outside the data tree
+ //
+
+ setCDDir();
+
+ //
+ // start up with baseq2 by default
+ //
+ AddGameDirectory(fs_basedir.string + '/' + Globals.BASEDIRNAME);
+
+ // any set gamedirs will be freed up to here
+ markBaseSearchPaths();
+
+ // check for game override
+ checkOverride();
+ }
+
+ /**
+ * set baseq2 directory
+ */
+ static void setCDDir() {
+ fs_cddir = Cvar.get("cddir", "", CVAR_ARCHIVE);
+ if (fs_cddir.string.length() > 0)
+ AddGameDirectory(fs_cddir.string + '/' + Globals.BASEDIRNAME);
+ }
+
+ /**
+ * Check for "+set game" override - Used to properly set gamedir
+ */
+ static void checkOverride() {
+ fs_gamedirvar = Cvar.get("game", "", CVAR_LATCH | CVAR_SERVERINFO);
+
+ if (fs_gamedirvar.string.length() > 0)
+ SetGamedir(fs_gamedirvar.string);
+ }
+
+ static void markBaseSearchPaths() {
+ // any set gamedirs will be freed up to here
+ fs_base_searchpaths = fs_searchpaths;
+ }
+
+ // RAFAEL
+ /*
+ * Developer_searchpath
+ */
+ public static int Developer_searchpath(int who) {
+
+ // PMM - warning removal
+ // char *start;
+ searchpath_t s;
+
+
+ for (s = fs_searchpaths; s != null; s = s.next) {
+ if (s.filename.contains("xatrix"))
+ return 1;
+
+ if (s.filename.contains("rogue"))
+ return 2;
+ }
+
+ return 0;
+ }
+
+ public static class packfile_t {
+ static final int SIZE = 64;
+
+ static final int NAME_SIZE = 56;
+
+ String name; // char name[56]
+
+ int filepos, filelen;
+
+ public String toString() {
+ return name + " [ length: " + filelen + " pos: " + filepos + " ]";
+ }
+ }
+
+ public static class pack_t {
+ String filename;
+
+ RandomAccessFile handle;
+
+ ByteBuffer backbuffer;
+
+ int numfiles;
+
+ Hashtable<String, packfile_t> files; // with packfile_t entries
+ }
+
+ public static class filelink_t {
+ String from;
+
+ int fromlength;
+
+ String to;
+ }
+
+ public static class searchpath_t {
+ String filename;
+
+ pack_t pack; // only one of filename or pack will be used
+
+ searchpath_t next;
+ }
+
+ static class dpackheader_t {
+ int ident; // IDPAKHEADER
+
+ int dirofs;
+
+ int dirlen;
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.qcommon;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.security.MessageDigest;
+
+
+public class MD4 extends MessageDigest implements Cloneable {
+ // MD4 specific object variables
+ //...........................................................................
+
+ /**
+ * The size in bytes of the input block to the tranformation algorithm.
+ */
+ private static final int BLOCK_LENGTH = 64; // = 512 / 8;
+ /**
+ * 512 bits work buffer = 16 x 32-bit words
+ */
+ private final int[] X = new int[16];
+ /**
+ * 4 32-bit words (interim result)
+ */
+ private int[] context = new int[4];
+ /**
+ * Number of bytes processed so far mod. 2 power of 64.
+ */
+ private long count;
+ /**
+ * 512 bits input buffer = 16 x 32-bit words holds until reaches 512 bits.
+ */
+ private byte[] buffer = new byte[BLOCK_LENGTH];
+
+ // Constructors
+ //...........................................................................
+
+ public MD4() {
+ super("MD4");
+ engineReset();
+ }
+
+ /**
+ * This constructor is here to implement cloneability of this class.
+ */
+ private MD4(MD4 md) {
+ this();
+ context = md.context.clone();
+ buffer = md.buffer.clone();
+ count = md.count;
+ }
+
+ // Cloneable method implementation
+ //...........................................................................
+
+ /**
+ * Bugfixed, now works prima (RST).
+ */
+ public static int Com_BlockChecksum(byte[] buffer, int length) {
+
+ int val;
+ MD4 md4 = new MD4();
+
+ md4.engineUpdate(buffer, 0, length);
+ byte data[] = md4.engineDigest();
+ ByteBuffer bb = ByteBuffer.wrap(data);
+ bb.order(ByteOrder.LITTLE_ENDIAN);
+ val = bb.getInt() ^ bb.getInt() ^ bb.getInt() ^ bb.getInt();
+ return val;
+ }
+
+ // JCE methods
+ //...........................................................................
+
+ /**
+ * Returns a copy of this MD object.
+ */
+ public Object clone() {
+ return new MD4(this);
+ }
+
+ /**
+ * Resets this object disregarding any temporary data present at the
+ * time of the invocation of this call.
+ */
+ public void engineReset() {
+ // initial values of MD4 i.e. A, B, C, D
+ // as per rfc-1320; they are low-order byte first
+ context[0] = 0x67452301;
+ context[1] = 0xEFCDAB89;
+ context[2] = 0x98BADCFE;
+ context[3] = 0x10325476;
+ count = 0L;
+ for (int i = 0; i < BLOCK_LENGTH; i++)
+ buffer[i] = 0;
+ }
+
+ /**
+ * Continues an MD4 message digest using the input byte.
+ */
+ public void engineUpdate(byte b) {
+ // compute number of bytes still unhashed; ie. present in buffer
+ int i = (int) (count % BLOCK_LENGTH);
+ count++; // update number of bytes
+ buffer[i] = b;
+ if (i == BLOCK_LENGTH - 1)
+ transform(buffer, 0);
+ }
+
+ /**
+ * MD4 block update operation.
+ * <p>
+ * Continues an MD4 message digest operation, by filling the buffer,
+ * transform(ing) data in 512-bit message block(s), updating the variables
+ * context and count, and leaving (buffering) the remaining bytes in buffer
+ * for the next update or finish.
+ *
+ * @param input input block
+ * @param offset start of meaningful bytes in input
+ * @param len count of bytes in input block to consider
+ */
+ public void engineUpdate(byte[] input, int offset, int len) {
+ // make sure we don't exceed input's allocated size/length
+ if (offset < 0 || len < 0 || (long) offset + len > input.length)
+ throw new ArrayIndexOutOfBoundsException();
+
+ // compute number of bytes still unhashed; ie. present in buffer
+ int bufferNdx = (int) (count % BLOCK_LENGTH);
+ count += len; // update number of bytes
+ int partLen = BLOCK_LENGTH - bufferNdx;
+ int i = 0;
+ if (len >= partLen) {
+ System.arraycopy(input, offset, buffer, bufferNdx, partLen);
+
+ transform(buffer, 0);
+
+ for (i = partLen; i + BLOCK_LENGTH - 1 < len; i += BLOCK_LENGTH)
+ transform(input, offset + i);
+ bufferNdx = 0;
+ }
+ // buffer remaining input
+ if (i < len)
+ System.arraycopy(input, offset + i, buffer, bufferNdx, len - i);
+ }
+
+ // own methods
+ //...........................................................................
+
+ /**
+ * Completes the hash computation by performing final operations such
+ * as padding. At the return of this engineDigest, the MD engine is
+ * reset.
+ *
+ * @return the array of bytes for the resulting hash value.
+ */
+ public byte[] engineDigest() {
+ // pad output to 56 mod 64; as RFC1320 puts it: congruent to 448 mod 512
+ int bufferNdx = (int) (count % BLOCK_LENGTH);
+ int padLen = (bufferNdx < 56) ? (56 - bufferNdx) : (120 - bufferNdx);
+
+ // padding is alwas binary 1 followed by binary 0s
+ byte[] tail = new byte[padLen + 8];
+ tail[0] = (byte) 0x80;
+
+ // append length before final transform:
+ // save number of bits, casting the long to an array of 8 bytes
+ // save low-order byte first.
+ for (int i = 0; i < 8; i++)
+ tail[padLen + i] = (byte) ((count * 8) >>> (8 * i));
+
+ engineUpdate(tail, 0, tail.length);
+
+ byte[] result = new byte[16];
+ // cast this MD4's context (array of 4 ints) into an array of 16 bytes.
+ for (int i = 0; i < 4; i++)
+ for (int j = 0; j < 4; j++)
+ result[i * 4 + j] = (byte) (context[i] >>> (8 * j));
+
+ // reset the engine
+ engineReset();
+ return result;
+ }
+
+ // The basic MD4 atomic functions.
+
+ /**
+ * MD4 basic transformation.
+ * <p>
+ * Transforms context based on 512 bits from input block starting
+ * from the offset'th byte.
+ *
+ * @param block input sub-array.
+ * @param offset starting position of sub-array.
+ */
+ private void transform(byte[] block, int offset) {
+
+ // encodes 64 bytes from input block into an array of 16 32-bit
+ // entities. Use A as a temp var.
+ for (int i = 0; i < 16; i++)
+ X[i] =
+ (block[offset++] & 0xFF) | (block[offset++] & 0xFF)
+ << 8 | (block[offset++] & 0xFF)
+ << 16 | (block[offset++] & 0xFF)
+ << 24;
+
+ int A = context[0];
+ int B = context[1];
+ int C = context[2];
+ int D = context[3];
+
+ A = FF(A, B, C, D, X[0], 3);
+ D = FF(D, A, B, C, X[1], 7);
+ C = FF(C, D, A, B, X[2], 11);
+ B = FF(B, C, D, A, X[3], 19);
+ A = FF(A, B, C, D, X[4], 3);
+ D = FF(D, A, B, C, X[5], 7);
+ C = FF(C, D, A, B, X[6], 11);
+ B = FF(B, C, D, A, X[7], 19);
+ A = FF(A, B, C, D, X[8], 3);
+ D = FF(D, A, B, C, X[9], 7);
+ C = FF(C, D, A, B, X[10], 11);
+ B = FF(B, C, D, A, X[11], 19);
+ A = FF(A, B, C, D, X[12], 3);
+ D = FF(D, A, B, C, X[13], 7);
+ C = FF(C, D, A, B, X[14], 11);
+ B = FF(B, C, D, A, X[15], 19);
+
+ A = GG(A, B, C, D, X[0], 3);
+ D = GG(D, A, B, C, X[4], 5);
+ C = GG(C, D, A, B, X[8], 9);
+ B = GG(B, C, D, A, X[12], 13);
+ A = GG(A, B, C, D, X[1], 3);
+ D = GG(D, A, B, C, X[5], 5);
+ C = GG(C, D, A, B, X[9], 9);
+ B = GG(B, C, D, A, X[13], 13);
+ A = GG(A, B, C, D, X[2], 3);
+ D = GG(D, A, B, C, X[6], 5);
+ C = GG(C, D, A, B, X[10], 9);
+ B = GG(B, C, D, A, X[14], 13);
+ A = GG(A, B, C, D, X[3], 3);
+ D = GG(D, A, B, C, X[7], 5);
+ C = GG(C, D, A, B, X[11], 9);
+ B = GG(B, C, D, A, X[15], 13);
+
+ A = HH(A, B, C, D, X[0], 3);
+ D = HH(D, A, B, C, X[8], 9);
+ C = HH(C, D, A, B, X[4], 11);
+ B = HH(B, C, D, A, X[12], 15);
+ A = HH(A, B, C, D, X[2], 3);
+ D = HH(D, A, B, C, X[10], 9);
+ C = HH(C, D, A, B, X[6], 11);
+ B = HH(B, C, D, A, X[14], 15);
+ A = HH(A, B, C, D, X[1], 3);
+ D = HH(D, A, B, C, X[9], 9);
+ C = HH(C, D, A, B, X[5], 11);
+ B = HH(B, C, D, A, X[13], 15);
+ A = HH(A, B, C, D, X[3], 3);
+ D = HH(D, A, B, C, X[11], 9);
+ C = HH(C, D, A, B, X[7], 11);
+ B = HH(B, C, D, A, X[15], 15);
+
+ context[0] += A;
+ context[1] += B;
+ context[2] += C;
+ context[3] += D;
+ }
+
+ private int FF(int a, int b, int c, int d, int x, int s) {
+ int t = a + ((b & c) | (~b & d)) + x;
+ return t << s | t >>> (32 - s);
+ }
+
+ private int GG(int a, int b, int c, int d, int x, int s) {
+ int t = a + ((b & (c | d)) | (c & d)) + x + 0x5A827999;
+ return t << s | t >>> (32 - s);
+ }
+
+ private int HH(int a, int b, int c, int d, int x, int s) {
+ int t = a + (b ^ c ^ d) + x + 0x6ED9EBA1;
+ return t << s | t >>> (32 - s);
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.qcommon;
+
+import lwjake2.Globals;
+import lwjake2.game.entity_state_t;
+import lwjake2.game.usercmd_t;
+import lwjake2.util.Lib;
+import lwjake2.util.Math3D;
+
+public class MSG extends Globals {
+
+ //
+ // writing functions
+ //
+
+ // 2k read buffer.
+ public static final byte[] readbuf = new byte[2048];
+
+ //ok.
+ public static void WriteChar(sizebuf_t sb, int c) {
+ sb.data[SZ.GetSpace(sb, 1)] = (byte) (c & 0xFF);
+ }
+
+ //ok.
+ public static void WriteChar(sizebuf_t sb, float c) {
+
+ WriteChar(sb, (int) c);
+ }
+
+ //ok.
+ public static void WriteByte(sizebuf_t sb, int c) {
+ sb.data[SZ.GetSpace(sb, 1)] = (byte) (c & 0xFF);
+ }
+
+ //ok.
+ public static void WriteByte(sizebuf_t sb, float c) {
+ WriteByte(sb, (int) c);
+ }
+
+ public static void WriteShort(sizebuf_t sb, int c) {
+ int i = SZ.GetSpace(sb, 2);
+ sb.data[i++] = (byte) (c & 0xff);
+ sb.data[i] = (byte) ((c >>> 8) & 0xFF);
+ }
+
+ //ok.
+ public static void WriteInt(sizebuf_t sb, int c) {
+ int i = SZ.GetSpace(sb, 4);
+ sb.data[i++] = (byte) ((c & 0xff));
+ sb.data[i++] = (byte) ((c >>> 8) & 0xff);
+ sb.data[i++] = (byte) ((c >>> 16) & 0xff);
+ sb.data[i++] = (byte) ((c >>> 24) & 0xff);
+ }
+
+ //ok.
+ public static void WriteLong(sizebuf_t sb, int c) {
+ WriteInt(sb, c);
+ }
+
+ // had a bug, now its ok.
+ public static void WriteString(sizebuf_t sb, String s) {
+ String x = s;
+
+ if (s == null)
+ x = "";
+
+ SZ.Write(sb, Lib.stringToBytes(x));
+ WriteByte(sb, 0);
+ //Com.dprintln("MSG.WriteString:" + s.replace('\0', '@'));
+ }
+
+ //ok.
+ public static void WriteString(sizebuf_t sb, byte s[]) {
+ WriteString(sb, new String(s).trim());
+ }
+
+ public static void WriteCoord(sizebuf_t sb, float f) {
+ WriteShort(sb, (int) (f * 8));
+ }
+
+ public static void WritePos(sizebuf_t sb, float[] pos) {
+ assert (pos.length == 3) : "vec3_t bug";
+ WriteShort(sb, (int) (pos[0] * 8));
+ WriteShort(sb, (int) (pos[1] * 8));
+ WriteShort(sb, (int) (pos[2] * 8));
+ }
+
+ public static void WriteAngle(sizebuf_t sb, float f) {
+ WriteByte(sb, (int) (f * 256 / 360) & 255);
+ }
+
+ public static void WriteAngle16(sizebuf_t sb, float f) {
+ WriteShort(sb, Math3D.angle2Short(f));
+ }
+
+ public static void WriteDeltaUsercmd(sizebuf_t buf, usercmd_t from,
+ usercmd_t cmd) {
+ int bits;
+
+ //
+ // send the movement message
+ //
+ bits = 0;
+ if (cmd.angles[0] != from.angles[0])
+ bits |= CM_ANGLE1;
+ if (cmd.angles[1] != from.angles[1])
+ bits |= CM_ANGLE2;
+ if (cmd.angles[2] != from.angles[2])
+ bits |= CM_ANGLE3;
+ if (cmd.forwardmove != from.forwardmove)
+ bits |= CM_FORWARD;
+ if (cmd.sidemove != from.sidemove)
+ bits |= CM_SIDE;
+ if (cmd.upmove != from.upmove)
+ bits |= CM_UP;
+ if (cmd.buttons != from.buttons)
+ bits |= CM_BUTTONS;
+ if (cmd.impulse != from.impulse)
+ bits |= CM_IMPULSE;
+
+ WriteByte(buf, bits);
+
+ if ((bits & CM_ANGLE1) != 0)
+ WriteShort(buf, cmd.angles[0]);
+ if ((bits & CM_ANGLE2) != 0)
+ WriteShort(buf, cmd.angles[1]);
+ if ((bits & CM_ANGLE3) != 0)
+ WriteShort(buf, cmd.angles[2]);
+
+ if ((bits & CM_FORWARD) != 0)
+ WriteShort(buf, cmd.forwardmove);
+ if ((bits & CM_SIDE) != 0)
+ WriteShort(buf, cmd.sidemove);
+ if ((bits & CM_UP) != 0)
+ WriteShort(buf, cmd.upmove);
+
+ if ((bits & CM_BUTTONS) != 0)
+ WriteByte(buf, cmd.buttons);
+ if ((bits & CM_IMPULSE) != 0)
+ WriteByte(buf, cmd.impulse);
+
+ WriteByte(buf, cmd.msec);
+ WriteByte(buf, cmd.lightlevel);
+ }
+
+ //should be ok.
+ public static void WriteDir(sizebuf_t sb, float[] dir) {
+ int i, best;
+ float d, bestd;
+
+ if (dir == null) {
+ WriteByte(sb, 0);
+ return;
+ }
+
+ bestd = 0;
+ best = 0;
+ for (i = 0; i < NUMVERTEXNORMALS; i++) {
+ d = Math3D.dotProduct(dir, bytedirs[i]);
+ if (d > bestd) {
+ bestd = d;
+ best = i;
+ }
+ }
+ WriteByte(sb, best);
+ }
+
+ //should be ok.
+ public static void ReadDir(sizebuf_t sb, float[] dir) {
+ int b;
+
+ b = ReadByte(sb);
+ if (b >= NUMVERTEXNORMALS)
+ Com.Error(ERR_DROP, "MSF_ReadDir: out of range");
+ Math3D.vectorCopy(bytedirs[b], dir);
+ }
+
+ //============================================================
+
+ //
+ // reading functions
+ //
+
+ /*
+ * ================== WriteDeltaEntity
+ *
+ * Writes part of a packetentities message. Can delta from either a baseline
+ * or a previous packet_entity ==================
+ */
+ public static void WriteDeltaEntity(entity_state_t from, entity_state_t to,
+ sizebuf_t msg, boolean force, boolean newentity) {
+ int bits;
+
+ if (0 == to.number)
+ Com.Error(ERR_FATAL, "Unset entity number");
+ if (to.number >= MAX_EDICTS)
+ Com.Error(ERR_FATAL, "Entity number >= MAX_EDICTS");
+
+ // send an update
+ bits = 0;
+
+ if (to.number >= 256)
+ bits |= U_NUMBER16; // number8 is implicit otherwise
+
+ if (to.origin[0] != from.origin[0])
+ bits |= U_ORIGIN1;
+ if (to.origin[1] != from.origin[1])
+ bits |= U_ORIGIN2;
+ if (to.origin[2] != from.origin[2])
+ bits |= U_ORIGIN3;
+
+ if (to.angles[0] != from.angles[0])
+ bits |= U_ANGLE1;
+ if (to.angles[1] != from.angles[1])
+ bits |= U_ANGLE2;
+ if (to.angles[2] != from.angles[2])
+ bits |= U_ANGLE3;
+
+ if (to.skinnum != from.skinnum) {
+ if (to.skinnum < 256)
+ bits |= U_SKIN8;
+ else if (to.skinnum < 0x10000)
+ bits |= U_SKIN16;
+ else
+ bits |= (U_SKIN8 | U_SKIN16);
+ }
+
+ if (to.frame != from.frame) {
+ if (to.frame < 256)
+ bits |= U_FRAME8;
+ else
+ bits |= U_FRAME16;
+ }
+
+ if (to.effects != from.effects) {
+ if (to.effects < 256)
+ bits |= U_EFFECTS8;
+ else if (to.effects < 0x8000)
+ bits |= U_EFFECTS16;
+ else
+ bits |= U_EFFECTS8 | U_EFFECTS16;
+ }
+
+ if (to.renderfx != from.renderfx) {
+ if (to.renderfx < 256)
+ bits |= U_RENDERFX8;
+ else if (to.renderfx < 0x8000)
+ bits |= U_RENDERFX16;
+ else
+ bits |= U_RENDERFX8 | U_RENDERFX16;
+ }
+
+ if (to.solid != from.solid)
+ bits |= U_SOLID;
+
+ // event is not delta compressed, just 0 compressed
+ if (to.event != 0)
+ bits |= U_EVENT;
+
+ if (to.modelindex != from.modelindex)
+ bits |= U_MODEL;
+ if (to.modelindex2 != from.modelindex2)
+ bits |= U_MODEL2;
+ if (to.modelindex3 != from.modelindex3)
+ bits |= U_MODEL3;
+ if (to.modelindex4 != from.modelindex4)
+ bits |= U_MODEL4;
+
+ if (to.sound != from.sound)
+ bits |= U_SOUND;
+
+ //
+ // write the message
+ //
+ if (bits == 0 && !force)
+ return; // nothing to send!
+
+ //----------
+
+ if ((bits & 0xff000000) != 0)
+ bits |= U_MOREBITS3 | U_MOREBITS2 | U_MOREBITS1;
+ else if ((bits & 0x00ff0000) != 0)
+ bits |= U_MOREBITS2 | U_MOREBITS1;
+ else if ((bits & 0x0000ff00) != 0)
+ bits |= U_MOREBITS1;
+
+ WriteByte(msg, bits & 255);
+
+ if ((bits & 0xff000000) != 0) {
+ WriteByte(msg, (bits >>> 8) & 255);
+ WriteByte(msg, (bits >>> 16) & 255);
+ WriteByte(msg, (bits >>> 24) & 255);
+ } else if ((bits & 0x00ff0000) != 0) {
+ WriteByte(msg, (bits >>> 8) & 255);
+ WriteByte(msg, (bits >>> 16) & 255);
+ } else if ((bits & 0x0000ff00) != 0) {
+ WriteByte(msg, (bits >>> 8) & 255);
+ }
+
+ //----------
+
+ if ((bits & U_NUMBER16) != 0)
+ WriteShort(msg, to.number);
+ else
+ WriteByte(msg, to.number);
+
+ if ((bits & U_MODEL) != 0)
+ WriteByte(msg, to.modelindex);
+ if ((bits & U_MODEL2) != 0)
+ WriteByte(msg, to.modelindex2);
+ if ((bits & U_MODEL3) != 0)
+ WriteByte(msg, to.modelindex3);
+ if ((bits & U_MODEL4) != 0)
+ WriteByte(msg, to.modelindex4);
+
+ if ((bits & U_FRAME8) != 0)
+ WriteByte(msg, to.frame);
+ if ((bits & U_FRAME16) != 0)
+ WriteShort(msg, to.frame);
+
+ if ((bits & U_SKIN8) != 0 && (bits & U_SKIN16) != 0) //used for laser
+ // colors
+ WriteInt(msg, to.skinnum);
+ else if ((bits & U_SKIN8) != 0)
+ WriteByte(msg, to.skinnum);
+ else if ((bits & U_SKIN16) != 0)
+ WriteShort(msg, to.skinnum);
+
+ if ((bits & (U_EFFECTS8 | U_EFFECTS16)) == (U_EFFECTS8 | U_EFFECTS16))
+ WriteInt(msg, to.effects);
+ else if ((bits & U_EFFECTS8) != 0)
+ WriteByte(msg, to.effects);
+ else if ((bits & U_EFFECTS16) != 0)
+ WriteShort(msg, to.effects);
+
+ if ((bits & (U_RENDERFX8 | U_RENDERFX16)) == (U_RENDERFX8 | U_RENDERFX16))
+ WriteInt(msg, to.renderfx);
+ else if ((bits & U_RENDERFX8) != 0)
+ WriteByte(msg, to.renderfx);
+ else if ((bits & U_RENDERFX16) != 0)
+ WriteShort(msg, to.renderfx);
+
+ if ((bits & U_ORIGIN1) != 0)
+ WriteCoord(msg, to.origin[0]);
+ if ((bits & U_ORIGIN2) != 0)
+ WriteCoord(msg, to.origin[1]);
+ if ((bits & U_ORIGIN3) != 0)
+ WriteCoord(msg, to.origin[2]);
+
+ if ((bits & U_ANGLE1) != 0)
+ WriteAngle(msg, to.angles[0]);
+ if ((bits & U_ANGLE2) != 0)
+ WriteAngle(msg, to.angles[1]);
+ if ((bits & U_ANGLE3) != 0)
+ WriteAngle(msg, to.angles[2]);
+
+ if ((bits & U_OLDORIGIN) != 0) {
+ WriteCoord(msg, to.old_origin[0]);
+ WriteCoord(msg, to.old_origin[1]);
+ WriteCoord(msg, to.old_origin[2]);
+ }
+
+ if ((bits & U_SOUND) != 0)
+ WriteByte(msg, to.sound);
+ if ((bits & U_EVENT) != 0)
+ WriteByte(msg, to.event);
+ if ((bits & U_SOLID) != 0)
+ WriteShort(msg, to.solid);
+ }
+
+ public static void BeginReading(sizebuf_t msg) {
+ msg.readcount = 0;
+ }
+
+ // returns -1 if no more characters are available, but also [-128 , 127]
+ public static int ReadChar(sizebuf_t msg_read) {
+ int c;
+
+ if (msg_read.readcount + 1 > msg_read.cursize)
+ c = -1;
+ else
+ c = msg_read.data[msg_read.readcount];
+ msg_read.readcount++;
+ // kickangles bugfix (rst)
+ return c;
+ }
+
+ public static int ReadByte(sizebuf_t msg_read) {
+ int c;
+
+ if (msg_read.readcount + 1 > msg_read.cursize)
+ c = -1;
+ else
+ c = msg_read.data[msg_read.readcount] & 0xff;
+
+ msg_read.readcount++;
+
+ return c;
+ }
+
+ public static short ReadShort(sizebuf_t msg_read) {
+ int c;
+
+ if (msg_read.readcount + 2 > msg_read.cursize)
+ c = -1;
+ else
+ c = (short) ((msg_read.data[msg_read.readcount] & 0xff) + (msg_read.data[msg_read.readcount + 1] << 8));
+
+ msg_read.readcount += 2;
+
+ return (short) c;
+ }
+
+ public static int ReadLong(sizebuf_t msg_read) {
+ int c;
+
+ if (msg_read.readcount + 4 > msg_read.cursize) {
+ Com.Printf("buffer underrun in ReadLong!");
+ c = -1;
+ } else
+ c = (msg_read.data[msg_read.readcount] & 0xff)
+ | ((msg_read.data[msg_read.readcount + 1] & 0xff) << 8)
+ | ((msg_read.data[msg_read.readcount + 2] & 0xff) << 16)
+ | ((msg_read.data[msg_read.readcount + 3] & 0xff) << 24);
+
+ msg_read.readcount += 4;
+
+ return c;
+ }
+
+ public static String ReadString(sizebuf_t msg_read) {
+
+ byte c;
+ int l = 0;
+ do {
+ c = (byte) ReadByte(msg_read);
+ if (c == -1 || c == 0)
+ break;
+
+ readbuf[l] = c;
+ l++;
+ } while (l < 2047);
+
+ // Com.dprintln("MSG.ReadString:[" + ret + "]");
+ return new String(readbuf, 0, l);
+ }
+
+ public static String ReadStringLine(sizebuf_t msg_read) {
+
+ int l;
+ byte c;
+
+ l = 0;
+ do {
+ c = (byte) ReadChar(msg_read);
+ if (c == -1 || c == 0 || c == 0x0a)
+ break;
+ readbuf[l] = c;
+ l++;
+ } while (l < 2047);
+
+ String ret = new String(readbuf, 0, l).trim();
+ Com.dprintln("MSG.ReadStringLine:[" + ret.replace('\0', '@') + "]");
+ return ret;
+ }
+
+ public static float ReadCoord(sizebuf_t msg_read) {
+ return ReadShort(msg_read) * (1.0f / 8);
+ }
+
+ public static void ReadPos(sizebuf_t msg_read, float pos[]) {
+ assert (pos.length == 3) : "vec3_t bug";
+ pos[0] = ReadShort(msg_read) * (1.0f / 8);
+ pos[1] = ReadShort(msg_read) * (1.0f / 8);
+ pos[2] = ReadShort(msg_read) * (1.0f / 8);
+ }
+
+ public static float ReadAngle(sizebuf_t msg_read) {
+ return ReadChar(msg_read) * (360.0f / 256);
+ }
+
+ public static float ReadAngle16(sizebuf_t msg_read) {
+ return Math3D.short2Angle(ReadShort(msg_read));
+ }
+
+ public static void ReadDeltaUsercmd(sizebuf_t msg_read, usercmd_t from,
+ usercmd_t move) {
+ int bits;
+
+ //memcpy(move, from, sizeof(* move));
+ // IMPORTANT!! copy without new
+ move.set(from);
+ bits = ReadByte(msg_read);
+
+ // read current angles
+ if ((bits & CM_ANGLE1) != 0)
+ move.angles[0] = ReadShort(msg_read);
+ if ((bits & CM_ANGLE2) != 0)
+ move.angles[1] = ReadShort(msg_read);
+ if ((bits & CM_ANGLE3) != 0)
+ move.angles[2] = ReadShort(msg_read);
+
+ // read movement
+ if ((bits & CM_FORWARD) != 0)
+ move.forwardmove = ReadShort(msg_read);
+ if ((bits & CM_SIDE) != 0)
+ move.sidemove = ReadShort(msg_read);
+ if ((bits & CM_UP) != 0)
+ move.upmove = ReadShort(msg_read);
+
+ // read buttons
+ if ((bits & CM_BUTTONS) != 0)
+ move.buttons = (byte) ReadByte(msg_read);
+
+ if ((bits & CM_IMPULSE) != 0)
+ move.impulse = (byte) ReadByte(msg_read);
+
+ // read time to run command
+ move.msec = (byte) ReadByte(msg_read);
+
+ // read the light level
+ move.lightlevel = (byte) ReadByte(msg_read);
+
+ }
+
+ public static void ReadData(sizebuf_t msg_read, byte data[], int len) {
+ for (int i = 0; i < len; i++)
+ data[i] = (byte) ReadByte(msg_read);
+ }
+
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.qcommon;
+
+import lwjake2.Defines;
+import lwjake2.sys.NET;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+public class NetadrT {
+
+ public int type;
+
+ public int port;
+
+ public byte ip[];
+
+ public NetadrT() {
+ this.type = Defines.NA_LOOPBACK;
+ this.port = 0; // any
+ try {
+ // localhost / 127.0.0.1
+ this.ip = InetAddress.getByName(null).getAddress();
+ } catch (UnknownHostException ignored) {
+ }
+ }
+
+ public InetAddress getInetAddress() throws UnknownHostException {
+ switch (type) {
+ case Defines.NA_BROADCAST:
+ return InetAddress.getByName("255.255.255.255");
+ case Defines.NA_LOOPBACK:
+ // localhost / 127.0.0.1
+ return InetAddress.getByName(null);
+ case Defines.NA_IP:
+ return InetAddress.getByAddress(ip);
+ default:
+ return null;
+ }
+ }
+
+ public void set(NetadrT from) {
+ type = from.type;
+ port = from.port;
+ ip[0] = from.ip[0];
+ ip[1] = from.ip[1];
+ ip[2] = from.ip[2];
+ ip[3] = from.ip[3];
+ }
+
+ public String toString() {
+ return (type == Defines.NA_LOOPBACK) ? "loopback" : NET
+ .AdrToString(this);
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.qcommon;
+
+import lwjake2.Defines;
+import lwjake2.Globals;
+import lwjake2.game.CvarT;
+import lwjake2.server.Server;
+import lwjake2.sys.NET;
+import lwjake2.sys.Timer;
+import lwjake2.util.Lib;
+
+/**
+ * Netchan
+ */
+public final class Netchan extends Server {
+
+ /*
+ *
+ * packet header ------------- 31 sequence 1 does this message contains a
+ * reliable payload 31 acknowledge sequence 1 acknowledge receipt of
+ * even/odd message 16 qport
+ *
+ * The remote connection never knows if it missed a reliable message, the
+ * local side detects that it has been dropped by seeing a sequence
+ * acknowledge higher thatn the last reliable sequence, but without the
+ * correct evon/odd bit for the reliable set.
+ *
+ * If the sender notices that a reliable message has been dropped, it will
+ * be retransmitted. It will not be retransmitted again until a message
+ * after the retransmit has been acknowledged and the reliable still failed
+ * to get there.
+ *
+ * if the sequence number is -1, the packet should be handled without a
+ * netcon
+ *
+ * The reliable message can be added to at any time by doing MSG_Write*
+ * (&netchan.message, <data>).
+ *
+ * If the message buffer is overflowed, either by a single message, or by
+ * multiple frames worth piling up while the last reliable transmit goes
+ * unacknowledged, the netchan signals a fatal error.
+ *
+ * Reliable messages are always placed first in a packet, then the
+ * unreliable message is included if there is sufficient room.
+ *
+ * To the receiver, there is no distinction between the reliable and
+ * unreliable parts of the message, they are just processed out as a single
+ * larger message.
+ *
+ * Illogical packet sequence numbers cause the packet to be dropped, but do
+ * not kill the connection. This, combined with the tight window of valid
+ * reliable acknowledgement numbers provides protection against malicious
+ * address spoofing.
+ *
+ *
+ * The qport field is a workaround for bad address translating routers that
+ * sometimes remap the client's source port on a packet during gameplay.
+ *
+ * If the base part of the net address matches and the qport matches, then
+ * the channel matches even if the IP port differs. The IP port should be
+ * updated to the new value before sending out any replies.
+ *
+ *
+ * If there is no information that needs to be transfered on a given frame,
+ * such as during the connection stage while waiting for the client to load,
+ * then a packet only needs to be delivered if there is something in the
+ * unacknowledged reliable
+ */
+
+ private static final byte send_buf[] = new byte[Defines.MAX_MSGLEN];
+ private static final sizebuf_t send = new sizebuf_t();
+ public static CvarT showpackets;
+ public static CvarT showdrop;
+ public static CvarT qport;
+ //public static NetadrT net_from = new NetadrT();
+ public static sizebuf_t net_message = new sizebuf_t();
+ public static byte net_message_buffer[] = new byte[Defines.MAX_MSGLEN];
+
+ /*
+ * =============== Netchan_Init
+ *
+ * ===============
+ */
+ //ok.
+ public static void Netchan_Init() {
+ long port;
+
+ // pick a port value that should be nice and random
+ port = Timer.getCurrentTimeMillis() & 0xffff;
+
+ showpackets = Cvar.get("showpackets", "0", 0);
+ showdrop = Cvar.get("showdrop", "0", 0);
+ qport = Cvar.get("qport", "" + port, Defines.CVAR_NOSET);
+ }
+
+ /*
+ * =============== Netchan_OutOfBand
+ *
+ * Sends an out-of-band datagram ================
+ */
+ //ok.
+ public static void Netchan_OutOfBand(int net_socket, NetadrT adr,
+ int length, byte data[]) {
+
+ // write the packet header
+ SZ.Init(send, send_buf, Defines.MAX_MSGLEN);
+
+ MSG.WriteInt(send, -1); // -1 sequence means out of band
+ SZ.Write(send, data, length);
+
+ // send the datagram
+ NET.SendPacket(net_socket, send.cursize, send.data, adr);
+ }
+
+ public static void OutOfBandPrint(int net_socket, NetadrT adr, String s) {
+ Netchan_OutOfBand(net_socket, adr, s.length(), Lib.stringToBytes(s));
+ }
+
+ /*
+ * ============== Netchan_Setup
+ *
+ * called to open a channel to a remote system ==============
+ */
+ public static void Setup(int sock, netchan_t chan, NetadrT adr, int qport) {
+ //memset (chan, 0, sizeof(*chan));
+
+ chan.clear();
+ chan.sock = sock;
+ chan.remote_address.set(adr);
+ chan.qport = qport;
+ chan.last_received = Globals.curtime;
+ chan.incoming_sequence = 0;
+ chan.outgoing_sequence = 1;
+
+ SZ.Init(chan.message, chan.message_buf, chan.message_buf.length);
+ chan.message.allowoverflow = true;
+ }
+
+ /*
+ * =============== Netchan_CanReliable
+ *
+ * Returns true if the last reliable message has acked ================
+ */
+ public static boolean Netchan_CanReliable(netchan_t chan) {
+ return chan.reliable_length == 0;
+ }
+
+ // das ist richtig !!!
+ public static boolean Netchan_NeedReliable(netchan_t chan) {
+ boolean send_reliable;
+
+ // if the remote side dropped the last reliable message, resend it
+
+ send_reliable = chan.incoming_acknowledged > chan.last_reliable_sequence
+ && chan.incoming_reliable_acknowledged != chan.reliable_sequence;
+
+ // if the reliable transmit buffer is empty, copy the current message
+ // out
+ if (0 == chan.reliable_length && chan.message.cursize != 0) {
+ send_reliable = true;
+ }
+
+ return send_reliable;
+ }
+
+ // private static final byte send_buf[] = new byte[Defines.MAX_MSGLEN];
+ // private static final sizebuf_t send = new sizebuf_t();
+ /*
+ * =============== Netchan_Transmit
+ *
+ * tries to send an unreliable message to a connection, and handles the
+ * transmition / retransmition of the reliable messages.
+ *
+ * A 0 length will still generate a packet and deal with the reliable
+ * messages. ================
+ */
+ public static void Transmit(netchan_t chan, int length, byte data[]) {
+ int send_reliable;
+ int w1, w2;
+
+ // check for message overflow
+ if (chan.message.overflowed) {
+ chan.fatal_error = true;
+ Com.Printf(NET.AdrToString(chan.remote_address)
+ + ":Outgoing message overflow\n");
+ return;
+ }
+
+ send_reliable = Netchan_NeedReliable(chan) ? 1 : 0;
+
+ if (chan.reliable_length == 0 && chan.message.cursize != 0) {
+ System.arraycopy(chan.message_buf, 0, chan.reliable_buf, 0,
+ chan.message.cursize);
+ chan.reliable_length = chan.message.cursize;
+ chan.message.cursize = 0;
+ chan.reliable_sequence ^= 1;
+ }
+
+ // write the packet header
+ SZ.Init(send, send_buf, send_buf.length);
+
+ w1 = (chan.outgoing_sequence & ~(1 << 31)) | (send_reliable << 31);
+ w2 = (chan.incoming_sequence & ~(1 << 31))
+ | (chan.incoming_reliable_sequence << 31);
+
+ chan.outgoing_sequence++;
+ chan.last_sent = Globals.curtime;
+
+ MSG.WriteInt(send, w1);
+ MSG.WriteInt(send, w2);
+
+ // send the qport if we are a client
+ if (chan.sock == Defines.NS_CLIENT)
+ MSG.WriteShort(send, (int) qport.value);
+
+ // copy the reliable message to the packet first
+ if (send_reliable != 0) {
+ SZ.Write(send, chan.reliable_buf, chan.reliable_length);
+ chan.last_reliable_sequence = chan.outgoing_sequence;
+ }
+
+ // add the unreliable part if space is available
+ if (send.maxsize - send.cursize >= length)
+ SZ.Write(send, data, length);
+ else
+ Com.Printf("Netchan_Transmit: dumped unreliable\n");
+
+ // send the datagram
+ NET.SendPacket(chan.sock, send.cursize, send.data, chan.remote_address);
+
+ if (showpackets.value != 0) {
+ if (send_reliable != 0)
+ Com.Printf(
+ //"send %4i : s=%i reliable=%i ack=%i rack=%i\n"
+ "send " + send.cursize + " : s="
+ + (chan.outgoing_sequence - 1) + " reliable="
+ + chan.reliable_sequence + " ack="
+ + chan.incoming_sequence + " rack="
+ + chan.incoming_reliable_sequence + "\n");
+ else
+ Com.Printf(
+ //"send %4i : s=%i ack=%i rack=%i\n"
+ "send " + send.cursize + " : s="
+ + (chan.outgoing_sequence - 1) + " ack="
+ + chan.incoming_sequence + " rack="
+ + chan.incoming_reliable_sequence + "\n");
+ }
+ }
+
+ /*
+ * ================= Netchan_Process
+ *
+ * called when the current net_message is from remote_address modifies
+ * net_message so that it points to the packet payload =================
+ */
+ public static boolean Process(netchan_t chan, sizebuf_t msg) {
+ int sequence, sequence_ack;
+ int reliable_ack, reliable_message;
+
+ // get sequence numbers
+ MSG.BeginReading(msg);
+ sequence = MSG.ReadLong(msg);
+ sequence_ack = MSG.ReadLong(msg);
+
+ // read the qport if we are a server
+ if (chan.sock == Defines.NS_SERVER)
+ MSG.ReadShort(msg);
+
+ // achtung unsigned int
+ reliable_message = sequence >>> 31;
+ reliable_ack = sequence_ack >>> 31;
+
+ sequence &= ~(1 << 31);
+ sequence_ack &= ~(1 << 31);
+
+ if (showpackets.value != 0) {
+ if (reliable_message != 0)
+ Com.Printf(
+ //"recv %4i : s=%i reliable=%i ack=%i rack=%i\n"
+ "recv " + msg.cursize + " : s=" + sequence
+ + " reliable="
+ + (chan.incoming_reliable_sequence ^ 1)
+ + " ack=" + sequence_ack + " rack="
+ + reliable_ack + "\n");
+ else
+ Com
+ .Printf(
+ //"recv %4i : s=%i ack=%i rack=%i\n"
+ "recv " + msg.cursize + " : s=" + sequence + " ack="
+ + sequence_ack + " rack=" + reliable_ack + "\n");
+ }
+
+ //
+ // discard stale or duplicated packets
+ //
+ if (sequence <= chan.incoming_sequence) {
+ if (showdrop.value != 0)
+ Com.Printf(NET.AdrToString(chan.remote_address)
+ + ":Out of order packet " + sequence + " at "
+ + chan.incoming_sequence + "\n");
+ return false;
+ }
+
+ //
+ // dropped packets don't keep the message from being used
+ //
+ chan.dropped = sequence - (chan.incoming_sequence + 1);
+ if (chan.dropped > 0) {
+ if (showdrop.value != 0)
+ Com.Printf(NET.AdrToString(chan.remote_address) + ":Dropped "
+ + chan.dropped + " packets at " + sequence + "\n");
+ }
+
+ //
+ // if the current outgoing reliable message has been acknowledged
+ // clear the buffer to make way for the next
+ //
+ if (reliable_ack == chan.reliable_sequence)
+ chan.reliable_length = 0; // it has been received
+
+ //
+ // if this message contains a reliable message, bump
+ // incoming_reliable_sequence
+ //
+ chan.incoming_sequence = sequence;
+ chan.incoming_acknowledged = sequence_ack;
+ chan.incoming_reliable_acknowledged = reliable_ack;
+ if (reliable_message != 0) {
+ chan.incoming_reliable_sequence ^= 1;
+ }
+
+ //
+ // the message can now be read from the current message pointer
+ //
+ chan.last_received = Globals.curtime;
+
+ return true;
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.qcommon;
+
+import lwjake2.Defines;
+import lwjake2.Globals;
+import lwjake2.game.Move;
+import lwjake2.game.csurface_t;
+import lwjake2.game.trace_t;
+import lwjake2.server.SV;
+import lwjake2.util.Math3D;
+
+public class PMove {
+
+ // all of the locals will be zeroed before each
+ // pmove, just to make damn sure we don't have
+ // any differences when running on client or server
+
+ public static final pml_t pml = new pml_t();
+ // movement parameters
+ public static final float pm_stopspeed = 100;
+ public static final float pm_maxspeed = 300;
+ public static final float pm_duckspeed = 100;
+ public static final float pm_accelerate = 10;
+ public static final float pm_wateraccelerate = 10;
+ public static final float pm_friction = 6;
+ public static final float pm_waterfriction = 1;
+ public static final float pm_waterspeed = 400;
+ // try all single bits first
+ public static final int[] jitterbits = {0, 4, 1, 2, 3, 5, 6, 7};
+ public static final int[] offset = {0, -1, 1};
+ static final float[][] planes = new float[SV.MAX_CLIP_PLANES][3];
+ public static Move pm;
+ public static float pm_airaccelerate = 0;
+
+ /**
+ * Slide off of the impacting object returns the blocked flags (1 = floor, 2 = step / wall)
+ */
+ public static void PM_ClipVelocity(float[] in, float[] normal, float[] out, float overbounce) {
+ float backoff;
+ float change;
+ int i;
+
+ backoff = Math3D.dotProduct(in, normal) * overbounce;
+
+ for (i = 0; i < 3; i++) {
+ change = normal[i] * backoff;
+ out[i] = in[i] - change;
+ if (out[i] > -Defines.MOVE_STOP_EPSILON
+ && out[i] < Defines.MOVE_STOP_EPSILON)
+ out[i] = 0;
+ }
+ }
+
+ public static void PM_StepSlideMove_() {
+ int bumpcount, numbumps;
+ float[] dir = {0, 0, 0};
+ float d;
+ int numplanes;
+
+ float[] primal_velocity = {0, 0, 0};
+ int i, j;
+ trace_t trace;
+ float[] end = {0, 0, 0};
+ float time_left;
+
+ numbumps = 4;
+
+ Math3D.vectorCopy(pml.velocity, primal_velocity);
+ numplanes = 0;
+
+ time_left = pml.frametime;
+
+ for (bumpcount = 0; bumpcount < numbumps; bumpcount++) {
+ for (i = 0; i < 3; i++)
+ end[i] = pml.origin[i] + time_left
+ * pml.velocity[i];
+
+ trace = pm.trace.trace(pml.origin, pm.mins,
+ pm.maxs, end);
+
+ if (trace.allsolid) { // entity is trapped in another solid
+ pml.velocity[2] = 0; // don't build up falling damage
+ return;
+ }
+
+ if (trace.fraction > 0) { // actually covered some distance
+ Math3D.vectorCopy(trace.endpos, pml.origin);
+ numplanes = 0;
+ }
+
+ if (trace.fraction == 1)
+ break; // moved the entire distance
+
+ // save entity for contact
+ if (pm.numtouch < Defines.MAXTOUCH && trace.ent != null) {
+ pm.touchents[pm.numtouch] = trace.ent;
+ pm.numtouch++;
+ }
+
+ time_left -= time_left * trace.fraction;
+
+ // slide along this plane
+ if (numplanes >= SV.MAX_CLIP_PLANES) {
+ // this shouldn't really happen
+ Math3D.vectorCopy(Globals.vec3_origin, pml.velocity);
+ break;
+ }
+
+ Math3D.vectorCopy(trace.plane.normal, planes[numplanes]);
+ numplanes++;
+
+ // modify original_velocity so it parallels all of the clip planes
+ for (i = 0; i < numplanes; i++) {
+ PM_ClipVelocity(pml.velocity, planes[i],
+ pml.velocity, 1.01f);
+ for (j = 0; j < numplanes; j++)
+ if (j != i) {
+ if (Math3D.dotProduct(pml.velocity, planes[j]) < 0)
+ break; // not ok
+ }
+ if (j == numplanes)
+ break;
+ }
+
+ if (i != numplanes) {
+ // go along this plane
+ } else {
+ // go along the crease
+ if (numplanes != 2) {
+ // Com.printf("clip velocity, numplanes == " + numplanes + "\n");
+ Math3D.vectorCopy(Globals.vec3_origin, pml.velocity);
+ break;
+ }
+ Math3D.crossProduct(planes[0], planes[1], dir);
+ d = Math3D.dotProduct(dir, pml.velocity);
+ Math3D.vectorScale(dir, d, pml.velocity);
+ }
+
+
+ // if velocity is against the original velocity, stop dead
+ // to avoid tiny occilations in sloping corners
+ if (Math3D.dotProduct(pml.velocity, primal_velocity) <= 0) {
+ Math3D.vectorCopy(Globals.vec3_origin, pml.velocity);
+ break;
+ }
+ }
+
+ if (pm.s.pm_time != 0) {
+ Math3D.vectorCopy(primal_velocity, pml.velocity);
+ }
+ }
+
+ /**
+ * Each intersection will try to step over the obstruction instead of
+ * sliding along it.
+ * <p>
+ * Returns a new origin, velocity, and contact entity.
+ * Does not modify any world state?
+ */
+ public static void PM_StepSlideMove() {
+ float[] start_o = {0, 0, 0}, start_v = {0, 0, 0};
+ float[] down_o = {0, 0, 0}, down_v = {0, 0, 0};
+ trace_t trace;
+ float down_dist, up_dist;
+ // float [] delta;
+ float[] up = {0, 0, 0}, down = {0, 0, 0};
+
+ Math3D.vectorCopy(pml.origin, start_o);
+ Math3D.vectorCopy(pml.velocity, start_v);
+
+ PM_StepSlideMove_();
+
+ Math3D.vectorCopy(pml.origin, down_o);
+ Math3D.vectorCopy(pml.velocity, down_v);
+
+ Math3D.vectorCopy(start_o, up);
+ up[2] += Defines.STEPSIZE;
+
+ trace = pm.trace.trace(up, pm.mins, pm.maxs, up);
+ if (trace.allsolid)
+ return; // can't step up
+
+ // try sliding above
+ Math3D.vectorCopy(up, pml.origin);
+ Math3D.vectorCopy(start_v, pml.velocity);
+
+ PM_StepSlideMove_();
+
+ // push down the final amount
+ Math3D.vectorCopy(pml.origin, down);
+ down[2] -= Defines.STEPSIZE;
+ trace = pm.trace.trace(pml.origin, pm.mins,
+ pm.maxs, down);
+ if (!trace.allsolid) {
+ Math3D.vectorCopy(trace.endpos, pml.origin);
+ }
+
+ Math3D.vectorCopy(pml.origin, up);
+
+ // decide which one went farther
+ down_dist = (down_o[0] - start_o[0]) * (down_o[0] - start_o[0])
+ + (down_o[1] - start_o[1]) * (down_o[1] - start_o[1]);
+ up_dist = (up[0] - start_o[0]) * (up[0] - start_o[0])
+ + (up[1] - start_o[1]) * (up[1] - start_o[1]);
+
+ if (down_dist > up_dist || trace.plane.normal[2] < Defines.MIN_STEP_NORMAL) {
+ Math3D.vectorCopy(down_o, pml.origin);
+ Math3D.vectorCopy(down_v, pml.velocity);
+ return;
+ }
+ //!! Special case
+ // if we were walking along a plane, then we need to copy the Z over
+ pml.velocity[2] = down_v[2];
+ }
+
+ /**
+ * Handles both ground friction and water friction.
+ */
+ public static void PM_Friction() {
+ float vel[];
+ float speed, newspeed, control;
+ float friction;
+ float drop;
+
+ vel = pml.velocity;
+
+ speed = (float) (Math.sqrt(vel[0] * vel[0] + vel[1] * vel[1] + vel[2] * vel[2]));
+ if (speed < 1) {
+ vel[0] = 0;
+ vel[1] = 0;
+ return;
+ }
+
+ drop = 0;
+
+ // apply ground friction
+ if ((pm.groundentity != null && pml.groundsurface != null &&
+ 0 == (pml.groundsurface.flags & Defines.SURF_SLICK))
+ || (pml.ladder)) {
+ friction = pm_friction;
+ control = speed < pm_stopspeed ? pm_stopspeed : speed;
+ drop += control * friction * pml.frametime;
+ }
+
+ // apply water friction
+ if (pm.waterlevel != 0 && !pml.ladder)
+ drop += speed * pm_waterfriction * pm.waterlevel
+ * pml.frametime;
+
+ // scale the velocity
+ newspeed = speed - drop;
+ if (newspeed < 0) {
+ newspeed = 0;
+ }
+ newspeed /= speed;
+
+ vel[0] = vel[0] * newspeed;
+ vel[1] = vel[1] * newspeed;
+ vel[2] = vel[2] * newspeed;
+ }
+
+ /**
+ * Handles user intended acceleration.
+ */
+ public static void PM_Accelerate(float[] wishdir, float wishspeed,
+ float accel) {
+ int i;
+ float addspeed, accelspeed, currentspeed;
+
+ currentspeed = Math3D.dotProduct(pml.velocity, wishdir);
+ addspeed = wishspeed - currentspeed;
+ if (addspeed <= 0)
+ return;
+ accelspeed = accel * pml.frametime * wishspeed;
+ if (accelspeed > addspeed)
+ accelspeed = addspeed;
+
+ for (i = 0; i < 3; i++)
+ pml.velocity[i] += accelspeed * wishdir[i];
+ }
+
+ /**
+ * PM_AirAccelerate.
+ */
+
+ public static void PM_AirAccelerate(float[] wishdir, float wishspeed,
+ float accel) {
+ int i;
+ float addspeed, accelspeed, currentspeed, wishspd = wishspeed;
+
+ if (wishspd > 30)
+ wishspd = 30;
+ currentspeed = Math3D.dotProduct(pml.velocity, wishdir);
+ addspeed = wishspd - currentspeed;
+ if (addspeed <= 0)
+ return;
+ accelspeed = accel * wishspeed * pml.frametime;
+ if (accelspeed > addspeed)
+ accelspeed = addspeed;
+
+ for (i = 0; i < 3; i++)
+ pml.velocity[i] += accelspeed * wishdir[i];
+ }
+
+ /**
+ * PM_AddCurrents.
+ */
+ public static void PM_AddCurrents(float[] wishvel) {
+ float[] v = {0, 0, 0};
+ float s;
+
+ // account for ladders
+ if (pml.ladder && Math.abs(pml.velocity[2]) <= 200) {
+ if ((pm.viewangles[Defines.PITCH] <= -15)
+ && (pm.cmd.forwardmove > 0))
+ wishvel[2] = 200;
+ else if ((pm.viewangles[Defines.PITCH] >= 15)
+ && (pm.cmd.forwardmove > 0))
+ wishvel[2] = -200;
+ else if (pm.cmd.upmove > 0)
+ wishvel[2] = 200;
+ else if (pm.cmd.upmove < 0)
+ wishvel[2] = -200;
+ else
+ wishvel[2] = 0;
+
+ // limit horizontal speed when on a ladder
+ if (wishvel[0] < -25)
+ wishvel[0] = -25;
+ else if (wishvel[0] > 25)
+ wishvel[0] = 25;
+
+ if (wishvel[1] < -25)
+ wishvel[1] = -25;
+ else if (wishvel[1] > 25)
+ wishvel[1] = 25;
+ }
+
+ // add water currents
+ if ((pm.watertype & Defines.MASK_CURRENT) != 0) {
+ Math3D.vectorClear(v);
+
+ if ((pm.watertype & Defines.CONTENTS_CURRENT_0) != 0)
+ v[0] += 1;
+ if ((pm.watertype & Defines.CONTENTS_CURRENT_90) != 0)
+ v[1] += 1;
+ if ((pm.watertype & Defines.CONTENTS_CURRENT_180) != 0)
+ v[0] -= 1;
+ if ((pm.watertype & Defines.CONTENTS_CURRENT_270) != 0)
+ v[1] -= 1;
+ if ((pm.watertype & Defines.CONTENTS_CURRENT_UP) != 0)
+ v[2] += 1;
+ if ((pm.watertype & Defines.CONTENTS_CURRENT_DOWN) != 0)
+ v[2] -= 1;
+
+ s = pm_waterspeed;
+ if ((pm.waterlevel == 1) && (pm.groundentity != null))
+ s /= 2;
+
+ Math3D.vectorMA(wishvel, s, v, wishvel);
+ }
+
+ // add conveyor belt velocities
+ if (pm.groundentity != null) {
+ Math3D.vectorClear(v);
+
+ if ((pml.groundcontents & Defines.CONTENTS_CURRENT_0) != 0)
+ v[0] += 1;
+ if ((pml.groundcontents & Defines.CONTENTS_CURRENT_90) != 0)
+ v[1] += 1;
+ if ((pml.groundcontents & Defines.CONTENTS_CURRENT_180) != 0)
+ v[0] -= 1;
+ if ((pml.groundcontents & Defines.CONTENTS_CURRENT_270) != 0)
+ v[1] -= 1;
+ if ((pml.groundcontents & Defines.CONTENTS_CURRENT_UP) != 0)
+ v[2] += 1;
+ if ((pml.groundcontents & Defines.CONTENTS_CURRENT_DOWN) != 0)
+ v[2] -= 1;
+
+ Math3D.vectorMA(wishvel, 100 /* pm.groundentity.speed */, v, wishvel);
+ }
+ }
+
+ /**
+ * PM_WaterMove.
+ */
+ public static void PM_WaterMove() {
+ int i;
+ float[] wishvel = {0, 0, 0};
+ float wishspeed;
+ float[] wishdir = {0, 0, 0};
+
+
+ // user intentions
+ for (i = 0; i < 3; i++)
+ wishvel[i] = pml.forward[i] * pm.cmd.forwardmove
+ + pml.right[i] * pm.cmd.sidemove;
+
+ if (0 == pm.cmd.forwardmove && 0 == pm.cmd.sidemove
+ && 0 == pm.cmd.upmove)
+ wishvel[2] -= 60; // drift towards bottom
+ else
+ wishvel[2] += pm.cmd.upmove;
+
+ PM_AddCurrents(wishvel);
+
+ Math3D.vectorCopy(wishvel, wishdir);
+ wishspeed = Math3D.vectorNormalize(wishdir);
+
+ if (wishspeed > pm_maxspeed) {
+ Math3D.vectorScale(wishvel, pm_maxspeed / wishspeed, wishvel);
+ wishspeed = pm_maxspeed;
+ }
+ wishspeed *= 0.5;
+
+ PM_Accelerate(wishdir, wishspeed, pm_wateraccelerate);
+
+ PM_StepSlideMove();
+ }
+
+ /**
+ * PM_AirMove.
+ */
+ public static void PM_AirMove() {
+ float[] wishvel = {0, 0, 0};
+ float fmove, smove;
+ float[] wishdir = {0, 0, 0};
+ float wishspeed;
+ float maxspeed;
+
+ fmove = pm.cmd.forwardmove;
+ smove = pm.cmd.sidemove;
+
+ wishvel[0] = pml.forward[0] * fmove + pml.right[0] * smove;
+ wishvel[1] = pml.forward[1] * fmove + pml.right[1] * smove;
+
+ wishvel[2] = 0;
+
+ PM_AddCurrents(wishvel);
+
+ Math3D.vectorCopy(wishvel, wishdir);
+ wishspeed = Math3D.vectorNormalize(wishdir);
+
+
+ // clamp to server defined max speed
+ maxspeed = (pm.s.pm_flags & Move.PMF_DUCKED) != 0 ? pm_duckspeed
+ : pm_maxspeed;
+
+ if (wishspeed > maxspeed) {
+ Math3D.vectorScale(wishvel, maxspeed / wishspeed, wishvel);
+ wishspeed = maxspeed;
+ }
+
+ if (pml.ladder) {
+ PM_Accelerate(wishdir, wishspeed, pm_accelerate);
+ if (0 == wishvel[2]) {
+ if (pml.velocity[2] > 0) {
+ pml.velocity[2] -= pm.s.gravity * pml.frametime;
+ if (pml.velocity[2] < 0)
+ pml.velocity[2] = 0;
+ } else {
+ pml.velocity[2] += pm.s.gravity * pml.frametime;
+ if (pml.velocity[2] > 0)
+ pml.velocity[2] = 0;
+ }
+ }
+ PM_StepSlideMove();
+ } else if (pm.groundentity != null) { // walking on ground
+ pml.velocity[2] = 0; //!!! this is before the accel
+ PM_Accelerate(wishdir, wishspeed, pm_accelerate);
+
+ // PGM -- fix for negative trigger_gravity fields
+ // pml.velocity[2] = 0;
+ if (pm.s.gravity > 0)
+ pml.velocity[2] = 0;
+ else
+ pml.velocity[2] -= pm.s.gravity * pml.frametime;
+ // PGM
+ if (0 == pml.velocity[0] && 0 == pml.velocity[1])
+ return;
+ PM_StepSlideMove();
+ } else { // not on ground, so little effect on velocity
+ if (pm_airaccelerate != 0)
+ PM_AirAccelerate(wishdir, wishspeed, pm_accelerate);
+ else
+ PM_Accelerate(wishdir, wishspeed, 1);
+ // add gravity
+ pml.velocity[2] -= pm.s.gravity * pml.frametime;
+ PM_StepSlideMove();
+ }
+ }
+
+ /**
+ * PM_CatagorizePosition.
+ */
+ public static void PM_CatagorizePosition() {
+ float[] point = {0, 0, 0};
+ int cont;
+ trace_t trace;
+ int sample1;
+ int sample2;
+
+ // if the player hull point one unit down is solid, the player
+ // is on ground
+
+ // see if standing on something solid
+ point[0] = pml.origin[0];
+ point[1] = pml.origin[1];
+ point[2] = pml.origin[2] - 0.25f;
+ if (pml.velocity[2] > 180) //!!ZOID changed from 100 to 180 (ramp
+ // accel)
+ {
+ pm.s.pm_flags &= ~Move.PMF_ON_GROUND;
+ pm.groundentity = null;
+ } else {
+ trace = pm.trace.trace(pml.origin, pm.mins,
+ pm.maxs, point);
+ pml.groundsurface = trace.surface;
+ pml.groundcontents = trace.contents;
+
+ if (null == trace.ent
+ || (trace.plane.normal[2] < 0.7 && !trace.startsolid)) {
+ pm.groundentity = null;
+ pm.s.pm_flags &= ~Move.PMF_ON_GROUND;
+ } else {
+ pm.groundentity = trace.ent;
+ // hitting solid ground will end a waterjump
+ if ((pm.s.pm_flags & Move.PMF_TIME_WATERJUMP) != 0) {
+ pm.s.pm_flags &= ~(Move.PMF_TIME_WATERJUMP
+ | Move.PMF_TIME_LAND | Move.PMF_TIME_TELEPORT);
+ pm.s.pm_time = 0;
+ }
+
+ if (0 == (pm.s.pm_flags & Move.PMF_ON_GROUND)) {
+
+ // just hit the ground
+ pm.s.pm_flags |= Move.PMF_ON_GROUND;
+ // don't do landing time if we were just going down a slope
+ if (pml.velocity[2] < -200) {
+ pm.s.pm_flags |= Move.PMF_TIME_LAND;
+ // don't allow another jump for a little while
+ if (pml.velocity[2] < -400)
+ pm.s.pm_time = 25;
+ else
+ pm.s.pm_time = 18;
+ }
+ }
+ }
+
+ if (pm.numtouch < Defines.MAXTOUCH && trace.ent != null) {
+ pm.touchents[pm.numtouch] = trace.ent;
+ pm.numtouch++;
+ }
+ }
+
+
+ // get waterlevel, accounting for ducking
+
+ pm.waterlevel = 0;
+ pm.watertype = 0;
+
+ sample2 = (int) (pm.viewheight - pm.mins[2]);
+ sample1 = sample2 / 2;
+
+ point[2] = pml.origin[2] + pm.mins[2] + 1;
+ cont = pm.pointcontents.pointcontents(point);
+
+ if ((cont & Defines.MASK_WATER) != 0) {
+ pm.watertype = cont;
+ pm.waterlevel = 1;
+ point[2] = pml.origin[2] + pm.mins[2] + sample1;
+ cont = pm.pointcontents.pointcontents(point);
+ if ((cont & Defines.MASK_WATER) != 0) {
+ pm.waterlevel = 2;
+ point[2] = pml.origin[2] + pm.mins[2] + sample2;
+ cont = pm.pointcontents.pointcontents(point);
+ if ((cont & Defines.MASK_WATER) != 0)
+ pm.waterlevel = 3;
+ }
+ }
+
+ }
+
+ /**
+ * PM_CheckJump.
+ */
+ public static void PM_CheckJump() {
+ if ((pm.s.pm_flags & Move.PMF_TIME_LAND) != 0) {
+ // hasn't been long enough since landing to jump again
+ return;
+ }
+
+ if (pm.cmd.upmove < 10) { // not holding jump
+ pm.s.pm_flags &= ~Move.PMF_JUMP_HELD;
+ return;
+ }
+
+ // must wait for jump to be released
+ if ((pm.s.pm_flags & Move.PMF_JUMP_HELD) != 0)
+ return;
+
+ if (pm.s.pm_type == Defines.PM_DEAD)
+ return;
+
+ if (pm.waterlevel >= 2) { // swimming, not jumping
+ pm.groundentity = null;
+
+ if (pml.velocity[2] <= -300)
+ return;
+
+ if (pm.watertype == Defines.CONTENTS_WATER)
+ pml.velocity[2] = 100;
+ else if (pm.watertype == Defines.CONTENTS_SLIME)
+ pml.velocity[2] = 80;
+ else
+ pml.velocity[2] = 50;
+ return;
+ }
+
+ if (pm.groundentity == null)
+ return; // in air, so no effect
+
+ pm.s.pm_flags |= Move.PMF_JUMP_HELD;
+
+ pm.groundentity = null;
+ pml.velocity[2] += 270;
+ if (pml.velocity[2] < 270)
+ pml.velocity[2] = 270;
+ }
+
+ /**
+ * PM_CheckSpecialMovement.
+ */
+ public static void PM_CheckSpecialMovement() {
+ float[] spot = {0, 0, 0};
+ int cont;
+ float[] flatforward = {0, 0, 0};
+ trace_t trace;
+
+ if (pm.s.pm_time != 0)
+ return;
+
+ pml.ladder = false;
+
+ // check for ladder
+ flatforward[0] = pml.forward[0];
+ flatforward[1] = pml.forward[1];
+ flatforward[2] = 0;
+ Math3D.vectorNormalize(flatforward);
+
+ Math3D.vectorMA(pml.origin, 1, flatforward, spot);
+ trace = pm.trace.trace(pml.origin, pm.mins,
+ pm.maxs, spot);
+ if ((trace.fraction < 1)
+ && (trace.contents & Defines.CONTENTS_LADDER) != 0)
+ pml.ladder = true;
+
+ // check for water jump
+ if (pm.waterlevel != 2)
+ return;
+
+ Math3D.vectorMA(pml.origin, 30, flatforward, spot);
+ spot[2] += 4;
+ cont = pm.pointcontents.pointcontents(spot);
+ if (0 == (cont & Defines.CONTENTS_SOLID))
+ return;
+
+ spot[2] += 16;
+ cont = pm.pointcontents.pointcontents(spot);
+ if (cont != 0)
+ return;
+ // jump out of water
+ Math3D.vectorScale(flatforward, 50, pml.velocity);
+ pml.velocity[2] = 350;
+
+ pm.s.pm_flags |= Move.PMF_TIME_WATERJUMP;
+ pm.s.pm_time = -1; // was 255
+ }
+
+ /**
+ * PM_FlyMove.
+ */
+ public static void PM_FlyMove(boolean doclip) {
+ float speed, drop, friction, control, newspeed;
+ float currentspeed, addspeed, accelspeed;
+ int i;
+ float[] wishvel = {0, 0, 0};
+ float fmove, smove;
+ float[] wishdir = {0, 0, 0};
+ float wishspeed;
+ float[] end = {0, 0, 0};
+ trace_t trace;
+
+ pm.viewheight = 22;
+
+ // friction
+
+ speed = Math3D.vectorLength(pml.velocity);
+ if (speed < 1) {
+ Math3D.vectorCopy(Globals.vec3_origin, pml.velocity);
+ } else {
+ drop = 0;
+
+ friction = pm_friction * 1.5f; // extra friction
+ control = speed < pm_stopspeed ? pm_stopspeed : speed;
+ drop += control * friction * pml.frametime;
+
+ // scale the velocity
+ newspeed = speed - drop;
+ if (newspeed < 0)
+ newspeed = 0;
+ newspeed /= speed;
+
+ Math3D.vectorScale(pml.velocity, newspeed, pml.velocity);
+ }
+
+ // accelerate
+ fmove = pm.cmd.forwardmove;
+ smove = pm.cmd.sidemove;
+
+ Math3D.vectorNormalize(pml.forward);
+ Math3D.vectorNormalize(pml.right);
+
+ for (i = 0; i < 3; i++)
+ wishvel[i] = pml.forward[i] * fmove + pml.right[i]
+ * smove;
+ wishvel[2] += pm.cmd.upmove;
+
+ Math3D.vectorCopy(wishvel, wishdir);
+ wishspeed = Math3D.vectorNormalize(wishdir);
+
+ // clamp to server defined max speed
+ if (wishspeed > pm_maxspeed) {
+ Math3D.vectorScale(wishvel, pm_maxspeed / wishspeed, wishvel);
+ wishspeed = pm_maxspeed;
+ }
+
+ currentspeed = Math3D.dotProduct(pml.velocity, wishdir);
+ addspeed = wishspeed - currentspeed;
+ if (addspeed <= 0)
+ return;
+ accelspeed = pm_accelerate * pml.frametime * wishspeed;
+ if (accelspeed > addspeed)
+ accelspeed = addspeed;
+
+ for (i = 0; i < 3; i++)
+ pml.velocity[i] += accelspeed * wishdir[i];
+
+ if (doclip) {
+ for (i = 0; i < 3; i++)
+ end[i] = pml.origin[i] + pml.frametime * pml.velocity[i];
+
+ trace = pm.trace.trace(pml.origin, pm.mins, pm.maxs, end);
+
+ Math3D.vectorCopy(trace.endpos, pml.origin);
+ } else {
+ // move
+ Math3D.vectorMA(pml.origin, pml.frametime, pml.velocity, pml.origin);
+ }
+ }
+
+ /**
+ * Sets mins, maxs, and pm.viewheight.
+ */
+ public static void PM_CheckDuck() {
+ trace_t trace;
+
+ pm.mins[0] = -16;
+ pm.mins[1] = -16;
+
+ pm.maxs[0] = 16;
+ pm.maxs[1] = 16;
+
+ if (pm.s.pm_type == Defines.PM_GIB) {
+ pm.mins[2] = 0;
+ pm.maxs[2] = 16;
+ pm.viewheight = 8;
+ return;
+ }
+
+ pm.mins[2] = -24;
+
+ if (pm.s.pm_type == Defines.PM_DEAD) {
+ pm.s.pm_flags |= Move.PMF_DUCKED;
+ } else if (pm.cmd.upmove < 0 && (pm.s.pm_flags & Move.PMF_ON_GROUND) != 0) { // duck
+ pm.s.pm_flags |= Move.PMF_DUCKED;
+ } else { // stand up if possible
+ if ((pm.s.pm_flags & Move.PMF_DUCKED) != 0) {
+ // try to stand up
+ pm.maxs[2] = 32;
+ trace = pm.trace.trace(pml.origin, pm.mins, pm.maxs, pml.origin);
+ if (!trace.allsolid)
+ pm.s.pm_flags &= ~Move.PMF_DUCKED;
+ }
+ }
+
+ if ((pm.s.pm_flags & Move.PMF_DUCKED) != 0) {
+ pm.maxs[2] = 4;
+ pm.viewheight = -2;
+ } else {
+ pm.maxs[2] = 32;
+ pm.viewheight = 22;
+ }
+ }
+
+ /**
+ * Dead bodies have extra friction.
+ */
+ public static void PM_DeadMove() {
+ float forward;
+
+ if (null == pm.groundentity)
+ return;
+
+ // extra friction
+ forward = Math3D.vectorLength(pml.velocity);
+ forward -= 20;
+ if (forward <= 0) {
+ Math3D.vectorClear(pml.velocity);
+ } else {
+ Math3D.vectorNormalize(pml.velocity);
+ Math3D.vectorScale(pml.velocity, forward, pml.velocity);
+ }
+ }
+
+ public static boolean PM_GoodPosition() {
+ trace_t trace;
+ float[] origin = {0, 0, 0}, end = {0, 0, 0};
+ int i;
+
+ if (pm.s.pm_type == Defines.PM_SPECTATOR)
+ return true;
+
+ for (i = 0; i < 3; i++)
+ origin[i] = end[i] = pm.s.origin[i] * 0.125f;
+ trace = pm.trace.trace(origin, pm.mins, pm.maxs, end);
+
+ return !trace.allsolid;
+ }
+
+ /**
+ * On exit, the origin will have a value that is pre-quantized to the 0.125
+ * precision of the network channel and in a valid position.
+ */
+
+ public static void PM_SnapPosition() {
+ int sign[] = {0, 0, 0};
+ int i, j, bits;
+ short base[] = {0, 0, 0};
+
+ // snap velocity to eigths
+ for (i = 0; i < 3; i++)
+ pm.s.velocity[i] = (short) (pml.velocity[i] * 8);
+
+ for (i = 0; i < 3; i++) {
+ if (pml.origin[i] >= 0)
+ sign[i] = 1;
+ else
+ sign[i] = -1;
+ pm.s.origin[i] = (short) (pml.origin[i] * 8);
+ if (pm.s.origin[i] * 0.125 == pml.origin[i])
+ sign[i] = 0;
+ }
+ Math3D.vectorCopy(pm.s.origin, base);
+
+ // try all combinations
+ for (j = 0; j < 8; j++) {
+ bits = jitterbits[j];
+ Math3D.vectorCopy(base, pm.s.origin);
+ for (i = 0; i < 3; i++)
+ if ((bits & (1 << i)) != 0)
+ pm.s.origin[i] += sign[i];
+
+ if (PM_GoodPosition())
+ return;
+ }
+
+ // go back to the last position
+ Math3D.vectorCopy(pml.previous_origin, pm.s.origin);
+ // Com.DPrintf("using previous_origin\n");
+ }
+
+ /**
+ * Snaps the origin of the player move to 0.125 grid.
+ */
+ public static void PM_InitialSnapPosition() {
+ int x, y, z;
+ short base[] = {0, 0, 0};
+
+ Math3D.vectorCopy(pm.s.origin, base);
+
+ for (z = 0; z < 3; z++) {
+ pm.s.origin[2] = (short) (base[2] + offset[z]);
+ for (y = 0; y < 3; y++) {
+ pm.s.origin[1] = (short) (base[1] + offset[y]);
+ for (x = 0; x < 3; x++) {
+ pm.s.origin[0] = (short) (base[0] + offset[x]);
+ if (PM_GoodPosition()) {
+ pml.origin[0] = pm.s.origin[0] * 0.125f;
+ pml.origin[1] = pm.s.origin[1] * 0.125f;
+ pml.origin[2] = pm.s.origin[2] * 0.125f;
+ Math3D.vectorCopy(pm.s.origin,
+ pml.previous_origin);
+ return;
+ }
+ }
+ }
+ }
+
+ Com.DPrintf("Bad InitialSnapPosition\n");
+ }
+
+ /**
+ * PM_ClampAngles.
+ */
+ public static void PM_ClampAngles() {
+ short temp;
+ int i;
+
+ if ((pm.s.pm_flags & Move.PMF_TIME_TELEPORT) != 0) {
+ pm.viewangles[Defines.YAW] = Math3D
+ .short2Angle(pm.cmd.angles[Defines.YAW]
+ + pm.s.delta_angles[Defines.YAW]);
+ pm.viewangles[Defines.PITCH] = 0;
+ pm.viewangles[Defines.ROLL] = 0;
+ } else {
+ // circularly clamp the angles with deltas
+ for (i = 0; i < 3; i++) {
+ temp = (short) (pm.cmd.angles[i] + pm.s.delta_angles[i]);
+ pm.viewangles[i] = Math3D.short2Angle(temp);
+ }
+
+ // don't let the player look up or down more than 90 degrees
+ if (pm.viewangles[Defines.PITCH] > 89 && pm.viewangles[Defines.PITCH] < 180)
+ pm.viewangles[Defines.PITCH] = 89;
+ else if (pm.viewangles[Defines.PITCH] < 271 && pm.viewangles[Defines.PITCH] >= 180)
+ pm.viewangles[Defines.PITCH] = 271;
+ }
+ Math3D.angleVectors(pm.viewangles, pml.forward, pml.right, pml.up);
+ }
+
+ /**
+ * Can be called by either the server or the client.
+ */
+ public static void Pmove(Move move) {
+ pm = move;
+
+ // clear results
+ pm.numtouch = 0;
+ Math3D.vectorClear(pm.viewangles);
+ pm.viewheight = 0;
+ pm.groundentity = null;
+ pm.watertype = 0;
+ pm.waterlevel = 0;
+
+ pml.groundsurface = null;
+ pml.groundcontents = 0;
+
+ // convert origin and velocity to float values
+ pml.origin[0] = pm.s.origin[0] * 0.125f;
+ pml.origin[1] = pm.s.origin[1] * 0.125f;
+ pml.origin[2] = pm.s.origin[2] * 0.125f;
+
+ pml.velocity[0] = pm.s.velocity[0] * 0.125f;
+ pml.velocity[1] = pm.s.velocity[1] * 0.125f;
+ pml.velocity[2] = pm.s.velocity[2] * 0.125f;
+
+ // save old org in case we get stuck
+ Math3D.vectorCopy(pm.s.origin, pml.previous_origin);
+
+ pml.frametime = (pm.cmd.msec & 0xFF) * 0.001f;
+
+ PM_ClampAngles();
+
+ if (pm.s.pm_type == Defines.PM_SPECTATOR) {
+ PM_FlyMove(false);
+ PM_SnapPosition();
+ return;
+ }
+
+ if (pm.s.pm_type >= Defines.PM_DEAD) {
+ pm.cmd.forwardmove = 0;
+ pm.cmd.sidemove = 0;
+ pm.cmd.upmove = 0;
+ }
+
+ if (pm.s.pm_type == Defines.PM_FREEZE)
+ return; // no movement at all
+
+ // set mins, maxs, and viewheight
+ PM_CheckDuck();
+
+ if (pm.snapinitial)
+ PM_InitialSnapPosition();
+
+ // set groundentity, watertype, and waterlevel
+ PM_CatagorizePosition();
+
+ if (pm.s.pm_type == Defines.PM_DEAD)
+ PM_DeadMove();
+
+ PM_CheckSpecialMovement();
+
+ // drop timing counter
+ if (pm.s.pm_time != 0) {
+ int msec;
+
+ // TOD o bugfix cwei
+ msec = pm.cmd.msec >>> 3;
+ if (msec == 0)
+ msec = 1;
+ if (msec >= (pm.s.pm_time & 0xFF)) {
+ pm.s.pm_flags &= ~(Move.PMF_TIME_WATERJUMP
+ | Move.PMF_TIME_LAND | Move.PMF_TIME_TELEPORT);
+ pm.s.pm_time = 0;
+ } else
+ pm.s.pm_time = (byte) ((pm.s.pm_time & 0xFF) - msec);
+ }
+
+ if ((pm.s.pm_flags & Move.PMF_TIME_TELEPORT) != 0) {
+ // teleport pause stays exaclty in place
+ } else if ((pm.s.pm_flags & Move.PMF_TIME_WATERJUMP) != 0) {
+ // waterjump has no control, but falls
+ pml.velocity[2] -= pm.s.gravity * pml.frametime;
+ if (pml.velocity[2] < 0) {
+ // cancel as soon as we are falling down again
+ pm.s.pm_flags &= ~(Move.PMF_TIME_WATERJUMP
+ | Move.PMF_TIME_LAND | Move.PMF_TIME_TELEPORT);
+ pm.s.pm_time = 0;
+ }
+
+ PM_StepSlideMove();
+ } else {
+ PM_CheckJump();
+
+ PM_Friction();
+
+ if (pm.waterlevel >= 2)
+ PM_WaterMove();
+ else {
+ float[] angles = {0, 0, 0};
+
+ Math3D.vectorCopy(pm.viewangles, angles);
+
+ if (angles[Defines.PITCH] > 180)
+ angles[Defines.PITCH] = angles[Defines.PITCH] - 360;
+
+ angles[Defines.PITCH] /= 3;
+
+ Math3D.angleVectors(angles, pml.forward, pml.right, pml.up);
+
+ PM_AirMove();
+ }
+ }
+
+ // set groundentity, watertype, and waterlevel for final spot
+ PM_CatagorizePosition();
+ PM_SnapPosition();
+ }
+
+ public static class pml_t {
+ public final float[] origin = {0, 0, 0}; // full float precision
+
+ public final float[] velocity = {0, 0, 0}; // full float precision
+
+ public final float[] forward = {0, 0, 0};
+ public final float[] right = {0, 0, 0};
+ public final float[] up = {0, 0,
+ 0};
+ public final float[] previous_origin = {0, 0, 0};
+ public float frametime;
+ public csurface_t groundsurface;
+ public int groundcontents;
+ public boolean ladder;
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.qcommon;
+
+import lwjake2.Globals;
+import lwjake2.client.Client;
+import lwjake2.client.Key;
+import lwjake2.client.SCR;
+import lwjake2.game.Cmd;
+import lwjake2.server.Server;
+import lwjake2.sys.NET;
+import lwjake2.sys.Sys;
+import lwjake2.util.Vargs;
+
+/**
+ * QCommon contains some basic routines for the game engine
+ * namely initialization, shutdown and frame generation.
+ */
+public final class QCommon extends Globals {
+
+ public static final String BUILDSTRING = "Java " + System.getProperty("java.version");
+ public static final String CPUSTRING = System.getProperty("os.arch");
+
+ /**
+ * This function initializes the different subsystems of
+ * the game engine. The setjmp/longjmp mechanism of the original
+ * was replaced with exceptions.
+ *
+ * @param args the original unmodified command line arguments
+ */
+ public static void init(String[] args) {
+ try {
+
+ // prepare enough of the subsystems to handle
+ // cvar and command buffer management
+ Com.InitArgv(args);
+
+ CommandBuffer.Init();
+
+ Cmd.Init();
+ Cvar.Init();
+
+ Key.Init();
+
+ // we need to add the early commands twice, because
+ // a basedir or cddir needs to be set before execing
+ // config files, but we want other parms to override
+ // the settings of the config files
+ CommandBuffer.addEarlyCommands(false);
+ CommandBuffer.execute();
+
+ FS.initFilesystem();
+
+ reconfigure(false);
+
+ FS.setCDDir(); // use cddir from config.cfg
+ FS.markBaseSearchPaths(); // mark the default search paths
+ FS.checkOverride();
+
+ reconfigure(true); // reload default.cfg and config.cfg
+
+ //
+ // init commands and vars
+ //
+ Cmd.AddCommand("error", Com.Error_f);
+
+ Globals.developer = Cvar.get("developer", "0", CVAR_ARCHIVE);
+ Globals.showtrace = Cvar.get("showtrace", "0", 0);
+ Globals.dedicated = Cvar.get("dedicated", "0", CVAR_NOSET);
+
+ String versionString = Com.sprintf("%4.2f %s %s %s",
+ new Vargs(4)
+ .add(Globals.VERSION)
+ .add(CPUSTRING)
+ .add(Globals.__DATE__)
+ .add(BUILDSTRING));
+ Cvar.get("version", versionString, CVAR_SERVERINFO | CVAR_NOSET);
+
+ NET.Init(); //ok
+ Netchan.Netchan_Init(); //ok
+
+ Server.SV_Init(); //ok
+
+ Client.Init();
+
+ // add + commands from command line
+ if (!CommandBuffer.AddLateCommands()) {
+ // if the user didn't give any commands, run default action
+ if (Globals.dedicated.value == 0)
+ CommandBuffer.AddText("d1\n");
+ else
+ CommandBuffer.AddText("dedicated_start\n");
+
+ CommandBuffer.execute();
+ } else {
+ // the user asked for something explicit
+ // so drop the loading plaque
+ SCR.EndLoadingPlaque();
+ }
+
+ Com.Printf("====== Quake2 Initialized ======\n\n");
+
+ // save config when configuration is completed
+ Client.WriteConfiguration();
+
+ } catch (longjmpException e) {
+ Sys.Error("Error during initialization");
+ }
+ }
+
+ /**
+ * Trigger generation of a frame for the given time. The setjmp/longjmp
+ * mechanism of the original was replaced with exceptions.
+ *
+ * @param timeSinceLastFrameMs the current game time
+ */
+ public static void doFrame(int timeSinceLastFrameMs) {
+ try {
+
+ logTracing();
+
+ CommandBuffer.execute();
+
+ Com.debugContext = "SV:";
+ Server.doFrame(timeSinceLastFrameMs);
+
+ Com.debugContext = "CL:";
+ Client.doFrame(timeSinceLastFrameMs);
+
+ } catch (longjmpException e) {
+ Com.DPrintf("lonjmp exception:" + e);
+ }
+ }
+
+ private static void logTracing() {
+ if (Globals.showtrace.value != 0.0f) {
+ Com.Printf("%4i traces %4i points\n",
+ new Vargs(2).add(Globals.c_traces)
+ .add(Globals.c_pointcontents));
+
+
+ Globals.c_traces = 0;
+ Globals.c_brush_traces = 0;
+ Globals.c_pointcontents = 0;
+ }
+ }
+
+ static void reconfigure(boolean clear) {
+ String dir = Cvar.get("cddir", "", CVAR_ARCHIVE).string;
+ CommandBuffer.AddText("exec default.cfg\n");
+ CommandBuffer.AddText("bind MWHEELUP weapnext\n");
+ CommandBuffer.AddText("bind MWHEELDOWN weapprev\n");
+ CommandBuffer.AddText("bind w +forward\n");
+ CommandBuffer.AddText("bind s +back\n");
+ CommandBuffer.AddText("bind a +moveleft\n");
+ CommandBuffer.AddText("bind d +moveright\n");
+ CommandBuffer.execute();
+ Cvar.set("vid_fullscreen", "0");
+ CommandBuffer.AddText("exec config.cfg\n");
+
+ CommandBuffer.addEarlyCommands(clear);
+ CommandBuffer.execute();
+ if (!("".equals(dir))) Cvar.set("cddir", dir);
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.qcommon;
+
+import lwjake2.Defines;
+import lwjake2.util.Lib;
+
+/**
+ * SZ
+ */
+public final class SZ {
+
+ public static void Clear(sizebuf_t buf) {
+ buf.clear();
+ }
+
+ //===========================================================================
+
+ public static void Init(sizebuf_t buf, byte data[], int length) {
+ // TODO check this. cwei
+ buf.readcount = 0;
+
+ buf.data = data;
+ buf.maxsize = length;
+ buf.cursize = 0;
+ buf.allowoverflow = buf.overflowed = false;
+ }
+
+
+ /**
+ * Ask for the pointer using sizebuf_t.cursize (RST)
+ */
+ public static int GetSpace(sizebuf_t buf, int length) {
+ int oldsize;
+
+ if (buf.cursize + length > buf.maxsize) {
+ if (!buf.allowoverflow)
+ Com.Error(Defines.ERR_FATAL, "SZ_GetSpace: overflow without allowoverflow set");
+
+ if (length > buf.maxsize)
+ Com.Error(Defines.ERR_FATAL, "SZ_GetSpace: " + length + " is > full buffer size");
+
+ Com.Printf("SZ_GetSpace: overflow\n");
+ Clear(buf);
+ buf.overflowed = true;
+ }
+
+ oldsize = buf.cursize;
+ buf.cursize += length;
+
+ return oldsize;
+ }
+
+ public static void Write(sizebuf_t buf, byte data[], int length) {
+ //memcpy(SZ_GetSpace(buf, length), data, length);
+ System.arraycopy(data, 0, buf.data, GetSpace(buf, length), length);
+ }
+
+ public static void Write(sizebuf_t buf, byte data[], int offset, int length) {
+ System.arraycopy(data, offset, buf.data, GetSpace(buf, length), length);
+ }
+
+ public static void Write(sizebuf_t buf, byte data[]) {
+ int length = data.length;
+ //memcpy(SZ_GetSpace(buf, length), data, length);
+ System.arraycopy(data, 0, buf.data, GetSpace(buf, length), length);
+ }
+
+ //
+ public static void Print(sizebuf_t buf, String data) {
+ Com.dprintln("SZ.print():<" + data + ">");
+ int length = data.length();
+ byte str[] = Lib.stringToBytes(data);
+
+ if (buf.cursize != 0) {
+
+ if (buf.data[buf.cursize - 1] != 0) {
+ //memcpy( SZ_GetSpace(buf, len), data, len); // no trailing 0
+ System.arraycopy(str, 0, buf.data, GetSpace(buf, length + 1), length);
+ } else {
+ System.arraycopy(str, 0, buf.data, GetSpace(buf, length) - 1, length);
+ //memcpy(SZ_GetSpace(buf, len - 1) - 1, data, len); // write over trailing 0
+ }
+ } else
+ // first print.
+ System.arraycopy(str, 0, buf.data, GetSpace(buf, length), length);
+ //memcpy(SZ_GetSpace(buf, len), data, len);
+
+ buf.data[buf.cursize - 1] = 0;
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.qcommon;
+
+/**
+ * cmd_function_t
+ */
+public final class cmd_function_t {
+ public cmd_function_t next = null;
+ public String name = null;
+ public xcommand_t function;
+}
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.qcommon;
+
+/**
+ * longjmpException is used to replace the setjmp/longjmp code.
+ */
+@SuppressWarnings("serial")
+public final class longjmpException extends IllegalStateException {
+
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.qcommon;
+
+public class lump_t {
+ public final int fileofs;
+ public final int filelen;
+
+ public lump_t(int offset, int len) {
+ this.fileofs = offset;
+ this.filelen = len;
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.qcommon;
+
+import lwjake2.Defines;
+
+public class netchan_t {
+
+ public boolean fatal_error;
+
+ // was enum {NS_CLIENT, NS_SERVER}
+ public int sock;
+
+ public int dropped; // between last packet and previous
+
+ public int last_received; // for timeouts
+
+ public int last_sent; // for retransmits
+
+ public NetadrT remote_address = new NetadrT();
+
+ public int qport; // qport value to write when transmitting
+
+ // sequencing variables
+ public int incoming_sequence;
+
+ public int incoming_acknowledged;
+
+ public int incoming_reliable_acknowledged; // single bit
+
+ public int incoming_reliable_sequence; // single bit, maintained local
+
+ public int outgoing_sequence;
+
+ public int reliable_sequence; // single bit
+
+ public int last_reliable_sequence; // sequence number of last send
+
+ // reliable staging and holding areas
+ public sizebuf_t message = new sizebuf_t(); // writing buffer to send to
+ // server
+
+ public byte message_buf[] = new byte[Defines.MAX_MSGLEN - 16]; // leave
+ // space for
+ // header
+
+ // message is copied to this buffer when it is first transfered
+ public int reliable_length;
+
+ public byte reliable_buf[] = new byte[Defines.MAX_MSGLEN - 16]; // unpcked
+ // reliable
+ // message
+
+ //ok.
+ public void clear() {
+ sock = dropped = last_received = last_sent = 0;
+ remote_address = new NetadrT();
+ qport = incoming_sequence = incoming_acknowledged = incoming_reliable_acknowledged = incoming_reliable_sequence = outgoing_sequence = reliable_sequence = last_reliable_sequence = 0;
+ message = new sizebuf_t();
+
+ message_buf = new byte[Defines.MAX_MSGLEN - 16];
+
+ reliable_length = 0;
+ reliable_buf = new byte[Defines.MAX_MSGLEN - 16];
+ }
+
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.qcommon;
+
+import lwjake2.Defines;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.FloatBuffer;
+import java.nio.IntBuffer;
+
+/**
+ * qfiles
+ *
+ * @author cwei
+ */
+public class qfiles {
+ //
+ // qfiles.h: quake file formats
+ // This file must be identical in the quake and utils directories
+ //
+
+ /*
+ ========================================================================
+
+ The .pak files are just a linear collapse of a directory tree
+
+ ========================================================================
+ */
+
+ public static final int IDALIASHEADER = (('2' << 24) + ('P' << 16) + ('D' << 8) + 'I');
+ public static final int ALIAS_VERSION = 8;
+
+ /*
+ ========================================================================
+
+ .MD2 triangle model file format
+
+ ========================================================================
+ */
+ public static final int MAX_TRIANGLES = 4096;
+ public static final int MAX_VERTS = 2048;
+ public static final int MAX_FRAMES = 512;
+ public static final int MAX_MD2SKINS = 32;
+ public static final int MAX_SKINNAME = 64;
+ public static final int DTRIVERTX_V0 = 0;
+ public static final int DTRIVERTX_V1 = 1;
+ public static final int DTRIVERTX_V2 = 2;
+ public static final int DTRIVERTX_LNI = 3;
+ public static final int DTRIVERTX_SIZE = 4;
+ /*
+ ========================================================================
+
+ .SP2 sprite file format
+
+ ========================================================================
+ */
+ // little-endian "IDS2"
+ public static final int IDSPRITEHEADER = (('2' << 24) + ('S' << 16) + ('D' << 8) + 'I');
+ public static final int SPRITE_VERSION = 2;
+ public static final int IDBSPHEADER = (('P' << 24) + ('S' << 16) + ('B' << 8) + 'I');
+
+ /*
+ ========================================================================
+
+ PCX files are used for as many images as possible
+
+ ========================================================================
+ */
+ public static class pcx_t {
+
+ // size of byte arrays
+ static final int PALETTE_SIZE = 48;
+ static final int FILLER_SIZE = 58;
+
+ public final byte manufacturer;
+ public final byte version;
+ public final byte encoding;
+ public final byte bits_per_pixel;
+ public final int xmin;
+ public final int ymin;
+ public final int xmax;
+ public final int ymax; // unsigned short
+ public final int hres;
+ public final int vres; // unsigned short
+ public final byte[] palette; //unsigned byte; size 48
+ public final byte reserved;
+ public final byte color_planes;
+ public final int bytes_per_line; // unsigned short
+ public final int palette_type; // unsigned short
+ public final byte[] filler; // size 58
+ public final ByteBuffer data; //unbounded data
+
+ public pcx_t(byte[] dataBytes) {
+ this(ByteBuffer.wrap(dataBytes));
+ }
+
+ public pcx_t(ByteBuffer b) {
+ // is stored as little endian
+ b.order(ByteOrder.LITTLE_ENDIAN);
+
+ // fill header
+ manufacturer = b.get();
+ version = b.get();
+ encoding = b.get();
+ bits_per_pixel = b.get();
+ xmin = b.getShort() & 0xffff;
+ ymin = b.getShort() & 0xffff;
+ xmax = b.getShort() & 0xffff;
+ ymax = b.getShort() & 0xffff;
+ hres = b.getShort() & 0xffff;
+ vres = b.getShort() & 0xffff;
+ b.get(palette = new byte[PALETTE_SIZE]);
+ reserved = b.get();
+ color_planes = b.get();
+ bytes_per_line = b.getShort() & 0xffff;
+ palette_type = b.getShort() & 0xffff;
+ b.get(filler = new byte[FILLER_SIZE]);
+
+ // fill data
+ data = b.slice();
+ }
+ }
+
+ /*
+ ========================================================================
+
+ TGA files are used for sky planes
+
+ ========================================================================
+ */
+ public static class tga_t {
+
+ // targa header
+ public final int id_length;
+ public final int colormap_type;
+ public final int image_type; // unsigned char
+ public final int colormap_index;
+ public final int colormap_length; // unsigned short
+ public final int colormap_size; // unsigned char
+ public final int x_origin;
+ public final int y_origin;
+ public final int width;
+ public final int height; // unsigned short
+ public final int pixel_size;
+ public final int attributes; // unsigned char
+
+ public final ByteBuffer data; // (un)compressed data
+
+ public tga_t(byte[] dataBytes) {
+ this(ByteBuffer.wrap(dataBytes));
+ }
+
+ public tga_t(ByteBuffer b) {
+ // is stored as little endian
+ b.order(ByteOrder.LITTLE_ENDIAN);
+
+ // fill header
+ id_length = b.get() & 0xFF;
+ colormap_type = b.get() & 0xFF;
+ image_type = b.get() & 0xFF;
+ colormap_index = b.getShort() & 0xFFFF;
+ colormap_length = b.getShort() & 0xFFFF;
+ colormap_size = b.get() & 0xFF;
+ x_origin = b.getShort() & 0xFFFF;
+ y_origin = b.getShort() & 0xFFFF;
+ width = b.getShort() & 0xFFFF;
+ height = b.getShort() & 0xFFFF;
+ pixel_size = b.get() & 0xFF;
+ attributes = b.get() & 0xFF;
+
+ // fill data
+ data = b.slice();
+ }
+
+ }
+
+ // the glcmd format:
+ // a positive integer starts a tristrip command, followed by that many
+ // vertex structures.
+ // a negative integer starts a trifan command, followed by -x vertexes
+ // a zero indicates the end of the command list.
+ // a vertex consists of a floating point s, a floating point t,
+ // and an integer vertex index.
+
+ public static class dstvert_t {
+ public final short s;
+ public final short t;
+
+ public dstvert_t(ByteBuffer b) {
+ s = b.getShort();
+ t = b.getShort();
+ }
+ }
+
+ public static class dtriangle_t {
+ public final short[] index_xyz = {0, 0, 0};
+ public final short[] index_st = {0, 0, 0};
+
+ public dtriangle_t(ByteBuffer b) {
+ index_xyz[0] = b.getShort();
+ index_xyz[1] = b.getShort();
+ index_xyz[2] = b.getShort();
+
+ index_st[0] = b.getShort();
+ index_st[1] = b.getShort();
+ index_st[2] = b.getShort();
+ }
+ }
+
+ public static class daliasframe_t {
+ public final float[] scale = {0, 0, 0}; // multiply byte verts by this
+ public final float[] translate = {0, 0, 0}; // then add this
+ public final String name; // frame name from grabbing (size 16)
+ public int[] verts; // variable sized
+
+ public daliasframe_t(ByteBuffer b) {
+ scale[0] = b.getFloat();
+ scale[1] = b.getFloat();
+ scale[2] = b.getFloat();
+ translate[0] = b.getFloat();
+ translate[1] = b.getFloat();
+ translate[2] = b.getFloat();
+ byte[] nameBuf = new byte[16];
+ b.get(nameBuf);
+ name = new String(nameBuf).trim();
+ }
+ }
+
+ public static class dmdl_t {
+ public final int ident;
+ public final int version;
+
+ public final int skinwidth;
+ public final int skinheight;
+ public final int framesize; // byte size of each frame
+
+ public final int num_skins;
+ public final int num_xyz;
+ public final int num_st; // greater than num_xyz for seams
+ public final int num_tris;
+ public final int num_glcmds; // dwords in strip/fan command list
+ public final int num_frames;
+
+ public final int ofs_skins; // each skin is a MAX_SKINNAME string
+ public final int ofs_st; // byte offset from start for stverts
+ public final int ofs_tris; // offset for dtriangles
+ public final int ofs_frames; // offset for first frame
+ public final int ofs_glcmds;
+ public final int ofs_end; // end of file
+
+ // wird extra gebraucht
+ public String[] skinNames;
+ public dstvert_t[] stVerts;
+ public dtriangle_t[] triAngles;
+ public int[] glCmds;
+ public daliasframe_t[] aliasFrames;
+ /*
+ * new members for vertex array handling
+ */
+ public FloatBuffer textureCoordBuf = null;
+ public IntBuffer vertexIndexBuf = null;
+ public int[] counts = null;
+ public IntBuffer[] indexElements = null;
+
+ public dmdl_t(ByteBuffer b) {
+ ident = b.getInt();
+ version = b.getInt();
+
+ skinwidth = b.getInt();
+ skinheight = b.getInt();
+ framesize = b.getInt(); // byte size of each frame
+
+ num_skins = b.getInt();
+ num_xyz = b.getInt();
+ num_st = b.getInt(); // greater than num_xyz for seams
+ num_tris = b.getInt();
+ num_glcmds = b.getInt(); // dwords in strip/fan command list
+ num_frames = b.getInt();
+
+ ofs_skins = b.getInt(); // each skin is a MAX_SKINNAME string
+ ofs_st = b.getInt(); // byte offset from start for stverts
+ ofs_tris = b.getInt(); // offset for dtriangles
+ ofs_frames = b.getInt(); // offset for first frame
+ ofs_glcmds = b.getInt();
+ ofs_end = b.getInt(); // end of file
+ }
+ }
+
+ public static class dsprframe_t {
+ public final int width;
+ public final int height;
+ public final int origin_x;
+ public final int origin_y; // raster coordinates inside pic
+ public final String name; // name of pcx file (MAX_SKINNAME)
+
+ public dsprframe_t(ByteBuffer b) {
+ width = b.getInt();
+ height = b.getInt();
+ origin_x = b.getInt();
+ origin_y = b.getInt();
+
+ byte[] nameBuf = new byte[MAX_SKINNAME];
+ b.get(nameBuf);
+ name = new String(nameBuf).trim();
+ }
+ }
+
+ public static class dsprite_t {
+ public final int ident;
+ public final int version;
+ public final int numframes;
+ public final dsprframe_t[] frames; // variable sized
+
+ public dsprite_t(ByteBuffer b) {
+ ident = b.getInt();
+ version = b.getInt();
+ numframes = b.getInt();
+
+ frames = new dsprframe_t[numframes];
+ for (int i = 0; i < numframes; i++) {
+ frames[i] = new dsprframe_t(b);
+ }
+ }
+ }
+
+ /*
+ ==============================================================================
+
+ .BSP file format
+
+ ==============================================================================
+ */
+
+ /*
+ ==============================================================================
+
+ .WAL texture file format
+
+ ==============================================================================
+ */
+ public static class miptex_t {
+
+ static final int MIPLEVELS = 4;
+ static final int NAME_SIZE = 32;
+
+ public final String name; // char name[32];
+ public final int width;
+ public final int height;
+ public final int[] offsets = new int[MIPLEVELS]; // 4 mip maps stored
+ // next frame in animation chain
+ public final String animname; // char animname[32];
+ public final int flags;
+ public final int contents;
+ public final int value;
+
+ public miptex_t(byte[] dataBytes) {
+ this(ByteBuffer.wrap(dataBytes));
+ }
+
+ public miptex_t(ByteBuffer b) {
+ // is stored as little endian
+ b.order(ByteOrder.LITTLE_ENDIAN);
+
+ byte[] nameBuf = new byte[NAME_SIZE];
+ // fill header
+ b.get(nameBuf);
+ name = new String(nameBuf).trim();
+ width = b.getInt();
+ height = b.getInt();
+ offsets[0] = b.getInt();
+ offsets[1] = b.getInt();
+ offsets[2] = b.getInt();
+ offsets[3] = b.getInt();
+ b.get(nameBuf);
+ animname = new String(nameBuf).trim();
+ flags = b.getInt();
+ contents = b.getInt();
+ value = b.getInt();
+ }
+
+ }
+
+ // =============================================================================
+
+ public static class dheader_t {
+
+ public final int ident;
+ public final int version;
+ public final lump_t[] lumps = new lump_t[Defines.HEADER_LUMPS];
+
+ public dheader_t(ByteBuffer bb) {
+ bb.order(ByteOrder.LITTLE_ENDIAN);
+ this.ident = bb.getInt();
+ this.version = bb.getInt();
+
+ for (int n = 0; n < Defines.HEADER_LUMPS; n++)
+ lumps[n] = new lump_t(bb.getInt(), bb.getInt());
+
+ }
+ }
+
+ public static class dmodel_t {
+
+ public static final int SIZE = 3 * 4 + 3 * 4 + 3 * 4 + 4 + 8;
+ public final float[] mins = {0, 0, 0};
+ public final float[] maxs = {0, 0, 0};
+ public final float[] origin = {0, 0, 0}; // for sounds or lights
+ public final int headnode;
+ public final int firstface;
+ public final int numfaces; // submodels just draw faces
+ // without walking the bsp tree
+
+ public dmodel_t(ByteBuffer bb) {
+ bb.order(ByteOrder.LITTLE_ENDIAN);
+
+ for (int j = 0; j < 3; j++)
+ mins[j] = bb.getFloat();
+
+ for (int j = 0; j < 3; j++)
+ maxs[j] = bb.getFloat();
+
+ for (int j = 0; j < 3; j++)
+ origin[j] = bb.getFloat();
+
+ headnode = bb.getInt();
+ firstface = bb.getInt();
+ numfaces = bb.getInt();
+ }
+ }
+
+ public static class dvertex_t {
+
+ public static final int SIZE = 3 * 4; // 3 mal 32 bit float
+
+ public final float[] point = {0, 0, 0};
+
+ public dvertex_t(ByteBuffer b) {
+ point[0] = b.getFloat();
+ point[1] = b.getFloat();
+ point[2] = b.getFloat();
+ }
+ }
+
+
+ // planes (x&~1) and (x&~1)+1 are always opposites
+ public static class dplane_t {
+
+ public static final int SIZE = 3 * 4 + 4 + 4;
+ public final float[] normal = {0, 0, 0};
+ public final float dist;
+ public final int type; // PLANE_X - PLANE_ANYZ ?remove? trivial to regenerate
+
+ public dplane_t(ByteBuffer bb) {
+ bb.order(ByteOrder.LITTLE_ENDIAN);
+
+ normal[0] = (bb.getFloat());
+ normal[1] = (bb.getFloat());
+ normal[2] = (bb.getFloat());
+
+ dist = (bb.getFloat());
+ type = (bb.getInt());
+ }
+ }
+
+ public static class dnode_t {
+
+ public static final int SIZE = 4 + 8 + 6 + 6 + 2 + 2; // counting both sides
+ public final int planenum;
+ public final int[] children = {0, 0};
+ // negative numbers are -(leafs+1), not nodes
+ public final short[] mins = {0, 0, 0}; // for frustom culling
+ public final short[] maxs = {0, 0, 0};
+
+ /*
+ unsigned short firstface;
+ unsigned short numfaces; // counting both sides
+ */
+
+ public final int firstface;
+ public final int numfaces;
+
+ public dnode_t(ByteBuffer bb) {
+
+ bb.order(ByteOrder.LITTLE_ENDIAN);
+ planenum = bb.getInt();
+
+ children[0] = bb.getInt();
+ children[1] = bb.getInt();
+
+ for (int j = 0; j < 3; j++)
+ mins[j] = bb.getShort();
+
+ for (int j = 0; j < 3; j++)
+ maxs[j] = bb.getShort();
+
+ firstface = bb.getShort() & 0xffff;
+ numfaces = bb.getShort() & 0xffff;
+
+ }
+ }
+
+
+ // note that edge 0 is never used, because negative edge nums are used for
+ // counterclockwise use of the edge in a face
+
+ public static class dedge_t {
+ // unsigned short v[2];
+ int v[] = {0, 0};
+ }
+
+ public static class dface_t {
+
+ public static final int SIZE =
+ 4 * Defines.SIZE_OF_SHORT
+ + 2 * Defines.SIZE_OF_INT
+ + Defines.MAXLIGHTMAPS;
+
+ //unsigned short planenum;
+ public final int planenum;
+ public final short side;
+
+ public final int firstedge; // we must support > 64k edges
+ public final short numedges;
+ public final short texinfo;
+
+ // lighting info
+ public final byte[] styles = new byte[Defines.MAXLIGHTMAPS];
+ public final int lightofs; // start of [numstyles*surfsize] samples
+
+ public dface_t(ByteBuffer b) {
+ planenum = b.getShort() & 0xFFFF;
+ side = b.getShort();
+ firstedge = b.getInt();
+ numedges = b.getShort();
+ texinfo = b.getShort();
+ b.get(styles);
+ lightofs = b.getInt();
+ }
+
+ }
+
+ public static class dleaf_t {
+
+ public static final int SIZE = 4 + 8 * 2 + 4 * 2;
+ public final int contents; // OR of all brushes (not needed?)
+ public final short cluster;
+ public final short area;
+ public final short[] mins = {0, 0, 0}; // for frustum culling
+ public final short[] maxs = {0, 0, 0};
+ public final int firstleafface; // unsigned short
+ public final int numleaffaces; // unsigned short
+ public final int firstleafbrush; // unsigned short
+ public final int numleafbrushes; // unsigned short
+
+ public dleaf_t(byte[] cmod_base, int i, int j) {
+ this(ByteBuffer.wrap(cmod_base, i, j).order(ByteOrder.LITTLE_ENDIAN));
+ }
+
+ public dleaf_t(ByteBuffer bb) {
+ contents = bb.getInt();
+ cluster = bb.getShort();
+ area = bb.getShort();
+
+ mins[0] = bb.getShort();
+ mins[1] = bb.getShort();
+ mins[2] = bb.getShort();
+
+ maxs[0] = bb.getShort();
+ maxs[1] = bb.getShort();
+ maxs[2] = bb.getShort();
+
+ firstleafface = bb.getShort() & 0xffff;
+ numleaffaces = bb.getShort() & 0xffff;
+
+ firstleafbrush = bb.getShort() & 0xffff;
+ numleafbrushes = bb.getShort() & 0xffff;
+ }
+ }
+
+ public static class dbrushside_t {
+
+ public static final int SIZE = 4;
+ //unsigned short planenum;
+ final int planenum; // facing out of the leaf
+
+ final short texinfo;
+
+ public dbrushside_t(ByteBuffer bb) {
+ bb.order(ByteOrder.LITTLE_ENDIAN);
+
+ planenum = bb.getShort() & 0xffff;
+ texinfo = bb.getShort();
+ }
+ }
+
+ public static class dbrush_t {
+
+ public static final int SIZE = 3 * 4;
+ final int firstside;
+ final int numsides;
+ final int contents;
+
+ public dbrush_t(ByteBuffer bb) {
+ bb.order(ByteOrder.LITTLE_ENDIAN);
+ firstside = bb.getInt();
+ numsides = bb.getInt();
+ contents = bb.getInt();
+ }
+ }
+
+ // #define ANGLE_UP -1
+ // #define ANGLE_DOWN -2
+
+ // the visibility lump consists of a header with a count, then
+ // byte offsets for the PVS and PHS of each cluster, then the raw
+ // compressed bit vectors
+ // #define DVIS_PVS 0
+ // #define DVIS_PHS 1
+
+ public static class dvis_t {
+
+ public final int numclusters;
+ public int bitofs[][] = new int[8][2]; // bitofs[numclusters][2]
+
+ public dvis_t(ByteBuffer bb) {
+ numclusters = bb.getInt();
+ bitofs = new int[numclusters][2];
+
+ for (int i = 0; i < numclusters; i++) {
+ bitofs[i][0] = bb.getInt();
+ bitofs[i][1] = bb.getInt();
+ }
+ }
+ }
+
+ // each area has a list of portals that lead into other areas
+ // when portals are closed, other areas may not be visible or
+ // hearable even if the vis info says that it should be
+
+ public static class dareaportal_t {
+
+ public static final int SIZE = 8;
+ int portalnum;
+ int otherarea;
+
+ public dareaportal_t() {
+ }
+
+ public dareaportal_t(ByteBuffer bb) {
+ bb.order(ByteOrder.LITTLE_ENDIAN);
+ portalnum = bb.getInt();
+ otherarea = bb.getInt();
+ }
+ }
+
+ public static class darea_t {
+
+ public static final int SIZE = 8;
+ final int numareaportals;
+ final int firstareaportal;
+
+ public darea_t(ByteBuffer bb) {
+
+ bb.order(ByteOrder.LITTLE_ENDIAN);
+
+ numareaportals = bb.getInt();
+ firstareaportal = bb.getInt();
+
+ }
+ }
+
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.qcommon;
+
+import java.util.Arrays;
+
+/**
+ * sizebuf_t
+ */
+public final class sizebuf_t {
+ public boolean allowoverflow = false;
+ public boolean overflowed = false;
+ public byte[] data = null;
+ public int maxsize = 0;
+ public int cursize = 0;
+ public int readcount = 0;
+
+ public void clear() {
+ if (data != null)
+ Arrays.fill(data, (byte) 0);
+ cursize = 0;
+ overflowed = false;
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.qcommon;
+
+import lwjake2.util.Lib;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+public class texinfo_t {
+
+ public static final int SIZE = 32 + 4 + 4 + 32 + 4;
+ //float vecs[2][4]; // [s/t][xyz offset]
+ public final float[][] vecs = {
+ {0, 0, 0, 0},
+ {0, 0, 0, 0}
+ };
+ public final int flags; // miptex flags + overrides
+ public final int value; // light emission, etc
+ public final int nexttexinfo; // for animations, -1 = end of chain
+ //char texture[32]; // texture name (textures/*.wal)
+ public String texture = "";
+
+ // works fine.
+ public texinfo_t(byte[] cmod_base, int o, int len) {
+ this(ByteBuffer.wrap(cmod_base, o, len).order(ByteOrder.LITTLE_ENDIAN));
+ }
+
+ public texinfo_t(ByteBuffer bb) {
+
+ byte str[] = new byte[32];
+
+ vecs[0] = new float[]{bb.getFloat(), bb.getFloat(), bb.getFloat(), bb.getFloat()};
+ vecs[1] = new float[]{bb.getFloat(), bb.getFloat(), bb.getFloat(), bb.getFloat()};
+
+ flags = bb.getInt();
+ value = bb.getInt();
+
+ bb.get(str);
+ texture = new String(str, 0, Lib.strlen(str));
+ nexttexinfo = bb.getInt();
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.qcommon;
+
+/**
+ * xcommand_t
+ */
+public abstract class xcommand_t {
+
+ abstract public void execute();
+}
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.render;
+
+import lwjake2.client.refdef_t;
+import lwjake2.client.refexport_t;
+import lwjake2.qcommon.xcommand_t;
+import lwjake2.sys.KBD;
+
+import java.awt.*;
+
+/**
+ * DummyRenderer
+ *
+ * @author cwei
+ */
+public class DummyRenderer implements refexport_t {
+
+ /* (non-Javadoc)
+ * @see jake2.client.refexport_t#Init(int, int)
+ */
+ public boolean Init(int vid_xpos, int vid_ypos) {
+ return false;
+ }
+
+ /* (non-Javadoc)
+ * @see jake2.client.refexport_t#Shutdown()
+ */
+ public void Shutdown() {
+ }
+
+ /* (non-Javadoc)
+ * @see jake2.client.refexport_t#BeginRegistration(java.lang.String)
+ */
+ public void BeginRegistration(String map) {
+ }
+
+ /* (non-Javadoc)
+ * @see jake2.client.refexport_t#RegisterModel(java.lang.String)
+ */
+ public Model RegisterModel(String name) {
+ return null;
+ }
+
+ /* (non-Javadoc)
+ * @see jake2.client.refexport_t#RegisterSkin(java.lang.String)
+ */
+ public Image RegisterSkin(String name) {
+ return null;
+ }
+
+ /* (non-Javadoc)
+ * @see jake2.client.refexport_t#RegisterPic(java.lang.String)
+ */
+ public Image RegisterPic(String name) {
+ return null;
+ }
+
+ /* (non-Javadoc)
+ * @see jake2.client.refexport_t#SetSky(java.lang.String, float, float[])
+ */
+ public void SetSky(String name, float rotate, float[] axis) {
+ }
+
+ /* (non-Javadoc)
+ * @see jake2.client.refexport_t#EndRegistration()
+ */
+ public void EndRegistration() {
+ }
+
+ /* (non-Javadoc)
+ * @see jake2.client.refexport_t#RenderFrame(jake2.client.refdef_t)
+ */
+ public void RenderFrame(refdef_t fd) {
+ }
+
+ /* (non-Javadoc)
+ * @see jake2.client.refexport_t#DrawGetPicSize(java.awt.Dimension, java.lang.String)
+ */
+ public void DrawGetPicSize(Dimension dim, String name) {
+ }
+
+ /* (non-Javadoc)
+ * @see jake2.client.refexport_t#DrawPic(int, int, java.lang.String)
+ */
+ public void DrawPic(int x, int y, String name) {
+ }
+
+ /* (non-Javadoc)
+ * @see jake2.client.refexport_t#DrawStretchPic(int, int, int, int, java.lang.String)
+ */
+ public void DrawStretchPic(int x, int y, int w, int h, String name) {
+ }
+
+ /* (non-Javadoc)
+ * @see jake2.client.refexport_t#DrawChar(int, int, int)
+ */
+ public void DrawChar(int x, int y, int num) {
+ }
+
+ /* (non-Javadoc)
+ * @see jake2.client.refexport_t#DrawTileClear(int, int, int, int, java.lang.String)
+ */
+ public void DrawTileClear(int x, int y, int w, int h, String name) {
+ }
+
+ /* (non-Javadoc)
+ * @see jake2.client.refexport_t#DrawFill(int, int, int, int, int)
+ */
+ public void DrawFill(int x, int y, int w, int h, int c) {
+ }
+
+ /* (non-Javadoc)
+ * @see jake2.client.refexport_t#DrawFadeScreen()
+ */
+ public void DrawFadeScreen() {
+ }
+
+ /* (non-Javadoc)
+ * @see jake2.client.refexport_t#DrawStretchRaw(int, int, int, int, int, int, byte[])
+ */
+ public void DrawStretchRaw(int x, int y, int w, int h, int cols, int rows, byte[] data) {
+ }
+
+ /* (non-Javadoc)
+ * @see jake2.client.refexport_t#CinematicSetPalette(byte[])
+ */
+ public void CinematicSetPalette(byte[] palette) {
+ }
+
+ /* (non-Javadoc)
+ * @see jake2.client.refexport_t#BeginFrame(float)
+ */
+ public void BeginFrame(float camera_separation) {
+ }
+
+ /* (non-Javadoc)
+ * @see jake2.client.refexport_t#EndFrame()
+ */
+ public void EndFrame() {
+ }
+
+ /* (non-Javadoc)
+ * @see jake2.client.refexport_t#AppActivate(boolean)
+ */
+ public void AppActivate(boolean activate) {
+ }
+
+ /* (non-Javadoc)
+ * @see jake2.client.refexport_t#updateScreen(jake2.qcommon.xcommand_t)
+ */
+ public void updateScreen(xcommand_t callback) {
+ callback.execute();
+ }
+
+ /* (non-Javadoc)
+ * @see jake2.client.refexport_t#apiVersion()
+ */
+ public int apiVersion() {
+ return 0;
+ }
+
+ /* (non-Javadoc)
+ * @see jake2.client.refexport_t#getModeList()
+ */
+ public DisplayMode[] getModeList() {
+ return null;
+ }
+
+ /* (non-Javadoc)
+ * @see jake2.client.refexport_t#getKeyboardHandler()
+ */
+ public KBD getKeyboardHandler() {
+ return null;
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.render;
+
+import lwjake2.Defines;
+
+public class Image {
+
+ public static final int MAX_NAME_SIZE = Defines.MAX_QPATH;
+ // used to get the pos in array
+ // added by cwei
+ private final int id;
+ // quake 2 variables
+ public String name = ""; // game path, including extension
+ // enum imagetype_t
+ public int type;
+ public int width, height; // source image
+ public int upload_width, upload_height; // after power of two and picmip
+ public int registration_sequence; // 0 = free
+ public msurface_t texturechain; // for sort-by-texture world drawing
+ public int texnum; // gl texture binding
+ public float sl, tl, sh, th; // 0,0 - 1,1 unless part of the scrap
+ public boolean scrap;
+ public boolean has_alpha;
+ public boolean paletted;
+
+ public Image(int id) {
+ this.id = id;
+ }
+
+ public void clear() {
+ // don't clear the id
+ // wichtig !!!
+ name = "";
+ type = 0;
+ width = height = 0;
+ upload_width = upload_height = 0;
+ registration_sequence = 0; // 0 = free
+ texturechain = null;
+ texnum = 0; // gl texture binding
+ sl = tl = sh = th = 0;
+ scrap = false;
+ has_alpha = false;
+ paletted = false;
+ }
+
+ public int getId() {
+ return id;
+ }
+
+ public String toString() {
+ return name + ":" + texnum;
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.render;
+
+import lwjake2.Defines;
+import lwjake2.client.VideoDriver;
+import lwjake2.client.refdef_t;
+import lwjake2.client.refexport_t;
+import lwjake2.render.lwjgl.Misc;
+import lwjake2.sys.KBD;
+import lwjake2.sys.LWJGLKBD;
+
+import java.awt.*;
+
+/**
+ * LWJGLRenderer
+ *
+ * @author dsanders/cwei
+ */
+final class LWJGLRenderer extends Misc implements refexport_t, Ref {
+
+ public static final String DRIVER_NAME = "lwjgl";
+
+ static {
+ Renderer.register(new LWJGLRenderer());
+ }
+
+ private final LWJGLKBD kbd = new LWJGLKBD();
+
+ private LWJGLRenderer() {
+ }
+
+ // ============================================================================
+ // public interface for Renderer implementations
+ //
+ // refexport_t (ref.h)
+ // ============================================================================
+
+ /**
+ * @see jake2.client.refexport_t#Init()
+ */
+ public boolean Init(int vid_xpos, int vid_ypos) {
+
+ // pre init
+ if (!R_Init(vid_xpos, vid_ypos)) return false;
+ // post init
+ boolean ok = R_Init2();
+ if (!ok) {
+ VideoDriver.Printf(Defines.PRINT_ALL, "Missing multi-texturing for LWJGL renderer\n");
+ }
+ return ok;
+ }
+
+ /**
+ * @see jake2.client.refexport_t#Shutdown()
+ */
+ public void Shutdown() {
+ R_Shutdown();
+ }
+
+ /**
+ * @see jake2.client.refexport_t#BeginRegistration(java.lang.String)
+ */
+ public final void BeginRegistration(String map) {
+ R_BeginRegistration(map);
+ }
+
+ /**
+ * @see jake2.client.refexport_t#RegisterModel(java.lang.String)
+ */
+ public final Model RegisterModel(String name) {
+ return R_RegisterModel(name);
+ }
+
+ /**
+ * @see jake2.client.refexport_t#RegisterSkin(java.lang.String)
+ */
+ public final Image RegisterSkin(String name) {
+ return R_RegisterSkin(name);
+ }
+
+ /**
+ * @see jake2.client.refexport_t#RegisterPic(java.lang.String)
+ */
+ public final Image RegisterPic(String name) {
+ return Draw_FindPic(name);
+ }
+
+ /**
+ * @see jake2.client.refexport_t#SetSky(java.lang.String, float, float[])
+ */
+ public final void SetSky(String name, float rotate, float[] axis) {
+ R_SetSky(name, rotate, axis);
+ }
+
+ /**
+ * @see jake2.client.refexport_t#EndRegistration()
+ */
+ public final void EndRegistration() {
+ R_EndRegistration();
+ }
+
+ /**
+ * @see jake2.client.refexport_t#RenderFrame(jake2.client.refdef_t)
+ */
+ public final void RenderFrame(refdef_t fd) {
+ R_RenderFrame(fd);
+ }
+
+ /**
+ * @see jake2.client.refexport_t#DrawGetPicSize(java.awt.Dimension, java.lang.String)
+ */
+ public final void DrawGetPicSize(Dimension dim, String name) {
+ Draw_GetPicSize(dim, name);
+ }
+
+ /**
+ * @see jake2.client.refexport_t#DrawPic(int, int, java.lang.String)
+ */
+ public final void DrawPic(int x, int y, String name) {
+ Draw_Pic(x, y, name);
+ }
+
+ /**
+ * @see jake2.client.refexport_t#DrawStretchPic(int, int, int, int, java.lang.String)
+ */
+ public final void DrawStretchPic(int x, int y, int w, int h, String name) {
+ Draw_StretchPic(x, y, w, h, name);
+ }
+
+ /**
+ * @see jake2.client.refexport_t#DrawChar(int, int, int)
+ */
+ public final void DrawChar(int x, int y, int num) {
+ Draw_Char(x, y, num);
+ }
+
+ /**
+ * @see jake2.client.refexport_t#DrawTileClear(int, int, int, int, java.lang.String)
+ */
+ public final void DrawTileClear(int x, int y, int w, int h, String name) {
+ Draw_TileClear(x, y, w, h, name);
+ }
+
+ /**
+ * @see jake2.client.refexport_t#DrawFill(int, int, int, int, int)
+ */
+ public final void DrawFill(int x, int y, int w, int h, int c) {
+ Draw_Fill(x, y, w, h, c);
+ }
+
+ /**
+ * @see jake2.client.refexport_t#DrawFadeScreen()
+ */
+ public final void DrawFadeScreen() {
+ Draw_FadeScreen();
+ }
+
+ /**
+ * @see jake2.client.refexport_t#DrawStretchRaw(int, int, int, int, int, int, byte[])
+ */
+ public final void DrawStretchRaw(int x, int y, int w, int h, int cols, int rows, byte[] data) {
+ Draw_StretchRaw(x, y, w, h, cols, rows, data);
+ }
+
+ /**
+ * @see jake2.client.refexport_t#CinematicSetPalette(byte[])
+ */
+ public final void CinematicSetPalette(byte[] palette) {
+ R_SetPalette(palette);
+ }
+
+ /**
+ * @see jake2.client.refexport_t#BeginFrame(float)
+ */
+ public final void BeginFrame(float camera_separation) {
+ R_BeginFrame(camera_separation);
+ }
+
+ /**
+ * @see jake2.client.refexport_t#EndFrame()
+ */
+ public final void EndFrame() {
+ GLimp_EndFrame();
+ }
+
+ /**
+ * @see jake2.client.refexport_t#AppActivate(boolean)
+ */
+ public final void AppActivate(boolean activate) {
+ GLimp_AppActivate(activate);
+ }
+
+ public final int apiVersion() {
+ return Defines.API_VERSION;
+ }
+
+ // ============================================================================
+ // Ref interface
+ // ============================================================================
+
+ public final String getName() {
+ return DRIVER_NAME;
+ }
+
+ public final String toString() {
+ return DRIVER_NAME;
+ }
+
+ public final refexport_t GetRefAPI() {
+ return this;
+ }
+
+ public final KBD getKeyboardHandler() {
+ return kbd;
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.render;
+
+import lwjake2.Defines;
+import lwjake2.game.cplane_t;
+import lwjake2.qcommon.qfiles;
+import lwjake2.util.Lib;
+import lwjake2.util.Math3D;
+
+import java.util.Arrays;
+
+public class Model implements Cloneable {
+
+ // for alias models and skins
+ // was image_t *skins[]; (array of pointers)
+ public final Image[] skins = new Image[Defines.MAX_MD2SKINS];
+ public String name = "";
+ public int registration_sequence;
+ // was enum modtype_t
+ public int type;
+ public int numframes;
+ public int flags;
+ //
+ // volume occupied by the model graphics
+ //
+ public float[] mins = {0, 0, 0}, maxs = {0, 0, 0};
+ public float radius;
+ //
+ // solid volume for clipping
+ //
+ public boolean clipbox;
+ public float clipmins[] = {0, 0, 0}, clipmaxs[] = {0, 0, 0};
+ //
+ // brush model
+ //
+ public int firstmodelsurface, nummodelsurfaces;
+ public int lightmap; // only for submodels
+ public int numsubmodels;
+ public mmodel_t submodels[];
+ public int numplanes;
+ public cplane_t planes[];
+ public int numleafs; // number of visible leafs, not counting 0
+ public mleaf_t leafs[];
+ public int numvertexes;
+ public mvertex_t vertexes[];
+ public int numedges;
+ public medge_t edges[];
+ public int numnodes;
+ public int firstnode;
+ public mnode_t nodes[];
+ public int numtexinfo;
+ public mtexinfo_t texinfo[];
+ public int numsurfaces;
+ public msurface_t surfaces[];
+ public int numsurfedges;
+ public int surfedges[];
+ public int nummarksurfaces;
+ public msurface_t marksurfaces[];
+ public qfiles.dvis_t vis;
+ public byte lightdata[];
+ public int extradatasize;
+
+ // or whatever
+ public Object extradata;
+
+ public void clear() {
+ name = "";
+ registration_sequence = 0;
+
+ // was enum modtype_t
+ type = 0;
+ numframes = 0;
+ flags = 0;
+
+ //
+ // volume occupied by the model graphics
+ //
+ Math3D.vectorClear(mins);
+ Math3D.vectorClear(maxs);
+ radius = 0;
+
+ //
+ // solid volume for clipping
+ //
+ clipbox = false;
+ Math3D.vectorClear(clipmins);
+ Math3D.vectorClear(clipmaxs);
+
+ //
+ // brush model
+ //
+ firstmodelsurface = nummodelsurfaces = 0;
+ lightmap = 0; // only for submodels
+
+ numsubmodels = 0;
+ submodels = null;
+
+ numplanes = 0;
+ planes = null;
+
+ numleafs = 0; // number of visible leafs, not counting 0
+ leafs = null;
+
+ numvertexes = 0;
+ vertexes = null;
+
+ numedges = 0;
+ edges = null;
+
+ numnodes = 0;
+ firstnode = 0;
+ nodes = null;
+
+ numtexinfo = 0;
+ texinfo = null;
+
+ numsurfaces = 0;
+ surfaces = null;
+
+ numsurfedges = 0;
+ surfedges = null;
+
+ nummarksurfaces = 0;
+ marksurfaces = null;
+
+ vis = null;
+
+ lightdata = null;
+
+ // for alias models and skins
+ // was image_t *skins[]; (array of pointers)
+ Arrays.fill(skins, null);
+
+ extradatasize = 0;
+ // or whatever
+ extradata = null;
+ }
+
+ // TODO replace with set(model_t from)
+ public Model copy() {
+ Model theClone = null;
+ try {
+ theClone = (Model) super.clone();
+ theClone.mins = Lib.clone(this.mins);
+ theClone.maxs = Lib.clone(this.maxs);
+ theClone.clipmins = Lib.clone(this.clipmins);
+ theClone.clipmaxs = Lib.clone(this.clipmaxs);
+
+ } catch (CloneNotSupportedException ignored) {
+ }
+ return theClone;
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.render;
+
+import lwjake2.client.refexport_t;
+
+/**
+ * Ref
+ *
+ * @author cwei
+ */
+public interface Ref {
+
+ // ============================================================================
+ // extensions (cwei)
+ // ============================================================================
+ refexport_t GetRefAPI();
+
+ String getName();
+}
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.render;
+
+import lwjake2.client.refexport_t;
+
+import java.util.Vector;
+
+/**
+ * Renderer
+ *
+ * @author cwei
+ */
+public class Renderer {
+
+ static final Vector<Ref> drivers = new Vector<>(1);
+
+ static {
+ try {
+ try {
+ Class.forName("org.lwjgl.opengl.GL11");
+ Class.forName("lwjake2.render.LWJGLRenderer");
+ } catch (ClassNotFoundException e) {
+ // ignore the lwjgl driver if runtime not in classpath
+ }
+ } catch (Throwable e) {
+ e.printStackTrace();
+ }
+ }
+
+ public static void register(Ref impl) {
+ if (impl == null) {
+ throw new IllegalArgumentException("Ref implementation can't be null");
+ }
+ if (!drivers.contains(impl)) {
+ drivers.add(impl);
+ }
+ }
+
+ /**
+ * Factory method to get the Renderer implementation.
+ *
+ * @return refexport_t (Renderer singleton)
+ */
+ public static refexport_t getDriver(String driverName) {
+ // find a driver
+ Ref driver = null;
+ int count = drivers.size();
+ for (Ref driver1 : drivers) {
+ driver = driver1;
+ if (driver.getName().equals(driverName)) {
+ return driver.GetRefAPI();
+ }
+ }
+ // null if driver not found
+ return null;
+ }
+
+ public static String getDefaultName() {
+ return (drivers.isEmpty()) ? null : (drivers.firstElement()).getName();
+ }
+
+ public static String getPreferedName() {
+ return (drivers.isEmpty()) ? null : (drivers.lastElement()).getName();
+ }
+
+ public static String[] getDriverNames() {
+ if (drivers.isEmpty()) return null;
+ int count = drivers.size();
+ String[] names = new String[count];
+ for (int i = 0; i < count; i++) {
+ names[i] = (drivers.get(i)).getName();
+ }
+ return names;
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.render;
+
+public class glconfig_t {
+
+ public int renderer;
+ public String renderer_string;
+ public String vendor_string;
+ public String version_string;
+ public String extensions_string;
+
+ public boolean allow_cds;
+
+ private float version = 1.1f;
+
+ public void parseOpenGLVersion() {
+ version = Float.parseFloat(version_string.substring(0, 3));
+ }
+
+ public float getOpenGLVersion() {
+ return version;
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.render;
+
+import lwjake2.util.Lib;
+
+public abstract class glpoly_t {
+ public final static int STRIDE = 7;
+ public final static int BYTE_STRIDE = 7 * Lib.SIZEOF_FLOAT;
+ public final static int MAX_VERTICES = 64;
+
+ public glpoly_t next;
+ public glpoly_t chain;
+ public int numverts;
+
+ // the array position (glDrawArrays)
+ public int pos = 0;
+
+ public abstract float x(int index);
+
+ public abstract void x(int index, float value);
+
+ public abstract float y(int index);
+
+ public abstract void y(int index, float value);
+
+ public abstract float z(int index);
+
+ public abstract void z(int index, float value);
+
+ public abstract float s1(int index);
+
+ public abstract void s1(int index, float value);
+
+ public abstract float t1(int index);
+
+ public abstract void t1(int index, float value);
+
+ public abstract float s2(int index);
+
+ public abstract void s2(int index, float value);
+
+ public abstract float t2(int index);
+
+ public abstract void t2(int index, float value);
+
+ public abstract void beginScrolling(float s1);
+
+ public abstract void endScrolling();
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.render;
+
+public class glstate_t {
+ public final int[] currenttextures = {0, 0};
+ public float inverse_intensity;
+ public boolean fullscreen;
+ public int prev_mode;
+ public byte d_16to8table[];
+ public int lightmap_textures;
+ public int currenttmu;
+
+ public float camera_separation;
+ public boolean stereo_enabled;
+
+ public byte originalRedGammaTable[] = new byte[256];
+ public byte originalGreenGammaTable[] = new byte[256];
+ public byte originalBlueGammaTable[] = new byte[256];
+
+}
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.render.lwjgl;
+
+/**
+ * Anorms
+ *
+ * @author cwei
+ */
+interface Anorms {
+
+ float[][] VERTEXNORMALS = {
+ {-0.525731f, 0.000000f, 0.850651f},
+ {-0.442863f, 0.238856f, 0.864188f},
+ {-0.295242f, 0.000000f, 0.955423f},
+ {-0.309017f, 0.500000f, 0.809017f},
+ {-0.162460f, 0.262866f, 0.951056f},
+ {0.000000f, 0.000000f, 1.000000f},
+ {0.000000f, 0.850651f, 0.525731f},
+ {-0.147621f, 0.716567f, 0.681718f},
+ {0.147621f, 0.716567f, 0.681718f},
+ {0.000000f, 0.525731f, 0.850651f},
+ {0.309017f, 0.500000f, 0.809017f},
+ {0.525731f, 0.000000f, 0.850651f},
+ {0.295242f, 0.000000f, 0.955423f},
+ {0.442863f, 0.238856f, 0.864188f},
+ {0.162460f, 0.262866f, 0.951056f},
+ {-0.681718f, 0.147621f, 0.716567f},
+ {-0.809017f, 0.309017f, 0.500000f},
+ {-0.587785f, 0.425325f, 0.688191f},
+ {-0.850651f, 0.525731f, 0.000000f},
+ {-0.864188f, 0.442863f, 0.238856f},
+ {-0.716567f, 0.681718f, 0.147621f},
+ {-0.688191f, 0.587785f, 0.425325f},
+ {-0.500000f, 0.809017f, 0.309017f},
+ {-0.238856f, 0.864188f, 0.442863f},
+ {-0.425325f, 0.688191f, 0.587785f},
+ {-0.716567f, 0.681718f, -0.147621f},
+ {-0.500000f, 0.809017f, -0.309017f},
+ {-0.525731f, 0.850651f, 0.000000f},
+ {0.000000f, 0.850651f, -0.525731f},
+ {-0.238856f, 0.864188f, -0.442863f},
+ {0.000000f, 0.955423f, -0.295242f},
+ {-0.262866f, 0.951056f, -0.162460f},
+ {0.000000f, 1.000000f, 0.000000f},
+ {0.000000f, 0.955423f, 0.295242f},
+ {-0.262866f, 0.951056f, 0.162460f},
+ {0.238856f, 0.864188f, 0.442863f},
+ {0.262866f, 0.951056f, 0.162460f},
+ {0.500000f, 0.809017f, 0.309017f},
+ {0.238856f, 0.864188f, -0.442863f},
+ {0.262866f, 0.951056f, -0.162460f},
+ {0.500000f, 0.809017f, -0.309017f},
+ {0.850651f, 0.525731f, 0.000000f},
+ {0.716567f, 0.681718f, 0.147621f},
+ {0.716567f, 0.681718f, -0.147621f},
+ {0.525731f, 0.850651f, 0.000000f},
+ {0.425325f, 0.688191f, 0.587785f},
+ {0.864188f, 0.442863f, 0.238856f},
+ {0.688191f, 0.587785f, 0.425325f},
+ {0.809017f, 0.309017f, 0.500000f},
+ {0.681718f, 0.147621f, 0.716567f},
+ {0.587785f, 0.425325f, 0.688191f},
+ {0.955423f, 0.295242f, 0.000000f},
+ {1.000000f, 0.000000f, 0.000000f},
+ {0.951056f, 0.162460f, 0.262866f},
+ {0.850651f, -0.525731f, 0.000000f},
+ {0.955423f, -0.295242f, 0.000000f},
+ {0.864188f, -0.442863f, 0.238856f},
+ {0.951056f, -0.162460f, 0.262866f},
+ {0.809017f, -0.309017f, 0.500000f},
+ {0.681718f, -0.147621f, 0.716567f},
+ {0.850651f, 0.000000f, 0.525731f},
+ {0.864188f, 0.442863f, -0.238856f},
+ {0.809017f, 0.309017f, -0.500000f},
+ {0.951056f, 0.162460f, -0.262866f},
+ {0.525731f, 0.000000f, -0.850651f},
+ {0.681718f, 0.147621f, -0.716567f},
+ {0.681718f, -0.147621f, -0.716567f},
+ {0.850651f, 0.000000f, -0.525731f},
+ {0.809017f, -0.309017f, -0.500000f},
+ {0.864188f, -0.442863f, -0.238856f},
+ {0.951056f, -0.162460f, -0.262866f},
+ {0.147621f, 0.716567f, -0.681718f},
+ {0.309017f, 0.500000f, -0.809017f},
+ {0.425325f, 0.688191f, -0.587785f},
+ {0.442863f, 0.238856f, -0.864188f},
+ {0.587785f, 0.425325f, -0.688191f},
+ {0.688191f, 0.587785f, -0.425325f},
+ {-0.147621f, 0.716567f, -0.681718f},
+ {-0.309017f, 0.500000f, -0.809017f},
+ {0.000000f, 0.525731f, -0.850651f},
+ {-0.525731f, 0.000000f, -0.850651f},
+ {-0.442863f, 0.238856f, -0.864188f},
+ {-0.295242f, 0.000000f, -0.955423f},
+ {-0.162460f, 0.262866f, -0.951056f},
+ {0.000000f, 0.000000f, -1.000000f},
+ {0.295242f, 0.000000f, -0.955423f},
+ {0.162460f, 0.262866f, -0.951056f},
+ {-0.442863f, -0.238856f, -0.864188f},
+ {-0.309017f, -0.500000f, -0.809017f},
+ {-0.162460f, -0.262866f, -0.951056f},
+ {0.000000f, -0.850651f, -0.525731f},
+ {-0.147621f, -0.716567f, -0.681718f},
+ {0.147621f, -0.716567f, -0.681718f},
+ {0.000000f, -0.525731f, -0.850651f},
+ {0.309017f, -0.500000f, -0.809017f},
+ {0.442863f, -0.238856f, -0.864188f},
+ {0.162460f, -0.262866f, -0.951056f},
+ {0.238856f, -0.864188f, -0.442863f},
+ {0.500000f, -0.809017f, -0.309017f},
+ {0.425325f, -0.688191f, -0.587785f},
+ {0.716567f, -0.681718f, -0.147621f},
+ {0.688191f, -0.587785f, -0.425325f},
+ {0.587785f, -0.425325f, -0.688191f},
+ {0.000000f, -0.955423f, -0.295242f},
+ {0.000000f, -1.000000f, 0.000000f},
+ {0.262866f, -0.951056f, -0.162460f},
+ {0.000000f, -0.850651f, 0.525731f},
+ {0.000000f, -0.955423f, 0.295242f},
+ {0.238856f, -0.864188f, 0.442863f},
+ {0.262866f, -0.951056f, 0.162460f},
+ {0.500000f, -0.809017f, 0.309017f},
+ {0.716567f, -0.681718f, 0.147621f},
+ {0.525731f, -0.850651f, 0.000000f},
+ {-0.238856f, -0.864188f, -0.442863f},
+ {-0.500000f, -0.809017f, -0.309017f},
+ {-0.262866f, -0.951056f, -0.162460f},
+ {-0.850651f, -0.525731f, 0.000000f},
+ {-0.716567f, -0.681718f, -0.147621f},
+ {-0.716567f, -0.681718f, 0.147621f},
+ {-0.525731f, -0.850651f, 0.000000f},
+ {-0.500000f, -0.809017f, 0.309017f},
+ {-0.238856f, -0.864188f, 0.442863f},
+ {-0.262866f, -0.951056f, 0.162460f},
+ {-0.864188f, -0.442863f, 0.238856f},
+ {-0.809017f, -0.309017f, 0.500000f},
+ {-0.688191f, -0.587785f, 0.425325f},
+ {-0.681718f, -0.147621f, 0.716567f},
+ {-0.442863f, -0.238856f, 0.864188f},
+ {-0.587785f, -0.425325f, 0.688191f},
+ {-0.309017f, -0.500000f, 0.809017f},
+ {-0.147621f, -0.716567f, 0.681718f},
+ {-0.425325f, -0.688191f, 0.587785f},
+ {-0.162460f, -0.262866f, 0.951056f},
+ {0.442863f, -0.238856f, 0.864188f},
+ {0.162460f, -0.262866f, 0.951056f},
+ {0.309017f, -0.500000f, 0.809017f},
+ {0.147621f, -0.716567f, 0.681718f},
+ {0.000000f, -0.525731f, 0.850651f},
+ {0.425325f, -0.688191f, 0.587785f},
+ {0.587785f, -0.425325f, 0.688191f},
+ {0.688191f, -0.587785f, 0.425325f},
+ {-0.955423f, 0.295242f, 0.000000f},
+ {-0.951056f, 0.162460f, 0.262866f},
+ {-1.000000f, 0.000000f, 0.000000f},
+ {-0.850651f, 0.000000f, 0.525731f},
+ {-0.955423f, -0.295242f, 0.000000f},
+ {-0.951056f, -0.162460f, 0.262866f},
+ {-0.864188f, 0.442863f, -0.238856f},
+ {-0.951056f, 0.162460f, -0.262866f},
+ {-0.809017f, 0.309017f, -0.500000f},
+ {-0.864188f, -0.442863f, -0.238856f},
+ {-0.951056f, -0.162460f, -0.262866f},
+ {-0.809017f, -0.309017f, -0.500000f},
+ {-0.681718f, 0.147621f, -0.716567f},
+ {-0.681718f, -0.147621f, -0.716567f},
+ {-0.850651f, 0.000000f, -0.525731f},
+ {-0.688191f, 0.587785f, -0.425325f},
+ {-0.587785f, 0.425325f, -0.688191f},
+ {-0.425325f, 0.688191f, -0.587785f},
+ {-0.425325f, -0.688191f, -0.587785f},
+ {-0.587785f, -0.425325f, -0.688191f},
+ {-0.688191f, -0.587785f, -0.425325f}
+ };
+
+ float[][] VERTEXNORMAL_DOTS = {
+ {1.23f, 1.30f, 1.47f, 1.35f, 1.56f, 1.71f, 1.37f, 1.38f, 1.59f, 1.60f, 1.79f, 1.97f, 1.88f, 1.92f, 1.79f, 1.02f, 0.93f, 1.07f, 0.82f, 0.87f, 0.88f, 0.94f, 0.96f, 1.14f, 1.11f, 0.82f, 0.83f, 0.89f, 0.89f, 0.86f, 0.94f, 0.91f, 1.00f, 1.21f, 0.98f, 1.48f, 1.30f, 1.57f, 0.96f, 1.07f, 1.14f, 1.60f, 1.61f, 1.40f, 1.37f, 1.72f, 1.78f, 1.79f, 1.93f, 1.99f, 1.90f, 1.68f, 1.71f, 1.86f, 1.60f, 1.68f, 1.78f, 1.86f, 1.93f, 1.99f, 1.97f, 1.44f, 1.22f, 1.49f, 0.93f, 0.99f, 0.99f, 1.23f, 1.22f, 1.44f, 1.49f, 0.89f, 0.89f, 0.97f, 0.91f, 0.98f, 1.19f, 0.82f, 0.76f, 0.82f, 0.71f, 0.72f, 0.73f, 0.76f, 0.79f, 0.86f, 0.83f, 0.72f, 0.76f, 0.76f, 0.89f, 0.82f, 0.89f, 0.82f, 0.89f, 0.91f, 0.83f, 0.96f, 1.14f, 0.97f, 1.40f, 1.19f, 0.98f, 0.94f, 1.00f, 1.07f, 1.37f, 1.21f, 1.48f, 1.30f, 1.57f, 1.61f, 1.37f, 0.86f, 0.83f, 0.91f, 0.82f, 0.82f, 0.88f, 0.89f, 0.96f, 1.14f, 0.98f, 0.87f, 0.93f, 0.94f, 1.02f, 1.30f, 1.07f, 1.35f, 1.38f, 1.11f, 1.56f, 1.92f, 1.79f, 1.79f, 1.59f, 1.60f, 1.72f, 1.90f, 1.79f, 0.80f, 0.85f, 0.79f, 0.93f, 0.80f, 0.85f, 0.77f, 0.74f, 0.72f, 0.77f, 0.74f, 0.72f, 0.70f, 0.70f, 0.71f, 0.76f, 0.73f, 0.79f, 0.79f, 0.73f, 0.76f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f},
+ {1.26f, 1.26f, 1.48f, 1.23f, 1.50f, 1.71f, 1.14f, 1.19f, 1.38f, 1.46f, 1.64f, 1.94f, 1.87f, 1.84f, 1.71f, 1.02f, 0.92f, 1.00f, 0.79f, 0.85f, 0.84f, 0.91f, 0.90f, 0.98f, 0.99f, 0.77f, 0.77f, 0.83f, 0.82f, 0.79f, 0.86f, 0.84f, 0.92f, 0.99f, 0.91f, 1.24f, 1.03f, 1.33f, 0.88f, 0.94f, 0.97f, 1.41f, 1.39f, 1.18f, 1.11f, 1.51f, 1.61f, 1.59f, 1.80f, 1.91f, 1.76f, 1.54f, 1.65f, 1.76f, 1.70f, 1.70f, 1.85f, 1.85f, 1.97f, 1.99f, 1.93f, 1.28f, 1.09f, 1.39f, 0.92f, 0.97f, 0.99f, 1.18f, 1.26f, 1.52f, 1.48f, 0.83f, 0.85f, 0.90f, 0.88f, 0.93f, 1.00f, 0.77f, 0.73f, 0.78f, 0.72f, 0.71f, 0.74f, 0.75f, 0.79f, 0.86f, 0.81f, 0.75f, 0.81f, 0.79f, 0.96f, 0.88f, 0.94f, 0.86f, 0.93f, 0.92f, 0.85f, 1.08f, 1.33f, 1.05f, 1.55f, 1.31f, 1.01f, 1.05f, 1.27f, 1.31f, 1.60f, 1.47f, 1.70f, 1.54f, 1.76f, 1.76f, 1.57f, 0.93f, 0.90f, 0.99f, 0.88f, 0.88f, 0.95f, 0.97f, 1.11f, 1.39f, 1.20f, 0.92f, 0.97f, 1.01f, 1.10f, 1.39f, 1.22f, 1.51f, 1.58f, 1.32f, 1.64f, 1.97f, 1.85f, 1.91f, 1.77f, 1.74f, 1.88f, 1.99f, 1.91f, 0.79f, 0.86f, 0.80f, 0.94f, 0.84f, 0.88f, 0.74f, 0.74f, 0.71f, 0.82f, 0.77f, 0.76f, 0.70f, 0.73f, 0.72f, 0.73f, 0.70f, 0.74f, 0.85f, 0.77f, 0.82f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f},
+ {1.34f, 1.27f, 1.53f, 1.17f, 1.46f, 1.71f, 0.98f, 1.05f, 1.20f, 1.34f, 1.48f, 1.86f, 1.82f, 1.71f, 1.62f, 1.09f, 0.94f, 0.99f, 0.79f, 0.85f, 0.82f, 0.90f, 0.87f, 0.93f, 0.96f, 0.76f, 0.74f, 0.79f, 0.76f, 0.74f, 0.79f, 0.78f, 0.85f, 0.92f, 0.85f, 1.00f, 0.93f, 1.06f, 0.81f, 0.86f, 0.89f, 1.16f, 1.12f, 0.97f, 0.95f, 1.28f, 1.38f, 1.35f, 1.60f, 1.77f, 1.57f, 1.33f, 1.50f, 1.58f, 1.69f, 1.63f, 1.82f, 1.74f, 1.91f, 1.92f, 1.80f, 1.04f, 0.97f, 1.21f, 0.90f, 0.93f, 0.97f, 1.05f, 1.21f, 1.48f, 1.37f, 0.77f, 0.80f, 0.84f, 0.85f, 0.88f, 0.92f, 0.73f, 0.71f, 0.74f, 0.74f, 0.71f, 0.75f, 0.73f, 0.79f, 0.84f, 0.78f, 0.79f, 0.86f, 0.81f, 1.05f, 0.94f, 0.99f, 0.90f, 0.95f, 0.92f, 0.86f, 1.24f, 1.44f, 1.14f, 1.59f, 1.34f, 1.02f, 1.27f, 1.50f, 1.49f, 1.80f, 1.69f, 1.86f, 1.72f, 1.87f, 1.80f, 1.69f, 1.00f, 0.98f, 1.23f, 0.95f, 0.96f, 1.09f, 1.16f, 1.37f, 1.63f, 1.46f, 0.99f, 1.10f, 1.25f, 1.24f, 1.51f, 1.41f, 1.67f, 1.77f, 1.55f, 1.72f, 1.95f, 1.89f, 1.98f, 1.91f, 1.86f, 1.97f, 1.99f, 1.94f, 0.81f, 0.89f, 0.85f, 0.98f, 0.90f, 0.94f, 0.75f, 0.78f, 0.73f, 0.89f, 0.83f, 0.82f, 0.72f, 0.77f, 0.76f, 0.72f, 0.70f, 0.71f, 0.91f, 0.83f, 0.89f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f},
+ {1.46f, 1.34f, 1.60f, 1.16f, 1.46f, 1.71f, 0.94f, 0.99f, 1.05f, 1.26f, 1.33f, 1.74f, 1.76f, 1.57f, 1.54f, 1.23f, 0.98f, 1.05f, 0.83f, 0.89f, 0.84f, 0.92f, 0.87f, 0.91f, 0.96f, 0.78f, 0.74f, 0.79f, 0.72f, 0.72f, 0.75f, 0.76f, 0.80f, 0.88f, 0.83f, 0.94f, 0.87f, 0.95f, 0.76f, 0.80f, 0.82f, 0.97f, 0.96f, 0.89f, 0.88f, 1.08f, 1.11f, 1.10f, 1.37f, 1.59f, 1.37f, 1.07f, 1.27f, 1.34f, 1.57f, 1.45f, 1.69f, 1.55f, 1.77f, 1.79f, 1.60f, 0.93f, 0.90f, 0.99f, 0.86f, 0.87f, 0.93f, 0.96f, 1.07f, 1.35f, 1.18f, 0.73f, 0.76f, 0.77f, 0.81f, 0.82f, 0.85f, 0.70f, 0.71f, 0.72f, 0.78f, 0.73f, 0.77f, 0.73f, 0.79f, 0.82f, 0.76f, 0.83f, 0.90f, 0.84f, 1.18f, 0.98f, 1.03f, 0.92f, 0.95f, 0.90f, 0.86f, 1.32f, 1.45f, 1.15f, 1.53f, 1.27f, 0.99f, 1.42f, 1.65f, 1.58f, 1.93f, 1.83f, 1.94f, 1.81f, 1.88f, 1.74f, 1.70f, 1.19f, 1.17f, 1.44f, 1.11f, 1.15f, 1.36f, 1.41f, 1.61f, 1.81f, 1.67f, 1.22f, 1.34f, 1.50f, 1.42f, 1.65f, 1.61f, 1.82f, 1.91f, 1.75f, 1.80f, 1.89f, 1.89f, 1.98f, 1.99f, 1.94f, 1.98f, 1.92f, 1.87f, 0.86f, 0.95f, 0.92f, 1.14f, 0.98f, 1.03f, 0.79f, 0.84f, 0.77f, 0.97f, 0.90f, 0.89f, 0.76f, 0.82f, 0.82f, 0.74f, 0.72f, 0.71f, 0.98f, 0.89f, 0.97f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f},
+ {1.60f, 1.44f, 1.68f, 1.22f, 1.49f, 1.71f, 0.93f, 0.99f, 0.99f, 1.23f, 1.22f, 1.60f, 1.68f, 1.44f, 1.49f, 1.40f, 1.14f, 1.19f, 0.89f, 0.96f, 0.89f, 0.97f, 0.89f, 0.91f, 0.98f, 0.82f, 0.76f, 0.82f, 0.71f, 0.72f, 0.73f, 0.76f, 0.79f, 0.86f, 0.83f, 0.91f, 0.83f, 0.89f, 0.72f, 0.76f, 0.76f, 0.89f, 0.89f, 0.82f, 0.82f, 0.98f, 0.96f, 0.97f, 1.14f, 1.40f, 1.19f, 0.94f, 1.00f, 1.07f, 1.37f, 1.21f, 1.48f, 1.30f, 1.57f, 1.61f, 1.37f, 0.86f, 0.83f, 0.91f, 0.82f, 0.82f, 0.88f, 0.89f, 0.96f, 1.14f, 0.98f, 0.70f, 0.72f, 0.73f, 0.77f, 0.76f, 0.79f, 0.70f, 0.72f, 0.71f, 0.82f, 0.77f, 0.80f, 0.74f, 0.79f, 0.80f, 0.74f, 0.87f, 0.93f, 0.85f, 1.23f, 1.02f, 1.02f, 0.93f, 0.93f, 0.87f, 0.85f, 1.30f, 1.35f, 1.07f, 1.38f, 1.11f, 0.94f, 1.47f, 1.71f, 1.56f, 1.97f, 1.88f, 1.92f, 1.79f, 1.79f, 1.59f, 1.60f, 1.30f, 1.35f, 1.56f, 1.37f, 1.38f, 1.59f, 1.60f, 1.79f, 1.92f, 1.79f, 1.48f, 1.57f, 1.72f, 1.61f, 1.78f, 1.79f, 1.93f, 1.99f, 1.90f, 1.86f, 1.78f, 1.86f, 1.93f, 1.99f, 1.97f, 1.90f, 1.79f, 1.72f, 0.94f, 1.07f, 1.00f, 1.37f, 1.21f, 1.30f, 0.86f, 0.91f, 0.83f, 1.14f, 0.98f, 0.96f, 0.82f, 0.88f, 0.89f, 0.79f, 0.76f, 0.73f, 1.07f, 0.94f, 1.11f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f},
+ {1.74f, 1.57f, 1.76f, 1.33f, 1.54f, 1.71f, 0.94f, 1.05f, 0.99f, 1.26f, 1.16f, 1.46f, 1.60f, 1.34f, 1.46f, 1.59f, 1.37f, 1.37f, 0.97f, 1.11f, 0.96f, 1.10f, 0.95f, 0.94f, 1.08f, 0.89f, 0.82f, 0.88f, 0.72f, 0.76f, 0.75f, 0.80f, 0.80f, 0.88f, 0.87f, 0.91f, 0.83f, 0.87f, 0.72f, 0.76f, 0.74f, 0.83f, 0.84f, 0.78f, 0.79f, 0.96f, 0.89f, 0.92f, 0.98f, 1.23f, 1.05f, 0.86f, 0.92f, 0.95f, 1.11f, 0.98f, 1.22f, 1.03f, 1.34f, 1.42f, 1.14f, 0.79f, 0.77f, 0.84f, 0.78f, 0.76f, 0.82f, 0.82f, 0.89f, 0.97f, 0.90f, 0.70f, 0.71f, 0.71f, 0.73f, 0.72f, 0.74f, 0.73f, 0.76f, 0.72f, 0.86f, 0.81f, 0.82f, 0.76f, 0.79f, 0.77f, 0.73f, 0.90f, 0.95f, 0.86f, 1.18f, 1.03f, 0.98f, 0.92f, 0.90f, 0.83f, 0.84f, 1.19f, 1.17f, 0.98f, 1.15f, 0.97f, 0.89f, 1.42f, 1.65f, 1.44f, 1.93f, 1.83f, 1.81f, 1.67f, 1.61f, 1.36f, 1.41f, 1.32f, 1.45f, 1.58f, 1.57f, 1.53f, 1.74f, 1.70f, 1.88f, 1.94f, 1.81f, 1.69f, 1.77f, 1.87f, 1.79f, 1.89f, 1.92f, 1.98f, 1.99f, 1.98f, 1.89f, 1.65f, 1.80f, 1.82f, 1.91f, 1.94f, 1.75f, 1.61f, 1.50f, 1.07f, 1.34f, 1.27f, 1.60f, 1.45f, 1.55f, 0.93f, 0.99f, 0.90f, 1.35f, 1.18f, 1.07f, 0.87f, 0.93f, 0.96f, 0.85f, 0.82f, 0.77f, 1.15f, 0.99f, 1.27f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f},
+ {1.86f, 1.71f, 1.82f, 1.48f, 1.62f, 1.71f, 0.98f, 1.20f, 1.05f, 1.34f, 1.17f, 1.34f, 1.53f, 1.27f, 1.46f, 1.77f, 1.60f, 1.57f, 1.16f, 1.38f, 1.12f, 1.35f, 1.06f, 1.00f, 1.28f, 0.97f, 0.89f, 0.95f, 0.76f, 0.81f, 0.79f, 0.86f, 0.85f, 0.92f, 0.93f, 0.93f, 0.85f, 0.87f, 0.74f, 0.78f, 0.74f, 0.79f, 0.82f, 0.76f, 0.79f, 0.96f, 0.85f, 0.90f, 0.94f, 1.09f, 0.99f, 0.81f, 0.85f, 0.89f, 0.95f, 0.90f, 0.99f, 0.94f, 1.10f, 1.24f, 0.98f, 0.75f, 0.73f, 0.78f, 0.74f, 0.72f, 0.77f, 0.76f, 0.82f, 0.89f, 0.83f, 0.73f, 0.71f, 0.71f, 0.71f, 0.70f, 0.72f, 0.77f, 0.80f, 0.74f, 0.90f, 0.85f, 0.84f, 0.78f, 0.79f, 0.75f, 0.73f, 0.92f, 0.95f, 0.86f, 1.05f, 0.99f, 0.94f, 0.90f, 0.86f, 0.79f, 0.81f, 1.00f, 0.98f, 0.91f, 0.96f, 0.89f, 0.83f, 1.27f, 1.50f, 1.23f, 1.80f, 1.69f, 1.63f, 1.46f, 1.37f, 1.09f, 1.16f, 1.24f, 1.44f, 1.49f, 1.69f, 1.59f, 1.80f, 1.69f, 1.87f, 1.86f, 1.72f, 1.82f, 1.91f, 1.94f, 1.92f, 1.95f, 1.99f, 1.98f, 1.91f, 1.97f, 1.89f, 1.51f, 1.72f, 1.67f, 1.77f, 1.86f, 1.55f, 1.41f, 1.25f, 1.33f, 1.58f, 1.50f, 1.80f, 1.63f, 1.74f, 1.04f, 1.21f, 0.97f, 1.48f, 1.37f, 1.21f, 0.93f, 0.97f, 1.05f, 0.92f, 0.88f, 0.84f, 1.14f, 1.02f, 1.34f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f},
+ {1.94f, 1.84f, 1.87f, 1.64f, 1.71f, 1.71f, 1.14f, 1.38f, 1.19f, 1.46f, 1.23f, 1.26f, 1.48f, 1.26f, 1.50f, 1.91f, 1.80f, 1.76f, 1.41f, 1.61f, 1.39f, 1.59f, 1.33f, 1.24f, 1.51f, 1.18f, 0.97f, 1.11f, 0.82f, 0.88f, 0.86f, 0.94f, 0.92f, 0.99f, 1.03f, 0.98f, 0.91f, 0.90f, 0.79f, 0.84f, 0.77f, 0.79f, 0.84f, 0.77f, 0.83f, 0.99f, 0.85f, 0.91f, 0.92f, 1.02f, 1.00f, 0.79f, 0.80f, 0.86f, 0.88f, 0.84f, 0.92f, 0.88f, 0.97f, 1.10f, 0.94f, 0.74f, 0.71f, 0.74f, 0.72f, 0.70f, 0.73f, 0.72f, 0.76f, 0.82f, 0.77f, 0.77f, 0.73f, 0.74f, 0.71f, 0.70f, 0.73f, 0.83f, 0.85f, 0.78f, 0.92f, 0.88f, 0.86f, 0.81f, 0.79f, 0.74f, 0.75f, 0.92f, 0.93f, 0.85f, 0.96f, 0.94f, 0.88f, 0.86f, 0.81f, 0.75f, 0.79f, 0.93f, 0.90f, 0.85f, 0.88f, 0.82f, 0.77f, 1.05f, 1.27f, 0.99f, 1.60f, 1.47f, 1.39f, 1.20f, 1.11f, 0.95f, 0.97f, 1.08f, 1.33f, 1.31f, 1.70f, 1.55f, 1.76f, 1.57f, 1.76f, 1.70f, 1.54f, 1.85f, 1.97f, 1.91f, 1.99f, 1.97f, 1.99f, 1.91f, 1.77f, 1.88f, 1.85f, 1.39f, 1.64f, 1.51f, 1.58f, 1.74f, 1.32f, 1.22f, 1.01f, 1.54f, 1.76f, 1.65f, 1.93f, 1.70f, 1.85f, 1.28f, 1.39f, 1.09f, 1.52f, 1.48f, 1.26f, 0.97f, 0.99f, 1.18f, 1.00f, 0.93f, 0.90f, 1.05f, 1.01f, 1.31f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f},
+ {1.97f, 1.92f, 1.88f, 1.79f, 1.79f, 1.71f, 1.37f, 1.59f, 1.38f, 1.60f, 1.35f, 1.23f, 1.47f, 1.30f, 1.56f, 1.99f, 1.93f, 1.90f, 1.60f, 1.78f, 1.61f, 1.79f, 1.57f, 1.48f, 1.72f, 1.40f, 1.14f, 1.37f, 0.89f, 0.96f, 0.94f, 1.07f, 1.00f, 1.21f, 1.30f, 1.14f, 0.98f, 0.96f, 0.86f, 0.91f, 0.83f, 0.82f, 0.88f, 0.82f, 0.89f, 1.11f, 0.87f, 0.94f, 0.93f, 1.02f, 1.07f, 0.80f, 0.79f, 0.85f, 0.82f, 0.80f, 0.87f, 0.85f, 0.93f, 1.02f, 0.93f, 0.77f, 0.72f, 0.74f, 0.71f, 0.70f, 0.70f, 0.71f, 0.72f, 0.77f, 0.74f, 0.82f, 0.76f, 0.79f, 0.72f, 0.73f, 0.76f, 0.89f, 0.89f, 0.82f, 0.93f, 0.91f, 0.86f, 0.83f, 0.79f, 0.73f, 0.76f, 0.91f, 0.89f, 0.83f, 0.89f, 0.89f, 0.82f, 0.82f, 0.76f, 0.72f, 0.76f, 0.86f, 0.83f, 0.79f, 0.82f, 0.76f, 0.73f, 0.94f, 1.00f, 0.91f, 1.37f, 1.21f, 1.14f, 0.98f, 0.96f, 0.88f, 0.89f, 0.96f, 1.14f, 1.07f, 1.60f, 1.40f, 1.61f, 1.37f, 1.57f, 1.48f, 1.30f, 1.78f, 1.93f, 1.79f, 1.99f, 1.92f, 1.90f, 1.79f, 1.59f, 1.72f, 1.79f, 1.30f, 1.56f, 1.35f, 1.38f, 1.60f, 1.11f, 1.07f, 0.94f, 1.68f, 1.86f, 1.71f, 1.97f, 1.68f, 1.86f, 1.44f, 1.49f, 1.22f, 1.44f, 1.49f, 1.22f, 0.99f, 0.99f, 1.23f, 1.19f, 0.98f, 0.97f, 0.97f, 0.98f, 1.19f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f},
+ {1.94f, 1.97f, 1.87f, 1.91f, 1.85f, 1.71f, 1.60f, 1.77f, 1.58f, 1.74f, 1.51f, 1.26f, 1.48f, 1.39f, 1.64f, 1.99f, 1.97f, 1.99f, 1.70f, 1.85f, 1.76f, 1.91f, 1.76f, 1.70f, 1.88f, 1.55f, 1.33f, 1.57f, 0.96f, 1.08f, 1.05f, 1.31f, 1.27f, 1.47f, 1.54f, 1.39f, 1.20f, 1.11f, 0.93f, 0.99f, 0.90f, 0.88f, 0.95f, 0.88f, 0.97f, 1.32f, 0.92f, 1.01f, 0.97f, 1.10f, 1.22f, 0.84f, 0.80f, 0.88f, 0.79f, 0.79f, 0.85f, 0.86f, 0.92f, 1.02f, 0.94f, 0.82f, 0.76f, 0.77f, 0.72f, 0.73f, 0.70f, 0.72f, 0.71f, 0.74f, 0.74f, 0.88f, 0.81f, 0.85f, 0.75f, 0.77f, 0.82f, 0.94f, 0.93f, 0.86f, 0.92f, 0.92f, 0.86f, 0.85f, 0.79f, 0.74f, 0.79f, 0.88f, 0.85f, 0.81f, 0.82f, 0.83f, 0.77f, 0.78f, 0.73f, 0.71f, 0.75f, 0.79f, 0.77f, 0.74f, 0.77f, 0.73f, 0.70f, 0.86f, 0.92f, 0.84f, 1.14f, 0.99f, 0.98f, 0.91f, 0.90f, 0.84f, 0.83f, 0.88f, 0.97f, 0.94f, 1.41f, 1.18f, 1.39f, 1.11f, 1.33f, 1.24f, 1.03f, 1.61f, 1.80f, 1.59f, 1.91f, 1.84f, 1.76f, 1.64f, 1.38f, 1.51f, 1.71f, 1.26f, 1.50f, 1.23f, 1.19f, 1.46f, 0.99f, 1.00f, 0.91f, 1.70f, 1.85f, 1.65f, 1.93f, 1.54f, 1.76f, 1.52f, 1.48f, 1.26f, 1.28f, 1.39f, 1.09f, 0.99f, 0.97f, 1.18f, 1.31f, 1.01f, 1.05f, 0.90f, 0.93f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f},
+ {1.86f, 1.95f, 1.82f, 1.98f, 1.89f, 1.71f, 1.80f, 1.91f, 1.77f, 1.86f, 1.67f, 1.34f, 1.53f, 1.51f, 1.72f, 1.92f, 1.91f, 1.99f, 1.69f, 1.82f, 1.80f, 1.94f, 1.87f, 1.86f, 1.97f, 1.59f, 1.44f, 1.69f, 1.05f, 1.24f, 1.27f, 1.49f, 1.50f, 1.69f, 1.72f, 1.63f, 1.46f, 1.37f, 1.00f, 1.23f, 0.98f, 0.95f, 1.09f, 0.96f, 1.16f, 1.55f, 0.99f, 1.25f, 1.10f, 1.24f, 1.41f, 0.90f, 0.85f, 0.94f, 0.79f, 0.81f, 0.85f, 0.89f, 0.94f, 1.09f, 0.98f, 0.89f, 0.82f, 0.83f, 0.74f, 0.77f, 0.72f, 0.76f, 0.73f, 0.75f, 0.78f, 0.94f, 0.86f, 0.91f, 0.79f, 0.83f, 0.89f, 0.99f, 0.95f, 0.90f, 0.90f, 0.92f, 0.84f, 0.86f, 0.79f, 0.75f, 0.81f, 0.85f, 0.80f, 0.78f, 0.76f, 0.77f, 0.73f, 0.74f, 0.71f, 0.71f, 0.73f, 0.74f, 0.74f, 0.71f, 0.76f, 0.72f, 0.70f, 0.79f, 0.85f, 0.78f, 0.98f, 0.92f, 0.93f, 0.85f, 0.87f, 0.82f, 0.79f, 0.81f, 0.89f, 0.86f, 1.16f, 0.97f, 1.12f, 0.95f, 1.06f, 1.00f, 0.93f, 1.38f, 1.60f, 1.35f, 1.77f, 1.71f, 1.57f, 1.48f, 1.20f, 1.28f, 1.62f, 1.27f, 1.46f, 1.17f, 1.05f, 1.34f, 0.96f, 0.99f, 0.90f, 1.63f, 1.74f, 1.50f, 1.80f, 1.33f, 1.58f, 1.48f, 1.37f, 1.21f, 1.04f, 1.21f, 0.97f, 0.97f, 0.93f, 1.05f, 1.34f, 1.02f, 1.14f, 0.84f, 0.88f, 0.92f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f},
+ {1.74f, 1.89f, 1.76f, 1.98f, 1.89f, 1.71f, 1.93f, 1.99f, 1.91f, 1.94f, 1.82f, 1.46f, 1.60f, 1.65f, 1.80f, 1.79f, 1.77f, 1.92f, 1.57f, 1.69f, 1.74f, 1.87f, 1.88f, 1.94f, 1.98f, 1.53f, 1.45f, 1.70f, 1.18f, 1.32f, 1.42f, 1.58f, 1.65f, 1.83f, 1.81f, 1.81f, 1.67f, 1.61f, 1.19f, 1.44f, 1.17f, 1.11f, 1.36f, 1.15f, 1.41f, 1.75f, 1.22f, 1.50f, 1.34f, 1.42f, 1.61f, 0.98f, 0.92f, 1.03f, 0.83f, 0.86f, 0.89f, 0.95f, 0.98f, 1.23f, 1.14f, 0.97f, 0.89f, 0.90f, 0.78f, 0.82f, 0.76f, 0.82f, 0.77f, 0.79f, 0.84f, 0.98f, 0.90f, 0.98f, 0.83f, 0.89f, 0.97f, 1.03f, 0.95f, 0.92f, 0.86f, 0.90f, 0.82f, 0.86f, 0.79f, 0.77f, 0.84f, 0.81f, 0.76f, 0.76f, 0.72f, 0.73f, 0.70f, 0.72f, 0.71f, 0.73f, 0.73f, 0.72f, 0.74f, 0.71f, 0.78f, 0.74f, 0.72f, 0.75f, 0.80f, 0.76f, 0.94f, 0.88f, 0.91f, 0.83f, 0.87f, 0.84f, 0.79f, 0.76f, 0.82f, 0.80f, 0.97f, 0.89f, 0.96f, 0.88f, 0.95f, 0.94f, 0.87f, 1.11f, 1.37f, 1.10f, 1.59f, 1.57f, 1.37f, 1.33f, 1.05f, 1.08f, 1.54f, 1.34f, 1.46f, 1.16f, 0.99f, 1.26f, 0.96f, 1.05f, 0.92f, 1.45f, 1.55f, 1.27f, 1.60f, 1.07f, 1.34f, 1.35f, 1.18f, 1.07f, 0.93f, 0.99f, 0.90f, 0.93f, 0.87f, 0.96f, 1.27f, 0.99f, 1.15f, 0.77f, 0.82f, 0.85f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f},
+ {1.60f, 1.78f, 1.68f, 1.93f, 1.86f, 1.71f, 1.97f, 1.99f, 1.99f, 1.97f, 1.93f, 1.60f, 1.68f, 1.78f, 1.86f, 1.61f, 1.57f, 1.79f, 1.37f, 1.48f, 1.59f, 1.72f, 1.79f, 1.92f, 1.90f, 1.38f, 1.35f, 1.60f, 1.23f, 1.30f, 1.47f, 1.56f, 1.71f, 1.88f, 1.79f, 1.92f, 1.79f, 1.79f, 1.30f, 1.56f, 1.35f, 1.37f, 1.59f, 1.38f, 1.60f, 1.90f, 1.48f, 1.72f, 1.57f, 1.61f, 1.79f, 1.21f, 1.00f, 1.30f, 0.89f, 0.94f, 0.96f, 1.07f, 1.14f, 1.40f, 1.37f, 1.14f, 0.96f, 0.98f, 0.82f, 0.88f, 0.82f, 0.89f, 0.83f, 0.86f, 0.91f, 1.02f, 0.93f, 1.07f, 0.87f, 0.94f, 1.11f, 1.02f, 0.93f, 0.93f, 0.82f, 0.87f, 0.80f, 0.85f, 0.79f, 0.80f, 0.85f, 0.77f, 0.72f, 0.74f, 0.71f, 0.70f, 0.70f, 0.71f, 0.72f, 0.77f, 0.74f, 0.72f, 0.76f, 0.73f, 0.82f, 0.79f, 0.76f, 0.73f, 0.79f, 0.76f, 0.93f, 0.86f, 0.91f, 0.83f, 0.89f, 0.89f, 0.82f, 0.72f, 0.76f, 0.76f, 0.89f, 0.82f, 0.89f, 0.82f, 0.89f, 0.91f, 0.83f, 0.96f, 1.14f, 0.97f, 1.40f, 1.44f, 1.19f, 1.22f, 0.99f, 0.98f, 1.49f, 1.44f, 1.49f, 1.22f, 0.99f, 1.23f, 0.98f, 1.19f, 0.97f, 1.21f, 1.30f, 1.00f, 1.37f, 0.94f, 1.07f, 1.14f, 0.98f, 0.96f, 0.86f, 0.91f, 0.83f, 0.88f, 0.82f, 0.89f, 1.11f, 0.94f, 1.07f, 0.73f, 0.76f, 0.79f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f},
+ {1.46f, 1.65f, 1.60f, 1.82f, 1.80f, 1.71f, 1.93f, 1.91f, 1.99f, 1.94f, 1.98f, 1.74f, 1.76f, 1.89f, 1.89f, 1.42f, 1.34f, 1.61f, 1.11f, 1.22f, 1.36f, 1.50f, 1.61f, 1.81f, 1.75f, 1.15f, 1.17f, 1.41f, 1.18f, 1.19f, 1.42f, 1.44f, 1.65f, 1.83f, 1.67f, 1.94f, 1.81f, 1.88f, 1.32f, 1.58f, 1.45f, 1.57f, 1.74f, 1.53f, 1.70f, 1.98f, 1.69f, 1.87f, 1.77f, 1.79f, 1.92f, 1.45f, 1.27f, 1.55f, 0.97f, 1.07f, 1.11f, 1.34f, 1.37f, 1.59f, 1.60f, 1.35f, 1.07f, 1.18f, 0.86f, 0.93f, 0.87f, 0.96f, 0.90f, 0.93f, 0.99f, 1.03f, 0.95f, 1.15f, 0.90f, 0.99f, 1.27f, 0.98f, 0.90f, 0.92f, 0.78f, 0.83f, 0.77f, 0.84f, 0.79f, 0.82f, 0.86f, 0.73f, 0.71f, 0.73f, 0.72f, 0.70f, 0.73f, 0.72f, 0.76f, 0.81f, 0.76f, 0.76f, 0.82f, 0.77f, 0.89f, 0.85f, 0.82f, 0.75f, 0.80f, 0.80f, 0.94f, 0.88f, 0.94f, 0.87f, 0.95f, 0.96f, 0.88f, 0.72f, 0.74f, 0.76f, 0.83f, 0.78f, 0.84f, 0.79f, 0.87f, 0.91f, 0.83f, 0.89f, 0.98f, 0.92f, 1.23f, 1.34f, 1.05f, 1.16f, 0.99f, 0.96f, 1.46f, 1.57f, 1.54f, 1.33f, 1.05f, 1.26f, 1.08f, 1.37f, 1.10f, 0.98f, 1.03f, 0.92f, 1.14f, 0.86f, 0.95f, 0.97f, 0.90f, 0.89f, 0.79f, 0.84f, 0.77f, 0.82f, 0.76f, 0.82f, 0.97f, 0.89f, 0.98f, 0.71f, 0.72f, 0.74f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f},
+ {1.34f, 1.51f, 1.53f, 1.67f, 1.72f, 1.71f, 1.80f, 1.77f, 1.91f, 1.86f, 1.98f, 1.86f, 1.82f, 1.95f, 1.89f, 1.24f, 1.10f, 1.41f, 0.95f, 0.99f, 1.09f, 1.25f, 1.37f, 1.63f, 1.55f, 0.96f, 0.98f, 1.16f, 1.05f, 1.00f, 1.27f, 1.23f, 1.50f, 1.69f, 1.46f, 1.86f, 1.72f, 1.87f, 1.24f, 1.49f, 1.44f, 1.69f, 1.80f, 1.59f, 1.69f, 1.97f, 1.82f, 1.94f, 1.91f, 1.92f, 1.99f, 1.63f, 1.50f, 1.74f, 1.16f, 1.33f, 1.38f, 1.58f, 1.60f, 1.77f, 1.80f, 1.48f, 1.21f, 1.37f, 0.90f, 0.97f, 0.93f, 1.05f, 0.97f, 1.04f, 1.21f, 0.99f, 0.95f, 1.14f, 0.92f, 1.02f, 1.34f, 0.94f, 0.86f, 0.90f, 0.74f, 0.79f, 0.75f, 0.81f, 0.79f, 0.84f, 0.86f, 0.71f, 0.71f, 0.73f, 0.76f, 0.73f, 0.77f, 0.74f, 0.80f, 0.85f, 0.78f, 0.81f, 0.89f, 0.84f, 0.97f, 0.92f, 0.88f, 0.79f, 0.85f, 0.86f, 0.98f, 0.92f, 1.00f, 0.93f, 1.06f, 1.12f, 0.95f, 0.74f, 0.74f, 0.78f, 0.79f, 0.76f, 0.82f, 0.79f, 0.87f, 0.93f, 0.85f, 0.85f, 0.94f, 0.90f, 1.09f, 1.27f, 0.99f, 1.17f, 1.05f, 0.96f, 1.46f, 1.71f, 1.62f, 1.48f, 1.20f, 1.34f, 1.28f, 1.57f, 1.35f, 0.90f, 0.94f, 0.85f, 0.98f, 0.81f, 0.89f, 0.89f, 0.83f, 0.82f, 0.75f, 0.78f, 0.73f, 0.77f, 0.72f, 0.76f, 0.89f, 0.83f, 0.91f, 0.71f, 0.70f, 0.72f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f},
+ {1.26f, 1.39f, 1.48f, 1.51f, 1.64f, 1.71f, 1.60f, 1.58f, 1.77f, 1.74f, 1.91f, 1.94f, 1.87f, 1.97f, 1.85f, 1.10f, 0.97f, 1.22f, 0.88f, 0.92f, 0.95f, 1.01f, 1.11f, 1.39f, 1.32f, 0.88f, 0.90f, 0.97f, 0.96f, 0.93f, 1.05f, 0.99f, 1.27f, 1.47f, 1.20f, 1.70f, 1.54f, 1.76f, 1.08f, 1.31f, 1.33f, 1.70f, 1.76f, 1.55f, 1.57f, 1.88f, 1.85f, 1.91f, 1.97f, 1.99f, 1.99f, 1.70f, 1.65f, 1.85f, 1.41f, 1.54f, 1.61f, 1.76f, 1.80f, 1.91f, 1.93f, 1.52f, 1.26f, 1.48f, 0.92f, 0.99f, 0.97f, 1.18f, 1.09f, 1.28f, 1.39f, 0.94f, 0.93f, 1.05f, 0.92f, 1.01f, 1.31f, 0.88f, 0.81f, 0.86f, 0.72f, 0.75f, 0.74f, 0.79f, 0.79f, 0.86f, 0.85f, 0.71f, 0.73f, 0.75f, 0.82f, 0.77f, 0.83f, 0.78f, 0.85f, 0.88f, 0.81f, 0.88f, 0.97f, 0.90f, 1.18f, 1.00f, 0.93f, 0.86f, 0.92f, 0.94f, 1.14f, 0.99f, 1.24f, 1.03f, 1.33f, 1.39f, 1.11f, 0.79f, 0.77f, 0.84f, 0.79f, 0.77f, 0.84f, 0.83f, 0.90f, 0.98f, 0.91f, 0.85f, 0.92f, 0.91f, 1.02f, 1.26f, 1.00f, 1.23f, 1.19f, 0.99f, 1.50f, 1.84f, 1.71f, 1.64f, 1.38f, 1.46f, 1.51f, 1.76f, 1.59f, 0.84f, 0.88f, 0.80f, 0.94f, 0.79f, 0.86f, 0.82f, 0.77f, 0.76f, 0.74f, 0.74f, 0.71f, 0.73f, 0.70f, 0.72f, 0.82f, 0.77f, 0.85f, 0.74f, 0.70f, 0.73f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f}
+ };
+
+}
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.render.lwjgl;
+
+import org.lwjgl.opengl.GL11;
+
+/**
+ * Base
+ *
+ * @author dsanders/cwei
+ */
+abstract class Base extends LWJGLBase {
+
+ static final int GL_COLOR_INDEX8_EXT = GL11.GL_COLOR_INDEX;
+
+ static final String REF_VERSION = "GL 0.01";
+
+ // up / down
+ static final int PITCH = 0;
+
+ // left / right
+ static final int YAW = 1;
+
+ /*
+ * skins will be outline flood filled and mip mapped pics and sprites with
+ * alpha will be outline flood filled pic won't be mip mapped
+ *
+ * model skin sprite frame wall texture pic
+ */
+ // enum imagetype_t
+ static final int it_skin = 0;
+
+ static final int it_sprite = 1;
+
+ static final int it_wall = 2;
+
+ static final int it_pic = 3;
+
+ static final int it_sky = 4;
+
+ static final int mod_brush = 1;
+
+ static final int mod_sprite = 2;
+
+ static final int mod_alias = 3;
+
+ static final int TEXNUM_LIGHTMAPS = 1024;
+
+ static final int TEXNUM_SCRAPS = 1152;
+
+ static final int TEXNUM_IMAGES = 1153;
+
+ static final int MAX_GLTEXTURES = 1024;
+
+ static final int MAX_LBM_HEIGHT = 480;
+
+ static final float BACKFACE_EPSILON = 0.01f;
+
+ /*
+ * * GL config stuff
+ */
+ static final int GL_RENDERER_VOODOO = 0x00000001;
+
+ static final int GL_RENDERER_VOODOO2 = 0x00000002;
+
+ static final int GL_RENDERER_VOODOO_RUSH = 0x00000004;
+
+ static final int GL_RENDERER_PCX2 = 0x00000020;
+
+ static final int GL_RENDERER_POWERVR = 0x00000070;
+
+ static final int GL_RENDERER_PERMEDIA2 = 0x00000100;
+
+ static final int GL_RENDERER_GLINT_MX = 0x00000200;
+
+ static final int GL_RENDERER_3DLABS = 0x00000F00;
+
+ static final int GL_RENDERER_REALIZM = 0x00001000;
+
+ static final int GL_RENDERER_RENDITION = 0x001C0000;
+
+ static final int GL_RENDERER_SGI = 0x00F00000;
+
+ static final int GL_RENDERER_MCD = 0x01000000;
+
+ static final int GL_RENDERER_OTHER = 0x80000000;
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.render.lwjgl;
+
+import lwjake2.Defines;
+import lwjake2.client.VideoDriver;
+import lwjake2.qcommon.Com;
+import lwjake2.render.Image;
+import lwjake2.util.Lib;
+import org.lwjgl.opengl.GL11;
+
+import java.awt.*;
+import java.nio.ByteBuffer;
+import java.nio.IntBuffer;
+
+/**
+ * Draw
+ * (gl_draw.c)
+ *
+ * @author cwei
+ */
+public abstract class Draw extends lwjake2.render.lwjgl.Image {
+
+ // allocate a 256 * 256 texture buffer
+ private final ByteBuffer image8 = Lib.newByteBuffer(256 * 256 * Defines.SIZE_OF_INT);
+ // share the buffer
+ private final IntBuffer image32 = image8.asIntBuffer();
+
+ /*
+ ===============
+ Draw_InitLocal
+ ===============
+ */
+ void Draw_InitLocal() {
+ // load console characters (don't bilerp characters)
+ draw_chars = GL_FindImage("pics/conchars.pcx", it_pic);
+ GL_Bind(draw_chars.texnum);
+ GL11.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_NEAREST);
+ GL11.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_NEAREST);
+ }
+
+ /*
+ ================
+ Draw_Char
+
+ Draws one 8*8 graphics character with 0 being transparent.
+ It can be clipped to the top of the screen to allow the console to be
+ smoothly scrolled off.
+ ================
+ */
+ protected void Draw_Char(int x, int y, int num) {
+
+ num &= 255;
+
+ if ((num & 127) == 32) return; // space
+
+ if (y <= -8) return; // totally off screen
+
+ int row = num >> 4;
+ int col = num & 15;
+
+ float frow = row * 0.0625f;
+ float fcol = col * 0.0625f;
+ float size = 0.0625f;
+
+ GL_Bind(draw_chars.texnum);
+
+ GL11.glBegin(GL11.GL_QUADS);
+ GL11.glTexCoord2f(fcol, frow);
+ GL11.glVertex2f(x, y);
+ GL11.glTexCoord2f(fcol + size, frow);
+ GL11.glVertex2f(x + 8, y);
+ GL11.glTexCoord2f(fcol + size, frow + size);
+ GL11.glVertex2f(x + 8, y + 8);
+ GL11.glTexCoord2f(fcol, frow + size);
+ GL11.glVertex2f(x, y + 8);
+ GL11.glEnd();
+ }
+
+ /*
+ =============
+ Draw_FindPic
+ =============
+ */
+ protected Image Draw_FindPic(String name) {
+ Image image = null;
+ String fullname;
+
+ if (!name.startsWith("/") && !name.startsWith("\\")) {
+ fullname = "pics/" + name + ".pcx";
+ image = GL_FindImage(fullname, it_pic);
+ } else {
+ image = GL_FindImage(name.substring(1), it_pic);
+ }
+ return image;
+ }
+
+ /*
+ =============
+ Draw_GetPicSize
+ =============
+ */
+ protected void Draw_GetPicSize(Dimension dim, String pic) {
+
+ Image image = Draw_FindPic(pic);
+ dim.width = (image != null) ? image.width : -1;
+ dim.height = (image != null) ? image.height : -1;
+ }
+
+ /*
+ =============
+ Draw_StretchPic
+ =============
+ */
+ protected void Draw_StretchPic(int x, int y, int w, int h, String pic) {
+
+ Image image;
+
+ image = Draw_FindPic(pic);
+ if (image == null) {
+ VideoDriver.Printf(Defines.PRINT_ALL, "Can't find pic: " + pic + '\n');
+ return;
+ }
+
+ if (scrap_dirty)
+ Scrap_Upload();
+
+ if (((gl_config.renderer == GL_RENDERER_MCD) || ((gl_config.renderer & GL_RENDERER_RENDITION) != 0)) && !image.has_alpha)
+ GL11.glDisable(GL11.GL_ALPHA_TEST);
+
+ GL_Bind(image.texnum);
+ GL11.glBegin(GL11.GL_QUADS);
+ GL11.glTexCoord2f(image.sl, image.tl);
+ GL11.glVertex2f(x, y);
+ GL11.glTexCoord2f(image.sh, image.tl);
+ GL11.glVertex2f(x + w, y);
+ GL11.glTexCoord2f(image.sh, image.th);
+ GL11.glVertex2f(x + w, y + h);
+ GL11.glTexCoord2f(image.sl, image.th);
+ GL11.glVertex2f(x, y + h);
+ GL11.glEnd();
+
+ if (((gl_config.renderer == GL_RENDERER_MCD) || ((gl_config.renderer & GL_RENDERER_RENDITION) != 0)) && !image.has_alpha)
+ GL11.glEnable(GL11.GL_ALPHA_TEST);
+ }
+
+ /*
+ =============
+ Draw_Pic
+ =============
+ */
+ protected void Draw_Pic(int x, int y, String pic) {
+ Image image;
+
+ image = Draw_FindPic(pic);
+ if (image == null) {
+ VideoDriver.Printf(Defines.PRINT_ALL, "Can't find pic: " + pic + '\n');
+ return;
+ }
+ if (scrap_dirty)
+ Scrap_Upload();
+
+ if (((gl_config.renderer == GL_RENDERER_MCD) || ((gl_config.renderer & GL_RENDERER_RENDITION) != 0)) && !image.has_alpha)
+ GL11.glDisable(GL11.GL_ALPHA_TEST);
+
+ GL_Bind(image.texnum);
+
+ GL11.glBegin(GL11.GL_QUADS);
+ GL11.glTexCoord2f(image.sl, image.tl);
+ GL11.glVertex2f(x, y);
+ GL11.glTexCoord2f(image.sh, image.tl);
+ GL11.glVertex2f(x + image.width, y);
+ GL11.glTexCoord2f(image.sh, image.th);
+ GL11.glVertex2f(x + image.width, y + image.height);
+ GL11.glTexCoord2f(image.sl, image.th);
+ GL11.glVertex2f(x, y + image.height);
+ GL11.glEnd();
+
+ if (((gl_config.renderer == GL_RENDERER_MCD) || ((gl_config.renderer & GL_RENDERER_RENDITION) != 0)) && !image.has_alpha)
+ GL11.glEnable(GL11.GL_ALPHA_TEST);
+ }
+
+ //=============================================================================
+
+ /*
+ =============
+ Draw_TileClear
+
+ This repeats a 64*64 tile graphic to fill the screen around a sized down
+ refresh window.
+ =============
+ */
+ protected void Draw_TileClear(int x, int y, int w, int h, String pic) {
+ Image image;
+
+ image = Draw_FindPic(pic);
+ if (image == null) {
+ VideoDriver.Printf(Defines.PRINT_ALL, "Can't find pic: " + pic + '\n');
+ return;
+ }
+
+ if (((gl_config.renderer == GL_RENDERER_MCD) || ((gl_config.renderer & GL_RENDERER_RENDITION) != 0)) && !image.has_alpha)
+ GL11.glDisable(GL11.GL_ALPHA_TEST);
+
+ GL_Bind(image.texnum);
+ GL11.glBegin(GL11.GL_QUADS);
+ GL11.glTexCoord2f(x / 64.0f, y / 64.0f);
+ GL11.glVertex2f(x, y);
+ GL11.glTexCoord2f((x + w) / 64.0f, y / 64.0f);
+ GL11.glVertex2f(x + w, y);
+ GL11.glTexCoord2f((x + w) / 64.0f, (y + h) / 64.0f);
+ GL11.glVertex2f(x + w, y + h);
+ GL11.glTexCoord2f(x / 64.0f, (y + h) / 64.0f);
+ GL11.glVertex2f(x, y + h);
+ GL11.glEnd();
+
+ if (((gl_config.renderer == GL_RENDERER_MCD) || ((gl_config.renderer & GL_RENDERER_RENDITION) != 0)) && !image.has_alpha)
+ GL11.glEnable(GL11.GL_ALPHA_TEST);
+ }
+
+// ====================================================================
+
+ /*
+ =============
+ Draw_Fill
+
+ Fills a box of pixels with a single color
+ =============
+ */
+ protected void Draw_Fill(int x, int y, int w, int h, int colorIndex) {
+
+ if (colorIndex > 255)
+ Com.Error(Defines.ERR_FATAL, "Draw_Fill: bad color");
+
+ GL11.glDisable(GL11.GL_TEXTURE_2D);
+
+ int color = d_8to24table[colorIndex];
+
+ GL11.glColor3ub(
+ (byte) ((color) & 0xff), // r
+ (byte) ((color >> 8) & 0xff), // g
+ (byte) ((color >> 16) & 0xff) // b
+ );
+
+ GL11.glBegin(GL11.GL_QUADS);
+
+ GL11.glVertex2f(x, y);
+ GL11.glVertex2f(x + w, y);
+ GL11.glVertex2f(x + w, y + h);
+ GL11.glVertex2f(x, y + h);
+
+ GL11.glEnd();
+ GL11.glColor3f(1, 1, 1);
+ GL11.glEnable(GL11.GL_TEXTURE_2D);
+ }
+
+ /*
+ ================
+ Draw_FadeScreen
+ ================
+ */
+ protected void Draw_FadeScreen() {
+ GL11.glEnable(GL11.GL_BLEND);
+ GL11.glDisable(GL11.GL_TEXTURE_2D);
+ GL11.glColor4f(0, 0, 0, 0.8f);
+ GL11.glBegin(GL11.GL_QUADS);
+
+ GL11.glVertex2f(0, 0);
+ GL11.glVertex2f(vid.width, 0);
+ GL11.glVertex2f(vid.width, vid.height);
+ GL11.glVertex2f(0, vid.height);
+
+ GL11.glEnd();
+ GL11.glColor4f(1, 1, 1, 1);
+ GL11.glEnable(GL11.GL_TEXTURE_2D);
+ GL11.glDisable(GL11.GL_BLEND);
+ }
+
+ /*
+ =============
+ Draw_StretchRaw
+ =============
+ */
+ protected void Draw_StretchRaw(int x, int y, int w, int h, int cols, int rows, byte[] data) {
+ int i, j, trows;
+ int sourceIndex;
+ int frac, fracstep;
+ float hscale;
+ int row;
+ float t;
+
+ GL_Bind(0);
+
+ if (rows <= 256) {
+ hscale = 1;
+ trows = rows;
+ } else {
+ hscale = rows / 256.0f;
+ trows = 256;
+ }
+ t = rows * hscale / 256;
+
+ if (!qglColorTableEXT) {
+ //int[] image32 = new int[256*256];
+ image32.clear();
+ int destIndex = 0;
+
+ for (i = 0; i < trows; i++) {
+ row = (int) (i * hscale);
+ if (row > rows)
+ break;
+ sourceIndex = cols * row;
+ destIndex = i * 256;
+ fracstep = cols * 0x10000 / 256;
+ frac = fracstep >> 1;
+ for (j = 0; j < 256; j++) {
+ image32.put(destIndex + j, r_rawpalette[data[sourceIndex + (frac >> 16)] & 0xff]);
+ frac += fracstep;
+ }
+ }
+ GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, gl_tex_solid_format, 256, 256, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, image32);
+ } else {
+ //byte[] image8 = new byte[256*256];
+ image8.clear();
+ int destIndex = 0;
+
+ for (i = 0; i < trows; i++) {
+ row = (int) (i * hscale);
+ if (row > rows)
+ break;
+ sourceIndex = cols * row;
+ destIndex = i * 256;
+ fracstep = cols * 0x10000 / 256;
+ frac = fracstep >> 1;
+ for (j = 0; j < 256; j++) {
+ image8.put(destIndex + j, data[sourceIndex + (frac >> 16)]);
+ frac += fracstep;
+ }
+ }
+
+ GL11.glTexImage2D(GL11.GL_TEXTURE_2D,
+ 0,
+ GL_COLOR_INDEX8_EXT,
+ 256, 256,
+ 0,
+ GL11.GL_COLOR_INDEX,
+ GL11.GL_UNSIGNED_BYTE,
+ image8);
+ }
+ GL11.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR);
+ GL11.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR);
+
+ if ((gl_config.renderer == GL_RENDERER_MCD) || ((gl_config.renderer & GL_RENDERER_RENDITION) != 0))
+ GL11.glDisable(GL11.GL_ALPHA_TEST);
+
+ GL11.glBegin(GL11.GL_QUADS);
+ GL11.glTexCoord2f(0, 0);
+ GL11.glVertex2f(x, y);
+ GL11.glTexCoord2f(1, 0);
+ GL11.glVertex2f(x + w, y);
+ GL11.glTexCoord2f(1, t);
+ GL11.glVertex2f(x + w, y + h);
+ GL11.glTexCoord2f(0, t);
+ GL11.glVertex2f(x, y + h);
+ GL11.glEnd();
+
+ if ((gl_config.renderer == GL_RENDERER_MCD) || ((gl_config.renderer & GL_RENDERER_RENDITION) != 0))
+ GL11.glEnable(GL11.GL_ALPHA_TEST);
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.render.lwjgl;
+
+import lwjake2.Defines;
+import lwjake2.client.VideoDriver;
+import lwjake2.client.particle_t;
+import lwjake2.game.CvarT;
+import lwjake2.qcommon.Com;
+import lwjake2.qcommon.Cvar;
+import lwjake2.qcommon.FS;
+import lwjake2.qcommon.qfiles;
+import lwjake2.util.Lib;
+import lwjake2.util.Vargs;
+import org.lwjgl.BufferUtils;
+import org.lwjgl.opengl.ARBImaging;
+import org.lwjgl.opengl.ARBMultitexture;
+import org.lwjgl.opengl.EXTSharedTexturePalette;
+import org.lwjgl.opengl.GL11;
+
+import java.awt.*;
+import java.awt.geom.AffineTransform;
+import java.awt.image.AffineTransformOp;
+import java.awt.image.BufferedImage;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.IntBuffer;
+import java.util.Arrays;
+
+/**
+ * Image
+ *
+ * @author cwei
+ */
+public abstract class Image extends Main {
+
+ static final glmode_t modes[] =
+ {
+ new glmode_t("GL_NEAREST", GL11.GL_NEAREST, GL11.GL_NEAREST),
+ new glmode_t("GL_LINEAR", GL11.GL_LINEAR, GL11.GL_LINEAR),
+ new glmode_t("GL_NEAREST_MIPMAP_NEAREST", GL11.GL_NEAREST_MIPMAP_NEAREST, GL11.GL_NEAREST),
+ new glmode_t("GL_LINEAR_MIPMAP_NEAREST", GL11.GL_LINEAR_MIPMAP_NEAREST, GL11.GL_LINEAR),
+ new glmode_t("GL_NEAREST_MIPMAP_LINEAR", GL11.GL_NEAREST_MIPMAP_LINEAR, GL11.GL_NEAREST),
+ new glmode_t("GL_LINEAR_MIPMAP_LINEAR", GL11.GL_LINEAR_MIPMAP_LINEAR, GL11.GL_LINEAR)};
+ static final int NUM_GL_MODES = modes.length;
+ static final gltmode_t[] gl_alpha_modes =
+ {
+ new gltmode_t("default", 4),
+ new gltmode_t("GL_RGBA", GL11.GL_RGBA),
+ new gltmode_t("GL_RGBA8", GL11.GL_RGBA8),
+ new gltmode_t("GL_RGB5_A1", GL11.GL_RGB5_A1),
+ new gltmode_t("GL_RGBA4", GL11.GL_RGBA4),
+ new gltmode_t("GL_RGBA2", GL11.GL_RGBA2),
+ };
+ static final int NUM_GL_ALPHA_MODES = gl_alpha_modes.length;
+ static final gltmode_t[] gl_solid_modes =
+ {
+ new gltmode_t("default", 3),
+ new gltmode_t("GL_RGB", GL11.GL_RGB),
+ new gltmode_t("GL_RGB8", GL11.GL_RGB8),
+ new gltmode_t("GL_RGB5", GL11.GL_RGB5),
+ new gltmode_t("GL_RGB4", GL11.GL_RGB4),
+ new gltmode_t("GL_R3_G3_B2", GL11.GL_R3_G3_B2),
+ // #ifdef GL_RGB2_EXT
+ //new gltmode_t("GL_RGB2", GL.GL_RGB2_EXT)
+ // #endif
+ };
+ static final int NUM_GL_SOLID_MODES = gl_solid_modes.length;
+ static final int MAX_SCRAPS = 1;
+
+ //
+ // qboolean GL_Upload8 (byte *data, int width, int height, qboolean mipmap, qboolean is_sky );
+ // qboolean GL_Upload32 (unsigned *data, int width, int height, qboolean mipmap);
+ //
+ static final int BLOCK_WIDTH = 256;
+ static final int BLOCK_HEIGHT = 256;
+ // must be a power of 2
+ static final int FLOODFILL_FIFO_SIZE = 0x1000;
+ static final int FLOODFILL_FIFO_MASK = FLOODFILL_FIFO_SIZE - 1;
+ // void FLOODFILL_STEP( int off, int dx, int dy )
+ // {
+ // if (pos[off] == fillcolor)
+ // {
+ // pos[off] = 255;
+ // fifo[inpt].x = x + dx; fifo[inpt].y = y + dy;
+ // inpt = (inpt + 1) & FLOODFILL_FIFO_MASK;
+ // }
+ // else if (pos[off] != 255) fdc = pos[off];
+ // }
+ static final floodfill_t[] fifo = new floodfill_t[FLOODFILL_FIFO_SIZE];
+
+ static {
+ for (int j = 0; j < fifo.length; j++) {
+ fifo[j] = new floodfill_t();
+ }
+ }
+
+ final lwjake2.render.Image[] gltextures = new lwjake2.render.Image[MAX_GLTEXTURES];
+ final byte[] intensitytable = new byte[256];
+ final byte[] gammatable = new byte[256];
+ final int gl_solid_format = 3;
+ final int gl_alpha_format = 4;
+ final int[] lastmodes = {-1, -1};
+ final int[][] scrap_allocated = new int[MAX_SCRAPS][BLOCK_WIDTH];
+ final byte[][] scrap_texels = new byte[MAX_SCRAPS][BLOCK_WIDTH * BLOCK_HEIGHT];
+ /*
+ =============================================================================
+
+ scrap allocation
+
+ Allocate all the little status bar objects into a single texture
+ to crutch up inefficient hardware / drivers
+
+ =============================================================================
+ */
+ /*
+ ===============
+ GL_Upload32
+
+ Returns has_alpha
+ ===============
+ */
+ final int[] scaled = new int[256 * 256];
+ //byte[] paletted_texture = new byte[256 * 256];
+ final ByteBuffer paletted_texture = BufferUtils.createByteBuffer(256 * 256);
+ final IntBuffer tex = Lib.newIntBuffer(512 * 256, ByteOrder.LITTLE_ENDIAN);
+ final int[] trans = new int[512 * 256];
+ final IntBuffer texnumBuffer = BufferUtils.createIntBuffer(1);
+ private final Throwable gotoBreakOut = new Throwable();
+ private final Throwable gotoDone = gotoBreakOut;
+ lwjake2.render.Image draw_chars;
+ //Map gltextures = new Hashtable(MAX_GLTEXTURES); // image_t
+ int numgltextures;
+ int base_textureid; // gltextures[i] = base_textureid+i
+ CvarT intensity;
+ int gl_tex_solid_format = 3;
+ int gl_tex_alpha_format = 4;
+ int gl_filter_min = GL11.GL_LINEAR_MIPMAP_NEAREST;
+ int gl_filter_max = GL11.GL_LINEAR;
+ boolean scrap_dirty;
+ int scrap_uploads = 0;
+ int upload_width, upload_height;
+ boolean uploaded_paletted;
+
+ Image() {
+ // init the texture cache
+ for (int i = 0; i < gltextures.length; i++) {
+ gltextures[i] = new lwjake2.render.Image(i);
+ }
+ numgltextures = 0;
+ }
+
+ void GL_SetTexturePalette(int[] palette) {
+
+ assert (palette != null && palette.length == 256) : "int palette[256] bug";
+
+ int i;
+ //byte[] temptable = new byte[768];
+
+ if (qglColorTableEXT && gl_ext_palettedtexture.value != 0.0f) {
+ ByteBuffer temptable = BufferUtils.createByteBuffer(768);
+ for (i = 0; i < 256; i++) {
+ temptable.put(i * 3, (byte) ((palette[i]) & 0xff));
+ temptable.put(i * 3 + 1, (byte) ((palette[i] >> 8) & 0xff));
+ temptable.put(i * 3 + 2, (byte) ((palette[i] >> 16) & 0xff));
+ }
+
+ ARBImaging.glColorTable(EXTSharedTexturePalette.GL_SHARED_TEXTURE_PALETTE_EXT, GL11.GL_RGB, 256, GL11.GL_RGB, GL11.GL_UNSIGNED_BYTE, temptable);
+ }
+ }
+
+ void GL_EnableMultitexture(boolean enable) {
+ if (enable) {
+ GL_SelectTexture(GL_TEXTURE1);
+ GL11.glEnable(GL11.GL_TEXTURE_2D);
+ GL_TexEnv(GL11.GL_REPLACE);
+ } else {
+ GL_SelectTexture(GL_TEXTURE1);
+ GL11.glDisable(GL11.GL_TEXTURE_2D);
+ GL_TexEnv(GL11.GL_REPLACE);
+ }
+ GL_SelectTexture(GL_TEXTURE0);
+ GL_TexEnv(GL11.GL_REPLACE);
+ }
+
+ /*
+ =================================================================
+
+ PCX LOADING
+
+ =================================================================
+ */
+
+ void GL_SelectTexture(int texture /* GLenum */) {
+ int tmu;
+
+ tmu = (texture == GL_TEXTURE0) ? 0 : 1;
+
+ if (tmu == gl_state.currenttmu) {
+ return;
+ }
+
+ gl_state.currenttmu = tmu;
+
+ ARBMultitexture.glActiveTextureARB(texture);
+ ARBMultitexture.glClientActiveTextureARB(texture);
+ }
+
+ void GL_TexEnv(int mode /* GLenum */
+ ) {
+
+ if (mode != lastmodes[gl_state.currenttmu]) {
+ GL11.glTexEnvi(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, mode);
+ lastmodes[gl_state.currenttmu] = mode;
+ }
+ }
+
+ void GL_Bind(int texnum) {
+
+ if ((gl_nobind.value != 0) && (draw_chars != null)) {
+ // performance evaluation option
+ texnum = draw_chars.texnum;
+ }
+ if (gl_state.currenttextures[gl_state.currenttmu] == texnum)
+ return;
+
+ gl_state.currenttextures[gl_state.currenttmu] = texnum;
+ GL11.glBindTexture(GL11.GL_TEXTURE_2D, texnum);
+ }
+
+ void GL_MBind(int target /* GLenum */, int texnum) {
+ GL_SelectTexture(target);
+ if (target == GL_TEXTURE0) {
+ if (gl_state.currenttextures[0] == texnum)
+ return;
+ } else {
+ if (gl_state.currenttextures[1] == texnum)
+ return;
+ }
+ GL_Bind(texnum);
+ }
+
+ /*
+ ====================================================================
+
+ IMAGE FLOOD FILLING
+
+ ====================================================================
+ */
+
+ /*
+ =================
+ Mod_FloodFillSkin
+
+ Fill background pixels so mipmapping doesn't have haloes
+ =================
+ */
+
+ /*
+ ===============
+ GL_TextureMode
+ ===============
+ */
+ void GL_TextureMode(String string) {
+
+ int i;
+ for (i = 0; i < NUM_GL_MODES; i++) {
+ if (modes[i].name.equalsIgnoreCase(string))
+ break;
+ }
+
+ if (i == NUM_GL_MODES) {
+ VideoDriver.Printf(Defines.PRINT_ALL, "bad filter name: [" + string + "]\n");
+ return;
+ }
+
+ gl_filter_min = modes[i].minimize;
+ gl_filter_max = modes[i].maximize;
+
+ lwjake2.render.Image glt;
+ // change all the existing mipmap texture objects
+ for (i = 0; i < numgltextures; i++) {
+ glt = gltextures[i];
+
+ if (glt.type != it_pic && glt.type != it_sky) {
+ GL_Bind(glt.texnum);
+ GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, gl_filter_min);
+ GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, gl_filter_max);
+ }
+ }
+ }
+
+ /*
+ ===============
+ GL_TextureAlphaMode
+ ===============
+ */
+ void GL_TextureAlphaMode(String string) {
+
+ int i;
+ for (i = 0; i < NUM_GL_ALPHA_MODES; i++) {
+ if (gl_alpha_modes[i].name.equalsIgnoreCase(string))
+ break;
+ }
+
+ if (i == NUM_GL_ALPHA_MODES) {
+ VideoDriver.Printf(Defines.PRINT_ALL, "bad alpha texture mode name: [" + string + "]\n");
+ return;
+ }
+
+ gl_tex_alpha_format = gl_alpha_modes[i].mode;
+ }
+
+ /*
+ ===============
+ GL_TextureSolidMode
+ ===============
+ */
+ void GL_TextureSolidMode(String string) {
+ int i;
+ for (i = 0; i < NUM_GL_SOLID_MODES; i++) {
+ if (gl_solid_modes[i].name.equalsIgnoreCase(string))
+ break;
+ }
+
+ if (i == NUM_GL_SOLID_MODES) {
+ VideoDriver.Printf(Defines.PRINT_ALL, "bad solid texture mode name: [" + string + "]\n");
+ return;
+ }
+
+ gl_tex_solid_format = gl_solid_modes[i].mode;
+ }
+ //
+ // #define FLOODFILL_STEP( off, dx, dy ) \
+ // { \
+ // if (pos[off] == fillcolor) \
+ // { \
+ // pos[off] = 255; \
+ // fifo[inpt].x = x + (dx), fifo[inpt].y = y + (dy); \
+ // inpt = (inpt + 1) & FLOODFILL_FIFO_MASK; \
+ // } \
+ // else if (pos[off] != 255) fdc = pos[off]; \
+ // }
+
+ /*
+ ===============
+ GL_ImageList_f
+ ===============
+ */
+ void GL_ImageList_f() {
+
+ lwjake2.render.Image image;
+ int texels;
+ final String[] palstrings = {"RGB", "PAL"};
+
+ VideoDriver.Printf(Defines.PRINT_ALL, "------------------\n");
+ texels = 0;
+
+ for (int i = 0; i < numgltextures; i++) {
+ image = gltextures[i];
+ if (image.texnum <= 0)
+ continue;
+
+ texels += image.upload_width * image.upload_height;
+ switch (image.type) {
+ case it_skin:
+ VideoDriver.Printf(Defines.PRINT_ALL, "M");
+ break;
+ case it_sprite:
+ VideoDriver.Printf(Defines.PRINT_ALL, "S");
+ break;
+ case it_wall:
+ VideoDriver.Printf(Defines.PRINT_ALL, "W");
+ break;
+ case it_pic:
+ VideoDriver.Printf(Defines.PRINT_ALL, "P");
+ break;
+ default:
+ VideoDriver.Printf(Defines.PRINT_ALL, " ");
+ break;
+ }
+
+ VideoDriver.Printf(
+ Defines.PRINT_ALL,
+ " %3i %3i %s: %s\n",
+ new Vargs(4).add(image.upload_width).add(image.upload_height).add(palstrings[(image.paletted) ? 1 : 0]).add(
+ image.name));
+ }
+ VideoDriver.Printf(Defines.PRINT_ALL, "Total texel count (not counting mipmaps): " + texels + '\n');
+ }
+
+ // returns a texture number and the position inside it
+ int Scrap_AllocBlock(int w, int h, pos_t pos) {
+ int i, j;
+ int best, best2;
+ int texnum;
+
+ for (texnum = 0; texnum < MAX_SCRAPS; texnum++) {
+ best = BLOCK_HEIGHT;
+
+ for (i = 0; i < BLOCK_WIDTH - w; i++) {
+ best2 = 0;
+
+ for (j = 0; j < w; j++) {
+ if (scrap_allocated[texnum][i + j] >= best)
+ break;
+ if (scrap_allocated[texnum][i + j] > best2)
+ best2 = scrap_allocated[texnum][i + j];
+ }
+ if (j == w) { // this is a valid spot
+ pos.x = i;
+ pos.y = best = best2;
+ }
+ }
+
+ if (best + h > BLOCK_HEIGHT)
+ continue;
+
+ for (i = 0; i < w; i++)
+ scrap_allocated[texnum][pos.x + i] = best + h;
+
+ return texnum;
+ }
+
+ return -1;
+ // Sys_Error ("Scrap_AllocBlock: full");
+ }
+
+ void Scrap_Upload() {
+ scrap_uploads++;
+ GL_Bind(TEXNUM_SCRAPS);
+ GL_Upload8(scrap_texels[0], BLOCK_WIDTH, BLOCK_HEIGHT, false, false);
+ scrap_dirty = false;
+ }
+
+ // =======================================================
+
+ /*
+ ==============
+ LoadPCX
+ ==============
+ */
+ byte[] LoadPCX(String filename, byte[][] palette, Dimension dim) {
+ qfiles.pcx_t pcx;
+
+ //
+ // load the file
+ //
+ byte[] raw = FS.LoadFile(filename);
+
+ if (raw == null) {
+ VideoDriver.Printf(Defines.PRINT_DEVELOPER, "Bad pcx file " + filename + '\n');
+ return null;
+ }
+
+ //
+ // parse the PCX file
+ //
+ pcx = new qfiles.pcx_t(raw);
+
+ if (pcx.manufacturer != 0x0a
+ || pcx.version != 5
+ || pcx.encoding != 1
+ || pcx.bits_per_pixel != 8
+ || pcx.xmax >= 640
+ || pcx.ymax >= 480) {
+
+ VideoDriver.Printf(Defines.PRINT_ALL, "Bad pcx file " + filename + '\n');
+ return null;
+ }
+
+ int width = pcx.xmax - pcx.xmin + 1;
+ int height = pcx.ymax - pcx.ymin + 1;
+
+ byte[] pix = new byte[width * height];
+
+ if (palette != null) {
+ palette[0] = new byte[768];
+ System.arraycopy(raw, raw.length - 768, palette[0], 0, 768);
+ }
+
+ if (dim != null) {
+ dim.width = width;
+ dim.height = height;
+ }
+
+ //
+ // decode pcx
+ //
+ int count = 0;
+ byte dataByte = 0;
+ int runLength = 0;
+ int x, y;
+
+ for (y = 0; y < height; y++) {
+ for (x = 0; x < width; ) {
+
+ dataByte = pcx.data.get();
+
+ if ((dataByte & 0xC0) == 0xC0) {
+ runLength = dataByte & 0x3F;
+ dataByte = pcx.data.get();
+ // write runLength pixel
+ while (runLength-- > 0) {
+ pix[count++] = dataByte;
+ x++;
+ }
+ } else {
+ // write one pixel
+ pix[count++] = dataByte;
+ x++;
+ }
+ }
+ }
+ return pix;
+ }
+
+ // /*
+ // =========================================================
+ //
+ // TARGA LOADING
+ //
+ // =========================================================
+ // */
+ /*
+ =============
+ LoadTGA
+ =============
+ */
+ byte[] LoadTGA(String name, Dimension dim) {
+ int columns, rows, numPixels;
+ int pixbuf; // index into pic
+ int row, column;
+ byte[] raw;
+ ByteBuffer buf_p;
+ qfiles.tga_t targa_header;
+ byte[] pic = null;
+
+ //
+ // load the file
+ //
+ raw = FS.LoadFile(name);
+
+ if (raw == null) {
+ VideoDriver.Printf(Defines.PRINT_DEVELOPER, "Bad tga file " + name + '\n');
+ return null;
+ }
+
+ targa_header = new qfiles.tga_t(raw);
+
+ if (targa_header.image_type != 2 && targa_header.image_type != 10)
+ Com.Error(Defines.ERR_DROP, "LoadTGA: Only type 2 and 10 targa RGB images supported\n");
+
+ if (targa_header.colormap_type != 0 || (targa_header.pixel_size != 32 && targa_header.pixel_size != 24))
+ Com.Error(Defines.ERR_DROP, "LoadTGA: Only 32 or 24 bit images supported (no colormaps)\n");
+
+ columns = targa_header.width;
+ rows = targa_header.height;
+ numPixels = columns * rows;
+
+ if (dim != null) {
+ dim.width = columns;
+ dim.height = rows;
+ }
+
+ pic = new byte[numPixels * 4]; // targa_rgba;
+
+ if (targa_header.id_length != 0)
+ targa_header.data.position(targa_header.id_length); // skip TARGA image comment
+
+ buf_p = targa_header.data;
+
+ byte red, green, blue, alphabyte;
+ red = green = blue = alphabyte = 0;
+ int packetHeader, packetSize, j;
+
+ if (targa_header.image_type == 2) { // Uncompressed, RGB images
+ for (row = rows - 1; row >= 0; row--) {
+
+ pixbuf = row * columns * 4;
+
+ for (column = 0; column < columns; column++) {
+ switch (targa_header.pixel_size) {
+ case 24:
+
+ blue = buf_p.get();
+ green = buf_p.get();
+ red = buf_p.get();
+ pic[pixbuf++] = red;
+ pic[pixbuf++] = green;
+ pic[pixbuf++] = blue;
+ pic[pixbuf++] = (byte) 255;
+ break;
+ case 32:
+ blue = buf_p.get();
+ green = buf_p.get();
+ red = buf_p.get();
+ alphabyte = buf_p.get();
+ pic[pixbuf++] = red;
+ pic[pixbuf++] = green;
+ pic[pixbuf++] = blue;
+ pic[pixbuf++] = alphabyte;
+ break;
+ }
+ }
+ }
+ } else if (targa_header.image_type == 10) { // Runlength encoded RGB images
+ for (row = rows - 1; row >= 0; row--) {
+
+ pixbuf = row * columns * 4;
+ try {
+
+ for (column = 0; column < columns; ) {
+
+ packetHeader = buf_p.get() & 0xFF;
+ packetSize = 1 + (packetHeader & 0x7f);
+
+ if ((packetHeader & 0x80) != 0) { // run-length packet
+ switch (targa_header.pixel_size) {
+ case 24:
+ blue = buf_p.get();
+ green = buf_p.get();
+ red = buf_p.get();
+ alphabyte = (byte) 255;
+ break;
+ case 32:
+ blue = buf_p.get();
+ green = buf_p.get();
+ red = buf_p.get();
+ alphabyte = buf_p.get();
+ break;
+ }
+
+ for (j = 0; j < packetSize; j++) {
+ pic[pixbuf++] = red;
+ pic[pixbuf++] = green;
+ pic[pixbuf++] = blue;
+ pic[pixbuf++] = alphabyte;
+ column++;
+ if (column == columns) { // run spans across rows
+ column = 0;
+ if (row > 0)
+ row--;
+ else
+ // goto label breakOut;
+ throw gotoBreakOut;
+
+ pixbuf = row * columns * 4;
+ }
+ }
+ } else { // non run-length packet
+ for (j = 0; j < packetSize; j++) {
+ switch (targa_header.pixel_size) {
+ case 24:
+ blue = buf_p.get();
+ green = buf_p.get();
+ red = buf_p.get();
+ pic[pixbuf++] = red;
+ pic[pixbuf++] = green;
+ pic[pixbuf++] = blue;
+ pic[pixbuf++] = (byte) 255;
+ break;
+ case 32:
+ blue = buf_p.get();
+ green = buf_p.get();
+ red = buf_p.get();
+ alphabyte = buf_p.get();
+ pic[pixbuf++] = red;
+ pic[pixbuf++] = green;
+ pic[pixbuf++] = blue;
+ pic[pixbuf++] = alphabyte;
+ break;
+ }
+ column++;
+ if (column == columns) { // pixel packet run spans across rows
+ column = 0;
+ if (row > 0)
+ row--;
+ else
+ // goto label breakOut;
+ throw gotoBreakOut;
+
+ pixbuf = row * columns * 4;
+ }
+ }
+ }
+ }
+ } catch (Throwable e) {
+ // label breakOut:
+ }
+ }
+ }
+ return pic;
+ }
+
+ // TODO check this: R_FloodFillSkin( byte[] skin, int skinwidth, int skinheight)
+ void R_FloodFillSkin(byte[] skin, int skinwidth, int skinheight) {
+ // byte fillcolor = *skin; // assume this is the pixel to fill
+ int fillcolor = skin[0] & 0xff;
+// floodfill_t[] fifo = new floodfill_t[FLOODFILL_FIFO_SIZE];
+ int inpt = 0, outpt = 0;
+ int filledcolor = -1;
+ int i;
+
+// for (int j = 0; j < fifo.length; j++) {
+// fifo[j] = new floodfill_t();
+// }
+
+ if (filledcolor == -1) {
+ filledcolor = 0;
+ // attempt to find opaque black
+ for (i = 0; i < 256; ++i)
+ // TODO check this
+ if (d_8to24table[i] == 0xFF000000) { // alpha 1.0
+ //if (d_8to24table[i] == (255 << 0)) // alpha 1.0
+ filledcolor = i;
+ break;
+ }
+ }
+
+ // can't fill to filled color or to transparent color (used as visited marker)
+ if ((fillcolor == filledcolor) || (fillcolor == 255)) {
+ return;
+ }
+
+ fifo[inpt].x = 0;
+ fifo[inpt].y = 0;
+ inpt = (inpt + 1) & FLOODFILL_FIFO_MASK;
+
+ while (outpt != inpt) {
+ int x = fifo[outpt].x;
+ int y = fifo[outpt].y;
+ int fdc = filledcolor;
+ // byte *pos = &skin[x + skinwidth * y];
+ int pos = x + skinwidth * y;
+ //
+ outpt = (outpt + 1) & FLOODFILL_FIFO_MASK;
+
+ int off, dx, dy;
+
+ if (x > 0) {
+ // FLOODFILL_STEP( -1, -1, 0 );
+ off = -1;
+ dx = -1;
+ dy = 0;
+ if (skin[pos + off] == (byte) fillcolor) {
+ skin[pos + off] = (byte) 255;
+ fifo[inpt].x = (short) (x + dx);
+ fifo[inpt].y = (short) (y + dy);
+ inpt = (inpt + 1) & FLOODFILL_FIFO_MASK;
+ } else if (skin[pos + off] != (byte) 255)
+ fdc = skin[pos + off] & 0xff;
+ }
+
+ if (x < skinwidth - 1) {
+ // FLOODFILL_STEP( 1, 1, 0 );
+ off = 1;
+ dx = 1;
+ dy = 0;
+ if (skin[pos + off] == (byte) fillcolor) {
+ skin[pos + off] = (byte) 255;
+ fifo[inpt].x = (short) (x + dx);
+ fifo[inpt].y = (short) (y + dy);
+ inpt = (inpt + 1) & FLOODFILL_FIFO_MASK;
+ } else if (skin[pos + off] != (byte) 255)
+ fdc = skin[pos + off] & 0xff;
+ }
+
+ if (y > 0) {
+ // FLOODFILL_STEP( -skinwidth, 0, -1 );
+ off = -skinwidth;
+ dx = 0;
+ dy = -1;
+ if (skin[pos + off] == (byte) fillcolor) {
+ skin[pos + off] = (byte) 255;
+ fifo[inpt].x = (short) (x + dx);
+ fifo[inpt].y = (short) (y + dy);
+ inpt = (inpt + 1) & FLOODFILL_FIFO_MASK;
+ } else if (skin[pos + off] != (byte) 255)
+ fdc = skin[pos + off] & 0xff;
+ }
+
+ if (y < skinheight - 1) {
+ // FLOODFILL_STEP( skinwidth, 0, 1 );
+ off = skinwidth;
+ dx = 0;
+ dy = 1;
+ if (skin[pos + off] == (byte) fillcolor) {
+ skin[pos + off] = (byte) 255;
+ fifo[inpt].x = (short) (x + dx);
+ fifo[inpt].y = (short) (y + dy);
+ inpt = (inpt + 1) & FLOODFILL_FIFO_MASK;
+ } else if (skin[pos + off] != (byte) 255)
+ fdc = skin[pos + off] & 0xff;
+
+ }
+
+ skin[x + skinwidth * y] = (byte) fdc;
+ }
+ }
+
+ /*
+ ================
+ GL_ResampleTexture
+ ================
+ */
+ // cwei :-)
+ void GL_ResampleTexture(int[] in, int inwidth, int inheight, int[] out, int outwidth, int outheight) {
+ // int i, j;
+ // unsigned *inrow, *inrow2;
+ // int frac, fracstep;
+ // int[] p1 = new int[1024];
+ // int[] p2 = new int[1024];
+ //
+
+ // *** this source do the same ***
+ BufferedImage image = new BufferedImage(inwidth, inheight, BufferedImage.TYPE_INT_ARGB);
+
+ image.setRGB(0, 0, inwidth, inheight, in, 0, inwidth);
+
+ AffineTransformOp op =
+ new AffineTransformOp(
+ AffineTransform.getScaleInstance(outwidth * 1.0 / inwidth, outheight * 1.0 / inheight),
+ AffineTransformOp.TYPE_NEAREST_NEIGHBOR);
+ BufferedImage tmp = op.filter(image, null);
+
+ tmp.getRGB(0, 0, outwidth, outheight, out, 0, outwidth);
+
+ // *** end ***
+
+ // byte *pix1, *pix2, *pix3, *pix4;
+ //
+ // fracstep = inwidth*0x10000/outwidth;
+ //
+ // frac = fracstep>>2;
+ // for (i=0 ; i<outwidth ; i++)
+ // {
+ // p1[i] = 4*(frac>>16);
+ // frac += fracstep;
+ // }
+ // frac = 3*(fracstep>>2);
+ // for (i=0 ; i<outwidth ; i++)
+ // {
+ // p2[i] = 4*(frac>>16);
+ // frac += fracstep;
+ // }
+ //
+ // for (i=0 ; i<outheight ; i++, out += outwidth)
+ // {
+ // inrow = in + inwidth*(int)((i+0.25)*inheight/outheight);
+ // inrow2 = in + inwidth*(int)((i+0.75)*inheight/outheight);
+ // frac = fracstep >> 1;
+ // for (j=0 ; j<outwidth ; j++)
+ // {
+ // pix1 = (byte *)inrow + p1[j];
+ // pix2 = (byte *)inrow + p2[j];
+ // pix3 = (byte *)inrow2 + p1[j];
+ // pix4 = (byte *)inrow2 + p2[j];
+ // ((byte *)(out+j))[0] = (pix1[0] + pix2[0] + pix3[0] + pix4[0])>>2;
+ // ((byte *)(out+j))[1] = (pix1[1] + pix2[1] + pix3[1] + pix4[1])>>2;
+ // ((byte *)(out+j))[2] = (pix1[2] + pix2[2] + pix3[2] + pix4[2])>>2;
+ // ((byte *)(out+j))[3] = (pix1[3] + pix2[3] + pix3[3] + pix4[3])>>2;
+ // }
+ // }
+ }
+
+ /*
+ ================
+ GL_LightScaleTexture
+
+ Scale up the pixel values in a texture to increase the
+ lighting range
+ ================
+ */
+ void GL_LightScaleTexture(int[] in, int inwidth, int inheight, boolean only_gamma) {
+ if (only_gamma) {
+ int i, c;
+ int r, g, b, color;
+
+ c = inwidth * inheight;
+ for (i = 0; i < c; i++) {
+ color = in[i];
+ r = (color) & 0xFF;
+ g = (color >> 8) & 0xFF;
+ b = (color >> 16) & 0xFF;
+
+ r = gammatable[r] & 0xFF;
+ g = gammatable[g] & 0xFF;
+ b = gammatable[b] & 0xFF;
+
+ in[i] = (r) | (g << 8) | (b << 16) | (color & 0xFF000000);
+ }
+ } else {
+ int i, c;
+ int r, g, b, color;
+
+ c = inwidth * inheight;
+ for (i = 0; i < c; i++) {
+ color = in[i];
+ r = (color) & 0xFF;
+ g = (color >> 8) & 0xFF;
+ b = (color >> 16) & 0xFF;
+
+ r = gammatable[intensitytable[r] & 0xFF] & 0xFF;
+ g = gammatable[intensitytable[g] & 0xFF] & 0xFF;
+ b = gammatable[intensitytable[b] & 0xFF] & 0xFF;
+
+ in[i] = (r) | (g << 8) | (b << 16) | (color & 0xFF000000);
+ }
+
+ }
+ }
+
+ /*
+ ================
+ GL_MipMap
+
+ Operates in place, quartering the size of the texture
+ ================
+ */
+ void GL_MipMap(int[] in, int width, int height) {
+ int i, j;
+ int[] out;
+
+ out = in;
+
+ int inIndex = 0;
+ int outIndex = 0;
+
+ int r, g, b, a;
+ int p1, p2, p3, p4;
+
+ for (i = 0; i < height; i += 2, inIndex += width) {
+ for (j = 0; j < width; j += 2, outIndex += 1, inIndex += 2) {
+
+ p1 = in[inIndex];
+ p2 = in[inIndex + 1];
+ p3 = in[inIndex + width];
+ p4 = in[inIndex + width + 1];
+
+ r = (((p1) & 0xFF) + ((p2) & 0xFF) + ((p3) & 0xFF) + ((p4) & 0xFF)) >> 2;
+ g = (((p1 >> 8) & 0xFF) + ((p2 >> 8) & 0xFF) + ((p3 >> 8) & 0xFF) + ((p4 >> 8) & 0xFF)) >> 2;
+ b = (((p1 >> 16) & 0xFF) + ((p2 >> 16) & 0xFF) + ((p3 >> 16) & 0xFF) + ((p4 >> 16) & 0xFF)) >> 2;
+ a = (((p1 >> 24) & 0xFF) + ((p2 >> 24) & 0xFF) + ((p3 >> 24) & 0xFF) + ((p4 >> 24) & 0xFF)) >> 2;
+
+ out[outIndex] = (r) | (g << 8) | (b << 16) | (a << 24);
+ }
+ }
+ }
+
+ /*
+ ===============
+ GL_Upload32
+
+ Returns has_alpha
+ ===============
+ */
+ void GL_BuildPalettedTexture(ByteBuffer paletted_texture, int[] scaled, int scaled_width, int scaled_height) {
+
+ int r, g, b, c;
+ int size = scaled_width * scaled_height;
+
+ for (int i = 0; i < size; i++) {
+
+ r = (scaled[i] >> 3) & 31;
+ g = (scaled[i] >> 10) & 63;
+ b = (scaled[i] >> 19) & 31;
+
+ c = r | (g << 5) | (b << 11);
+
+ paletted_texture.put(i, gl_state.d_16to8table[c]);
+ }
+ }
+
+ boolean GL_Upload32(int[] data, int width, int height, boolean mipmap) {
+ int samples;
+ int scaled_width, scaled_height;
+ int i, c;
+ int comp;
+
+ Arrays.fill(scaled, 0);
+ // Arrays.fill(paletted_texture, (byte)0);
+ paletted_texture.clear();
+ for (int j = 0; j < 256 * 256; j++) paletted_texture.put(j, (byte) 0);
+
+ uploaded_paletted = false;
+
+ for (scaled_width = 1; scaled_width < width; scaled_width <<= 1) ;
+ if (gl_round_down.value > 0.0f && scaled_width > width && mipmap)
+ scaled_width >>= 1;
+ for (scaled_height = 1; scaled_height < height; scaled_height <<= 1) ;
+ if (gl_round_down.value > 0.0f && scaled_height > height && mipmap)
+ scaled_height >>= 1;
+
+ // let people sample down the world textures for speed
+ if (mipmap) {
+ scaled_width >>= (int) gl_picmip.value;
+ scaled_height >>= (int) gl_picmip.value;
+ }
+
+ // don't ever bother with >256 textures
+ if (scaled_width > 256)
+ scaled_width = 256;
+ if (scaled_height > 256)
+ scaled_height = 256;
+
+ if (scaled_width < 1)
+ scaled_width = 1;
+ if (scaled_height < 1)
+ scaled_height = 1;
+
+ upload_width = scaled_width;
+ upload_height = scaled_height;
+
+ if (scaled_width * scaled_height > 256 * 256)
+ Com.Error(Defines.ERR_DROP, "GL_Upload32: too big");
+
+ // scan the texture for any non-255 alpha
+ c = width * height;
+ samples = gl_solid_format;
+
+ for (i = 0; i < c; i++) {
+ if ((data[i] & 0xff000000) != 0xff000000) {
+ samples = gl_alpha_format;
+ break;
+ }
+ }
+
+ if (samples == gl_solid_format)
+ comp = gl_tex_solid_format;
+ else if (samples == gl_alpha_format)
+ comp = gl_tex_alpha_format;
+ else {
+ VideoDriver.Printf(Defines.PRINT_ALL, "Unknown number of texture components " + samples + '\n');
+ comp = samples;
+ }
+
+ // simulates a goto
+ try {
+ if (scaled_width == width && scaled_height == height) {
+ if (!mipmap) {
+ if (qglColorTableEXT && gl_ext_palettedtexture.value != 0.0f && samples == gl_solid_format) {
+ uploaded_paletted = true;
+ GL_BuildPalettedTexture(paletted_texture, data, scaled_width, scaled_height);
+ GL11.glTexImage2D(
+ GL11.GL_TEXTURE_2D,
+ 0,
+ GL_COLOR_INDEX8_EXT,
+ scaled_width,
+ scaled_height,
+ 0,
+ GL11.GL_COLOR_INDEX,
+ GL11.GL_UNSIGNED_BYTE,
+ paletted_texture);
+ } else {
+ tex.rewind();
+ tex.put(data);
+ tex.rewind();
+ GL11.glTexImage2D(
+ GL11.GL_TEXTURE_2D,
+ 0,
+ comp,
+ scaled_width,
+ scaled_height,
+ 0,
+ GL11.GL_RGBA,
+ GL11.GL_UNSIGNED_BYTE,
+ tex);
+ }
+ //goto done;
+ throw gotoDone;
+ }
+ //memcpy (scaled, data, width*height*4); were bytes
+ System.arraycopy(data, 0, scaled, 0, width * height);
+ } else
+ GL_ResampleTexture(data, width, height, scaled, scaled_width, scaled_height);
+
+ GL_LightScaleTexture(scaled, scaled_width, scaled_height, !mipmap);
+
+ if (qglColorTableEXT && gl_ext_palettedtexture.value != 0.0f && (samples == gl_solid_format)) {
+ uploaded_paletted = true;
+ GL_BuildPalettedTexture(paletted_texture, scaled, scaled_width, scaled_height);
+ GL11.glTexImage2D(
+ GL11.GL_TEXTURE_2D,
+ 0,
+ GL_COLOR_INDEX8_EXT,
+ scaled_width,
+ scaled_height,
+ 0,
+ GL11.GL_COLOR_INDEX,
+ GL11.GL_UNSIGNED_BYTE,
+ paletted_texture);
+ } else {
+ tex.rewind();
+ tex.put(scaled);
+ tex.rewind();
+ GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, comp, scaled_width, scaled_height, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, tex);
+ }
+
+ if (mipmap) {
+ int miplevel;
+ miplevel = 0;
+ while (scaled_width > 1 || scaled_height > 1) {
+ GL_MipMap(scaled, scaled_width, scaled_height);
+ scaled_width >>= 1;
+ scaled_height >>= 1;
+ if (scaled_width < 1)
+ scaled_width = 1;
+ if (scaled_height < 1)
+ scaled_height = 1;
+
+ miplevel++;
+ if (qglColorTableEXT && gl_ext_palettedtexture.value != 0.0f && samples == gl_solid_format) {
+ uploaded_paletted = true;
+ GL_BuildPalettedTexture(paletted_texture, scaled, scaled_width, scaled_height);
+ GL11.glTexImage2D(
+ GL11.GL_TEXTURE_2D,
+ miplevel,
+ GL_COLOR_INDEX8_EXT,
+ scaled_width,
+ scaled_height,
+ 0,
+ GL11.GL_COLOR_INDEX,
+ GL11.GL_UNSIGNED_BYTE,
+ paletted_texture);
+ } else {
+ tex.rewind();
+ tex.put(scaled);
+ tex.rewind();
+ GL11.glTexImage2D(
+ GL11.GL_TEXTURE_2D,
+ miplevel,
+ comp,
+ scaled_width,
+ scaled_height,
+ 0,
+ GL11.GL_RGBA,
+ GL11.GL_UNSIGNED_BYTE,
+ tex);
+ }
+ }
+ }
+ // label done:
+ } catch (Throwable e) {
+ // replaces label done
+ }
+
+ if (mipmap) {
+ GL11.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, gl_filter_min);
+ GL11.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, gl_filter_max);
+ } else {
+ GL11.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, gl_filter_max);
+ GL11.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, gl_filter_max);
+ }
+
+ return (samples == gl_alpha_format);
+ }
+
+ boolean GL_Upload8(byte[] data, int width, int height, boolean mipmap, boolean is_sky) {
+
+ Arrays.fill(trans, 0);
+
+ int s = width * height;
+
+ if (s > trans.length)
+ Com.Error(Defines.ERR_DROP, "GL_Upload8: too large");
+
+ if (qglColorTableEXT && gl_ext_palettedtexture.value != 0.0f && is_sky) {
+ GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL_COLOR_INDEX8_EXT, width, height, 0, GL11.GL_COLOR_INDEX, GL11.GL_UNSIGNED_BYTE, ByteBuffer.wrap(data));
+
+ GL11.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, gl_filter_max);
+ GL11.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, gl_filter_max);
+
+ // TODO check this
+ return false;
+ } else {
+ int p;
+ for (int i = 0; i < s; i++) {
+ p = data[i] & 0xff;
+ trans[i] = d_8to24table[p];
+
+ if (p == 255) { // transparent, so scan around for another color
+ // to avoid alpha fringes
+ // FIXME: do a full flood fill so mips work...
+ if (i > width && (data[i - width] & 0xff) != 255)
+ p = data[i - width] & 0xff;
+ else if (i < s - width && (data[i + width] & 0xff) != 255)
+ p = data[i + width] & 0xff;
+ else if (i > 0 && (data[i - 1] & 0xff) != 255)
+ p = data[i - 1] & 0xff;
+ else if (i < s - 1 && (data[i + 1] & 0xff) != 255)
+ p = data[i + 1] & 0xff;
+ else
+ p = 0;
+ // copy rgb components
+
+ // ((byte *)&trans[i])[0] = ((byte *)&d_8to24table[p])[0];
+ // ((byte *)&trans[i])[1] = ((byte *)&d_8to24table[p])[1];
+ // ((byte *)&trans[i])[2] = ((byte *)&d_8to24table[p])[2];
+
+ trans[i] = d_8to24table[p] & 0x00FFFFFF; // only rgb
+ }
+ }
+
+ return GL_Upload32(trans, width, height, mipmap);
+ }
+ }
+
+ /*
+ ================
+ GL_LoadPic
+
+ This is also used as an entry point for the generated r_notexture
+ ================
+ */
+ lwjake2.render.Image GL_LoadPic(String name, byte[] pic, int width, int height, int type, int bits) {
+ lwjake2.render.Image image;
+ int i;
+
+ // find a free image_t
+ for (i = 0; i < numgltextures; i++) {
+ image = gltextures[i];
+ if (image.texnum == 0)
+ break;
+ }
+
+ if (i == numgltextures) {
+ if (numgltextures == MAX_GLTEXTURES)
+ Com.Error(Defines.ERR_DROP, "MAX_GLTEXTURES");
+
+ numgltextures++;
+ }
+ image = gltextures[i];
+
+ if (name.length() > Defines.MAX_QPATH)
+ Com.Error(Defines.ERR_DROP, "Draw_LoadPic: \"" + name + "\" is too long");
+
+ image.name = name;
+ image.registration_sequence = registration_sequence;
+
+ image.width = width;
+ image.height = height;
+ image.type = type;
+
+
+ if (type == it_skin && bits == 8)
+ R_FloodFillSkin(pic, width, height);
+
+ // load little pics into the scrap
+ if (image.type == it_pic && bits == 8 && image.width < 64 && image.height < 64) {
+ pos_t pos = new pos_t(0, 0);
+ int j, k;
+
+ int texnum = Scrap_AllocBlock(image.width, image.height, pos);
+
+ if (texnum == -1) {
+ // replace goto nonscrap
+
+ image.scrap = false;
+
+ image.texnum = TEXNUM_IMAGES + image.getId(); // image pos in array
+ GL_Bind(image.texnum);
+
+ if (bits == 8) {
+ image.has_alpha =
+ GL_Upload8(pic, width, height, (image.type != it_pic && image.type != it_sky), image.type == it_sky);
+ } else {
+ int[] tmp = new int[pic.length / 4];
+
+ for (i = 0; i < tmp.length; i++) {
+ tmp[i] = ((pic[4 * i] & 0xFF)); // & 0x000000FF;
+ tmp[i] |= ((pic[4 * i + 1] & 0xFF) << 8); // & 0x0000FF00;
+ tmp[i] |= ((pic[4 * i + 2] & 0xFF) << 16); // & 0x00FF0000;
+ tmp[i] |= ((pic[4 * i + 3] & 0xFF) << 24); // & 0xFF000000;
+ }
+
+ image.has_alpha = GL_Upload32(tmp, width, height, (image.type != it_pic && image.type != it_sky));
+ }
+
+ image.upload_width = upload_width; // after power of 2 and scales
+ image.upload_height = upload_height;
+ image.paletted = uploaded_paletted;
+ image.sl = 0;
+ image.sh = 1;
+ image.tl = 0;
+ image.th = 1;
+
+ return image;
+ }
+
+ scrap_dirty = true;
+
+ // copy the texels into the scrap block
+ k = 0;
+ for (i = 0; i < image.height; i++)
+ for (j = 0; j < image.width; j++, k++)
+ scrap_texels[texnum][(pos.y + i) * BLOCK_WIDTH + pos.x + j] = pic[k];
+
+ image.texnum = TEXNUM_SCRAPS + texnum;
+ image.scrap = true;
+ image.has_alpha = true;
+ image.sl = (pos.x + 0.01f) / (float) BLOCK_WIDTH;
+ image.sh = (pos.x + image.width - 0.01f) / (float) BLOCK_WIDTH;
+ image.tl = (pos.y + 0.01f) / (float) BLOCK_WIDTH;
+ image.th = (pos.y + image.height - 0.01f) / (float) BLOCK_WIDTH;
+
+ } else {
+ // this was label nonscrap
+
+ image.scrap = false;
+
+ image.texnum = TEXNUM_IMAGES + image.getId(); //image pos in array
+ GL_Bind(image.texnum);
+
+ if (bits == 8) {
+ image.has_alpha = GL_Upload8(pic, width, height, (image.type != it_pic && image.type != it_sky), image.type == it_sky);
+ } else {
+ int[] tmp = new int[pic.length / 4];
+
+ for (i = 0; i < tmp.length; i++) {
+ tmp[i] = ((pic[4 * i] & 0xFF)); // & 0x000000FF;
+ tmp[i] |= ((pic[4 * i + 1] & 0xFF) << 8); // & 0x0000FF00;
+ tmp[i] |= ((pic[4 * i + 2] & 0xFF) << 16); // & 0x00FF0000;
+ tmp[i] |= ((pic[4 * i + 3] & 0xFF) << 24); // & 0xFF000000;
+ }
+
+ image.has_alpha = GL_Upload32(tmp, width, height, (image.type != it_pic && image.type != it_sky));
+ }
+ image.upload_width = upload_width; // after power of 2 and scales
+ image.upload_height = upload_height;
+ image.paletted = uploaded_paletted;
+ image.sl = 0;
+ image.sh = 1;
+ image.tl = 0;
+ image.th = 1;
+ }
+ return image;
+ }
+
+ /*
+ ===============
+ GL_Upload8
+
+ Returns has_alpha
+ ===============
+ */
+
+ /*
+ ================
+ GL_LoadWal
+ ================
+ */
+ lwjake2.render.Image GL_LoadWal(String name) {
+
+ lwjake2.render.Image image = null;
+
+ byte[] raw = FS.LoadFile(name);
+ if (raw == null) {
+ VideoDriver.Printf(Defines.PRINT_ALL, "GL_FindImage: can't load " + name + '\n');
+ return r_notexture;
+ }
+
+ qfiles.miptex_t mt = new qfiles.miptex_t(raw);
+
+ byte[] pix = new byte[mt.width * mt.height];
+ System.arraycopy(raw, mt.offsets[0], pix, 0, pix.length);
+
+ image = GL_LoadPic(name, pix, mt.width, mt.height, it_wall, 8);
+
+ return image;
+ }
+
+ /*
+ ===============
+ GL_FindImage
+
+ Finds or loads the given image
+ ===============
+ */
+ lwjake2.render.Image GL_FindImage(String name, int type) {
+ lwjake2.render.Image image = null;
+
+// // TODO loest das grossschreibungs problem
+// name = name.toLowerCase();
+// // bughack for bad strings (fuck \0)
+// int index = name.indexOf('\0');
+// if (index != -1)
+// name = name.substring(0, index);
+
+ if (name == null || name.length() < 5)
+ return null; // Com.Error (ERR_DROP, "GL_FindImage: NULL name");
+ // Com.Error (ERR_DROP, "GL_FindImage: bad name: %s", name);
+
+ // look for it
+ for (int i = 0; i < numgltextures; i++) {
+ image = gltextures[i];
+ if (name.equals(image.name)) {
+ image.registration_sequence = registration_sequence;
+ return image;
+ }
+ }
+
+ //
+ // load the pic from disk
+ //
+ image = null;
+ byte[] pic = null;
+ Dimension dim = new Dimension();
+
+ if (name.endsWith(".pcx")) {
+
+ pic = LoadPCX(name, null, dim);
+ if (pic == null)
+ return null;
+ image = GL_LoadPic(name, pic, dim.width, dim.height, type, 8);
+
+ } else if (name.endsWith(".wal")) {
+
+ image = GL_LoadWal(name);
+
+ } else if (name.endsWith(".tga")) {
+
+ pic = LoadTGA(name, dim);
+
+ if (pic == null)
+ return null;
+
+ image = GL_LoadPic(name, pic, dim.width, dim.height, type, 32);
+
+ }
+
+ return image;
+ }
+
+ /*
+ ===============
+ R_RegisterSkin
+ ===============
+ */
+ protected lwjake2.render.Image R_RegisterSkin(String name) {
+ return GL_FindImage(name, it_skin);
+ }
+
+ /*
+ ================
+ GL_FreeUnusedImages
+
+ Any image that was not touched on this registration sequence
+ will be freed.
+ ================
+ */
+ void GL_FreeUnusedImages() {
+
+ // never free r_notexture or particle texture
+ r_notexture.registration_sequence = registration_sequence;
+ r_particletexture.registration_sequence = registration_sequence;
+
+ lwjake2.render.Image image = null;
+
+ for (int i = 0; i < numgltextures; i++) {
+ image = gltextures[i];
+ // used this sequence
+ if (image.registration_sequence == registration_sequence)
+ continue;
+ // free image_t slot
+ if (image.registration_sequence == 0)
+ continue;
+ // don't free pics
+ if (image.type == it_pic)
+ continue;
+
+ // free it
+ // TODO jogl bug
+ texnumBuffer.clear();
+ texnumBuffer.put(0, image.texnum);
+ GL11.glDeleteTextures(texnumBuffer);
+ image.clear();
+ }
+ }
+
+ /*
+ ===============
+ Draw_GetPalette
+ ===============
+ */
+ protected void Draw_GetPalette() {
+ int r, g, b;
+ byte[][] palette = new byte[1][]; //new byte[768];
+
+ // get the palette
+
+ LoadPCX("pics/colormap.pcx", palette, new Dimension());
+
+ if (palette[0] == null || palette[0].length != 768)
+ Com.Error(Defines.ERR_FATAL, "Couldn't load pics/colormap.pcx");
+
+ byte[] pal = palette[0];
+
+ int j = 0;
+ for (int i = 0; i < 256; i++) {
+ r = pal[j++] & 0xFF;
+ g = pal[j++] & 0xFF;
+ b = pal[j++] & 0xFF;
+
+ d_8to24table[i] = (255 << 24) | (b << 16) | (g << 8) | (r);
+ }
+
+ d_8to24table[255] &= 0x00FFFFFF; // 255 is transparent
+
+ particle_t.setColorPalette(d_8to24table);
+ }
+
+ /*
+ ===============
+ GL_InitImages
+ ===============
+ */
+ void GL_InitImages() {
+ int i, j;
+ float g = vid_gamma.value;
+
+ registration_sequence = 1;
+
+ // init intensity conversions
+ intensity = Cvar.get("intensity", "2", 0);
+
+ if (intensity.value <= 1)
+ Cvar.set("intensity", "1");
+
+ gl_state.inverse_intensity = 1 / intensity.value;
+
+ Draw_GetPalette();
+
+ if (qglColorTableEXT) {
+ gl_state.d_16to8table = FS.LoadFile("pics/16to8.dat");
+ if (gl_state.d_16to8table == null)
+ Com.Error(Defines.ERR_FATAL, "Couldn't load pics/16to8.pcx");
+ }
+
+ if ((gl_config.renderer & (GL_RENDERER_VOODOO | GL_RENDERER_VOODOO2)) != 0) {
+ g = 1.0F;
+ }
+
+ for (i = 0; i < 256; i++) {
+
+ if (g == 1.0f) {
+ gammatable[i] = (byte) i;
+ } else {
+
+ int inf = (int) (255.0f * Math.pow((i + 0.5) / 255.5, g) + 0.5);
+ if (inf < 0)
+ inf = 0;
+ if (inf > 255)
+ inf = 255;
+ gammatable[i] = (byte) inf;
+ }
+ }
+
+ for (i = 0; i < 256; i++) {
+ j = (int) (i * intensity.value);
+ if (j > 255)
+ j = 255;
+ intensitytable[i] = (byte) j;
+ }
+ }
+
+ /*
+ ===============
+ GL_ShutdownImages
+ ===============
+ */
+ void GL_ShutdownImages() {
+ lwjake2.render.Image image;
+
+ for (int i = 0; i < numgltextures; i++) {
+ image = gltextures[i];
+
+ if (image.registration_sequence == 0)
+ continue; // free image_t slot
+ // free it
+ // TODO jogl bug
+ texnumBuffer.clear();
+ texnumBuffer.put(0, image.texnum);
+ GL11.glDeleteTextures(texnumBuffer);
+ image.clear();
+ }
+ }
+
+ // glmode_t
+ static class glmode_t {
+ final String name;
+ final int minimize;
+ final int maximize;
+
+ glmode_t(String name, int minimize, int maximze) {
+ this.name = name;
+ this.minimize = minimize;
+ this.maximize = maximze;
+ }
+ }
+
+ // gltmode_t
+ static class gltmode_t {
+ final String name;
+ final int mode;
+
+ gltmode_t(String name, int mode) {
+ this.name = name;
+ this.mode = mode;
+ }
+ }
+
+ static class pos_t {
+ int x, y;
+
+ pos_t(int x, int y) {
+ this.x = x;
+ this.y = y;
+ }
+ }
+
+ static class floodfill_t {
+ short x, y;
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.render.lwjgl;
+
+import lwjake2.Defines;
+import lwjake2.client.VideoDriver;
+import lwjake2.client.viddef_t;
+import lwjake2.game.CvarT;
+import lwjake2.qcommon.xcommand_t;
+import org.lwjgl.LWJGLException;
+import org.lwjgl.opengl.Display;
+import org.lwjgl.opengl.DisplayMode;
+import org.lwjgl.opengl.GL11;
+
+import java.awt.*;
+import java.util.ArrayList;
+import java.util.LinkedList;
+
+/**
+ * LWJGLBase
+ *
+ * @author dsanders/cwei
+ */
+public abstract class LWJGLBase {
+ // enum rserr_t
+ protected static final int rserr_ok = 0;
+ protected static final int rserr_invalid_fullscreen = 1;
+ protected static final int rserr_invalid_mode = 2;
+ protected static final int rserr_unknown = 3;
+ protected final viddef_t vid = new viddef_t();
+ // IMPORTED FUNCTIONS
+ protected DisplayMode oldDisplayMode;
+ protected CvarT vid_fullscreen;
+ // window position on the screen
+ int window_xpos, window_ypos;
+
+ // handles the post initialization with LWJGLRenderer
+ protected abstract boolean R_Init2();
+
+ private java.awt.DisplayMode toAwtDisplayMode(DisplayMode m) {
+ return new java.awt.DisplayMode(m.getWidth(), m.getHeight(), m.getBitsPerPixel(), m.getFrequency());
+ }
+
+ public java.awt.DisplayMode[] getModeList() {
+ try {
+ DisplayMode[] modes;
+
+ modes = Display.getAvailableDisplayModes();
+
+ LinkedList<java.awt.DisplayMode> l = new LinkedList<>();
+ l.add(toAwtDisplayMode(oldDisplayMode));
+
+ for (DisplayMode m : modes) {
+ if (m.getBitsPerPixel() != oldDisplayMode.getBitsPerPixel()) continue;
+ if (m.getFrequency() > oldDisplayMode.getFrequency()) continue;
+ if (m.getHeight() < 240 || m.getWidth() < 320) continue;
+
+ int j = 0;
+ java.awt.DisplayMode ml = null;
+ for (j = 0; j < l.size(); j++) {
+ ml = l.get(j);
+ if (ml.getWidth() > m.getWidth()) break;
+ if (ml.getWidth() == m.getWidth() && ml.getHeight() >= m.getHeight()) break;
+ }
+ if (j == l.size()) {
+ l.addLast(toAwtDisplayMode(m));
+ } else if (ml.getWidth() > m.getWidth() || ml.getHeight() > m.getHeight()) {
+ l.add(j, toAwtDisplayMode(m));
+ } else if (m.getFrequency() > ml.getRefreshRate()) {
+ l.remove(j);
+ l.add(j, toAwtDisplayMode(m));
+ }
+ }
+ java.awt.DisplayMode[] ma = new java.awt.DisplayMode[l.size()];
+ l.toArray(ma);
+ return ma;
+ } catch (LWJGLException e) {
+ e.printStackTrace();
+ System.exit(0);
+ }
+ return null;
+ }
+
+ public DisplayMode[] getLWJGLModeList() {
+ try {
+ // Return value storage.
+ ArrayList<DisplayMode> displayModes;
+
+ // Get all possible display modes.
+ DisplayMode[] allDisplayModes = Display.getAvailableDisplayModes();
+
+ // Cut down all the ones with a height below 240.
+ displayModes = new ArrayList<>();
+ for (DisplayMode allDisplayMode : allDisplayModes) {
+ if (allDisplayMode.getHeight() >= 240)
+ displayModes.add(allDisplayMode);
+ }
+
+ // Gnome sort the display modes by height, width, and refresh rate.
+ int currentSpot = 0;
+ boolean needSwap = false;
+ DisplayMode tempStore;
+ while (currentSpot < displayModes.size() - 1) {
+ // Check DisplayMode heights.
+ if (displayModes.get(currentSpot).getHeight() > displayModes.get(currentSpot + 1).getHeight())
+ needSwap = true;
+ else if (displayModes.get(currentSpot).getHeight() == displayModes.get(currentSpot + 1).getHeight()) {
+ // Check DisplayMode widths.
+ if (displayModes.get(currentSpot).getWidth() > displayModes.get(currentSpot + 1).getWidth())
+ needSwap = true;
+ else if (displayModes.get(currentSpot).getWidth() == displayModes.get(currentSpot + 1).getWidth())
+ // Doesn't sort frequencies, but removes the lesser ones entirely.
+ if (displayModes.get(currentSpot).getFrequency() < displayModes.get(currentSpot + 1).getFrequency()) {
+ displayModes.remove(currentSpot);
+ currentSpot--;
+ } else if (displayModes.get(currentSpot).getFrequency() > displayModes.get(currentSpot + 1).getFrequency()) {
+ displayModes.remove(currentSpot + 1);
+ currentSpot--;
+ }
+ }
+ if (needSwap) {
+ needSwap = false;
+ tempStore = displayModes.get(currentSpot);
+ displayModes.set(currentSpot, displayModes.get(currentSpot + 1));
+ displayModes.set(currentSpot + 1, tempStore);
+ if (currentSpot > 0)
+ currentSpot--;
+ } else
+ currentSpot++;
+ }
+
+ // Return the array.
+ return displayModes.toArray(new DisplayMode[displayModes.size()]);
+
+ } catch (LWJGLException e) {
+ e.printStackTrace();
+ System.exit(0);
+ }
+ return null;
+ }
+
+ private DisplayMode findDisplayMode(Dimension dim) {
+ DisplayMode mode = null;
+ DisplayMode m = null;
+ DisplayMode[] modes = getLWJGLModeList();
+ int w = dim.width;
+ int h = dim.height;
+
+ for (DisplayMode mode1 : modes) {
+ m = mode1;
+ if (m.getWidth() == w && m.getHeight() == h) {
+ mode = m;
+ break;
+ }
+ }
+ if (mode == null) mode = oldDisplayMode;
+ return mode;
+ }
+
+ String getModeString(DisplayMode m) {
+ return String.valueOf(m.getWidth()) +
+ 'x' +
+ m.getHeight() +
+ 'x' +
+ m.getBitsPerPixel() +
+ '@' +
+ m.getFrequency() +
+ "Hz";
+ }
+
+ /**
+ * @param dim
+ * @param mode
+ * @param fullscreen
+ * @return enum rserr_t
+ */
+ protected int GLimp_SetMode(Dimension dim, int mode, boolean fullscreen) {
+
+ Dimension newDim = new Dimension();
+
+ VideoDriver.Printf(Defines.PRINT_ALL, "Initializing OpenGL display\n");
+
+ VideoDriver.Printf(Defines.PRINT_ALL, "...setting mode " + mode + ":");
+
+ /*
+ * fullscreen handling
+ */
+ if (oldDisplayMode == null) {
+ oldDisplayMode = Display.getDisplayMode();
+ }
+
+ if (!VideoDriver.GetModeInfo(newDim, mode)) {
+ VideoDriver.Printf(Defines.PRINT_ALL, " invalid mode\n");
+ return rserr_invalid_mode;
+ }
+
+ VideoDriver.Printf(Defines.PRINT_ALL, " " + newDim.width + " " + newDim.height + '\n');
+
+ // destroy the existing window
+ GLimp_Shutdown();
+
+ Display.setTitle("LWJake2");
+
+ DisplayMode displayMode = findDisplayMode(newDim);
+ newDim.width = displayMode.getWidth();
+ newDim.height = displayMode.getHeight();
+
+ if (fullscreen) {
+ try {
+ Display.setDisplayMode(displayMode);
+ } catch (LWJGLException e) {
+ return rserr_invalid_mode;
+ }
+
+ Display.setLocation(0, 0);
+
+ try {
+ Display.setFullscreen(true);
+ } catch (LWJGLException e) {
+ return rserr_invalid_fullscreen;
+ }
+
+ VideoDriver.Printf(Defines.PRINT_ALL, "...setting fullscreen " + getModeString(displayMode) + '\n');
+
+ } else {
+ try {
+ Display.setFullscreen(false);
+ } catch (LWJGLException e) {
+ return rserr_invalid_fullscreen;
+ }
+
+ try {
+ Display.setDisplayMode(displayMode);
+ } catch (LWJGLException e) {
+ return rserr_invalid_mode;
+ }
+ Display.setLocation(window_xpos, window_ypos);
+ }
+
+ vid.width = newDim.width;
+ vid.height = newDim.height;
+
+ try {
+ Display.create();
+ } catch (LWJGLException e) {
+ return rserr_unknown;
+ }
+
+ // let the sound and input subsystems know about the new window
+ VideoDriver.NewWindow(vid.width, vid.height);
+ return rserr_ok;
+ }
+
+ protected void GLimp_Shutdown() {
+ if (oldDisplayMode != null && Display.isFullscreen()) {
+ try {
+ Display.setDisplayMode(oldDisplayMode);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ while (Display.isCreated()) {
+ Display.destroy();
+ }
+ }
+
+ /**
+ * @return true
+ */
+ protected boolean GLimp_Init(int xpos, int ypos) {
+ // do nothing
+ window_xpos = xpos;
+ window_ypos = ypos;
+ return true;
+ }
+
+ protected void GLimp_EndFrame() {
+ GL11.glFlush();
+ // swap buffers
+ Display.update();
+ }
+
+ protected void GLimp_BeginFrame(float camera_separation) {
+ // do nothing
+ }
+
+ protected void GLimp_AppActivate(boolean activate) {
+ // do nothing
+ }
+
+ protected void GLimp_EnableLogging(boolean enable) {
+ // do nothing
+ }
+
+ protected void GLimp_LogNewFrame() {
+ // do nothing
+ }
+
+ /**
+ * this is a hack for jogl renderers.
+ *
+ * @param callback
+ */
+ public final void updateScreen(xcommand_t callback) {
+ callback.execute();
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.render.lwjgl;
+
+import lwjake2.Defines;
+import lwjake2.Globals;
+import lwjake2.client.dlight_t;
+import lwjake2.game.cplane_t;
+import lwjake2.qcommon.Com;
+import lwjake2.render.mnode_t;
+import lwjake2.render.msurface_t;
+import lwjake2.render.mtexinfo_t;
+import lwjake2.util.Math3D;
+import lwjake2.util.Vec3Cache;
+import org.lwjgl.opengl.GL11;
+
+import java.nio.ByteBuffer;
+import java.nio.IntBuffer;
+import java.util.Arrays;
+
+/**
+ * Light
+ *
+ * @author cwei
+ */
+public abstract class Light extends Warp {
+ // r_light.c
+
+ static final int DLIGHT_CUTOFF = 64;
+ /*
+ =============================================================================
+
+ DYNAMIC LIGHTS
+
+ =============================================================================
+ */
+ final float[] pointcolor = {0, 0, 0}; // vec3_t
+ /*
+ =============================================================================
+
+ LIGHT SAMPLING
+
+ =============================================================================
+ */
+ final float[] lightspot = {0, 0, 0}; // vec3_t
+ final float[] s_blocklights = new float[34 * 34 * 3];
+ // stack variable
+ private final float[] v = {0, 0, 0};
+ /*
+ =============================================================================
+
+ DYNAMIC LIGHTS BLEND RENDERING
+
+ =============================================================================
+ */
+ // stack variable
+ private final float[] end = {0, 0, 0};
+ // TODO sync with jogl renderer. hoz
+ private final float[] impact = {0, 0, 0};
+ private final Throwable gotoStore = new Throwable();
+ int r_dlightframecount;
+ cplane_t lightplane; // used as shadow plane
+
+ /**
+ * R_RenderDlight
+ */
+ void R_RenderDlight(dlight_t light) {
+ float rad = light.intensity * 0.35f;
+
+ Math3D.vectorSubtract(light.origin, r_origin, v);
+
+ GL11.glBegin(GL11.GL_TRIANGLE_FAN);
+ GL11.glColor3f(light.color[0] * 0.2f, light.color[1] * 0.2f, light.color[2] * 0.2f);
+ int i;
+ for (i = 0; i < 3; i++)
+ v[i] = light.origin[i] - vpn[i] * rad;
+
+ GL11.glVertex3f(v[0], v[1], v[2]);
+ GL11.glColor3f(0, 0, 0);
+
+ int j;
+ float a;
+ for (i = 16; i >= 0; i--) {
+ a = (float) (i / 16.0f * Math.PI * 2);
+ for (j = 0; j < 3; j++)
+ v[j] = (float) (light.origin[j] + vright[j] * Math.cos(a) * rad
+ + vup[j] * Math.sin(a) * rad);
+ GL11.glVertex3f(v[0], v[1], v[2]);
+ }
+ GL11.glEnd();
+ }
+
+ /**
+ * R_RenderDlights
+ */
+ void R_RenderDlights() {
+ if (gl_flashblend.value == 0)
+ return;
+
+ r_dlightframecount = r_framecount + 1; // because the count hasn't
+ // advanced yet for this frame
+ GL11.glDepthMask(false);
+ GL11.glDisable(GL11.GL_TEXTURE_2D);
+ GL11.glShadeModel(GL11.GL_SMOOTH);
+ GL11.glEnable(GL11.GL_BLEND);
+ GL11.glBlendFunc(GL11.GL_ONE, GL11.GL_ONE);
+
+ for (int i = 0; i < r_newrefdef.num_dlights; i++) {
+ R_RenderDlight(r_newrefdef.dlights[i]);
+ }
+
+ GL11.glColor3f(1, 1, 1);
+ GL11.glDisable(GL11.GL_BLEND);
+ GL11.glEnable(GL11.GL_TEXTURE_2D);
+ GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
+ GL11.glDepthMask(true);
+ }
+
+ /**
+ * R_MarkLights
+ */
+ void R_MarkLights(dlight_t light, int bit, mnode_t node) {
+ if (node.contents != -1)
+ return;
+
+ cplane_t splitplane = node.plane;
+ float dist = Math3D.dotProduct(light.origin, splitplane.normal) - splitplane.dist;
+
+ if (dist > light.intensity - DLIGHT_CUTOFF) {
+ R_MarkLights(light, bit, node.children[0]);
+ return;
+ }
+ if (dist < -light.intensity + DLIGHT_CUTOFF) {
+ R_MarkLights(light, bit, node.children[1]);
+ return;
+ }
+
+ // mark the polygons
+ msurface_t surf;
+ int sidebit;
+ for (int i = 0; i < node.numsurfaces; i++) {
+
+ surf = r_worldmodel.surfaces[node.firstsurface + i];
+
+ /*
+ * cwei
+ * bugfix for dlight behind the walls
+ */
+ dist = Math3D.dotProduct(light.origin, surf.plane.normal) - surf.plane.dist;
+ sidebit = (dist >= 0) ? 0 : Defines.SURF_PLANEBACK;
+ if ((surf.flags & Defines.SURF_PLANEBACK) != sidebit)
+ continue;
+ /*
+ * cwei
+ * bugfix end
+ */
+
+ if (surf.dlightframe != r_dlightframecount) {
+ surf.dlightbits = 0;
+ surf.dlightframe = r_dlightframecount;
+ }
+ surf.dlightbits |= bit;
+ }
+
+ R_MarkLights(light, bit, node.children[0]);
+ R_MarkLights(light, bit, node.children[1]);
+ }
+
+// ===================================================================
+
+ /**
+ * R_PushDlights
+ */
+ void R_PushDlights() {
+ if (gl_flashblend.value != 0)
+ return;
+
+ r_dlightframecount = r_framecount + 1; // because the count hasn't
+ // advanced yet for this frame
+ dlight_t l;
+ for (int i = 0; i < r_newrefdef.num_dlights; i++) {
+ l = r_newrefdef.dlights[i];
+ R_MarkLights(l, 1 << i, r_worldmodel.nodes[0]);
+ }
+ }
+
+ /**
+ * RecursiveLightPoint
+ *
+ * @param node
+ * @param start
+ * @param end
+ * @return
+ */
+ int RecursiveLightPoint(mnode_t node, float[] start, float[] end) {
+ if (node.contents != -1)
+ return -1; // didn't hit anything
+
+ // calculate mid point
+
+ // FIXME: optimize for axial
+ cplane_t plane = node.plane;
+ float front = Math3D.dotProduct(start, plane.normal) - plane.dist;
+ float back = Math3D.dotProduct(end, plane.normal) - plane.dist;
+ boolean side = (front < 0);
+ int sideIndex = (side) ? 1 : 0;
+
+ if ((back < 0) == side)
+ return RecursiveLightPoint(node.children[sideIndex], start, end);
+
+ float frac = front / (front - back);
+ float[] mid = Vec3Cache.get();
+ mid[0] = start[0] + (end[0] - start[0]) * frac;
+ mid[1] = start[1] + (end[1] - start[1]) * frac;
+ mid[2] = start[2] + (end[2] - start[2]) * frac;
+
+ // go down front side
+ int r = RecursiveLightPoint(node.children[sideIndex], start, mid);
+ if (r >= 0) {
+ Vec3Cache.release(); // mid
+ return r; // hit something
+ }
+
+ if ((back < 0) == side) {
+ Vec3Cache.release(); // mid
+ return -1; // didn't hit anuthing
+ }
+
+ // check for impact on this node
+ Math3D.vectorCopy(mid, lightspot);
+ lightplane = plane;
+ int surfIndex = node.firstsurface;
+
+ msurface_t surf;
+ int s, t, ds, dt;
+ mtexinfo_t tex;
+ ByteBuffer lightmap;
+ int maps;
+ for (int i = 0; i < node.numsurfaces; i++, surfIndex++) {
+ surf = r_worldmodel.surfaces[surfIndex];
+
+ if ((surf.flags & (Defines.SURF_DRAWTURB | Defines.SURF_DRAWSKY)) != 0)
+ continue; // no lightmaps
+
+ tex = surf.texinfo;
+
+ s = (int) (Math3D.dotProduct(mid, tex.vecs[0]) + tex.vecs[0][3]);
+ t = (int) (Math3D.dotProduct(mid, tex.vecs[1]) + tex.vecs[1][3]);
+
+ if (s < surf.texturemins[0] || t < surf.texturemins[1])
+ continue;
+
+ ds = s - surf.texturemins[0];
+ dt = t - surf.texturemins[1];
+
+ if (ds > surf.extents[0] || dt > surf.extents[1])
+ continue;
+
+ if (surf.samples == null)
+ return 0;
+
+ ds >>= 4;
+ dt >>= 4;
+
+ lightmap = surf.samples;
+ int lightmapIndex = 0;
+
+ Math3D.vectorCopy(Globals.vec3_origin, pointcolor);
+ if (lightmap != null) {
+ float[] rgb;
+ lightmapIndex += 3 * (dt * ((surf.extents[0] >> 4) + 1) + ds);
+
+ float scale0, scale1, scale2;
+ for (maps = 0; maps < Defines.MAXLIGHTMAPS && surf.styles[maps] != (byte) 255; maps++) {
+ rgb = r_newrefdef.lightstyles[surf.styles[maps] & 0xFF].rgb;
+ scale0 = gl_modulate.value * rgb[0];
+ scale1 = gl_modulate.value * rgb[1];
+ scale2 = gl_modulate.value * rgb[2];
+
+ pointcolor[0] += (lightmap.get(lightmapIndex) & 0xFF) * scale0 * (1.0f / 255);
+ pointcolor[1] += (lightmap.get(lightmapIndex + 1) & 0xFF) * scale1 * (1.0f / 255);
+ pointcolor[2] += (lightmap.get(lightmapIndex + 2) & 0xFF) * scale2 * (1.0f / 255);
+ lightmapIndex += 3 * ((surf.extents[0] >> 4) + 1) * ((surf.extents[1] >> 4) + 1);
+ }
+ }
+ Vec3Cache.release(); // mid
+ return 1;
+ }
+
+ // go down back side
+ r = RecursiveLightPoint(node.children[1 - sideIndex], mid, end);
+ Vec3Cache.release(); // mid
+ return r;
+ }
+
+ /**
+ * R_LightPoint
+ */
+ void R_LightPoint(float[] p, float[] color) {
+ assert (p.length == 3) : "vec3_t bug";
+ assert (color.length == 3) : "rgb bug";
+
+ if (r_worldmodel.lightdata == null) {
+ color[0] = color[1] = color[2] = 1.0f;
+ return;
+ }
+
+ end[0] = p[0];
+ end[1] = p[1];
+ end[2] = p[2] - 2048;
+
+ float r = RecursiveLightPoint(r_worldmodel.nodes[0], p, end);
+
+ if (r == -1) {
+ Math3D.vectorCopy(Globals.vec3_origin, color);
+ } else {
+ Math3D.vectorCopy(pointcolor, color);
+ }
+
+ //
+ // add dynamic lights
+ //
+ dlight_t dl;
+ float add;
+ for (int lnum = 0; lnum < r_newrefdef.num_dlights; lnum++) {
+ dl = r_newrefdef.dlights[lnum];
+
+ Math3D.vectorSubtract(currententity.origin, dl.origin, end);
+ add = dl.intensity - Math3D.vectorLength(end);
+ add *= (1.0f / 256);
+ if (add > 0) {
+ Math3D.vectorMA(color, add, dl.color, color);
+ }
+ }
+ Math3D.vectorScale(color, gl_modulate.value, color);
+ }
+
+ /**
+ * R_AddDynamicLights
+ */
+ void R_AddDynamicLights(msurface_t surf) {
+ int sd, td;
+ float fdist, frad, fminlight;
+ int s, t;
+ dlight_t dl;
+ float[] pfBL;
+ float fsacc, ftacc;
+
+ int smax = (surf.extents[0] >> 4) + 1;
+ int tmax = (surf.extents[1] >> 4) + 1;
+ mtexinfo_t tex = surf.texinfo;
+
+ float local0, local1;
+ for (int lnum = 0; lnum < r_newrefdef.num_dlights; lnum++) {
+ if ((surf.dlightbits & (1 << lnum)) == 0)
+ continue; // not lit by this light
+
+ dl = r_newrefdef.dlights[lnum];
+ frad = dl.intensity;
+ fdist = Math3D.dotProduct(dl.origin, surf.plane.normal) -
+ surf.plane.dist;
+ frad -= Math.abs(fdist);
+ // rad is now the highest intensity on the plane
+
+ fminlight = DLIGHT_CUTOFF; // FIXME: make configurable?
+ if (frad < fminlight)
+ continue;
+ fminlight = frad - fminlight;
+
+ for (int i = 0; i < 3; i++) {
+ impact[i] = dl.origin[i] -
+ surf.plane.normal[i] * fdist;
+ }
+
+ local0 = Math3D.dotProduct(impact, tex.vecs[0]) + tex.vecs[0][3] - surf.texturemins[0];
+ local1 = Math3D.dotProduct(impact, tex.vecs[1]) + tex.vecs[1][3] - surf.texturemins[1];
+
+ pfBL = s_blocklights;
+ int pfBLindex = 0;
+ for (t = 0, ftacc = 0; t < tmax; t++, ftacc += 16) {
+ td = (int) (local1 - ftacc);
+ if (td < 0)
+ td = -td;
+
+ for (s = 0, fsacc = 0; s < smax; s++, fsacc += 16, pfBLindex += 3) {
+ sd = (int) (local0 - fsacc);
+
+ if (sd < 0)
+ sd = -sd;
+
+ if (sd > td)
+ fdist = sd + (td >> 1);
+ else
+ fdist = td + (sd >> 1);
+
+ if (fdist < fminlight) {
+ pfBL[pfBLindex] += (frad - fdist) * dl.color[0];
+ pfBL[pfBLindex + 1] += (frad - fdist) * dl.color[1];
+ pfBL[pfBLindex + 2] += (frad - fdist) * dl.color[2];
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * R_SetCacheState
+ */
+ void R_SetCacheState(msurface_t surf) {
+ for (int maps = 0; maps < Defines.MAXLIGHTMAPS && surf.styles[maps] != (byte) 255; maps++) {
+ surf.cached_light[maps] = r_newrefdef.lightstyles[surf.styles[maps] & 0xFF].white;
+ }
+ }
+
+// TODO sync with jogl renderer. hoz
+
+ /**
+ * R_BuildLightMap
+ * <p>
+ * Combine and scale multiple lightmaps into the floating format in blocklights
+ */
+ void R_BuildLightMap(msurface_t surf, IntBuffer dest, int stride) {
+ int r, g, b, a, max;
+ int i, j;
+ int nummaps;
+ float[] bl;
+ //lightstyle_t style;
+
+ if ((surf.texinfo.flags & (Defines.SURF_SKY | Defines.SURF_TRANS33
+ | Defines.SURF_TRANS66 | Defines.SURF_WARP)) != 0)
+ Com.Error(Defines.ERR_DROP,
+ "R_BuildLightMap called for non-lit surface");
+
+ int smax = (surf.extents[0] >> 4) + 1;
+ int tmax = (surf.extents[1] >> 4) + 1;
+ int size = smax * tmax;
+ if (size > ((s_blocklights.length * Defines.SIZE_OF_FLOAT) >> 4))
+ Com.Error(Defines.ERR_DROP, "Bad s_blocklights size");
+
+ try {
+ // set to full bright if no light data
+ if (surf.samples == null) {
+ // int maps;
+
+ for (i = 0; i < size * 3; i++)
+ s_blocklights[i] = 255;
+
+ // TODO useless? hoz
+ // for (maps = 0 ; maps < Defines.MAXLIGHTMAPS &&
+ // surf.styles[maps] != (byte)255; maps++)
+ // {
+ // style = r_newrefdef.lightstyles[surf.styles[maps] & 0xFF];
+ // }
+
+ // goto store;
+ throw gotoStore;
+ }
+
+ // count the # of maps
+ for (nummaps = 0; nummaps < Defines.MAXLIGHTMAPS
+ && surf.styles[nummaps] != (byte) 255; nummaps++)
+ ;
+
+ ByteBuffer lightmap = surf.samples;
+ int lightmapIndex = 0;
+
+ // add all the lightmaps
+ float scale0;
+ float scale1;
+ float scale2;
+ if (nummaps == 1) {
+ int maps;
+
+ for (maps = 0; maps < Defines.MAXLIGHTMAPS
+ && surf.styles[maps] != (byte) 255; maps++) {
+ bl = s_blocklights;
+ int blp = 0;
+
+// for (i = 0; i < 3; i++)
+// scale[i] = gl_modulate.value
+// * r_newrefdef.lightstyles[surf.styles[maps] & 0xFF].rgb[i];
+ scale0 = gl_modulate.value * r_newrefdef.lightstyles[surf.styles[maps] & 0xFF].rgb[0];
+ scale1 = gl_modulate.value * r_newrefdef.lightstyles[surf.styles[maps] & 0xFF].rgb[1];
+ scale2 = gl_modulate.value * r_newrefdef.lightstyles[surf.styles[maps] & 0xFF].rgb[2];
+
+ if (scale0 == 1.0F && scale1 == 1.0F
+ && scale2 == 1.0F) {
+ for (i = 0; i < size; i++) {
+ bl[blp++] = lightmap.get(lightmapIndex++) & 0xFF;
+ bl[blp++] = lightmap.get(lightmapIndex++) & 0xFF;
+ bl[blp++] = lightmap.get(lightmapIndex++) & 0xFF;
+ }
+ } else {
+ for (i = 0; i < size; i++) {
+ bl[blp++] = (lightmap.get(lightmapIndex++) & 0xFF)
+ * scale0;
+ bl[blp++] = (lightmap.get(lightmapIndex++) & 0xFF)
+ * scale1;
+ bl[blp++] = (lightmap.get(lightmapIndex++) & 0xFF)
+ * scale2;
+ }
+ }
+ //lightmap += size*3; // skip to next lightmap
+ }
+ } else {
+ int maps;
+
+ // memset( s_blocklights, 0, sizeof( s_blocklights[0] ) * size *
+ // 3 );
+
+ Arrays.fill(s_blocklights, 0, size * 3, 0.0f);
+
+ for (maps = 0; maps < Defines.MAXLIGHTMAPS
+ && surf.styles[maps] != (byte) 255; maps++) {
+ bl = s_blocklights;
+ int blp = 0;
+
+// for (i = 0; i < 3; i++)
+// scale[i] = gl_modulate.value
+// * r_newrefdef.lightstyles[surf.styles[maps] & 0xFF].rgb[i];
+ scale0 = gl_modulate.value
+ * r_newrefdef.lightstyles[surf.styles[maps] & 0xFF].rgb[0];
+ scale1 = gl_modulate.value
+ * r_newrefdef.lightstyles[surf.styles[maps] & 0xFF].rgb[1];
+ scale2 = gl_modulate.value
+ * r_newrefdef.lightstyles[surf.styles[maps] & 0xFF].rgb[2];
+
+
+ if (scale0 == 1.0F && scale1 == 1.0F
+ && scale2 == 1.0F) {
+ for (i = 0; i < size; i++) {
+ bl[blp++] += lightmap.get(lightmapIndex++) & 0xFF;
+ bl[blp++] += lightmap.get(lightmapIndex++) & 0xFF;
+ bl[blp++] += lightmap.get(lightmapIndex++) & 0xFF;
+ }
+ } else {
+ for (i = 0; i < size; i++) {
+ bl[blp++] += (lightmap.get(lightmapIndex++) & 0xFF)
+ * scale0;
+ bl[blp++] += (lightmap.get(lightmapIndex++) & 0xFF)
+ * scale1;
+ bl[blp++] += (lightmap.get(lightmapIndex++) & 0xFF)
+ * scale2;
+ }
+ }
+ //lightmap += size*3; // skip to next lightmap
+ }
+ }
+
+ // add all the dynamic lights
+ if (surf.dlightframe == r_framecount)
+ R_AddDynamicLights(surf);
+
+ // label store:
+ } catch (Throwable ignored) {
+ }
+
+ // put into texture format
+ stride -= smax;
+ bl = s_blocklights;
+ int blp = 0;
+
+ int monolightmap = gl_monolightmap.string.charAt(0);
+
+ int destp = 0;
+
+ if (monolightmap == '0') {
+ for (i = 0; i < tmax; i++, destp += stride) {
+ //dest.position(destp);
+
+ for (j = 0; j < smax; j++) {
+
+ r = (int) bl[blp++];
+ g = (int) bl[blp++];
+ b = (int) bl[blp++];
+
+ // catch negative lights
+ if (r < 0)
+ r = 0;
+ if (g < 0)
+ g = 0;
+ if (b < 0)
+ b = 0;
+
+ /*
+ * * determine the brightest of the three color components
+ */
+ if (r > g)
+ max = r;
+ else
+ max = g;
+ if (b > max)
+ max = b;
+
+ /*
+ * * alpha is ONLY used for the mono lightmap case. For this
+ * reason * we set it to the brightest of the color
+ * components so that * things don't get too dim.
+ */
+ a = max;
+
+ /*
+ * * rescale all the color components if the intensity of
+ * the greatest * channel exceeds 1.0
+ */
+ if (max > 255) {
+ float t = 255.0F / max;
+
+ r = (int) (r * t);
+ g = (int) (g * t);
+ b = (int) (b * t);
+ a = (int) (a * t);
+ }
+ //r &= 0xFF; g &= 0xFF; b &= 0xFF; a &= 0xFF;
+ dest.put(destp++, (a << 24) | (b << 16) | (g << 8) | r);
+ }
+ }
+ } else {
+ for (i = 0; i < tmax; i++, destp += stride) {
+ //dest.position(destp);
+
+ for (j = 0; j < smax; j++) {
+
+ r = (int) bl[blp++];
+ g = (int) bl[blp++];
+ b = (int) bl[blp++];
+
+ // catch negative lights
+ if (r < 0)
+ r = 0;
+ if (g < 0)
+ g = 0;
+ if (b < 0)
+ b = 0;
+
+ /*
+ * * determine the brightest of the three color components
+ */
+ if (r > g)
+ max = r;
+ else
+ max = g;
+ if (b > max)
+ max = b;
+
+ /*
+ * * alpha is ONLY used for the mono lightmap case. For this
+ * reason * we set it to the brightest of the color
+ * components so that * things don't get too dim.
+ */
+ a = max;
+
+ /*
+ * * rescale all the color components if the intensity of
+ * the greatest * channel exceeds 1.0
+ */
+ if (max > 255) {
+ float t = 255.0F / max;
+
+ r = (int) (r * t);
+ g = (int) (g * t);
+ b = (int) (b * t);
+ a = (int) (a * t);
+ }
+
+ /*
+ * * So if we are doing alpha lightmaps we need to set the
+ * R, G, and B * components to 0 and we need to set alpha to
+ * 1-alpha.
+ */
+ switch (monolightmap) {
+ case 'L':
+ case 'I':
+ r = a;
+ g = b = 0;
+ break;
+ case 'C':
+ // try faking colored lighting
+ a = 255 - ((r + g + b) / 3);
+ float af = a / 255.0f;
+ r *= af;
+ g *= af;
+ b *= af;
+ break;
+ case 'A':
+ default:
+ r = g = b = 0;
+ a = 255 - a;
+ break;
+ }
+ //r &= 0xFF; g &= 0xFF; b &= 0xFF; a &= 0xFF;
+ dest.put(destp++, (a << 24) | (b << 16) | (g << 8) | r);
+ }
+ }
+ }
+ }
+
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.render.lwjgl;
+
+import lwjake2.Defines;
+import lwjake2.Globals;
+import lwjake2.client.VideoDriver;
+import lwjake2.client.entity_t;
+import lwjake2.client.particle_t;
+import lwjake2.client.refdef_t;
+import lwjake2.game.Cmd;
+import lwjake2.game.CvarT;
+import lwjake2.game.cplane_t;
+import lwjake2.qcommon.Com;
+import lwjake2.qcommon.Cvar;
+import lwjake2.qcommon.qfiles;
+import lwjake2.qcommon.xcommand_t;
+import lwjake2.render.Image;
+import lwjake2.render.Model;
+import lwjake2.render.*;
+import lwjake2.util.Math3D;
+import lwjake2.util.Vargs;
+import org.lwjgl.BufferUtils;
+import org.lwjgl.opengl.ARBMultitexture;
+import org.lwjgl.opengl.GL11;
+import org.lwjgl.opengl.GL13;
+
+import java.awt.*;
+import java.nio.FloatBuffer;
+import java.nio.IntBuffer;
+
+/**
+ * Main
+ *
+ * @author cwei
+ */
+public abstract class Main extends Base {
+
+ public static final int[] d_8to24table = new int[256];
+ static final int NUM_BEAM_SEGS = 6;
+ final glconfig_t gl_config = new glconfig_t();
+ final glstate_t gl_state = new glstate_t();
+ final cplane_t[] frustum = {new cplane_t(), new cplane_t(), new cplane_t(), new cplane_t()};
+ final float[] v_blend = {0, 0, 0, 0}; // final blending color
+ //
+ // view origin
+ //
+ final float[] vup = {0, 0, 0};
+ /*
+ ====================================================================
+
+ from gl_rmain.c
+
+ ====================================================================
+ */
+ final float[] vpn = {0, 0, 0};
+ final float[] vright = {0, 0, 0};
+ final float[] r_origin = {0, 0, 0};
+ //float r_world_matrix[] = new float[16];
+ final FloatBuffer r_world_matrix = BufferUtils.createFloatBuffer(16);
+ final float[] r_turbsin = new float[256];
+ final int[] r_rawpalette = new int[256];
+ final float[][] start_points = new float[NUM_BEAM_SEGS][3];
+ // array of vec3_t
+ final float[][] end_points = new float[NUM_BEAM_SEGS][3]; // array of vec3_t
+ // stack variable
+ private final float[] point = {0, 0, 0};
+ // stack variable
+ private final float[] shadelight = {0, 0, 0};
+ // stack variable
+ private final float[] up = {0, 0, 0};
+ private final float[] right = {0, 0, 0};
+ // stack variable
+ private final float[] temp = {0, 0, 0};
+ // stack variable
+ private final float[] light = {0, 0, 0};
+ // stack variable
+ private final float[] perpvec = {0, 0, 0}; // vec3_t
+ private final float[] direction = {0, 0, 0}; // vec3_t
+ private final float[] normalized_direction = {0, 0, 0}; // vec3_t
+ private final float[] oldorigin = {0, 0, 0}; // vec3_t
+ private final float[] origin = {0, 0, 0}; // vec3_t
+ int c_visible_lightmaps;
+ int c_visible_textures;
+ int registration_sequence;
+ // this a hack for function pointer test
+ // default disabled
+ boolean qglColorTableEXT = false;
+ boolean qglActiveTextureARB = false;
+ boolean qglPointParameterfEXT = false;
+ boolean qglLockArraysEXT = false;
+ boolean qwglSwapIntervalEXT = false;
+ int GL_TEXTURE0 = GL13.GL_TEXTURE0;
+ int GL_TEXTURE1 = GL13.GL_TEXTURE1;
+ Model r_worldmodel;
+ float gldepthmin, gldepthmax;
+ Image r_notexture; // use for bad textures
+ Image r_particletexture; // little dot for particles
+ entity_t currententity;
+ Model currentmodel;
+ int r_visframecount; // bumped when going to a new PVS
+ int r_framecount; // used for dlight push checking
+ int c_brush_polys, c_alias_polys;
+ float r_base_world_matrix[] = new float[16];
+ //
+ // screen size info
+ //
+ refdef_t r_newrefdef = new refdef_t();
+ int r_viewcluster, r_viewcluster2, r_oldviewcluster, r_oldviewcluster2;
+ CvarT r_norefresh;
+ CvarT r_drawentities;
+ CvarT r_drawworld;
+ CvarT r_speeds;
+ CvarT r_fullbright;
+ CvarT r_novis;
+ CvarT r_nocull;
+ CvarT r_lerpmodels;
+ CvarT r_lefthand;
+ CvarT r_lightlevel;
+ CvarT gl_nosubimage;
+ CvarT gl_allow_software;
+ CvarT gl_vertex_arrays;
+ CvarT gl_particle_min_size;
+ CvarT gl_particle_max_size;
+ CvarT gl_particle_size;
+ CvarT gl_particle_att_a;
+ CvarT gl_particle_att_b;
+ CvarT gl_particle_att_c;
+ CvarT gl_ext_swapinterval;
+ CvarT gl_ext_palettedtexture;
+ CvarT gl_ext_multitexture;
+ CvarT gl_ext_pointparameters;
+ CvarT gl_ext_compiled_vertex_array;
+ CvarT gl_log;
+ CvarT gl_bitdepth;
+ // FIXME: This is a HACK to get the client's light level
+ CvarT gl_drawbuffer;
+ CvarT gl_driver;
+ CvarT gl_lightmap;
+ CvarT gl_shadows;
+ CvarT gl_mode;
+ CvarT gl_dynamic;
+ CvarT gl_monolightmap;
+ CvarT gl_modulate;
+ CvarT gl_nobind;
+ CvarT gl_round_down;
+ CvarT gl_picmip;
+ CvarT gl_skymip;
+ CvarT gl_showtris;
+ CvarT gl_ztrick;
+ CvarT gl_finish;
+ CvarT gl_clear;
+ CvarT gl_cull;
+ CvarT gl_polyblend;
+ CvarT gl_flashblend;
+ CvarT gl_playermip;
+ CvarT gl_saturatelighting;
+ CvarT gl_swapinterval;
+ CvarT gl_texturemode;
+ CvarT gl_texturealphamode;
+ CvarT gl_texturesolidmode;
+ CvarT gl_lockpvs;
+ CvarT gl_3dlabs_broken;
+ CvarT vid_gamma;
+ CvarT vid_ref;
+ int trickframe = 0;
+
+ // =================
+ // abstract methods
+ // =================
+ protected abstract void Draw_GetPalette();
+
+ abstract void GL_ImageList_f();
+
+ abstract void GL_ScreenShot_f();
+
+ abstract void GL_SetTexturePalette(int[] palette);
+
+ abstract void GL_Strings_f();
+
+ abstract void Mod_Modellist_f();
+
+ abstract mleaf_t Mod_PointInLeaf(float[] point, Model model);
+
+ abstract void GL_SetDefaultState();
+
+ abstract void GL_InitImages();
+
+ abstract void Mod_Init(); // Model.java
+
+ abstract void R_InitParticleTexture(); // MIsc.java
+
+ // ============================================================================
+ // to port from gl_rmain.c, ...
+ // ============================================================================
+
+ abstract void R_DrawAliasModel(entity_t e); // Mesh.java
+
+ abstract void R_DrawBrushModel(entity_t e); // Surf.java
+
+ /*
+ =============================================================
+
+ SPRITE MODELS
+
+ =============================================================
+ */
+
+ abstract void Draw_InitLocal();
+
+ abstract void R_LightPoint(float[] p, float[] color);
+
+ // ==================================================================================
+
+ abstract void R_PushDlights();
+
+ abstract void R_MarkLeaves();
+
+ abstract void R_DrawWorld();
+
+ abstract void R_RenderDlights();
+
+ abstract void R_DrawAlphaSurfaces();
+
+ abstract void Mod_FreeAll();
+
+ abstract void GL_ShutdownImages();
+
+ abstract void GL_Bind(int texnum);
+
+ // =======================================================================
+
+ abstract void GL_TexEnv(int mode);
+
+ abstract void GL_TextureMode(String string);
+
+ // =======================================================================
+
+ abstract void GL_TextureAlphaMode(String string);
+
+ abstract void GL_TextureSolidMode(String string);
+
+ abstract void GL_UpdateSwapInterval();
+
+ /**
+ * R_CullBox
+ * Returns true if the box is completely outside the frustum
+ */
+ final boolean R_CullBox(float[] mins, float[] maxs) {
+ assert (mins.length == 3 && maxs.length == 3) : "vec3_t bug";
+
+ if (r_nocull.value != 0)
+ return false;
+
+ for (int i = 0; i < 4; i++) {
+ if (Math3D.boxOnPlaneSide(mins, maxs, frustum[i]) == 2)
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * R_RotateForEntity
+ */
+ final void R_RotateForEntity(entity_t e) {
+ GL11.glTranslatef(e.origin[0], e.origin[1], e.origin[2]);
+
+ GL11.glRotatef(e.angles[1], 0, 0, 1);
+ GL11.glRotatef(-e.angles[0], 0, 1, 0);
+ GL11.glRotatef(-e.angles[2], 1, 0, 0);
+ }
+
+ /**
+ * R_DrawSpriteModel
+ */
+ void R_DrawSpriteModel(entity_t e) {
+ float alpha = 1.0F;
+
+ qfiles.dsprframe_t frame;
+ qfiles.dsprite_t psprite;
+
+ // don't even bother culling, because it's just a single
+ // polygon without a surface cache
+
+ psprite = (qfiles.dsprite_t) currentmodel.extradata;
+
+ e.frame %= psprite.numframes;
+
+ frame = psprite.frames[e.frame];
+
+ if ((e.flags & Defines.RF_TRANSLUCENT) != 0)
+ alpha = e.alpha;
+
+ if (alpha != 1.0F)
+ GL11.glEnable(GL11.GL_BLEND);
+
+ GL11.glColor4f(1, 1, 1, alpha);
+
+ GL_Bind(currentmodel.skins[e.frame].texnum);
+
+ GL_TexEnv(GL11.GL_MODULATE);
+
+ if (alpha == 1.0)
+ GL11.glEnable(GL11.GL_ALPHA_TEST);
+ else
+ GL11.glDisable(GL11.GL_ALPHA_TEST);
+
+ GL11.glBegin(GL11.GL_QUADS);
+
+ GL11.glTexCoord2f(0, 1);
+ Math3D.vectorMA(e.origin, -frame.origin_y, vup, point);
+ Math3D.vectorMA(point, -frame.origin_x, vright, point);
+ GL11.glVertex3f(point[0], point[1], point[2]);
+
+ GL11.glTexCoord2f(0, 0);
+ Math3D.vectorMA(e.origin, frame.height - frame.origin_y, vup, point);
+ Math3D.vectorMA(point, -frame.origin_x, vright, point);
+ GL11.glVertex3f(point[0], point[1], point[2]);
+
+ GL11.glTexCoord2f(1, 0);
+ Math3D.vectorMA(e.origin, frame.height - frame.origin_y, vup, point);
+ Math3D.vectorMA(point, frame.width - frame.origin_x, vright, point);
+ GL11.glVertex3f(point[0], point[1], point[2]);
+
+ GL11.glTexCoord2f(1, 1);
+ Math3D.vectorMA(e.origin, -frame.origin_y, vup, point);
+ Math3D.vectorMA(point, frame.width - frame.origin_x, vright, point);
+ GL11.glVertex3f(point[0], point[1], point[2]);
+
+ GL11.glEnd();
+
+ GL11.glDisable(GL11.GL_ALPHA_TEST);
+ GL_TexEnv(GL11.GL_REPLACE);
+
+ if (alpha != 1.0F)
+ GL11.glDisable(GL11.GL_BLEND);
+
+ GL11.glColor4f(1, 1, 1, 1);
+ }
+
+ /**
+ * R_DrawNullModel
+ */
+ void R_DrawNullModel() {
+ if ((currententity.flags & Defines.RF_FULLBRIGHT) != 0) {
+ // cwei wollte blau: shadelight[0] = shadelight[1] = shadelight[2] = 1.0F;
+ shadelight[0] = shadelight[1] = shadelight[2] = 0.0F;
+ shadelight[2] = 0.8F;
+ } else {
+ R_LightPoint(currententity.origin, shadelight);
+ }
+
+ GL11.glPushMatrix();
+ R_RotateForEntity(currententity);
+
+ GL11.glDisable(GL11.GL_TEXTURE_2D);
+ GL11.glColor3f(shadelight[0], shadelight[1], shadelight[2]);
+
+ // this replaces the TRIANGLE_FAN
+ //glut.glutWireCube(gl, 20);
+
+ GL11.glBegin(GL11.GL_TRIANGLE_FAN);
+ GL11.glVertex3f(0, 0, -16);
+ int i;
+ for (i = 0; i <= 4; i++) {
+ GL11.glVertex3f((float) (16.0f * Math.cos(i * Math.PI / 2)), (float) (16.0f * Math.sin(i * Math.PI / 2)), 0.0f);
+ }
+ GL11.glEnd();
+
+ GL11.glBegin(GL11.GL_TRIANGLE_FAN);
+ GL11.glVertex3f(0, 0, 16);
+ for (i = 4; i >= 0; i--) {
+ GL11.glVertex3f((float) (16.0f * Math.cos(i * Math.PI / 2)), (float) (16.0f * Math.sin(i * Math.PI / 2)), 0.0f);
+ }
+ GL11.glEnd();
+
+
+ GL11.glColor3f(1, 1, 1);
+ GL11.glPopMatrix();
+ GL11.glEnable(GL11.GL_TEXTURE_2D);
+ }
+
+ /**
+ * R_DrawEntitiesOnList
+ */
+ void R_DrawEntitiesOnList() {
+ if (r_drawentities.value == 0.0f)
+ return;
+
+ // draw non-transparent first
+ int i;
+ for (i = 0; i < r_newrefdef.num_entities; i++) {
+ currententity = r_newrefdef.entities[i];
+ if ((currententity.flags & Defines.RF_TRANSLUCENT) != 0)
+ continue; // solid
+
+ currentmodel = currententity.model;
+ if (currentmodel == null) {
+ R_DrawNullModel();
+ continue;
+ }
+ switch (currentmodel.type) {
+ case mod_alias:
+ R_DrawAliasModel(currententity);
+ break;
+ case mod_brush:
+ R_DrawBrushModel(currententity);
+ break;
+ case mod_sprite:
+ R_DrawSpriteModel(currententity);
+ break;
+ default:
+ Com.Error(Defines.ERR_DROP, "Bad modeltype");
+ break;
+ }
+ }
+ // draw transparent entities
+ // we could sort these if it ever becomes a problem...
+ GL11.glDepthMask(false); // no z writes
+ for (i = 0; i < r_newrefdef.num_entities; i++) {
+ currententity = r_newrefdef.entities[i];
+ if ((currententity.flags & Defines.RF_TRANSLUCENT) == 0)
+ continue; // solid
+
+ currentmodel = currententity.model;
+
+ if (currentmodel == null) {
+ R_DrawNullModel();
+ continue;
+ }
+ switch (currentmodel.type) {
+ case mod_alias:
+ R_DrawAliasModel(currententity);
+ break;
+ case mod_brush:
+ R_DrawBrushModel(currententity);
+ break;
+ case mod_sprite:
+ R_DrawSpriteModel(currententity);
+ break;
+ default:
+ Com.Error(Defines.ERR_DROP, "Bad modeltype");
+ break;
+ }
+ }
+ GL11.glDepthMask(true); // back to writing
+ }
+
+ /**
+ * GL_DrawParticles
+ */
+ void GL_DrawParticles(int num_particles) {
+ float origin_x, origin_y, origin_z;
+
+ Math3D.vectorScale(vup, 1.5f, up);
+ Math3D.vectorScale(vright, 1.5f, right);
+
+ GL_Bind(r_particletexture.texnum);
+ GL11.glDepthMask(false); // no z buffering
+ GL11.glEnable(GL11.GL_BLEND);
+ GL_TexEnv(GL11.GL_MODULATE);
+
+ GL11.glBegin(GL11.GL_TRIANGLES);
+
+ FloatBuffer sourceVertices = particle_t.vertexArray;
+ IntBuffer sourceColors = particle_t.colorArray;
+ float scale;
+ int color;
+ for (int j = 0, i = 0; i < num_particles; i++) {
+ origin_x = sourceVertices.get(j++);
+ origin_y = sourceVertices.get(j++);
+ origin_z = sourceVertices.get(j++);
+
+ // hack a scale up to keep particles from disapearing
+ scale =
+ (origin_x - r_origin[0]) * vpn[0]
+ + (origin_y - r_origin[1]) * vpn[1]
+ + (origin_z - r_origin[2]) * vpn[2];
+
+ scale = (scale < 20) ? 1 : 1 + scale * 0.004f;
+
+ color = sourceColors.get(i);
+
+ GL11.glColor4ub(
+ (byte) ((color) & 0xFF),
+ (byte) ((color >> 8) & 0xFF),
+ (byte) ((color >> 16) & 0xFF),
+ (byte) ((color >>> 24))
+ );
+ // first vertex
+ GL11.glTexCoord2f(0.0625f, 0.0625f);
+ GL11.glVertex3f(origin_x, origin_y, origin_z);
+ // second vertex
+ GL11.glTexCoord2f(1.0625f, 0.0625f);
+ GL11.glVertex3f(origin_x + up[0] * scale, origin_y + up[1] * scale, origin_z + up[2] * scale);
+ // third vertex
+ GL11.glTexCoord2f(0.0625f, 1.0625f);
+ GL11.glVertex3f(origin_x + right[0] * scale, origin_y + right[1] * scale, origin_z + right[2] * scale);
+ }
+ GL11.glEnd();
+
+ GL11.glDisable(GL11.GL_BLEND);
+ GL11.glColor4f(1, 1, 1, 1);
+ GL11.glDepthMask(true); // back to normal Z buffering
+ GL_TexEnv(GL11.GL_REPLACE);
+ }
+
+ /**
+ * R_DrawParticles
+ */
+ void R_DrawParticles() {
+
+ if (gl_ext_pointparameters.value != 0.0f && qglPointParameterfEXT) {
+
+ //GL11.glEnableClientState(GL11.GL_VERTEX_ARRAY);
+ GL11.glVertexPointer(3, 0, particle_t.vertexArray);
+ GL11.glEnableClientState(GL11.GL_COLOR_ARRAY);
+ GL11.glColorPointer(4, true, 0, particle_t.getColorAsByteBuffer());
+
+ GL11.glDepthMask(false);
+ GL11.glEnable(GL11.GL_BLEND);
+ GL11.glDisable(GL11.GL_TEXTURE_2D);
+ GL11.glPointSize(gl_particle_size.value);
+
+ GL11.glDrawArrays(GL11.GL_POINTS, 0, r_newrefdef.num_particles);
+
+ GL11.glDisableClientState(GL11.GL_COLOR_ARRAY);
+ //GL11.glDisableClientState(GL11.GL_VERTEX_ARRAY);
+
+ GL11.glDisable(GL11.GL_BLEND);
+ GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
+ GL11.glDepthMask(true);
+ GL11.glEnable(GL11.GL_TEXTURE_2D);
+
+ } else {
+ GL_DrawParticles(r_newrefdef.num_particles);
+ }
+ }
+
+ /**
+ * R_PolyBlend
+ */
+ void R_PolyBlend() {
+ if (gl_polyblend.value == 0.0f)
+ return;
+
+ if (v_blend[3] == 0.0f)
+ return;
+
+ GL11.glDisable(GL11.GL_ALPHA_TEST);
+ GL11.glEnable(GL11.GL_BLEND);
+ GL11.glDisable(GL11.GL_DEPTH_TEST);
+ GL11.glDisable(GL11.GL_TEXTURE_2D);
+
+ GL11.glLoadIdentity();
+
+ // FIXME: get rid of these
+ GL11.glRotatef(-90, 1, 0, 0); // put Z going up
+ GL11.glRotatef(90, 0, 0, 1); // put Z going up
+
+ GL11.glColor4f(v_blend[0], v_blend[1], v_blend[2], v_blend[3]);
+
+ GL11.glBegin(GL11.GL_QUADS);
+
+ GL11.glVertex3f(10, 100, 100);
+ GL11.glVertex3f(10, -100, 100);
+ GL11.glVertex3f(10, -100, -100);
+ GL11.glVertex3f(10, 100, -100);
+ GL11.glEnd();
+
+ GL11.glDisable(GL11.GL_BLEND);
+ GL11.glEnable(GL11.GL_TEXTURE_2D);
+ GL11.glEnable(GL11.GL_ALPHA_TEST);
+
+ GL11.glColor4f(1, 1, 1, 1);
+ }
+
+ /**
+ * SignbitsForPlane
+ */
+ int SignbitsForPlane(cplane_t out) {
+ // for fast box on planeside test
+ int bits = 0;
+ for (int j = 0; j < 3; j++) {
+ if (out.normal[j] < 0)
+ bits |= (1 << j);
+ }
+ return bits;
+ }
+
+ /**
+ * R_SetFrustum
+ */
+ void R_SetFrustum() {
+ // rotate VPN right by FOV_X/2 degrees
+ Math3D.rotatePointAroundVector(frustum[0].normal, vup, vpn, -(90f - r_newrefdef.fov_x / 2f));
+ // rotate VPN left by FOV_X/2 degrees
+ Math3D.rotatePointAroundVector(frustum[1].normal, vup, vpn, 90f - r_newrefdef.fov_x / 2f);
+ // rotate VPN up by FOV_X/2 degrees
+ Math3D.rotatePointAroundVector(frustum[2].normal, vright, vpn, 90f - r_newrefdef.fov_y / 2f);
+ // rotate VPN down by FOV_X/2 degrees
+ Math3D.rotatePointAroundVector(frustum[3].normal, vright, vpn, -(90f - r_newrefdef.fov_y / 2f));
+
+ for (int i = 0; i < 4; i++) {
+ frustum[i].type = Defines.PLANE_ANYZ;
+ frustum[i].dist = Math3D.dotProduct(r_origin, frustum[i].normal);
+ frustum[i].signbits = (byte) SignbitsForPlane(frustum[i]);
+ }
+ }
+
+ /**
+ * R_SetupFrame
+ */
+ void R_SetupFrame() {
+ r_framecount++;
+
+ // build the transformation matrix for the given view angles
+ Math3D.vectorCopy(r_newrefdef.vieworg, r_origin);
+
+ Math3D.angleVectors(r_newrefdef.viewangles, vpn, vright, vup);
+
+ // current viewcluster
+ mleaf_t leaf;
+ if ((r_newrefdef.rdflags & Defines.RDF_NOWORLDMODEL) == 0) {
+ r_oldviewcluster = r_viewcluster;
+ r_oldviewcluster2 = r_viewcluster2;
+ leaf = Mod_PointInLeaf(r_origin, r_worldmodel);
+ r_viewcluster = r_viewcluster2 = leaf.cluster;
+
+ // check above and below so crossing solid water doesn't draw wrong
+ if (leaf.contents == 0) { // look down a bit
+ Math3D.vectorCopy(r_origin, temp);
+ temp[2] -= 16;
+ leaf = Mod_PointInLeaf(temp, r_worldmodel);
+ if ((leaf.contents & Defines.CONTENTS_SOLID) == 0 && (leaf.cluster != r_viewcluster2))
+ r_viewcluster2 = leaf.cluster;
+ } else { // look up a bit
+ Math3D.vectorCopy(r_origin, temp);
+ temp[2] += 16;
+ leaf = Mod_PointInLeaf(temp, r_worldmodel);
+ if ((leaf.contents & Defines.CONTENTS_SOLID) == 0 && (leaf.cluster != r_viewcluster2))
+ r_viewcluster2 = leaf.cluster;
+ }
+ }
+
+ System.arraycopy(r_newrefdef.blend, 0, v_blend, 0, 4);
+
+ c_brush_polys = 0;
+ c_alias_polys = 0;
+
+ // clear out the portion of the screen that the NOWORLDMODEL defines
+ if ((r_newrefdef.rdflags & Defines.RDF_NOWORLDMODEL) != 0) {
+ GL11.glEnable(GL11.GL_SCISSOR_TEST);
+ GL11.glClearColor(0.3f, 0.3f, 0.3f, 1.0f);
+ GL11.glScissor(
+ r_newrefdef.x,
+ vid.height - r_newrefdef.height - r_newrefdef.y,
+ r_newrefdef.width,
+ r_newrefdef.height);
+ GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);
+ GL11.glClearColor(1.0f, 0.0f, 0.5f, 0.5f);
+ GL11.glDisable(GL11.GL_SCISSOR_TEST);
+ }
+ }
+
+ /**
+ * MYgluPerspective
+ *
+ * @param fovy
+ * @param aspect
+ */
+ void MYgluPerspective(double fovy, double aspect) {
+ double ymax = (double) 4 * Math.tan(fovy * Math.PI / 360.0);
+ double ymin = -ymax;
+
+ double xmin = ymin * aspect;
+ double xmax = ymax * aspect;
+
+ xmin += -(2 * gl_state.camera_separation) / (double) 4;
+ xmax += -(2 * gl_state.camera_separation) / (double) 4;
+
+ GL11.glFrustum(xmin, xmax, ymin, ymax, (double) 4, (double) 4096);
+ }
+
+ /**
+ * R_SetupGL
+ */
+ void R_SetupGL() {
+
+ //
+ // set up viewport
+ //
+ //int x = (int) Math.floor(r_newrefdef.x * vid.width / vid.width);
+ int x = r_newrefdef.x;
+ //int x2 = (int) Math.ceil((r_newrefdef.x + r_newrefdef.width) * vid.width / vid.width);
+ int x2 = r_newrefdef.x + r_newrefdef.width;
+ //int y = (int) Math.floor(vid.height - r_newrefdef.y * vid.height / vid.height);
+ int y = vid.height - r_newrefdef.y;
+ //int y2 = (int) Math.ceil(vid.height - (r_newrefdef.y + r_newrefdef.height) * vid.height / vid.height);
+ int y2 = vid.height - (r_newrefdef.y + r_newrefdef.height);
+
+ int w = x2 - x;
+ int h = y - y2;
+
+ GL11.glViewport(x, y2, w, h);
+
+ //
+ // set up projection matrix
+ //
+ float screenaspect = (float) r_newrefdef.width / r_newrefdef.height;
+ GL11.glMatrixMode(GL11.GL_PROJECTION);
+ GL11.glLoadIdentity();
+ MYgluPerspective(r_newrefdef.fov_y, screenaspect);
+
+ GL11.glCullFace(GL11.GL_FRONT);
+
+ GL11.glMatrixMode(GL11.GL_MODELVIEW);
+ GL11.glLoadIdentity();
+
+ GL11.glRotatef(-90, 1, 0, 0); // put Z going up
+ GL11.glRotatef(90, 0, 0, 1); // put Z going up
+ GL11.glRotatef(-r_newrefdef.viewangles[2], 1, 0, 0);
+ GL11.glRotatef(-r_newrefdef.viewangles[0], 0, 1, 0);
+ GL11.glRotatef(-r_newrefdef.viewangles[1], 0, 0, 1);
+ GL11.glTranslatef(-r_newrefdef.vieworg[0], -r_newrefdef.vieworg[1], -r_newrefdef.vieworg[2]);
+
+ GL11.glGetFloat(GL11.GL_MODELVIEW_MATRIX, r_world_matrix);
+ r_world_matrix.clear();
+
+ //
+ // set drawing parms
+ //
+ if (gl_cull.value != 0.0f)
+ GL11.glEnable(GL11.GL_CULL_FACE);
+ else
+ GL11.glDisable(GL11.GL_CULL_FACE);
+
+ GL11.glDisable(GL11.GL_BLEND);
+ GL11.glDisable(GL11.GL_ALPHA_TEST);
+ GL11.glEnable(GL11.GL_DEPTH_TEST);
+ }
+
+ /**
+ * R_Clear
+ */
+ void R_Clear() {
+ if (gl_ztrick.value != 0.0f) {
+
+ if (gl_clear.value != 0.0f) {
+ GL11.glClear(GL11.GL_COLOR_BUFFER_BIT);
+ }
+
+ trickframe++;
+ if ((trickframe & 1) != 0) {
+ gldepthmin = 0;
+ gldepthmax = 0.49999f;
+ GL11.glDepthFunc(GL11.GL_LEQUAL);
+ } else {
+ gldepthmin = 1;
+ gldepthmax = 0.5f;
+ GL11.glDepthFunc(GL11.GL_GEQUAL);
+ }
+ } else {
+ if (gl_clear.value != 0.0f)
+ GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);
+ else
+ GL11.glClear(GL11.GL_DEPTH_BUFFER_BIT);
+
+ gldepthmin = 0;
+ gldepthmax = 1;
+ GL11.glDepthFunc(GL11.GL_LEQUAL);
+ }
+ GL11.glDepthRange(gldepthmin, gldepthmax);
+ }
+
+ /**
+ * R_Flash
+ */
+ void R_Flash() {
+ R_PolyBlend();
+ }
+
+ /**
+ * R_RenderView
+ * r_newrefdef must be set before the first call
+ */
+ void R_RenderView(refdef_t fd) {
+
+ if (r_norefresh.value != 0.0f)
+ return;
+
+ r_newrefdef = fd;
+
+ // included by cwei
+ if (r_newrefdef == null) {
+ Com.Error(Defines.ERR_DROP, "R_RenderView: refdef_t fd is null");
+ }
+
+ if (r_worldmodel == null && (r_newrefdef.rdflags & Defines.RDF_NOWORLDMODEL) == 0)
+ Com.Error(Defines.ERR_DROP, "R_RenderView: NULL worldmodel");
+
+ if (r_speeds.value != 0.0f) {
+ c_brush_polys = 0;
+ c_alias_polys = 0;
+ }
+
+ R_PushDlights();
+
+ if (gl_finish.value != 0.0f)
+ GL11.glFinish();
+
+ R_SetupFrame();
+
+ R_SetFrustum();
+
+ R_SetupGL();
+
+ R_MarkLeaves(); // done here so we know if we're in water
+
+ R_DrawWorld();
+
+ R_DrawEntitiesOnList();
+
+ R_RenderDlights();
+
+ R_DrawParticles();
+
+ R_DrawAlphaSurfaces();
+
+ R_Flash();
+
+ if (r_speeds.value != 0.0f) {
+ VideoDriver.Printf(
+ Defines.PRINT_ALL,
+ "%4i wpoly %4i epoly %i tex %i lmaps\n",
+ new Vargs(4).add(c_brush_polys).add(c_alias_polys).add(c_visible_textures).add(c_visible_lightmaps));
+ }
+ }
+
+ /**
+ * R_SetGL2D
+ */
+ void R_SetGL2D() {
+ // set 2D virtual screen size
+ GL11.glViewport(0, 0, vid.width, vid.height);
+ GL11.glMatrixMode(GL11.GL_PROJECTION);
+ GL11.glLoadIdentity();
+ GL11.glOrtho(0, vid.width, vid.height, 0, -99999, 99999);
+ GL11.glMatrixMode(GL11.GL_MODELVIEW);
+ GL11.glLoadIdentity();
+ GL11.glDisable(GL11.GL_DEPTH_TEST);
+ GL11.glDisable(GL11.GL_CULL_FACE);
+ GL11.glDisable(GL11.GL_BLEND);
+ GL11.glEnable(GL11.GL_ALPHA_TEST);
+ GL11.glColor4f(1, 1, 1, 1);
+ }
+
+ /**
+ * R_SetLightLevel
+ */
+ void R_SetLightLevel() {
+ if ((r_newrefdef.rdflags & Defines.RDF_NOWORLDMODEL) != 0)
+ return;
+
+ // save off light value for server to look at (BIG HACK!)
+
+ R_LightPoint(r_newrefdef.vieworg, light);
+
+ // pick the greatest component, which should be the same
+ // as the mono value returned by software
+ if (light[0] > light[1]) {
+ if (light[0] > light[2])
+ r_lightlevel.value = 150 * light[0];
+ else
+ r_lightlevel.value = 150 * light[2];
+ } else {
+ if (light[1] > light[2])
+ r_lightlevel.value = 150 * light[1];
+ else
+ r_lightlevel.value = 150 * light[2];
+ }
+ }
+
+ /**
+ * R_RenderFrame
+ */
+ protected void R_RenderFrame(refdef_t fd) {
+ R_RenderView(fd);
+ R_SetLightLevel();
+ R_SetGL2D();
+ }
+
+ /**
+ * R_Register
+ */
+ protected void R_Register() {
+ r_lefthand = Cvar.get("hand", "0", Globals.CVAR_USERINFO | Globals.CVAR_ARCHIVE);
+ r_norefresh = Cvar.get("r_norefresh", "0", 0);
+ r_fullbright = Cvar.get("r_fullbright", "0", 0);
+ r_drawentities = Cvar.get("r_drawentities", "1", 0);
+ r_drawworld = Cvar.get("r_drawworld", "1", 0);
+ r_novis = Cvar.get("r_novis", "0", 0);
+ r_nocull = Cvar.get("r_nocull", "0", 0);
+ r_lerpmodels = Cvar.get("r_lerpmodels", "1", 0);
+ r_speeds = Cvar.get("r_speeds", "0", 0);
+
+ r_lightlevel = Cvar.get("r_lightlevel", "1", 0);
+
+ gl_nosubimage = Cvar.get("gl_nosubimage", "0", 0);
+ gl_allow_software = Cvar.get("gl_allow_software", "0", 0);
+
+ gl_particle_min_size = Cvar.get("gl_particle_min_size", "2", Globals.CVAR_ARCHIVE);
+ gl_particle_max_size = Cvar.get("gl_particle_max_size", "40", Globals.CVAR_ARCHIVE);
+ gl_particle_size = Cvar.get("gl_particle_size", "40", Globals.CVAR_ARCHIVE);
+ gl_particle_att_a = Cvar.get("gl_particle_att_a", "0.01", Globals.CVAR_ARCHIVE);
+ gl_particle_att_b = Cvar.get("gl_particle_att_b", "0.0", Globals.CVAR_ARCHIVE);
+ gl_particle_att_c = Cvar.get("gl_particle_att_c", "0.01", Globals.CVAR_ARCHIVE);
+
+ gl_modulate = Cvar.get("gl_modulate", "1.5", Globals.CVAR_ARCHIVE);
+ gl_log = Cvar.get("gl_log", "0", 0);
+ gl_bitdepth = Cvar.get("gl_bitdepth", "0", 0);
+ gl_mode = Cvar.get("gl_mode", "3", Globals.CVAR_ARCHIVE); // 640x480
+ gl_lightmap = Cvar.get("gl_lightmap", "0", 0);
+ gl_shadows = Cvar.get("gl_shadows", "0", Globals.CVAR_ARCHIVE);
+ gl_dynamic = Cvar.get("gl_dynamic", "1", 0);
+ gl_nobind = Cvar.get("gl_nobind", "0", 0);
+ gl_round_down = Cvar.get("gl_round_down", "1", 0);
+ gl_picmip = Cvar.get("gl_picmip", "0", 0);
+ gl_skymip = Cvar.get("gl_skymip", "0", 0);
+ gl_showtris = Cvar.get("gl_showtris", "0", 0);
+ gl_ztrick = Cvar.get("gl_ztrick", "0", 0);
+ gl_finish = Cvar.get("gl_finish", "0", Globals.CVAR_ARCHIVE);
+ gl_clear = Cvar.get("gl_clear", "0", 0);
+ gl_cull = Cvar.get("gl_cull", "1", 0);
+ gl_polyblend = Cvar.get("gl_polyblend", "1", 0);
+ gl_flashblend = Cvar.get("gl_flashblend", "0", 0);
+ gl_playermip = Cvar.get("gl_playermip", "0", 0);
+ gl_monolightmap = Cvar.get("gl_monolightmap", "0", 0);
+ gl_driver = Cvar.get("gl_driver", "opengl32", Globals.CVAR_ARCHIVE);
+ gl_texturemode = Cvar.get("gl_texturemode", "GL_LINEAR_MIPMAP_NEAREST", Globals.CVAR_ARCHIVE);
+ gl_texturealphamode = Cvar.get("gl_texturealphamode", "default", Globals.CVAR_ARCHIVE);
+ gl_texturesolidmode = Cvar.get("gl_texturesolidmode", "default", Globals.CVAR_ARCHIVE);
+ gl_lockpvs = Cvar.get("gl_lockpvs", "0", 0);
+
+ gl_vertex_arrays = Cvar.get("gl_vertex_arrays", "1", Globals.CVAR_ARCHIVE);
+
+ gl_ext_swapinterval = Cvar.get("gl_ext_swapinterval", "1", Globals.CVAR_ARCHIVE);
+ gl_ext_palettedtexture = Cvar.get("gl_ext_palettedtexture", "0", Globals.CVAR_ARCHIVE);
+ gl_ext_multitexture = Cvar.get("gl_ext_multitexture", "1", Globals.CVAR_ARCHIVE);
+ gl_ext_pointparameters = Cvar.get("gl_ext_pointparameters", "1", Globals.CVAR_ARCHIVE);
+ gl_ext_compiled_vertex_array = Cvar.get("gl_ext_compiled_vertex_array", "1", Globals.CVAR_ARCHIVE);
+
+ gl_drawbuffer = Cvar.get("gl_drawbuffer", "GL_BACK", 0);
+ gl_swapinterval = Cvar.get("gl_swapinterval", "0", Globals.CVAR_ARCHIVE);
+
+ gl_saturatelighting = Cvar.get("gl_saturatelighting", "0", 0);
+
+ gl_3dlabs_broken = Cvar.get("gl_3dlabs_broken", "1", Globals.CVAR_ARCHIVE);
+
+ vid_fullscreen = Cvar.get("vid_fullscreen", "0", Globals.CVAR_ARCHIVE);
+ vid_gamma = Cvar.get("vid_gamma", "1.0", Globals.CVAR_ARCHIVE);
+ vid_ref = Cvar.get("vid_ref", "lwjgl", Globals.CVAR_ARCHIVE);
+
+ Cmd.AddCommand("imagelist", new xcommand_t() {
+ public void execute() {
+ GL_ImageList_f();
+ }
+ });
+
+ Cmd.AddCommand("screenshot", new xcommand_t() {
+ public void execute() {
+ GL_ScreenShot_f();
+ }
+ });
+ Cmd.AddCommand("modellist", new xcommand_t() {
+ public void execute() {
+ Mod_Modellist_f();
+ }
+ });
+ Cmd.AddCommand("gl_strings", new xcommand_t() {
+ public void execute() {
+ GL_Strings_f();
+ }
+ });
+ }
+
+ /**
+ * R_SetMode
+ */
+ protected boolean R_SetMode() {
+ boolean fullscreen = (vid_fullscreen.value > 0.0f);
+
+ vid_fullscreen.modified = false;
+ gl_mode.modified = false;
+
+ Dimension dim = new Dimension(vid.width, vid.height);
+
+ int err; // enum rserr_t
+ if ((err = GLimp_SetMode(dim, (int) gl_mode.value, fullscreen)) == rserr_ok) {
+ gl_state.prev_mode = (int) gl_mode.value;
+ } else {
+ if (err == rserr_invalid_fullscreen) {
+ Cvar.setValue("vid_fullscreen", 0);
+ vid_fullscreen.modified = false;
+ VideoDriver.Printf(Defines.PRINT_ALL, "ref_gl::R_SetMode() - fullscreen unavailable in this mode\n");
+ if ((err = GLimp_SetMode(dim, (int) gl_mode.value, false)) == rserr_ok)
+ return true;
+ } else if (err == rserr_invalid_mode) {
+ Cvar.setValue("gl_mode", gl_state.prev_mode);
+ gl_mode.modified = false;
+ VideoDriver.Printf(Defines.PRINT_ALL, "ref_gl::R_SetMode() - invalid mode\n");
+ }
+
+ // try setting it back to something safe
+ if ((err = GLimp_SetMode(dim, gl_state.prev_mode, false)) != rserr_ok) {
+ VideoDriver.Printf(Defines.PRINT_ALL, "ref_gl::R_SetMode() - could not revert to safe mode\n");
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * R_Init
+ */
+ protected boolean R_Init(int vid_xpos, int vid_ypos) {
+
+ assert (Warp.SIN.length == 256) : "warpsin table bug";
+
+ // fill r_turbsin
+ for (int j = 0; j < 256; j++) {
+ r_turbsin[j] = Warp.SIN[j] * 0.5f;
+ }
+
+ VideoDriver.Printf(Defines.PRINT_ALL, "ref_gl version: " + REF_VERSION + '\n');
+
+ Draw_GetPalette();
+
+ R_Register();
+
+ // set our "safe" modes
+ gl_state.prev_mode = 3;
+
+ // create the window and set up the context
+ if (!R_SetMode()) {
+ VideoDriver.Printf(Defines.PRINT_ALL, "ref_gl::R_Init() - could not R_SetMode()\n");
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * R_Init2
+ */
+ protected boolean R_Init2() {
+ VideoDriver.MenuInit();
+
+ /*
+ ** get our various GL strings
+ */
+ gl_config.vendor_string = GL11.glGetString(GL11.GL_VENDOR);
+ VideoDriver.Printf(Defines.PRINT_ALL, "GL_VENDOR: " + gl_config.vendor_string + '\n');
+ gl_config.renderer_string = GL11.glGetString(GL11.GL_RENDERER);
+ VideoDriver.Printf(Defines.PRINT_ALL, "GL_RENDERER: " + gl_config.renderer_string + '\n');
+ gl_config.version_string = GL11.glGetString(GL11.GL_VERSION);
+ VideoDriver.Printf(Defines.PRINT_ALL, "GL_VERSION: " + gl_config.version_string + '\n');
+ gl_config.extensions_string = GL11.glGetString(GL11.GL_EXTENSIONS);
+ VideoDriver.Printf(Defines.PRINT_ALL, "GL_EXTENSIONS: " + gl_config.extensions_string + '\n');
+
+ gl_config.parseOpenGLVersion();
+
+ String renderer_buffer = gl_config.renderer_string.toLowerCase();
+ String vendor_buffer = gl_config.vendor_string.toLowerCase();
+
+ if (renderer_buffer.contains("voodoo")) {
+ if (!renderer_buffer.contains("rush"))
+ gl_config.renderer = GL_RENDERER_VOODOO;
+ else
+ gl_config.renderer = GL_RENDERER_VOODOO_RUSH;
+ } else if (vendor_buffer.contains("sgi"))
+ gl_config.renderer = GL_RENDERER_SGI;
+ else if (renderer_buffer.contains("permedia"))
+ gl_config.renderer = GL_RENDERER_PERMEDIA2;
+ else if (renderer_buffer.contains("glint"))
+ gl_config.renderer = GL_RENDERER_GLINT_MX;
+ else if (renderer_buffer.contains("glzicd"))
+ gl_config.renderer = GL_RENDERER_REALIZM;
+ else if (renderer_buffer.contains("gdi"))
+ gl_config.renderer = GL_RENDERER_MCD;
+ else if (renderer_buffer.contains("pcx2"))
+ gl_config.renderer = GL_RENDERER_PCX2;
+ else if (renderer_buffer.contains("verite"))
+ gl_config.renderer = GL_RENDERER_RENDITION;
+ else
+ gl_config.renderer = GL_RENDERER_OTHER;
+
+ String monolightmap = gl_monolightmap.string.toUpperCase();
+ if (monolightmap.length() < 2 || monolightmap.charAt(1) != 'F') {
+ if (gl_config.renderer == GL_RENDERER_PERMEDIA2) {
+ Cvar.set("gl_monolightmap", "A");
+ VideoDriver.Printf(Defines.PRINT_ALL, "...using gl_monolightmap 'a'\n");
+ } else if ((gl_config.renderer & GL_RENDERER_POWERVR) != 0) {
+ Cvar.set("gl_monolightmap", "0");
+ } else {
+ Cvar.set("gl_monolightmap", "0");
+ }
+ }
+
+ // power vr can't have anything stay in the framebuffer, so
+ // the screen needs to redraw the tiled background every frame
+ if ((gl_config.renderer & GL_RENDERER_POWERVR) != 0) {
+ Cvar.set("scr_drawall", "1");
+ } else {
+ Cvar.set("scr_drawall", "0");
+ }
+
+ // MCD has buffering issues
+ if (gl_config.renderer == GL_RENDERER_MCD) {
+ Cvar.setValue("gl_finish", 1);
+ }
+
+ if ((gl_config.renderer & GL_RENDERER_3DLABS) != 0) {
+ gl_config.allow_cds = gl_3dlabs_broken.value == 0.0f;
+ } else {
+ gl_config.allow_cds = true;
+ }
+
+ if (gl_config.allow_cds)
+ VideoDriver.Printf(Defines.PRINT_ALL, "...allowing CDS\n");
+ else
+ VideoDriver.Printf(Defines.PRINT_ALL, "...disabling CDS\n");
+
+ /*
+ ** grab extensions
+ */
+ if (gl_config.extensions_string.contains("GL_EXT_compiled_vertex_array")
+ || gl_config.extensions_string.contains("GL_SGI_compiled_vertex_array")) {
+ VideoDriver.Printf(Defines.PRINT_ALL, "...enabling GL_EXT_compiled_vertex_array\n");
+ // qglLockArraysEXT = ( void * ) qwglGetProcAddress( "glLockArraysEXT" );
+ qglLockArraysEXT = gl_ext_compiled_vertex_array.value != 0.0f;
+ // qglUnlockArraysEXT = ( void * ) qwglGetProcAddress( "glUnlockArraysEXT" );
+ //qglUnlockArraysEXT = true;
+ } else {
+ VideoDriver.Printf(Defines.PRINT_ALL, "...GL_EXT_compiled_vertex_array not found\n");
+ qglLockArraysEXT = false;
+ }
+
+ if (gl_config.extensions_string.contains("WGL_EXT_swap_control")) {
+ qwglSwapIntervalEXT = true;
+ VideoDriver.Printf(Defines.PRINT_ALL, "...enabling WGL_EXT_swap_control\n");
+ } else {
+ qwglSwapIntervalEXT = false;
+ VideoDriver.Printf(Defines.PRINT_ALL, "...WGL_EXT_swap_control not found\n");
+ }
+
+ if (gl_config.extensions_string.contains("GL_EXT_point_parameters")) {
+ if (gl_ext_pointparameters.value != 0.0f) {
+ // qglPointParameterfEXT = ( void (APIENTRY *)( GLenum, GLfloat ) ) qwglGetProcAddress( "glPointParameterfEXT" );
+ qglPointParameterfEXT = true;
+ // qglPointParameterfvEXT = ( void (APIENTRY *)( GLenum, const GLfloat * ) ) qwglGetProcAddress( "glPointParameterfvEXT" );
+ VideoDriver.Printf(Defines.PRINT_ALL, "...using GL_EXT_point_parameters\n");
+ } else {
+ VideoDriver.Printf(Defines.PRINT_ALL, "...ignoring GL_EXT_point_parameters\n");
+ }
+ } else {
+ VideoDriver.Printf(Defines.PRINT_ALL, "...GL_EXT_point_parameters not found\n");
+ }
+
+ // #ifdef __linux__
+ // if ( strstr( gl_config.extensions_string, "3DFX_set_global_palette" ))
+ // {
+ // if ( gl_ext_palettedtexture->value )
+ // {
+ // VideoDriver.Printf( Defines.PRINT_ALL, "...using 3DFX_set_global_palette\n" );
+ // qgl3DfxSetPaletteEXT = ( void ( APIENTRY * ) (GLuint *) )qwglGetProcAddress( "gl3DfxSetPaletteEXT" );
+ //// qglColorTableEXT = Fake_glColorTableEXT;
+ // }
+ // else
+ // {
+ // VideoDriver.Printf( Defines.PRINT_ALL, "...ignoring 3DFX_set_global_palette\n" );
+ // }
+ // }
+ // else
+ // {
+ // VideoDriver.Printf( Defines.PRINT_ALL, "...3DFX_set_global_palette not found\n" );
+ // }
+ // #endif
+
+ if (!qglColorTableEXT
+ && gl_config.extensions_string.contains("GL_EXT_paletted_texture")
+ && gl_config.extensions_string.contains("GL_EXT_shared_texture_palette")) {
+ if (gl_ext_palettedtexture.value != 0.0f) {
+ VideoDriver.Printf(Defines.PRINT_ALL, "...using GL_EXT_shared_texture_palette\n");
+ qglColorTableEXT = false; // true; TODO jogl bug
+ } else {
+ VideoDriver.Printf(Defines.PRINT_ALL, "...ignoring GL_EXT_shared_texture_palette\n");
+ qglColorTableEXT = false;
+ }
+ } else {
+ VideoDriver.Printf(Defines.PRINT_ALL, "...GL_EXT_shared_texture_palette not found\n");
+ }
+
+ if (gl_config.extensions_string.contains("GL_ARB_multitexture")) {
+ VideoDriver.Printf(Defines.PRINT_ALL, "...using GL_ARB_multitexture\n");
+ qglActiveTextureARB = true;
+ GL_TEXTURE0 = ARBMultitexture.GL_TEXTURE0_ARB;
+ GL_TEXTURE1 = ARBMultitexture.GL_TEXTURE1_ARB;
+ } else {
+ VideoDriver.Printf(Defines.PRINT_ALL, "...GL_ARB_multitexture not found\n");
+ }
+
+ if (!(qglActiveTextureARB))
+ return false;
+
+ GL_SetDefaultState();
+
+ GL_InitImages();
+ Mod_Init();
+ R_InitParticleTexture();
+ Draw_InitLocal();
+
+ int err = GL11.glGetError();
+ if (err != GL11.GL_NO_ERROR)
+ VideoDriver.Printf(
+ Defines.PRINT_ALL,
+ "glGetError() = 0x%x\n\t%s\n",
+ new Vargs(2).add(err).add("" + GL11.glGetString(err)));
+
+ return true;
+ }
+
+ /**
+ * R_Shutdown
+ */
+ protected void R_Shutdown() {
+ Cmd.RemoveCommand("modellist");
+ Cmd.RemoveCommand("screenshot");
+ Cmd.RemoveCommand("imagelist");
+ Cmd.RemoveCommand("gl_strings");
+
+ Mod_FreeAll();
+
+ GL_ShutdownImages();
+
+ /*
+ * shut down OS specific OpenGL stuff like contexts, etc.
+ */
+ GLimp_Shutdown();
+ }
+
+ /**
+ * R_BeginFrame
+ */
+ protected void R_BeginFrame(float camera_separation) {
+
+ gl_state.camera_separation = camera_separation;
+
+ /*
+ ** change modes if necessary
+ */
+ if (gl_mode.modified || vid_fullscreen.modified) {
+ // FIXME: only restart if CDS is required
+ CvarT ref;
+
+ ref = Cvar.get("vid_ref", "lwjgl", 0);
+ ref.modified = true;
+ }
+
+ if (gl_log.modified) {
+ GLimp_EnableLogging((gl_log.value != 0.0f));
+ gl_log.modified = false;
+ }
+
+ if (gl_log.value != 0.0f) {
+ GLimp_LogNewFrame();
+ }
+
+ /*
+ ** update 3Dfx gamma -- it is expected that a user will do a vid_restart
+ ** after tweaking this value
+ */
+ if (vid_gamma.modified) {
+ vid_gamma.modified = false;
+
+ if ((gl_config.renderer & GL_RENDERER_VOODOO) != 0) {
+ // wird erstmal nicht gebraucht
+
+ /*
+ char envbuffer[1024];
+ float g;
+
+ g = 2.00 * ( 0.8 - ( vid_gamma->value - 0.5 ) ) + 1.0F;
+ Com_sprintf( envbuffer, sizeof(envbuffer), "SSTV2_GAMMA=%f", g );
+ putenv( envbuffer );
+ Com_sprintf( envbuffer, sizeof(envbuffer), "SST_GAMMA=%f", g );
+ putenv( envbuffer );
+ */
+ VideoDriver.Printf(Defines.PRINT_DEVELOPER, "gamma anpassung fuer VOODOO nicht gesetzt");
+ }
+ }
+
+ GLimp_BeginFrame(camera_separation);
+
+ /*
+ ** go into 2D mode
+ */
+ GL11.glViewport(0, 0, vid.width, vid.height);
+ GL11.glMatrixMode(GL11.GL_PROJECTION);
+ GL11.glLoadIdentity();
+ GL11.glOrtho(0, vid.width, vid.height, 0, -99999, 99999);
+ GL11.glMatrixMode(GL11.GL_MODELVIEW);
+ GL11.glLoadIdentity();
+ GL11.glDisable(GL11.GL_DEPTH_TEST);
+ GL11.glDisable(GL11.GL_CULL_FACE);
+ GL11.glDisable(GL11.GL_BLEND);
+ GL11.glEnable(GL11.GL_ALPHA_TEST);
+ GL11.glColor4f(1, 1, 1, 1);
+
+ /*
+ ** draw buffer stuff
+ */
+ if (gl_drawbuffer.modified) {
+ gl_drawbuffer.modified = false;
+
+ if (gl_state.camera_separation == 0 || !gl_state.stereo_enabled) {
+ if (gl_drawbuffer.string.equalsIgnoreCase("GL_FRONT"))
+ GL11.glDrawBuffer(GL11.GL_FRONT);
+ else
+ GL11.glDrawBuffer(GL11.GL_BACK);
+ }
+ }
+
+ /*
+ ** texturemode stuff
+ */
+ if (gl_texturemode.modified) {
+ GL_TextureMode(gl_texturemode.string);
+ gl_texturemode.modified = false;
+ }
+
+ if (gl_texturealphamode.modified) {
+ GL_TextureAlphaMode(gl_texturealphamode.string);
+ gl_texturealphamode.modified = false;
+ }
+
+ if (gl_texturesolidmode.modified) {
+ GL_TextureSolidMode(gl_texturesolidmode.string);
+ gl_texturesolidmode.modified = false;
+ }
+
+ /*
+ ** swapinterval stuff
+ */
+ GL_UpdateSwapInterval();
+
+ //
+ // clear screen if desired
+ //
+ R_Clear();
+ }
+
+ /**
+ * R_SetPalette
+ */
+ protected void R_SetPalette(byte[] palette) {
+ // 256 RGB values (768 bytes)
+ // or null
+ int i;
+ int color = 0;
+
+ if (palette != null) {
+ int j = 0;
+ for (i = 0; i < 256; i++) {
+ color = (palette[j++] & 0xFF);
+ color |= (palette[j++] & 0xFF) << 8;
+ color |= (palette[j++] & 0xFF) << 16;
+ color |= 0xFF000000;
+ r_rawpalette[i] = color;
+ }
+ } else {
+ for (i = 0; i < 256; i++) {
+ r_rawpalette[i] = d_8to24table[i] | 0xff000000;
+ }
+ }
+ GL_SetTexturePalette(r_rawpalette);
+
+ GL11.glClearColor(0, 0, 0, 0);
+ GL11.glClear(GL11.GL_COLOR_BUFFER_BIT);
+ GL11.glClearColor(1f, 0f, 0.5f, 0.5f);
+ }
+
+ /**
+ * R_DrawBeam
+ */
+ void R_DrawBeam(entity_t e) {
+ oldorigin[0] = e.oldorigin[0];
+ oldorigin[1] = e.oldorigin[1];
+ oldorigin[2] = e.oldorigin[2];
+
+ origin[0] = e.origin[0];
+ origin[1] = e.origin[1];
+ origin[2] = e.origin[2];
+
+ normalized_direction[0] = direction[0] = oldorigin[0] - origin[0];
+ normalized_direction[1] = direction[1] = oldorigin[1] - origin[1];
+ normalized_direction[2] = direction[2] = oldorigin[2] - origin[2];
+
+ if (Math3D.vectorNormalize(normalized_direction) == 0.0f)
+ return;
+
+ Math3D.perpendicularVector(perpvec, normalized_direction);
+ Math3D.vectorScale(perpvec, e.frame / 2, perpvec);
+
+ for (int i = 0; i < 6; i++) {
+ Math3D.rotatePointAroundVector(
+ start_points[i],
+ normalized_direction,
+ perpvec,
+ (360.0f / NUM_BEAM_SEGS) * i);
+
+ Math3D.vectorAdd(start_points[i], origin, start_points[i]);
+ Math3D.vectorAdd(start_points[i], direction, end_points[i]);
+ }
+
+ GL11.glDisable(GL11.GL_TEXTURE_2D);
+ GL11.glEnable(GL11.GL_BLEND);
+ GL11.glDepthMask(false);
+
+ float r = (d_8to24table[e.skinnum & 0xFF]) & 0xFF;
+ float g = (d_8to24table[e.skinnum & 0xFF] >> 8) & 0xFF;
+ float b = (d_8to24table[e.skinnum & 0xFF] >> 16) & 0xFF;
+
+ r *= 1 / 255.0f;
+ g *= 1 / 255.0f;
+ b *= 1 / 255.0f;
+
+ GL11.glColor4f(r, g, b, e.alpha);
+
+ GL11.glBegin(GL11.GL_TRIANGLE_STRIP);
+
+ float[] v;
+
+ for (int i = 0; i < NUM_BEAM_SEGS; i++) {
+ v = start_points[i];
+ GL11.glVertex3f(v[0], v[1], v[2]);
+ v = end_points[i];
+ GL11.glVertex3f(v[0], v[1], v[2]);
+ v = start_points[(i + 1) % NUM_BEAM_SEGS];
+ GL11.glVertex3f(v[0], v[1], v[2]);
+ v = end_points[(i + 1) % NUM_BEAM_SEGS];
+ GL11.glVertex3f(v[0], v[1], v[2]);
+ }
+ GL11.glEnd();
+
+ GL11.glEnable(GL11.GL_TEXTURE_2D);
+ GL11.glDisable(GL11.GL_BLEND);
+ GL11.glDepthMask(true);
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.render.lwjgl;
+
+import lwjake2.Defines;
+import lwjake2.client.VideoDriver;
+import lwjake2.client.entity_t;
+import lwjake2.qcommon.qfiles;
+import lwjake2.render.Image;
+import lwjake2.util.Math3D;
+import org.lwjgl.BufferUtils;
+import org.lwjgl.opengl.ARBMultitexture;
+import org.lwjgl.opengl.GL11;
+
+import java.nio.FloatBuffer;
+import java.nio.IntBuffer;
+
+/**
+ * Mesh
+ *
+ * @author cwei
+ */
+public abstract class Mesh extends Light {
+
+ // g_mesh.c: triangle model functions
+ /*
+ =============================================================
+
+ ALIAS MODELS
+
+ =============================================================
+ */
+
+ static final int NUMVERTEXNORMALS = 162;
+ // precalculated dot products for quantized angles
+ static final int SHADEDOT_QUANT = 16;
+ final float[][] r_avertexnormals = Anorms.VERTEXNORMALS;
+ final float[] shadevector = {0, 0, 0};
+ final float[] shadelight = {0, 0, 0};
+ final float[][] r_avertexnormal_dots = Anorms.VERTEXNORMAL_DOTS;
+ final FloatBuffer colorArrayBuf = BufferUtils.createFloatBuffer(qfiles.MAX_VERTS * 4);
+ final FloatBuffer vertexArrayBuf = BufferUtils.createFloatBuffer(qfiles.MAX_VERTS * 3);
+ final FloatBuffer textureArrayBuf = BufferUtils.createFloatBuffer(qfiles.MAX_VERTS * 2);
+ final float[][] vectors = {
+ {0, 0, 0}, {0, 0, 0}, {0, 0, 0} // 3 mal vec3_t
+ };
+ // bounding box
+ final float[][] bbox = {
+ {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0},
+ {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}
+ };
+ // stack variable
+ private final float[] move = {0, 0, 0}; // vec3_t
+ private final float[] frontv = {0, 0, 0}; // vec3_t
+ private final float[] backv = {0, 0, 0}; // vec3_t
+ private final float[] point = {0, 0, 0};
+ // TODO sync with jogl renderer. hoz
+ // stack variable
+ private final float[] mins = {0, 0, 0};
+ private final float[] maxs = {0, 0, 0};
+ float[] shadedots = r_avertexnormal_dots[0];
+ boolean isFilled = false;
+ float[] tmpVec = {0, 0, 0};
+
+ /**
+ * GL_LerpVerts
+ *
+ * @param nverts
+ * @param ov
+ * @param move
+ * @param frontv
+ * @param backv
+ */
+ void GL_LerpVerts(int nverts, int[] ov, int[] v, float[] move, float[] frontv, float[] backv) {
+ FloatBuffer lerp = vertexArrayBuf;
+ lerp.limit((nverts << 2) - nverts); // nverts * 3
+
+ int ovv, vv;
+ //PMM -- added RF_SHELL_DOUBLE, RF_SHELL_HALF_DAM
+ if ((currententity.flags & (Defines.RF_SHELL_RED | Defines.RF_SHELL_GREEN | Defines.RF_SHELL_BLUE | Defines.RF_SHELL_DOUBLE | Defines.RF_SHELL_HALF_DAM)) != 0) {
+ float[] normal;
+ int j = 0;
+ for (int i = 0; i < nverts; i++/* , v++, ov++, lerp+=4 */) {
+ vv = v[i];
+ normal = r_avertexnormals[(vv >>> 24) & 0xFF];
+ ovv = ov[i];
+ lerp.put(j, move[0] + (ovv & 0xFF) * backv[0] + (vv & 0xFF) * frontv[0] + normal[0] * Defines.POWERSUIT_SCALE);
+ lerp.put(j + 1, move[1] + ((ovv >>> 8) & 0xFF) * backv[1] + ((vv >>> 8) & 0xFF) * frontv[1] + normal[1] * Defines.POWERSUIT_SCALE);
+ lerp.put(j + 2, move[2] + ((ovv >>> 16) & 0xFF) * backv[2] + ((vv >>> 16) & 0xFF) * frontv[2] + normal[2] * Defines.POWERSUIT_SCALE);
+ j += 3;
+ }
+ } else {
+ int j = 0;
+ for (int i = 0; i < nverts; i++ /* , v++, ov++, lerp+=4 */) {
+ ovv = ov[i];
+ vv = v[i];
+
+ lerp.put(j, move[0] + (ovv & 0xFF) * backv[0] + (vv & 0xFF) * frontv[0]);
+ lerp.put(j + 1, move[1] + ((ovv >>> 8) & 0xFF) * backv[1] + ((vv >>> 8) & 0xFF) * frontv[1]);
+ lerp.put(j + 2, move[2] + ((ovv >>> 16) & 0xFF) * backv[2] + ((vv >>> 16) & 0xFF) * frontv[2]);
+ j += 3;
+ }
+ }
+ }
+
+ /**
+ * GL_DrawAliasFrameLerp
+ * <p>
+ * interpolates between two frames and origins
+ * FIXME: batch lerp all vertexes
+ */
+ void GL_DrawAliasFrameLerp(qfiles.dmdl_t paliashdr, float backlerp) {
+ qfiles.daliasframe_t frame = paliashdr.aliasFrames[currententity.frame];
+
+ int[] verts = frame.verts;
+
+ qfiles.daliasframe_t oldframe = paliashdr.aliasFrames[currententity.oldframe];
+
+ int[] ov = oldframe.verts;
+
+ float alpha;
+ if ((currententity.flags & Defines.RF_TRANSLUCENT) != 0)
+ alpha = currententity.alpha;
+ else
+ alpha = 1.0f;
+
+ // PMM - added double shell
+ if ((currententity.flags & (Defines.RF_SHELL_RED | Defines.RF_SHELL_GREEN | Defines.RF_SHELL_BLUE | Defines.RF_SHELL_DOUBLE | Defines.RF_SHELL_HALF_DAM)) != 0)
+ GL11.glDisable(GL11.GL_TEXTURE_2D);
+
+ float frontlerp = 1.0f - backlerp;
+
+ // move should be the delta back to the previous frame * backlerp
+ Math3D.vectorSubtract(currententity.oldorigin, currententity.origin, frontv);
+ Math3D.angleVectors(currententity.angles, vectors[0], vectors[1], vectors[2]);
+
+ move[0] = Math3D.dotProduct(frontv, vectors[0]); // forward
+ move[1] = -Math3D.dotProduct(frontv, vectors[1]); // left
+ move[2] = Math3D.dotProduct(frontv, vectors[2]); // up
+
+ Math3D.vectorAdd(move, oldframe.translate, move);
+
+ for (int i = 0; i < 3; i++) {
+ move[i] = backlerp * move[i] + frontlerp * frame.translate[i];
+ frontv[i] = frontlerp * frame.scale[i];
+ backv[i] = backlerp * oldframe.scale[i];
+ }
+
+ // ab hier wird optimiert
+
+ GL_LerpVerts(paliashdr.num_xyz, ov, verts, move, frontv, backv);
+
+ //GL11.glEnableClientState( GL11.GL_VERTEX_ARRAY );
+ GL11.glVertexPointer(3, 0, vertexArrayBuf);
+
+ // PMM - added double damage shell
+ if ((currententity.flags & (Defines.RF_SHELL_RED | Defines.RF_SHELL_GREEN | Defines.RF_SHELL_BLUE | Defines.RF_SHELL_DOUBLE | Defines.RF_SHELL_HALF_DAM)) != 0) {
+ GL11.glColor4f(shadelight[0], shadelight[1], shadelight[2], alpha);
+ } else {
+ GL11.glEnableClientState(GL11.GL_COLOR_ARRAY);
+ GL11.glColorPointer(4, 0, colorArrayBuf);
+
+ //
+ // pre light everything
+ //
+ FloatBuffer color = colorArrayBuf;
+ float l;
+ int size = paliashdr.num_xyz;
+ int j = 0;
+ for (int i = 0; i < size; i++) {
+ l = shadedots[(verts[i] >>> 24) & 0xFF];
+ color.put(j, l * shadelight[0]);
+ color.put(j + 1, l * shadelight[1]);
+ color.put(j + 2, l * shadelight[2]);
+ color.put(j + 3, alpha);
+ j += 4;
+ }
+ }
+
+ ARBMultitexture.glClientActiveTextureARB(GL_TEXTURE0);
+ GL11.glTexCoordPointer(2, 0, textureArrayBuf);
+ //GL11.glEnableClientState( GL11.GL_TEXTURE_COORD_ARRAY);
+
+ int pos = 0;
+ int[] counts = paliashdr.counts;
+
+ IntBuffer srcIndexBuf = null;
+
+ FloatBuffer dstTextureCoords = textureArrayBuf;
+ FloatBuffer srcTextureCoords = paliashdr.textureCoordBuf;
+
+ int dstIndex = 0;
+ int srcIndex = 0;
+ int count;
+ int mode;
+ int size = counts.length;
+ for (int j = 0; j < size; j++) {
+
+ // get the vertex count and primitive type
+ count = counts[j];
+ if (count == 0)
+ break; // done
+
+ srcIndexBuf = paliashdr.indexElements[j];
+
+ mode = GL11.GL_TRIANGLE_STRIP;
+ if (count < 0) {
+ mode = GL11.GL_TRIANGLE_FAN;
+ count = -count;
+ }
+ srcIndex = pos << 1;
+ srcIndex--;
+ for (int k = 0; k < count; k++) {
+ dstIndex = srcIndexBuf.get(k) << 1;
+ dstTextureCoords.put(dstIndex, srcTextureCoords.get(++srcIndex));
+ dstTextureCoords.put(++dstIndex, srcTextureCoords.get(++srcIndex));
+ }
+
+ GL11.glDrawElements(mode, srcIndexBuf);
+ pos += count;
+ }
+
+ // PMM - added double damage shell
+ if ((currententity.flags & (Defines.RF_SHELL_RED | Defines.RF_SHELL_GREEN | Defines.RF_SHELL_BLUE | Defines.RF_SHELL_DOUBLE | Defines.RF_SHELL_HALF_DAM)) != 0)
+ GL11.glEnable(GL11.GL_TEXTURE_2D);
+
+ GL11.glDisableClientState(GL11.GL_COLOR_ARRAY);
+ }
+
+ /**
+ * GL_DrawAliasShadow
+ */
+ void GL_DrawAliasShadow(qfiles.dmdl_t paliashdr, int posenum) {
+ float lheight = currententity.origin[2] - lightspot[2];
+ // qfiles.daliasframe_t frame = paliashdr.aliasFrames[currententity.frame];
+ int[] order = paliashdr.glCmds;
+ float height = -lheight + 1.0f;
+
+ int orderIndex = 0;
+ int index = 0;
+
+ // TODO shadow drawing with vertex arrays
+
+ int count;
+ while (true) {
+ // get the vertex count and primitive type
+ count = order[orderIndex++];
+ if (count == 0)
+ break; // done
+ if (count < 0) {
+ count = -count;
+ GL11.glBegin(GL11.GL_TRIANGLE_FAN);
+ } else
+ GL11.glBegin(GL11.GL_TRIANGLE_STRIP);
+
+ do {
+ index = order[orderIndex + 2] * 3;
+ point[0] = vertexArrayBuf.get(index);
+ point[1] = vertexArrayBuf.get(index + 1);
+ point[2] = vertexArrayBuf.get(index + 2);
+
+ point[0] -= shadevector[0] * (point[2] + lheight);
+ point[1] -= shadevector[1] * (point[2] + lheight);
+ point[2] = height;
+ GL11.glVertex3f(point[0], point[1], point[2]);
+
+ orderIndex += 3;
+
+ } while (--count != 0);
+
+ GL11.glEnd();
+ }
+ }
+
+ /**
+ * R_CullAliasModel
+ */
+ boolean R_CullAliasModel(entity_t e) {
+ qfiles.dmdl_t paliashdr = (qfiles.dmdl_t) currentmodel.extradata;
+
+ if ((e.frame >= paliashdr.num_frames) || (e.frame < 0)) {
+ VideoDriver.Printf(Defines.PRINT_ALL, "R_CullAliasModel " + currentmodel.name + ": no such frame " + e.frame + '\n');
+ e.frame = 0;
+ }
+ if ((e.oldframe >= paliashdr.num_frames) || (e.oldframe < 0)) {
+ VideoDriver.Printf(Defines.PRINT_ALL, "R_CullAliasModel " + currentmodel.name + ": no such oldframe " + e.oldframe + '\n');
+ e.oldframe = 0;
+ }
+
+ qfiles.daliasframe_t pframe = paliashdr.aliasFrames[e.frame];
+ qfiles.daliasframe_t poldframe = paliashdr.aliasFrames[e.oldframe];
+
+ /*
+ ** compute axially aligned mins and maxs
+ */
+ if (pframe == poldframe) {
+ for (int i = 0; i < 3; i++) {
+ mins[i] = pframe.translate[i];
+ maxs[i] = mins[i] + pframe.scale[i] * 255;
+ }
+ } else {
+ float thismaxs, oldmaxs;
+ for (int i = 0; i < 3; i++) {
+ thismaxs = pframe.translate[i] + pframe.scale[i] * 255;
+
+ oldmaxs = poldframe.translate[i] + poldframe.scale[i] * 255;
+
+ if (pframe.translate[i] < poldframe.translate[i])
+ mins[i] = pframe.translate[i];
+ else
+ mins[i] = poldframe.translate[i];
+
+ if (thismaxs > oldmaxs)
+ maxs[i] = thismaxs;
+ else
+ maxs[i] = oldmaxs;
+ }
+ }
+
+ /*
+ ** compute a full bounding box
+ */
+ float[] tmp;
+ for (int i = 0; i < 8; i++) {
+ tmp = bbox[i];
+ if ((i & 1) != 0)
+ tmp[0] = mins[0];
+ else
+ tmp[0] = maxs[0];
+
+ if ((i & 2) != 0)
+ tmp[1] = mins[1];
+ else
+ tmp[1] = maxs[1];
+
+ if ((i & 4) != 0)
+ tmp[2] = mins[2];
+ else
+ tmp[2] = maxs[2];
+ }
+
+ /*
+ ** rotate the bounding box
+ */
+ tmp = mins;
+ Math3D.vectorCopy(e.angles, tmp);
+ tmp[YAW] = -tmp[YAW];
+ Math3D.angleVectors(tmp, vectors[0], vectors[1], vectors[2]);
+
+ for (int i = 0; i < 8; i++) {
+ Math3D.vectorCopy(bbox[i], tmp);
+
+ bbox[i][0] = Math3D.dotProduct(vectors[0], tmp);
+ bbox[i][1] = -Math3D.dotProduct(vectors[1], tmp);
+ bbox[i][2] = Math3D.dotProduct(vectors[2], tmp);
+
+ Math3D.vectorAdd(e.origin, bbox[i], bbox[i]);
+ }
+
+ int f, mask;
+ int aggregatemask = ~0; // 0xFFFFFFFF
+
+ for (int p = 0; p < 8; p++) {
+ mask = 0;
+
+ for (f = 0; f < 4; f++) {
+ float dp = Math3D.dotProduct(frustum[f].normal, bbox[p]);
+
+ if ((dp - frustum[f].dist) < 0) {
+ mask |= (1 << f);
+ }
+ }
+
+ aggregatemask &= mask;
+ }
+
+ return aggregatemask != 0;
+
+ }
+
+// TODO sync with jogl renderer. hoz
+
+ /**
+ * R_DrawAliasModel
+ */
+ void R_DrawAliasModel(entity_t e) {
+
+ qfiles.dmdl_t paliashdr = (qfiles.dmdl_t) currentmodel.extradata;
+
+ //
+ // get lighting information
+ //
+ // PMM - rewrote, reordered to handle new shells & mixing
+ // PMM - 3.20 code .. replaced with original way of doing it to keep mod authors happy
+ //
+ int i;
+ if ((currententity.flags & (Defines.RF_SHELL_HALF_DAM | Defines.RF_SHELL_GREEN | Defines.RF_SHELL_RED | Defines.RF_SHELL_BLUE | Defines.RF_SHELL_DOUBLE)) != 0) {
+ Math3D.vectorClear(shadelight);
+ if ((currententity.flags & Defines.RF_SHELL_HALF_DAM) != 0) {
+ shadelight[0] = 0.56f;
+ shadelight[1] = 0.59f;
+ shadelight[2] = 0.45f;
+ }
+ if ((currententity.flags & Defines.RF_SHELL_DOUBLE) != 0) {
+ shadelight[0] = 0.9f;
+ shadelight[1] = 0.7f;
+ }
+ if ((currententity.flags & Defines.RF_SHELL_RED) != 0)
+ shadelight[0] = 1.0f;
+ if ((currententity.flags & Defines.RF_SHELL_GREEN) != 0)
+ shadelight[1] = 1.0f;
+ if ((currententity.flags & Defines.RF_SHELL_BLUE) != 0)
+ shadelight[2] = 1.0f;
+ } else if ((currententity.flags & Defines.RF_FULLBRIGHT) != 0) {
+ for (i = 0; i < 3; i++)
+ shadelight[i] = 1.0f;
+ } else {
+ R_LightPoint(currententity.origin, shadelight);
+
+ // player lighting hack for communication back to server
+ // big hack!
+
+ if (gl_monolightmap.string.charAt(0) != '0') {
+ float s = shadelight[0];
+
+ if (s < shadelight[1])
+ s = shadelight[1];
+ if (s < shadelight[2])
+ s = shadelight[2];
+
+ shadelight[0] = s;
+ shadelight[1] = s;
+ shadelight[2] = s;
+ }
+ }
+
+ if ((currententity.flags & Defines.RF_MINLIGHT) != 0) {
+ for (i = 0; i < 3; i++)
+ if (shadelight[i] > 0.1f)
+ break;
+ if (i == 3) {
+ shadelight[0] = 0.1f;
+ shadelight[1] = 0.1f;
+ shadelight[2] = 0.1f;
+ }
+ }
+
+
+ // =================
+ // PGM ir goggles color override
+ if ((r_newrefdef.rdflags & Defines.RDF_IRGOGGLES) != 0 && (currententity.flags & Defines.RF_IR_VISIBLE) != 0) {
+ shadelight[0] = 1.0f;
+ shadelight[1] = 0.0f;
+ shadelight[2] = 0.0f;
+ }
+ // PGM
+ // =================
+
+ shadedots = r_avertexnormal_dots[((int) (currententity.angles[1] * (SHADEDOT_QUANT / 360.0))) & (SHADEDOT_QUANT - 1)];
+
+ float an = (float) (currententity.angles[1] / 180 * Math.PI);
+ shadevector[0] = (float) Math.cos(-an);
+ shadevector[1] = (float) Math.sin(-an);
+ shadevector[2] = 1;
+ Math3D.vectorNormalize(shadevector);
+
+ //
+ // locate the proper data
+ //
+
+ c_alias_polys += paliashdr.num_tris;
+
+
+ GL11.glPushMatrix();
+ e.angles[PITCH] = -e.angles[PITCH]; // sigh.
+ R_RotateForEntity(e);
+ e.angles[PITCH] = -e.angles[PITCH]; // sigh.
+
+
+ Image skin;
+ // select skin
+ if (currententity.skin != null)
+ skin = currententity.skin; // custom player skin
+ else {
+ if (currententity.skinnum >= qfiles.MAX_MD2SKINS)
+ skin = currentmodel.skins[0];
+ else {
+ skin = currentmodel.skins[currententity.skinnum];
+ if (skin == null)
+ skin = currentmodel.skins[0];
+ }
+ }
+ if (skin == null)
+ skin = r_notexture; // fallback...
+ GL_Bind(skin.texnum);
+
+ // draw it
+
+ GL11.glShadeModel(GL11.GL_SMOOTH);
+
+ GL_TexEnv(GL11.GL_MODULATE);
+ if ((currententity.flags & Defines.RF_TRANSLUCENT) != 0) {
+ GL11.glEnable(GL11.GL_BLEND);
+ }
+
+
+ if ((currententity.frame >= paliashdr.num_frames)
+ || (currententity.frame < 0)) {
+ VideoDriver.Printf(Defines.PRINT_ALL, "R_DrawAliasModel " + currentmodel.name + ": no such frame " + currententity.frame + '\n');
+ currententity.frame = 0;
+ currententity.oldframe = 0;
+ }
+
+ if ((currententity.oldframe >= paliashdr.num_frames)
+ || (currententity.oldframe < 0)) {
+ VideoDriver.Printf(Defines.PRINT_ALL, "R_DrawAliasModel " + currentmodel.name + ": no such oldframe " + currententity.oldframe + '\n');
+ currententity.frame = 0;
+ currententity.oldframe = 0;
+ }
+
+ if (r_lerpmodels.value == 0.0f)
+ currententity.backlerp = 0;
+
+ GL_DrawAliasFrameLerp(paliashdr, currententity.backlerp);
+
+ GL_TexEnv(GL11.GL_REPLACE);
+ GL11.glShadeModel(GL11.GL_FLAT);
+
+ GL11.glPopMatrix();
+
+
+ if ((currententity.flags & Defines.RF_TRANSLUCENT) != 0) {
+ GL11.glDisable(GL11.GL_BLEND);
+ }
+
+
+ GL11.glColor4f(1, 1, 1, 1);
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.render.lwjgl;
+
+import lwjake2.Defines;
+import lwjake2.client.VideoDriver;
+import lwjake2.qcommon.FS;
+import org.lwjgl.BufferUtils;
+import org.lwjgl.opengl.*;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.nio.FloatBuffer;
+import java.nio.MappedByteBuffer;
+import java.nio.channels.FileChannel;
+
+/**
+ * Misc
+ *
+ * @author cwei
+ */
+public abstract class Misc extends Mesh {
+
+ private final static int TGA_HEADER_SIZE = 18;
+ /*
+ ==================
+ R_InitParticleTexture
+ ==================
+ */
+ private final byte[][] dottexture =
+ {
+ {0, 0, 0, 0, 0, 0, 0, 0},
+ {0, 0, 1, 1, 0, 0, 0, 0},
+ {0, 1, 1, 1, 1, 0, 0, 0},
+ {0, 1, 1, 1, 1, 0, 0, 0},
+ {0, 0, 1, 1, 0, 0, 0, 0},
+ {0, 0, 0, 0, 0, 0, 0, 0},
+ {0, 0, 0, 0, 0, 0, 0, 0},
+ {0, 0, 0, 0, 0, 0, 0, 0},
+ };
+
+// /*
+// ==============================================================================
+//
+// SCREEN SHOTS
+//
+// ==============================================================================
+// */
+//
+// typedef struct _TargaHeader {
+// unsigned char id_length, colormap_type, image_type;
+// unsigned short colormap_index, colormap_length;
+// unsigned char colormap_size;
+// unsigned short x_origin, y_origin, width, height;
+// unsigned char pixel_size, attributes;
+// } TargaHeader;
+
+ void R_InitParticleTexture() {
+ int x, y;
+ byte[] data = new byte[8 * 8 * 4];
+
+ //
+ // particle texture
+ //
+ for (x = 0; x < 8; x++) {
+ for (y = 0; y < 8; y++) {
+ data[y * 32 + x * 4] = (byte) 255;
+ data[y * 32 + x * 4 + 1] = (byte) 255;
+ data[y * 32 + x * 4 + 2] = (byte) 255;
+ data[y * 32 + x * 4 + 3] = (byte) (dottexture[x][y] * 255);
+
+ }
+ }
+ r_particletexture = GL_LoadPic("***particle***", data, 8, 8, it_sprite, 32);
+
+ //
+ // also use this for bad textures, but without alpha
+ //
+ for (x = 0; x < 8; x++) {
+ for (y = 0; y < 8; y++) {
+ data[y * 32 + x * 4] = (byte) (dottexture[x & 3][y & 3] * 255);
+ data[y * 32 + x * 4 + 1] = 0; // dottexture[x&3][y&3]*255;
+ data[y * 32 + x * 4 + 2] = 0; //dottexture[x&3][y&3]*255;
+ data[y * 32 + x * 4 + 3] = (byte) 255;
+ }
+ }
+ r_notexture = GL_LoadPic("***r_notexture***", data, 8, 8, it_wall, 32);
+ }
+
+ /*
+ ==================
+ GL_ScreenShot_f
+ ==================
+ */
+ void GL_ScreenShot_f() {
+ StringBuilder sb = new StringBuilder(FS.Gamedir() + "/scrshot/jake00.tga");
+ FS.CreatePath(sb.toString());
+ File file = new File(sb.toString());
+ // find a valid file name
+ int i = 0;
+ int offset = sb.length() - 6;
+ while (file.exists() && i++ < 100) {
+ sb.setCharAt(offset, (char) ((i / 10) + '0'));
+ sb.setCharAt(offset + 1, (char) ((i % 10) + '0'));
+ file = new File(sb.toString());
+ }
+ if (i == 100) {
+ VideoDriver.Printf(Defines.PRINT_ALL, "Clean up your screenshots\n");
+ return;
+ }
+
+ try {
+ RandomAccessFile out = new RandomAccessFile(file, "rw");
+ FileChannel ch = out.getChannel();
+ int fileLength = TGA_HEADER_SIZE + vid.width * vid.height * 3;
+ out.setLength(fileLength);
+ MappedByteBuffer image = ch.map(FileChannel.MapMode.READ_WRITE, 0,
+ fileLength);
+
+ // write the TGA header
+ image.put(0, (byte) 0).put(1, (byte) 0);
+ image.put(2, (byte) 2); // uncompressed type
+ image.put(12, (byte) (vid.width & 0xFF)); // vid.width
+ image.put(13, (byte) (vid.width >> 8)); // vid.width
+ image.put(14, (byte) (vid.height & 0xFF)); // vid.height
+ image.put(15, (byte) (vid.height >> 8)); // vid.height
+ image.put(16, (byte) 24); // pixel size
+
+ // go to image data position
+ image.position(TGA_HEADER_SIZE);
+
+
+ // change pixel alignment for reading
+ if (vid.width % 4 != 0) {
+ GL11.glPixelStorei(GL11.GL_PACK_ALIGNMENT, 1);
+ }
+
+ // OpenGL 1.2+ supports the GL_BGR color format
+ // check the GL_VERSION to use the TARGA BGR order if possible
+ // e.g.: 1.5.2 NVIDIA 66.29
+ if (gl_config.getOpenGLVersion() >= 1.2f) {
+ // read the BGR values into the image buffer
+ GL11.glReadPixels(0, 0, vid.width, vid.height, GL12.GL_BGR, GL11.GL_UNSIGNED_BYTE, image);
+ } else {
+ // read the RGB values into the image buffer
+ GL11.glReadPixels(0, 0, vid.width, vid.height, GL11.GL_RGB, GL11.GL_UNSIGNED_BYTE, image);
+ // flip RGB to BGR
+ byte tmp;
+ for (i = TGA_HEADER_SIZE; i < fileLength; i += 3) {
+ tmp = image.get(i);
+ image.put(i, image.get(i + 2));
+ image.put(i + 2, tmp);
+ }
+ }
+ // reset to default alignment
+ GL11.glPixelStorei(GL11.GL_PACK_ALIGNMENT, 4);
+ // close the file channel
+ ch.close();
+ } catch (IOException e) {
+ VideoDriver.Printf(Defines.PRINT_ALL, e.getMessage() + '\n');
+ }
+
+ VideoDriver.Printf(Defines.PRINT_ALL, "Wrote " + file + '\n');
+ }
+
+ /*
+ ** GL_Strings_f
+ */
+ void GL_Strings_f() {
+ VideoDriver.Printf(Defines.PRINT_ALL, "GL_VENDOR: " + gl_config.vendor_string + '\n');
+ VideoDriver.Printf(Defines.PRINT_ALL, "GL_RENDERER: " + gl_config.renderer_string + '\n');
+ VideoDriver.Printf(Defines.PRINT_ALL, "GL_VERSION: " + gl_config.version_string + '\n');
+ VideoDriver.Printf(Defines.PRINT_ALL, "GL_EXTENSIONS: " + gl_config.extensions_string + '\n');
+ }
+
+ /*
+ ** GL_SetDefaultState
+ */
+ void GL_SetDefaultState() {
+ GL11.glClearColor(1f, 0f, 0.5f, 0.5f); // original quake2
+ //GL11.glClearColor(0, 0, 0, 0); // replaced with black
+ GL11.glCullFace(GL11.GL_FRONT);
+ GL11.glEnable(GL11.GL_TEXTURE_2D);
+
+ GL11.glEnable(GL11.GL_ALPHA_TEST);
+ GL11.glAlphaFunc(GL11.GL_GREATER, 0.666f);
+
+ GL11.glDisable(GL11.GL_DEPTH_TEST);
+ GL11.glDisable(GL11.GL_CULL_FACE);
+ GL11.glDisable(GL11.GL_BLEND);
+
+ GL11.glColor4f(1, 1, 1, 1);
+
+ GL11.glPolygonMode(GL11.GL_FRONT_AND_BACK, GL11.GL_FILL);
+ GL11.glShadeModel(GL11.GL_FLAT);
+
+ GL_TextureMode(gl_texturemode.string);
+ GL_TextureAlphaMode(gl_texturealphamode.string);
+ GL_TextureSolidMode(gl_texturesolidmode.string);
+
+ GL11.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, gl_filter_min);
+ GL11.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, gl_filter_max);
+
+ GL11.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL11.GL_REPEAT);
+ GL11.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, GL11.GL_REPEAT);
+
+ GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
+
+ GL_TexEnv(GL11.GL_REPLACE);
+
+ if (qglPointParameterfEXT) {
+ // float[] attenuations = { gl_particle_att_a.value, gl_particle_att_b.value, gl_particle_att_c.value };
+ FloatBuffer att_buffer = BufferUtils.createFloatBuffer(4);
+ att_buffer.put(0, gl_particle_att_a.value);
+ att_buffer.put(1, gl_particle_att_b.value);
+ att_buffer.put(2, gl_particle_att_c.value);
+
+ GL11.glEnable(GL11.GL_POINT_SMOOTH);
+ EXTPointParameters.glPointParameterfEXT(EXTPointParameters.GL_POINT_SIZE_MIN_EXT, gl_particle_min_size.value);
+ EXTPointParameters.glPointParameterfEXT(EXTPointParameters.GL_POINT_SIZE_MAX_EXT, gl_particle_max_size.value);
+ EXTPointParameters.glPointParameterEXT(EXTPointParameters.GL_DISTANCE_ATTENUATION_EXT, att_buffer);
+ }
+
+ if (qglColorTableEXT && gl_ext_palettedtexture.value != 0.0f) {
+ GL11.glEnable(EXTSharedTexturePalette.GL_SHARED_TEXTURE_PALETTE_EXT);
+
+ GL_SetTexturePalette(d_8to24table);
+ }
+
+ GL_UpdateSwapInterval();
+
+ /*
+ * vertex array extension
+ */
+ GL11.glEnableClientState(GL11.GL_VERTEX_ARRAY);
+ ARBMultitexture.glClientActiveTextureARB(GL_TEXTURE0);
+ GL11.glEnableClientState(GL11.GL_TEXTURE_COORD_ARRAY);
+ }
+
+ void GL_UpdateSwapInterval() {
+ if (gl_swapinterval.modified) {
+ gl_swapinterval.modified = false;
+ if (!gl_state.stereo_enabled) {
+ if (qwglSwapIntervalEXT) {
+ // ((WGL)gl).wglSwapIntervalEXT((int)gl_swapinterval.value);
+ }
+ }
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.render.lwjgl;
+
+import lwjake2.Defines;
+import lwjake2.client.VideoDriver;
+import lwjake2.game.CvarT;
+import lwjake2.game.cplane_t;
+import lwjake2.qcommon.*;
+import lwjake2.render.*;
+import lwjake2.util.Math3D;
+import lwjake2.util.Vargs;
+import org.lwjgl.BufferUtils;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.FloatBuffer;
+import java.nio.IntBuffer;
+import java.util.Arrays;
+import java.util.Vector;
+
+/**
+ * Model
+ *
+ * @author cwei
+ */
+public abstract class Model extends Surf {
+
+ // models.c -- model loading and caching
+
+ static final int MAX_MOD_KNOWN = 512;
+ /*
+ * new functions for vertex array handling
+ */
+ static final int MODEL_BUFFER_SIZE = 50000;
+ static final FloatBuffer globalModelTextureCoordBuf = BufferUtils.createFloatBuffer(MODEL_BUFFER_SIZE * 2);
+ static final IntBuffer globalModelVertexIndexBuf = BufferUtils.createIntBuffer(MODEL_BUFFER_SIZE);
+ final byte[] mod_novis = new byte[Defines.MAX_MAP_LEAFS / 8];
+ final lwjake2.render.Model[] mod_known = new lwjake2.render.Model[MAX_MOD_KNOWN];
+ // the inline * models from the current map are kept seperate
+ final lwjake2.render.Model[] mod_inline = new lwjake2.render.Model[MAX_MOD_KNOWN];
+ final byte[] decompressed = new byte[Defines.MAX_MAP_LEAFS / 8];
+ final byte[] model_visibility = new byte[Defines.MAX_MAP_VISIBILITY];
+ lwjake2.render.Model loadmodel;
+ int modfilelen;
+ int mod_numknown;
+ byte[] fileBuffer;
+
+
+ // ===============================================================================
+ byte[] mod_base;
+
+ static void resetModelArrays() {
+ globalModelTextureCoordBuf.rewind();
+ globalModelVertexIndexBuf.rewind();
+ }
+
+ static void modelMemoryUsage() {
+ System.out.println("AliasModels: globalVertexBuffer size " + globalModelVertexIndexBuf.position());
+ }
+
+ abstract void GL_SubdivideSurface(msurface_t surface); // Warp.java
+
+ /*
+ ===============================================================================
+
+ BRUSHMODEL LOADING
+
+ ===============================================================================
+ */
+
+ /*
+ ===============
+ Mod_PointInLeaf
+ ===============
+ */
+ mleaf_t Mod_PointInLeaf(float[] p, lwjake2.render.Model model) {
+ mnode_t node;
+ float d;
+ cplane_t plane;
+
+ if (model == null || model.nodes == null)
+ Com.Error(Defines.ERR_DROP, "Mod_PointInLeaf: bad model");
+
+ node = model.nodes[0]; // root node
+ while (true) {
+ if (node.contents != -1)
+ return (mleaf_t) node;
+
+ plane = node.plane;
+ d = Math3D.dotProduct(p, plane.normal) - plane.dist;
+ if (d > 0)
+ node = node.children[0];
+ else
+ node = node.children[1];
+ }
+ // never reached
+ }
+
+ /*
+ ===================
+ Mod_DecompressVis
+ ===================
+ */
+ byte[] Mod_DecompressVis(byte[] in, int offset, lwjake2.render.Model model) {
+ int c;
+ byte[] out;
+ int outp, inp;
+ int row;
+
+ row = (model.vis.numclusters + 7) >> 3;
+ out = decompressed;
+ outp = 0;
+ inp = offset;
+
+ if (in == null) { // no vis info, so make all visible
+ while (row != 0) {
+ out[outp++] = (byte) 0xFF;
+ row--;
+ }
+ return decompressed;
+ }
+
+ do {
+ if (in[inp] != 0) {
+ out[outp++] = in[inp++];
+ continue;
+ }
+
+ c = in[inp + 1] & 0xFF;
+ inp += 2;
+ while (c != 0) {
+ out[outp++] = 0;
+ c--;
+ }
+ } while (outp < row);
+
+ return decompressed;
+ }
+
+ /*
+ ==============
+ Mod_ClusterPVS
+ ==============
+ */
+ byte[] Mod_ClusterPVS(int cluster, lwjake2.render.Model model) {
+ if (cluster == -1 || model.vis == null)
+ return mod_novis;
+ //return Mod_DecompressVis( (byte *)model.vis + model.vis.bitofs[cluster][Defines.DVIS_PVS], model);
+ return Mod_DecompressVis(model_visibility, model.vis.bitofs[cluster][Defines.DVIS_PVS], model);
+ }
+
+ /*
+ ================
+ Mod_Modellist_f
+ ================
+ */
+ void Mod_Modellist_f() {
+ int i;
+ lwjake2.render.Model mod;
+ int total;
+
+ total = 0;
+ VideoDriver.Printf(Defines.PRINT_ALL, "Loaded models:\n");
+ for (i = 0; i < mod_numknown; i++) {
+ mod = mod_known[i];
+ if (mod.name.length() == 0)
+ continue;
+
+ VideoDriver.Printf(Defines.PRINT_ALL, "%8i : %s\n", new Vargs(2).add(mod.extradatasize).add(mod.name));
+ total += mod.extradatasize;
+ }
+ VideoDriver.Printf(Defines.PRINT_ALL, "Total resident: " + total + '\n');
+ }
+
+ /*
+ ===============
+ Mod_Init
+ ===============
+ */
+ void Mod_Init() {
+ // init mod_known
+ for (int i = 0; i < MAX_MOD_KNOWN; i++) {
+ mod_known[i] = new lwjake2.render.Model();
+ }
+ Arrays.fill(mod_novis, (byte) 0xff);
+ }
+
+ /*
+ ==================
+ Mod_ForName
+
+ Loads in a model for the given name
+ ==================
+ */
+ lwjake2.render.Model Mod_ForName(String name, boolean crash) {
+ lwjake2.render.Model mod = null;
+ int i;
+
+ if (name == null || name.length() == 0)
+ Com.Error(Defines.ERR_DROP, "Mod_ForName: NULL name");
+
+ //
+ // inline models are grabbed only from worldmodel
+ //
+ if (name.charAt(0) == '*') {
+ i = Integer.parseInt(name.substring(1));
+ if (i < 1 || r_worldmodel == null || i >= r_worldmodel.numsubmodels)
+ Com.Error(Defines.ERR_DROP, "bad inline model number");
+ return mod_inline[i];
+ }
+
+ //
+ // search the currently loaded models
+ //
+ for (i = 0; i < mod_numknown; i++) {
+ mod = mod_known[i];
+
+ if (mod.name.length() == 0)
+ continue;
+ if (mod.name.equals(name))
+ return mod;
+ }
+
+ //
+ // find a free model slot spot
+ //
+ for (i = 0; i < mod_numknown; i++) {
+ mod = mod_known[i];
+
+ if (mod.name.length() == 0)
+ break; // free spot
+ }
+ if (i == mod_numknown) {
+ if (mod_numknown == MAX_MOD_KNOWN)
+ Com.Error(Defines.ERR_DROP, "mod_numknown == MAX_MOD_KNOWN");
+ mod_numknown++;
+ mod = mod_known[i];
+ }
+
+ mod.name = name;
+
+ //
+ // load the file
+ //
+ fileBuffer = FS.LoadFile(name);
+
+ if (fileBuffer == null) {
+ if (crash)
+ Com.Error(Defines.ERR_DROP, "Mod_NumForName: " + mod.name + " not found");
+
+ mod.name = "";
+ return null;
+ }
+
+ modfilelen = fileBuffer.length;
+
+ loadmodel = mod;
+
+ //
+ // fill it in
+ //
+ ByteBuffer bb = ByteBuffer.wrap(fileBuffer);
+ bb.order(ByteOrder.LITTLE_ENDIAN);
+
+ // call the apropriate loader
+
+ bb.mark();
+ int ident = bb.getInt();
+
+ bb.reset();
+
+ switch (ident) {
+ case qfiles.IDALIASHEADER:
+ Mod_LoadAliasModel(mod, bb);
+ break;
+ case qfiles.IDSPRITEHEADER:
+ Mod_LoadSpriteModel(mod, bb);
+ break;
+ case qfiles.IDBSPHEADER:
+ Mod_LoadBrushModel(mod, bb);
+ break;
+ default:
+ Com.Error(Defines.ERR_DROP, "Mod_NumForName: unknown fileid for " + mod.name);
+ break;
+ }
+
+ this.fileBuffer = null; // free it for garbage collection
+ return mod;
+ }
+
+ /*
+ =================
+ Mod_LoadLighting
+ =================
+ */
+ void Mod_LoadLighting(lump_t l) {
+ if (l.filelen == 0) {
+ loadmodel.lightdata = null;
+ return;
+ }
+ // memcpy (loadmodel.lightdata, mod_base + l.fileofs, l.filelen);
+ loadmodel.lightdata = new byte[l.filelen];
+ System.arraycopy(mod_base, l.fileofs, loadmodel.lightdata, 0, l.filelen);
+ }
+
+ /*
+ =================
+ Mod_LoadVisibility
+ =================
+ */
+ void Mod_LoadVisibility(lump_t l) {
+
+ if (l.filelen == 0) {
+ loadmodel.vis = null;
+ return;
+ }
+
+ System.arraycopy(mod_base, l.fileofs, model_visibility, 0, l.filelen);
+
+ ByteBuffer bb = ByteBuffer.wrap(model_visibility, 0, l.filelen);
+
+ loadmodel.vis = new qfiles.dvis_t(bb.order(ByteOrder.LITTLE_ENDIAN));
+
+ /* done:
+ memcpy (loadmodel.vis, mod_base + l.fileofs, l.filelen);
+
+ loadmodel.vis.numclusters = LittleLong (loadmodel.vis.numclusters);
+ for (i=0 ; i<loadmodel.vis.numclusters ; i++)
+ {
+ loadmodel.vis.bitofs[i][0] = LittleLong (loadmodel.vis.bitofs[i][0]);
+ loadmodel.vis.bitofs[i][1] = LittleLong (loadmodel.vis.bitofs[i][1]);
+ }
+ */
+ }
+
+ /*
+ =================
+ Mod_LoadVertexes
+ =================
+ */
+ void Mod_LoadVertexes(lump_t l) {
+ mvertex_t[] vertexes;
+ int i, count;
+
+ if ((l.filelen % mvertex_t.DISK_SIZE) != 0)
+ Com.Error(Defines.ERR_DROP, "MOD_LoadBmodel: funny lump size in " + loadmodel.name);
+
+ count = l.filelen / mvertex_t.DISK_SIZE;
+
+ vertexes = new mvertex_t[count];
+
+ loadmodel.vertexes = vertexes;
+ loadmodel.numvertexes = count;
+
+ ByteBuffer bb = ByteBuffer.wrap(mod_base, l.fileofs, l.filelen);
+ bb.order(ByteOrder.LITTLE_ENDIAN);
+
+ for (i = 0; i < count; i++) {
+ vertexes[i] = new mvertex_t(bb);
+ }
+ }
+
+ /*
+ =================
+ RadiusFromBounds
+ =================
+ */
+ float RadiusFromBounds(float[] mins, float[] maxs) {
+ float[] corner = {0, 0, 0};
+
+ for (int i = 0; i < 3; i++) {
+ corner[i] = Math.abs(mins[i]) > Math.abs(maxs[i]) ? Math.abs(mins[i]) : Math.abs(maxs[i]);
+ }
+ return Math3D.vectorLength(corner);
+ }
+
+ /*
+ =================
+ Mod_LoadSubmodels
+ =================
+ */
+ void Mod_LoadSubmodels(lump_t l) {
+
+ if ((l.filelen % qfiles.dmodel_t.SIZE) != 0)
+ Com.Error(Defines.ERR_DROP, "MOD_LoadBmodel: funny lump size in "
+ + loadmodel.name);
+
+ int i, j;
+
+ int count = l.filelen / qfiles.dmodel_t.SIZE;
+ // out = Hunk_Alloc ( count*sizeof(*out));
+ mmodel_t out;
+ mmodel_t[] outs = new mmodel_t[count];
+ for (i = 0; i < count; i++) {
+ outs[i] = new mmodel_t();
+ }
+
+ loadmodel.submodels = outs;
+ loadmodel.numsubmodels = count;
+
+ ByteBuffer bb = ByteBuffer.wrap(mod_base, l.fileofs, l.filelen);
+ bb.order(ByteOrder.LITTLE_ENDIAN);
+
+ qfiles.dmodel_t in;
+
+ for (i = 0; i < count; i++) {
+ in = new qfiles.dmodel_t(bb);
+ out = outs[i];
+ for (j = 0; j < 3; j++) { // spread the mins / maxs by a
+ // pixel
+ out.mins[j] = in.mins[j] - 1;
+ out.maxs[j] = in.maxs[j] + 1;
+ out.origin[j] = in.origin[j];
+ }
+ out.radius = RadiusFromBounds(out.mins, out.maxs);
+ out.headnode = in.headnode;
+ out.firstface = in.firstface;
+ out.numfaces = in.numfaces;
+ }
+ }
+
+ /*
+ =================
+ Mod_LoadEdges
+ =================
+ */
+ void Mod_LoadEdges(lump_t l) {
+ medge_t[] edges;
+ int i, count;
+
+ if ((l.filelen % medge_t.DISK_SIZE) != 0)
+ Com.Error(Defines.ERR_DROP, "MOD_LoadBmodel: funny lump size in " + loadmodel.name);
+
+ count = l.filelen / medge_t.DISK_SIZE;
+ // out = Hunk_Alloc ( (count + 1) * sizeof(*out));
+ edges = new medge_t[count + 1];
+
+ loadmodel.edges = edges;
+ loadmodel.numedges = count;
+
+ ByteBuffer bb = ByteBuffer.wrap(mod_base, l.fileofs, l.filelen);
+ bb.order(ByteOrder.LITTLE_ENDIAN);
+
+ for (i = 0; i < count; i++) {
+ edges[i] = new medge_t(bb);
+ }
+ }
+
+ /*
+ =================
+ Mod_LoadTexinfo
+ =================
+ */
+ void Mod_LoadTexinfo(lump_t l) {
+ texinfo_t in;
+ mtexinfo_t[] out;
+ mtexinfo_t step;
+ int i, count;
+ int next;
+ String name;
+
+ if ((l.filelen % texinfo_t.SIZE) != 0)
+ Com.Error(Defines.ERR_DROP, "MOD_LoadBmodel: funny lump size in " + loadmodel.name);
+
+ count = l.filelen / texinfo_t.SIZE;
+ // out = Hunk_Alloc ( count*sizeof(*out));
+ out = new mtexinfo_t[count];
+ for (i = 0; i < count; i++) {
+ out[i] = new mtexinfo_t();
+ }
+
+ loadmodel.texinfo = out;
+ loadmodel.numtexinfo = count;
+
+ ByteBuffer bb = ByteBuffer.wrap(mod_base, l.fileofs, l.filelen);
+ bb.order(ByteOrder.LITTLE_ENDIAN);
+
+ for (i = 0; i < count; i++) {
+
+ in = new texinfo_t(bb);
+ out[i].vecs = in.vecs;
+ out[i].flags = in.flags;
+ next = in.nexttexinfo;
+ if (next > 0)
+ out[i].next = loadmodel.texinfo[next];
+ else
+ out[i].next = null;
+
+ name = "textures/" + in.texture + ".wal";
+
+ out[i].image = GL_FindImage(name, it_wall);
+ if (out[i].image == null) {
+ VideoDriver.Printf(Defines.PRINT_ALL, "Couldn't load " + name + '\n');
+ out[i].image = r_notexture;
+ }
+ }
+
+ // count animation frames
+ for (i = 0; i < count; i++) {
+ out[i].numframes = 1;
+ for (step = out[i].next; (step != null) && (step != out[i]); step = step.next)
+ out[i].numframes++;
+ }
+ }
+
+ /*
+ ================
+ CalcSurfaceExtents
+
+ Fills in s.texturemins[] and s.extents[]
+ ================
+ */
+ void CalcSurfaceExtents(msurface_t s) {
+ float[] mins = {0, 0};
+ float[] maxs = {0, 0};
+ float val;
+
+ int j, e;
+ mvertex_t v;
+ int[] bmins = {0, 0};
+ int[] bmaxs = {0, 0};
+
+ mins[0] = mins[1] = 999999;
+ maxs[0] = maxs[1] = -99999;
+
+ mtexinfo_t tex = s.texinfo;
+
+ for (int i = 0; i < s.numedges; i++) {
+ e = loadmodel.surfedges[s.firstedge + i];
+ if (e >= 0)
+ v = loadmodel.vertexes[loadmodel.edges[e].v[0]];
+ else
+ v = loadmodel.vertexes[loadmodel.edges[-e].v[1]];
+
+ for (j = 0; j < 2; j++) {
+ val = v.position[0] * tex.vecs[j][0] +
+ v.position[1] * tex.vecs[j][1] +
+ v.position[2] * tex.vecs[j][2] +
+ tex.vecs[j][3];
+ if (val < mins[j])
+ mins[j] = val;
+ if (val > maxs[j])
+ maxs[j] = val;
+ }
+ }
+
+ for (int i = 0; i < 2; i++) {
+ bmins[i] = (int) Math.floor(mins[i] / 16);
+ bmaxs[i] = (int) Math.ceil(maxs[i] / 16);
+
+ s.texturemins[i] = (short) (bmins[i] * 16);
+ s.extents[i] = (short) ((bmaxs[i] - bmins[i]) * 16);
+
+ }
+ }
+
+ /*
+ =================
+ Mod_LoadFaces
+ =================
+ */
+ void Mod_LoadFaces(lump_t l) {
+
+ int i, surfnum;
+ int planenum, side;
+ int ti;
+
+ if ((l.filelen % qfiles.dface_t.SIZE) != 0)
+ Com.Error(Defines.ERR_DROP, "MOD_LoadBmodel: funny lump size in "
+ + loadmodel.name);
+
+ int count = l.filelen / qfiles.dface_t.SIZE;
+ // out = Hunk_Alloc ( count*sizeof(*out));
+ msurface_t[] outs = new msurface_t[count];
+ for (i = 0; i < count; i++) {
+ outs[i] = new msurface_t();
+ }
+
+ loadmodel.surfaces = outs;
+ loadmodel.numsurfaces = count;
+
+ ByteBuffer bb = ByteBuffer.wrap(mod_base, l.fileofs, l.filelen);
+ bb.order(ByteOrder.LITTLE_ENDIAN);
+
+ currentmodel = loadmodel;
+
+ GL_BeginBuildingLightmaps(loadmodel);
+
+ qfiles.dface_t in;
+ msurface_t out;
+
+ for (surfnum = 0; surfnum < count; surfnum++) {
+ in = new qfiles.dface_t(bb);
+ out = outs[surfnum];
+ out.firstedge = in.firstedge;
+ out.numedges = in.numedges;
+ out.flags = 0;
+ out.polys = null;
+
+ planenum = in.planenum;
+ side = in.side;
+ if (side != 0)
+ out.flags |= Defines.SURF_PLANEBACK;
+
+ out.plane = loadmodel.planes[planenum];
+
+ ti = in.texinfo;
+ if (ti < 0 || ti >= loadmodel.numtexinfo)
+ Com.Error(Defines.ERR_DROP,
+ "MOD_LoadBmodel: bad texinfo number");
+
+ out.texinfo = loadmodel.texinfo[ti];
+
+ CalcSurfaceExtents(out);
+
+ // lighting info
+
+ for (i = 0; i < Defines.MAXLIGHTMAPS; i++)
+ out.styles[i] = in.styles[i];
+
+ i = in.lightofs;
+ if (i == -1)
+ out.samples = null;
+ else {
+ ByteBuffer pointer = ByteBuffer.wrap(loadmodel.lightdata);
+ pointer.position(i);
+ pointer = pointer.slice();
+ pointer.mark();
+ out.samples = pointer; // subarray
+ }
+
+ // set the drawing flags
+
+ if ((out.texinfo.flags & Defines.SURF_WARP) != 0) {
+ out.flags |= Defines.SURF_DRAWTURB;
+ for (i = 0; i < 2; i++) {
+ out.extents[i] = 16384;
+ out.texturemins[i] = -8192;
+ }
+ GL_SubdivideSurface(out); // cut up polygon for warps
+ }
+
+ // create lightmaps and polygons
+ if ((out.texinfo.flags & (Defines.SURF_SKY | Defines.SURF_TRANS33
+ | Defines.SURF_TRANS66 | Defines.SURF_WARP)) == 0)
+ GL_CreateSurfaceLightmap(out);
+
+ if ((out.texinfo.flags & Defines.SURF_WARP) == 0)
+ GL_BuildPolygonFromSurface(out);
+
+ }
+ GL_EndBuildingLightmaps();
+ }
+
+ /*
+ =================
+ Mod_SetParent
+ =================
+ */
+ void Mod_SetParent(mnode_t node, mnode_t parent) {
+ node.parent = parent;
+ if (node.contents != -1) return;
+ Mod_SetParent(node.children[0], node);
+ Mod_SetParent(node.children[1], node);
+ }
+
+ /*
+ =================
+ Mod_LoadNodes
+ =================
+ */
+ void Mod_LoadNodes(lump_t l) {
+ int i, j, count, p;
+ qfiles.dnode_t in;
+ mnode_t[] out;
+
+ if ((l.filelen % qfiles.dnode_t.SIZE) != 0)
+ Com.Error(Defines.ERR_DROP, "MOD_LoadBmodel: funny lump size in " + loadmodel.name);
+
+ count = l.filelen / qfiles.dnode_t.SIZE;
+ // out = Hunk_Alloc ( count*sizeof(*out));
+ out = new mnode_t[count];
+
+ loadmodel.nodes = out;
+ loadmodel.numnodes = count;
+
+ ByteBuffer bb = ByteBuffer.wrap(mod_base, l.fileofs, l.filelen);
+ bb.order(ByteOrder.LITTLE_ENDIAN);
+
+ // initialize the tree array
+ for (i = 0; i < count; i++) out[i] = new mnode_t(); // do first before linking
+
+ // fill and link the nodes
+ for (i = 0; i < count; i++) {
+ in = new qfiles.dnode_t(bb);
+ for (j = 0; j < 3; j++) {
+ out[i].mins[j] = in.mins[j];
+ out[i].maxs[j] = in.maxs[j];
+ }
+
+ p = in.planenum;
+ out[i].plane = loadmodel.planes[p];
+
+ out[i].firstsurface = in.firstface;
+ out[i].numsurfaces = in.numfaces;
+ out[i].contents = -1; // differentiate from leafs
+
+ for (j = 0; j < 2; j++) {
+ p = in.children[j];
+ if (p >= 0)
+ out[i].children[j] = loadmodel.nodes[p];
+ else
+ out[i].children[j] = loadmodel.leafs[-1 - p]; // mleaf_t extends mnode_t
+ }
+ }
+
+ Mod_SetParent(loadmodel.nodes[0], null); // sets nodes and leafs
+ }
+
+ /*
+ ==============================================================================
+
+ ALIAS MODELS
+
+ ==============================================================================
+ */
+
+ /*
+ =================
+ Mod_LoadLeafs
+ =================
+ */
+ void Mod_LoadLeafs(lump_t l) {
+ qfiles.dleaf_t in;
+ mleaf_t[] out;
+ int i, j, count;
+
+ if ((l.filelen % qfiles.dleaf_t.SIZE) != 0)
+ Com.Error(Defines.ERR_DROP, "MOD_LoadBmodel: funny lump size in " + loadmodel.name);
+
+ count = l.filelen / qfiles.dleaf_t.SIZE;
+ // out = Hunk_Alloc ( count*sizeof(*out));
+ out = new mleaf_t[count];
+
+ loadmodel.leafs = out;
+ loadmodel.numleafs = count;
+
+ ByteBuffer bb = ByteBuffer.wrap(mod_base, l.fileofs, l.filelen);
+ bb.order(ByteOrder.LITTLE_ENDIAN);
+
+ for (i = 0; i < count; i++) {
+ in = new qfiles.dleaf_t(bb);
+ out[i] = new mleaf_t();
+ for (j = 0; j < 3; j++) {
+ out[i].mins[j] = in.mins[j];
+ out[i].maxs[j] = in.maxs[j];
+
+ }
+
+ out[i].contents = in.contents;
+ out[i].cluster = in.cluster;
+ out[i].area = in.area;
+
+ out[i].setMarkSurface(in.firstleafface, loadmodel.marksurfaces);
+ out[i].nummarksurfaces = in.numleaffaces;
+ }
+ }
+
+ /*
+ ==============================================================================
+
+ SPRITE MODELS
+
+ ==============================================================================
+ */
+
+ /*
+ =================
+ Mod_LoadMarksurfaces
+ =================
+ */
+ void Mod_LoadMarksurfaces(lump_t l) {
+ int i, j, count;
+
+ msurface_t[] out;
+
+ if ((l.filelen % Defines.SIZE_OF_SHORT) != 0)
+ Com.Error(Defines.ERR_DROP, "MOD_LoadBmodel: funny lump size in " + loadmodel.name);
+ count = l.filelen / Defines.SIZE_OF_SHORT;
+ // out = Hunk_Alloc ( count*sizeof(*out));
+ out = new msurface_t[count];
+
+ loadmodel.marksurfaces = out;
+ loadmodel.nummarksurfaces = count;
+
+ ByteBuffer bb = ByteBuffer.wrap(mod_base, l.fileofs, l.filelen);
+ bb.order(ByteOrder.LITTLE_ENDIAN);
+
+ for (i = 0; i < count; i++) {
+ j = bb.getShort();
+ if (j < 0 || j >= loadmodel.numsurfaces)
+ Com.Error(Defines.ERR_DROP, "Mod_ParseMarksurfaces: bad surface number");
+
+ out[i] = loadmodel.surfaces[j];
+ }
+ }
+
+// =============================================================================
+
+ /*
+ =================
+ Mod_LoadSurfedges
+ =================
+ */
+ void Mod_LoadSurfedges(lump_t l) {
+ int i, count;
+ int[] offsets;
+
+ if ((l.filelen % Defines.SIZE_OF_INT) != 0)
+ Com.Error(Defines.ERR_DROP, "MOD_LoadBmodel: funny lump size in " + loadmodel.name);
+
+ count = l.filelen / Defines.SIZE_OF_INT;
+ if (count < 1 || count >= Defines.MAX_MAP_SURFEDGES)
+ Com.Error(Defines.ERR_DROP, "MOD_LoadBmodel: bad surfedges count in " + loadmodel.name + ": " + count);
+
+ offsets = new int[count];
+
+ loadmodel.surfedges = offsets;
+ loadmodel.numsurfedges = count;
+
+ ByteBuffer bb = ByteBuffer.wrap(mod_base, l.fileofs, l.filelen);
+ bb.order(ByteOrder.LITTLE_ENDIAN);
+
+ for (i = 0; i < count; i++) offsets[i] = bb.getInt();
+ }
+
+ /*
+ =================
+ Mod_LoadPlanes
+ =================
+ */
+ void Mod_LoadPlanes(lump_t l) {
+ int i, j;
+ cplane_t[] out;
+ qfiles.dplane_t in;
+ int count;
+ int bits;
+
+ if ((l.filelen % qfiles.dplane_t.SIZE) != 0)
+ Com.Error(Defines.ERR_DROP, "MOD_LoadBmodel: funny lump size in " + loadmodel.name);
+
+ count = l.filelen / qfiles.dplane_t.SIZE;
+ // out = Hunk_Alloc ( count*2*sizeof(*out));
+ out = new cplane_t[count * 2];
+ for (i = 0; i < count; i++) {
+ out[i] = new cplane_t();
+ }
+
+ loadmodel.planes = out;
+ loadmodel.numplanes = count;
+
+ ByteBuffer bb = ByteBuffer.wrap(mod_base, l.fileofs, l.filelen);
+ bb.order(ByteOrder.LITTLE_ENDIAN);
+
+ for (i = 0; i < count; i++) {
+ bits = 0;
+ in = new qfiles.dplane_t(bb);
+ for (j = 0; j < 3; j++) {
+ out[i].normal[j] = in.normal[j];
+ if (out[i].normal[j] < 0)
+ bits |= (1 << j);
+ }
+
+ out[i].dist = in.dist;
+ out[i].type = (byte) in.type;
+ out[i].signbits = (byte) bits;
+ }
+ }
+
+ /*
+ =================
+ Mod_LoadBrushModel
+ =================
+ */
+ void Mod_LoadBrushModel(lwjake2.render.Model mod, ByteBuffer buffer) {
+ int i;
+ qfiles.dheader_t header;
+ mmodel_t bm;
+
+ loadmodel.type = mod_brush;
+ if (loadmodel != mod_known[0])
+ Com.Error(Defines.ERR_DROP, "Loaded a brush model after the world");
+
+ header = new qfiles.dheader_t(buffer);
+
+ i = header.version;
+ if (i != Defines.BSPVERSION)
+ Com.Error(Defines.ERR_DROP, "Mod_LoadBrushModel: " + mod.name + " has wrong version number (" + i + " should be " + Defines.BSPVERSION + ")");
+
+ mod_base = fileBuffer; //(byte *)header;
+
+ // load into heap
+ Mod_LoadVertexes(header.lumps[Defines.LUMP_VERTEXES]); // ok
+ Mod_LoadEdges(header.lumps[Defines.LUMP_EDGES]); // ok
+ Mod_LoadSurfedges(header.lumps[Defines.LUMP_SURFEDGES]); // ok
+ Mod_LoadLighting(header.lumps[Defines.LUMP_LIGHTING]); // ok
+ Mod_LoadPlanes(header.lumps[Defines.LUMP_PLANES]); // ok
+ Mod_LoadTexinfo(header.lumps[Defines.LUMP_TEXINFO]); // ok
+ Mod_LoadFaces(header.lumps[Defines.LUMP_FACES]); // ok
+ Mod_LoadMarksurfaces(header.lumps[Defines.LUMP_LEAFFACES]);
+ Mod_LoadVisibility(header.lumps[Defines.LUMP_VISIBILITY]); // ok
+ Mod_LoadLeafs(header.lumps[Defines.LUMP_LEAFS]); // ok
+ Mod_LoadNodes(header.lumps[Defines.LUMP_NODES]); // ok
+ Mod_LoadSubmodels(header.lumps[Defines.LUMP_MODELS]);
+ mod.numframes = 2; // regular and alternate animation
+
+ //
+ // set up the submodels
+ //
+ lwjake2.render.Model starmod;
+
+ for (i = 0; i < mod.numsubmodels; i++) {
+
+ bm = mod.submodels[i];
+ starmod = mod_inline[i] = loadmodel.copy();
+
+ starmod.firstmodelsurface = bm.firstface;
+ starmod.nummodelsurfaces = bm.numfaces;
+ starmod.firstnode = bm.headnode;
+ if (starmod.firstnode >= loadmodel.numnodes)
+ Com.Error(Defines.ERR_DROP, "Inline model " + i + " has bad firstnode");
+
+ Math3D.vectorCopy(bm.maxs, starmod.maxs);
+ Math3D.vectorCopy(bm.mins, starmod.mins);
+ starmod.radius = bm.radius;
+
+ if (i == 0)
+ loadmodel = starmod.copy();
+
+ starmod.numleafs = bm.visleafs;
+ }
+ }
+
+
+// =============================================================================
+
+ /*
+ =================
+ Mod_LoadAliasModel
+ =================
+ */
+ void Mod_LoadAliasModel(lwjake2.render.Model mod, ByteBuffer buffer) {
+ int i;
+ qfiles.dmdl_t pheader;
+ qfiles.dstvert_t[] poutst;
+ qfiles.dtriangle_t[] pouttri;
+ qfiles.daliasframe_t[] poutframe;
+ int[] poutcmd;
+
+ pheader = new qfiles.dmdl_t(buffer);
+
+ if (pheader.version != qfiles.ALIAS_VERSION)
+ Com.Error(Defines.ERR_DROP, "%s has wrong version number (%i should be %i)",
+ new Vargs(3).add(mod.name).add(pheader.version).add(qfiles.ALIAS_VERSION));
+
+ if (pheader.skinheight > MAX_LBM_HEIGHT)
+ Com.Error(Defines.ERR_DROP, "model " + mod.name + " has a skin taller than " + MAX_LBM_HEIGHT);
+
+ if (pheader.num_xyz <= 0)
+ Com.Error(Defines.ERR_DROP, "model " + mod.name + " has no vertices");
+
+ if (pheader.num_xyz > qfiles.MAX_VERTS)
+ Com.Error(Defines.ERR_DROP, "model " + mod.name + " has too many vertices");
+
+ if (pheader.num_st <= 0)
+ Com.Error(Defines.ERR_DROP, "model " + mod.name + " has no st vertices");
+
+ if (pheader.num_tris <= 0)
+ Com.Error(Defines.ERR_DROP, "model " + mod.name + " has no triangles");
+
+ if (pheader.num_frames <= 0)
+ Com.Error(Defines.ERR_DROP, "model " + mod.name + " has no frames");
+
+ //
+ // load base s and t vertices (not used in gl version)
+ //
+ poutst = new qfiles.dstvert_t[pheader.num_st];
+ buffer.position(pheader.ofs_st);
+ for (i = 0; i < pheader.num_st; i++) {
+ poutst[i] = new qfiles.dstvert_t(buffer);
+ }
+
+ //
+ // load triangle lists
+ //
+ pouttri = new qfiles.dtriangle_t[pheader.num_tris];
+ buffer.position(pheader.ofs_tris);
+ for (i = 0; i < pheader.num_tris; i++) {
+ pouttri[i] = new qfiles.dtriangle_t(buffer);
+ }
+
+ //
+ // load the frames
+ //
+ poutframe = new qfiles.daliasframe_t[pheader.num_frames];
+ buffer.position(pheader.ofs_frames);
+ for (i = 0; i < pheader.num_frames; i++) {
+ poutframe[i] = new qfiles.daliasframe_t(buffer);
+ // verts are all 8 bit, so no swapping needed
+ poutframe[i].verts = new int[pheader.num_xyz];
+ for (int k = 0; k < pheader.num_xyz; k++) {
+ poutframe[i].verts[k] = buffer.getInt();
+ }
+ }
+
+ mod.type = mod_alias;
+
+ //
+ // load the glcmds
+ //
+ poutcmd = new int[pheader.num_glcmds];
+ buffer.position(pheader.ofs_glcmds);
+ for (i = 0; i < pheader.num_glcmds; i++)
+ poutcmd[i] = buffer.getInt(); // LittleLong (pincmd[i]);
+
+ // register all skins
+ String[] skinNames = new String[pheader.num_skins];
+ byte[] nameBuf = new byte[qfiles.MAX_SKINNAME];
+ buffer.position(pheader.ofs_skins);
+ for (i = 0; i < pheader.num_skins; i++) {
+ buffer.get(nameBuf);
+ skinNames[i] = new String(nameBuf);
+ int n = skinNames[i].indexOf('\0');
+ if (n > -1) {
+ skinNames[i] = skinNames[i].substring(0, n);
+ }
+ mod.skins[i] = GL_FindImage(skinNames[i], it_skin);
+ }
+
+ // set the model arrays
+ pheader.skinNames = skinNames; // skin names
+ pheader.stVerts = poutst; // textur koordinaten
+ pheader.triAngles = pouttri; // dreiecke
+ pheader.glCmds = poutcmd; // STRIP or FAN
+ pheader.aliasFrames = poutframe; // frames mit vertex array
+
+ mod.extradata = pheader;
+
+ mod.mins[0] = -32;
+ mod.mins[1] = -32;
+ mod.mins[2] = -32;
+ mod.maxs[0] = 32;
+ mod.maxs[1] = 32;
+ mod.maxs[2] = 32;
+
+ precompileGLCmds(pheader);
+ }
+
+ /*
+ =================
+ Mod_LoadSpriteModel
+ =================
+ */
+ void Mod_LoadSpriteModel(lwjake2.render.Model mod, ByteBuffer buffer) {
+ qfiles.dsprite_t sprout = new qfiles.dsprite_t(buffer);
+
+ if (sprout.version != qfiles.SPRITE_VERSION)
+ Com.Error(Defines.ERR_DROP, "%s has wrong version number (%i should be %i)",
+ new Vargs(3).add(mod.name).add(sprout.version).add(qfiles.SPRITE_VERSION));
+
+ if (sprout.numframes > qfiles.MAX_MD2SKINS)
+ Com.Error(Defines.ERR_DROP, "%s has too many frames (%i > %i)",
+ new Vargs(3).add(mod.name).add(sprout.numframes).add(qfiles.MAX_MD2SKINS));
+
+ for (int i = 0; i < sprout.numframes; i++) {
+ mod.skins[i] = GL_FindImage(sprout.frames[i].name, it_sprite);
+ }
+
+ mod.type = mod_sprite;
+ mod.extradata = sprout;
+ }
+
+ /*
+ @@@@@@@@@@@@@@@@@@@@@
+ R_BeginRegistration
+
+ Specifies the model that will be used as the world
+ @@@@@@@@@@@@@@@@@@@@@
+ */
+ protected void R_BeginRegistration(String model) {
+ resetModelArrays();
+ Polygon.reset();
+
+ CvarT flushmap;
+
+ registration_sequence++;
+ r_oldviewcluster = -1; // force markleafs
+
+ String fullname = "maps/" + model + ".bsp";
+
+ // explicitly free the old map if different
+ // this guarantees that mod_known[0] is the world map
+ flushmap = Cvar.get("flushmap", "0", 0);
+ if (!mod_known[0].name.equals(fullname) || flushmap.value != 0.0f)
+ Mod_Free(mod_known[0]);
+ r_worldmodel = Mod_ForName(fullname, true);
+
+ r_viewcluster = -1;
+ }
+
+ /*
+ @@@@@@@@@@@@@@@@@@@@@
+ R_RegisterModel
+
+ @@@@@@@@@@@@@@@@@@@@@
+ */
+ protected lwjake2.render.Model R_RegisterModel(String name) {
+ lwjake2.render.Model mod = null;
+ int i;
+ qfiles.dsprite_t sprout;
+ qfiles.dmdl_t pheader;
+
+ mod = Mod_ForName(name, false);
+ if (mod != null) {
+ mod.registration_sequence = registration_sequence;
+
+ // register any images used by the models
+ if (mod.type == mod_sprite) {
+ sprout = (qfiles.dsprite_t) mod.extradata;
+ for (i = 0; i < sprout.numframes; i++)
+ mod.skins[i] = GL_FindImage(sprout.frames[i].name, it_sprite);
+ } else if (mod.type == mod_alias) {
+ pheader = (qfiles.dmdl_t) mod.extradata;
+ for (i = 0; i < pheader.num_skins; i++)
+ mod.skins[i] = GL_FindImage(pheader.skinNames[i], it_skin);
+ // PGM
+ mod.numframes = pheader.num_frames;
+ // PGM
+ } else if (mod.type == mod_brush) {
+ for (i = 0; i < mod.numtexinfo; i++)
+ mod.texinfo[i].image.registration_sequence = registration_sequence;
+ }
+ }
+ return mod;
+ }
+
+ /*
+ @@@@@@@@@@@@@@@@@@@@@
+ R_EndRegistration
+
+ @@@@@@@@@@@@@@@@@@@@@
+ */
+ protected void R_EndRegistration() {
+ lwjake2.render.Model mod;
+
+ for (int i = 0; i < mod_numknown; i++) {
+ mod = mod_known[i];
+ if (mod.name.length() == 0)
+ continue;
+ if (mod.registration_sequence != registration_sequence) { // don't need this model
+ Mod_Free(mod);
+ } else {
+ // precompile AliasModels
+ if (mod.type == mod_alias)
+ precompileGLCmds((qfiles.dmdl_t) mod.extradata);
+ }
+ }
+ GL_FreeUnusedImages();
+ //modelMemoryUsage();
+ }
+
+ /*
+ ================
+ Mod_Free
+ ================
+ */
+ void Mod_Free(lwjake2.render.Model mod) {
+ mod.clear();
+ }
+
+ /*
+ ================
+ Mod_FreeAll
+ ================
+ */
+ void Mod_FreeAll() {
+ for (int i = 0; i < mod_numknown; i++) {
+ if (mod_known[i].extradata != null)
+ Mod_Free(mod_known[i]);
+ }
+ }
+
+ void precompileGLCmds(qfiles.dmdl_t model) {
+ model.textureCoordBuf = globalModelTextureCoordBuf.slice();
+ model.vertexIndexBuf = globalModelVertexIndexBuf.slice();
+ Vector<Integer> tmp = new Vector<>();
+
+ int count = 0;
+ int[] order = model.glCmds;
+ int orderIndex = 0;
+ while (true) {
+ // get the vertex count and primitive type
+ count = order[orderIndex++];
+ if (count == 0)
+ break; // done
+
+ tmp.addElement(count);
+
+ if (count < 0) {
+ count = -count;
+ //gl.glBegin (GL.GL_TRIANGLE_FAN);
+ } else {
+ //gl.glBegin (GL.GL_TRIANGLE_STRIP);
+ }
+
+ do {
+ // texture coordinates come from the draw list
+ globalModelTextureCoordBuf.put(Float.intBitsToFloat(order[orderIndex]));
+ globalModelTextureCoordBuf.put(Float.intBitsToFloat(order[orderIndex + 1]));
+ globalModelVertexIndexBuf.put(order[orderIndex + 2]);
+
+ orderIndex += 3;
+ } while (--count != 0);
+ }
+
+ int size = tmp.size();
+
+ model.counts = new int[size];
+ model.indexElements = new IntBuffer[size];
+
+ count = 0;
+ int pos = 0;
+ for (int i = 0; i < model.counts.length; i++) {
+ count = tmp.get(i);
+ model.counts[i] = count;
+
+ count = (count < 0) ? -count : count;
+ model.vertexIndexBuf.position(pos);
+ model.indexElements[i] = model.vertexIndexBuf.slice();
+ model.indexElements[i].limit(count);
+ pos += count;
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.render.lwjgl;
+
+import lwjake2.render.glpoly_t;
+import lwjake2.util.Lib;
+
+import java.nio.FloatBuffer;
+
+/**
+ * Polygon
+ *
+ * @author cwei
+ */
+public final class Polygon extends glpoly_t {
+
+ private final static int MAX_POLYS = 20000;
+ private final static int MAX_BUFFER_VERTICES = 120000;
+
+ // backup for s1 scrolling
+ private static final float[] s1_old = new float[MAX_VERTICES];
+
+ private static final FloatBuffer buffer = Lib.newFloatBuffer(MAX_BUFFER_VERTICES * STRIDE);
+ private static final Polygon[] polyCache = new Polygon[MAX_POLYS];
+ private static int bufferIndex = 0;
+ private static int polyCount = 0;
+
+ static {
+ for (int i = 0; i < polyCache.length; i++) {
+ polyCache[i] = new Polygon();
+ }
+ }
+
+ private Polygon() {
+ }
+
+ static glpoly_t create(int numverts) {
+ Polygon poly = polyCache[polyCount++];
+ poly.clear();
+ poly.numverts = numverts;
+ poly.pos = bufferIndex;
+ bufferIndex += numverts;
+ return poly;
+ }
+
+ static void reset() {
+ polyCount = 0;
+ bufferIndex = 0;
+ }
+
+ static FloatBuffer getInterleavedBuffer() {
+ return (FloatBuffer) buffer.rewind();
+ }
+
+ private void clear() {
+ next = null;
+ chain = null;
+ numverts = 0;
+ }
+
+ // the interleaved buffer has the format:
+ // textureCoord0 (index 0, 1)
+ // vertex (index 2, 3, 4)
+ // textureCoord1 (index 5, 6)
+
+ public final float x(int index) {
+ return buffer.get((index + pos) * 7 + 2);
+ }
+
+ public final void x(int index, float value) {
+ buffer.put((index + pos) * 7 + 2, value);
+ }
+
+ public final float y(int index) {
+ return buffer.get((index + pos) * 7 + 3);
+ }
+
+ public final void y(int index, float value) {
+ buffer.put((index + pos) * 7 + 3, value);
+ }
+
+ public final float z(int index) {
+ return buffer.get((index + pos) * 7 + 4);
+ }
+
+ public final void z(int index, float value) {
+ buffer.put((index + pos) * 7 + 4, value);
+ }
+
+ public final float s1(int index) {
+ return buffer.get((index + pos) * 7);
+ }
+
+ public final void s1(int index, float value) {
+ buffer.put((index + pos) * 7, value);
+ }
+
+ public final float t1(int index) {
+ return buffer.get((index + pos) * 7 + 1);
+ }
+
+ public final void t1(int index, float value) {
+ buffer.put((index + pos) * 7 + 1, value);
+ }
+
+ public final float s2(int index) {
+ return buffer.get((index + pos) * 7 + 5);
+ }
+
+ public final void s2(int index, float value) {
+ buffer.put((index + pos) * 7 + 5, value);
+ }
+
+ public final float t2(int index) {
+ return buffer.get((index + pos) * 7 + 6);
+ }
+
+ public final void t2(int index, float value) {
+ buffer.put((index + pos) * 7 + 6, value);
+ }
+
+ public final void beginScrolling(float scroll) {
+ int index = pos * 7;
+ for (int i = 0; i < numverts; i++, index += 7) {
+ scroll += s1_old[i] = buffer.get(index);
+ buffer.put(index, scroll);
+ }
+ }
+
+ public final void endScrolling() {
+ int index = pos * 7;
+ for (int i = 0; i < numverts; i++, index += 7) {
+ buffer.put(index, s1_old[i]);
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.render.lwjgl;
+
+import lwjake2.Defines;
+import lwjake2.client.dlight_t;
+import lwjake2.client.entity_t;
+import lwjake2.client.lightstyle_t;
+import lwjake2.game.cplane_t;
+import lwjake2.qcommon.Com;
+import lwjake2.render.Image;
+import lwjake2.render.Model;
+import lwjake2.render.*;
+import lwjake2.util.Lib;
+import lwjake2.util.Math3D;
+import org.lwjgl.BufferUtils;
+import org.lwjgl.opengl.ARBMultitexture;
+import org.lwjgl.opengl.GL11;
+
+import java.nio.ByteOrder;
+import java.nio.FloatBuffer;
+import java.nio.IntBuffer;
+import java.util.Arrays;
+
+/**
+ * Surf
+ *
+ * @author cwei
+ */
+public abstract class Surf extends Draw {
+
+ static final int DYNAMIC_LIGHT_WIDTH = 128;
+ static final int DYNAMIC_LIGHT_HEIGHT = 128;
+ static final int LIGHTMAP_BYTES = 4;
+ static final int BLOCK_WIDTH = 128;
+ static final int BLOCK_HEIGHT = 128;
+ static final int MAX_LIGHTMAPS = 128;
+ static final int GL_LIGHTMAP_FORMAT = GL11.GL_RGBA;
+ /*
+ * new buffers for vertex array handling
+ */
+ static final FloatBuffer globalPolygonInterleavedBuf = Polygon.getInterleavedBuffer();
+ static final FloatBuffer globalPolygonTexCoord1Buf;
+
+ static {
+ globalPolygonInterleavedBuf.position(Polygon.STRIDE - 2);
+ globalPolygonTexCoord1Buf = globalPolygonInterleavedBuf.slice();
+ globalPolygonInterleavedBuf.position(0);
+ }
+
+ final byte[] fatvis = new byte[Defines.MAX_MAP_LEAFS / 8];
+ /*
+ =============================================================
+
+ BRUSH MODELS
+
+ =============================================================
+ */
+ // GL_RSURF.C: surface-related refresh code
+ final float[] modelorg = {0, 0, 0}; // relative to viewpoint
+ final gllightmapstate_t gl_lms = new gllightmapstate_t();
+ private final IntBuffer temp2 = Lib.newIntBuffer(34 * 34, ByteOrder.LITTLE_ENDIAN);
+ // direct buffer
+ private final IntBuffer temp = Lib.newIntBuffer(128 * 128, ByteOrder.LITTLE_ENDIAN);
+ // stack variable
+ private final float[] mins = {0, 0, 0};
+ private final float[] maxs = {0, 0, 0};
+ private final float[] org = {0, 0, 0};
+ private final float[] forward = {0, 0, 0};
+ private final float[] right = {0, 0, 0};
+ private final float[] up = {0, 0, 0};
+ private final entity_t worldEntity = new entity_t();
+ private final IntBuffer dummy = BufferUtils.createIntBuffer(128 * 128);
+ msurface_t r_alpha_surfaces;
+ int c_visible_lightmaps;
+ int c_visible_textures;
+ lightstyle_t[] lightstyles;
+
+ // Model.java
+ abstract byte[] Mod_ClusterPVS(int cluster, Model model);
+
+ // Warp.java
+ abstract void R_DrawSkyBox();
+
+ abstract void R_AddSkySurface(msurface_t surface);
+
+ abstract void R_ClearSkyBox();
+
+ abstract void EmitWaterPolys(msurface_t fa);
+
+ // Light.java
+ abstract void R_MarkLights(dlight_t light, int bit, mnode_t node);
+
+ abstract void R_SetCacheState(msurface_t surf);
+
+ abstract void R_BuildLightMap(msurface_t surf, IntBuffer dest, int stride);
+
+ /**
+ * R_TextureAnimation
+ * Returns the proper texture for a given time and base texture
+ */
+ Image R_TextureAnimation(mtexinfo_t tex) {
+ if (tex.next == null)
+ return tex.image;
+
+ int c = currententity.frame % tex.numframes;
+ while (c != 0) {
+ tex = tex.next;
+ c--;
+ }
+
+ return tex.image;
+ }
+
+ /**
+ * DrawGLPoly
+ */
+ void DrawGLPoly(glpoly_t p) {
+ GL11.glDrawArrays(GL11.GL_POLYGON, p.pos, p.numverts);
+ }
+
+ /**
+ * DrawGLFlowingPoly
+ * version that handles scrolling texture
+ */
+ void DrawGLFlowingPoly(glpoly_t p) {
+ float scroll = -64 * ((r_newrefdef.time / 40.0f) - (int) (r_newrefdef.time / 40.0f));
+ if (scroll == 0.0f)
+ scroll = -64.0f;
+ p.beginScrolling(scroll);
+ GL11.glDrawArrays(GL11.GL_POLYGON, p.pos, p.numverts);
+ p.endScrolling();
+ }
+
+ /**
+ * R_DrawTriangleOutlines
+ */
+ void R_DrawTriangleOutlines() {
+ if (gl_showtris.value == 0)
+ return;
+
+ GL11.glDisable(GL11.GL_TEXTURE_2D);
+ GL11.glDisable(GL11.GL_DEPTH_TEST);
+ GL11.glColor4f(1, 1, 1, 1);
+
+ msurface_t surf;
+ glpoly_t p;
+ int j;
+ for (int i = 0; i < MAX_LIGHTMAPS; i++) {
+ for (surf = gl_lms.lightmap_surfaces[i]; surf != null; surf = surf.lightmapchain) {
+ for (p = surf.polys; p != null; p = p.chain) {
+ for (j = 2; j < p.numverts; j++) {
+ GL11.glBegin(GL11.GL_LINE_STRIP);
+ GL11.glVertex3f(p.x(0), p.y(0), p.z(0));
+ GL11.glVertex3f(p.x(j - 1), p.y(j - 1), p.z(j - 1));
+ GL11.glVertex3f(p.x(j), p.y(j), p.z(j));
+ GL11.glVertex3f(p.x(0), p.y(0), p.z(0));
+ GL11.glEnd();
+ }
+ }
+ }
+ }
+
+ GL11.glEnable(GL11.GL_DEPTH_TEST);
+ GL11.glEnable(GL11.GL_TEXTURE_2D);
+ }
+
+ /*
+ =============================================================
+
+ WORLD MODEL
+
+ =============================================================
+ */
+
+ /**
+ * R_RenderBrushPoly
+ */
+ void R_RenderBrushPoly(msurface_t fa) {
+ c_brush_polys++;
+
+ Image image = R_TextureAnimation(fa.texinfo);
+
+ if ((fa.flags & Defines.SURF_DRAWTURB) != 0) {
+ GL_Bind(image.texnum);
+
+ // warp texture, no lightmaps
+ GL_TexEnv(GL11.GL_MODULATE);
+ GL11.glColor4f(gl_state.inverse_intensity,
+ gl_state.inverse_intensity,
+ gl_state.inverse_intensity,
+ 1.0F);
+ EmitWaterPolys(fa);
+ GL_TexEnv(GL11.GL_REPLACE);
+
+ return;
+ } else {
+ GL_Bind(image.texnum);
+ GL_TexEnv(GL11.GL_REPLACE);
+ }
+
+ // ======
+ // PGM
+ if ((fa.texinfo.flags & Defines.SURF_FLOWING) != 0)
+ DrawGLFlowingPoly(fa.polys);
+ else
+ DrawGLPoly(fa.polys);
+ // PGM
+ // ======
+
+ // ersetzt goto
+ boolean gotoDynamic = false;
+ /*
+ ** check for lightmap modification
+ */
+ int maps;
+ for (maps = 0; maps < Defines.MAXLIGHTMAPS && fa.styles[maps] != (byte) 255; maps++) {
+ if (r_newrefdef.lightstyles[fa.styles[maps] & 0xFF].white != fa.cached_light[maps]) {
+ gotoDynamic = true;
+ break;
+ }
+ }
+
+ // this is a hack from cwei
+ if (maps == 4) maps--;
+
+ // dynamic this frame or dynamic previously
+ boolean is_dynamic = false;
+ if (gotoDynamic || (fa.dlightframe == r_framecount)) {
+ // label dynamic:
+ if (gl_dynamic.value != 0) {
+ if ((fa.texinfo.flags & (Defines.SURF_SKY | Defines.SURF_TRANS33 | Defines.SURF_TRANS66 | Defines.SURF_WARP)) == 0) {
+ is_dynamic = true;
+ }
+ }
+ }
+
+ if (is_dynamic) {
+ if (((fa.styles[maps] & 0xFF) >= 32 || fa.styles[maps] == 0) && (fa.dlightframe != r_framecount)) {
+ // ist ersetzt durch temp2: unsigned temp[34*34];
+ int smax, tmax;
+
+ smax = (fa.extents[0] >> 4) + 1;
+ tmax = (fa.extents[1] >> 4) + 1;
+
+ R_BuildLightMap(fa, temp2, smax);
+ R_SetCacheState(fa);
+
+ GL_Bind(gl_state.lightmap_textures + fa.lightmaptexturenum);
+
+ GL11.glTexSubImage2D(GL11.GL_TEXTURE_2D, 0,
+ fa.light_s, fa.light_t,
+ smax, tmax,
+ GL_LIGHTMAP_FORMAT,
+ GL11.GL_UNSIGNED_BYTE, temp2);
+
+ fa.lightmapchain = gl_lms.lightmap_surfaces[fa.lightmaptexturenum];
+ gl_lms.lightmap_surfaces[fa.lightmaptexturenum] = fa;
+ } else {
+ fa.lightmapchain = gl_lms.lightmap_surfaces[0];
+ gl_lms.lightmap_surfaces[0] = fa;
+ }
+ } else {
+ fa.lightmapchain = gl_lms.lightmap_surfaces[fa.lightmaptexturenum];
+ gl_lms.lightmap_surfaces[fa.lightmaptexturenum] = fa;
+ }
+ }
+
+ /**
+ * R_DrawAlphaSurfaces
+ * Draw water surfaces and windows.
+ * The BSP tree is waled front to back, so unwinding the chain
+ * of alpha_surfaces will draw back to front, giving proper ordering.
+ */
+ void R_DrawAlphaSurfaces() {
+ r_world_matrix.clear();
+ //
+ // go back to the world matrix
+ //
+ GL11.glLoadMatrix(r_world_matrix);
+
+ GL11.glEnable(GL11.GL_BLEND);
+ GL_TexEnv(GL11.GL_MODULATE);
+
+
+ // the textures are prescaled up for a better lighting range,
+ // so scale it back down
+ float intens = gl_state.inverse_intensity;
+
+ GL11.glInterleavedArrays(GL11.GL_T2F_V3F, Polygon.BYTE_STRIDE, globalPolygonInterleavedBuf);
+
+ for (msurface_t s = r_alpha_surfaces; s != null; s = s.texturechain) {
+ GL_Bind(s.texinfo.image.texnum);
+ c_brush_polys++;
+ if ((s.texinfo.flags & Defines.SURF_TRANS33) != 0)
+ GL11.glColor4f(intens, intens, intens, 0.33f);
+ else if ((s.texinfo.flags & Defines.SURF_TRANS66) != 0)
+ GL11.glColor4f(intens, intens, intens, 0.66f);
+ else
+ GL11.glColor4f(intens, intens, intens, 1);
+ if ((s.flags & Defines.SURF_DRAWTURB) != 0)
+ EmitWaterPolys(s);
+ else if ((s.texinfo.flags & Defines.SURF_FLOWING) != 0) // PGM 9/16/98
+ DrawGLFlowingPoly(s.polys); // PGM
+ else
+ DrawGLPoly(s.polys);
+ }
+
+ GL_TexEnv(GL11.GL_REPLACE);
+ GL11.glColor4f(1, 1, 1, 1);
+ GL11.glDisable(GL11.GL_BLEND);
+
+ r_alpha_surfaces = null;
+ }
+
+ /**
+ * DrawTextureChains
+ */
+ void DrawTextureChains() {
+ c_visible_textures = 0;
+
+ msurface_t s;
+ Image image;
+ int i;
+ for (i = 0; i < numgltextures; i++) {
+ image = gltextures[i];
+
+ if (image.registration_sequence == 0)
+ continue;
+ if (image.texturechain == null)
+ continue;
+ c_visible_textures++;
+
+ for (s = image.texturechain; s != null; s = s.texturechain) {
+ if ((s.flags & Defines.SURF_DRAWTURB) == 0)
+ R_RenderBrushPoly(s);
+ }
+ }
+
+ GL_EnableMultitexture(false);
+ for (i = 0; i < numgltextures; i++) {
+ image = gltextures[i];
+
+ if (image.registration_sequence == 0)
+ continue;
+ s = image.texturechain;
+ if (s == null)
+ continue;
+
+ for (; s != null; s = s.texturechain) {
+ if ((s.flags & Defines.SURF_DRAWTURB) != 0)
+ R_RenderBrushPoly(s);
+ }
+
+ image.texturechain = null;
+ }
+
+ GL_TexEnv(GL11.GL_REPLACE);
+ }
+
+ /**
+ * GL_RenderLightmappedPoly
+ *
+ * @param surf
+ */
+ void GL_RenderLightmappedPoly(msurface_t surf) {
+
+ // ersetzt goto
+ boolean gotoDynamic = false;
+ int map;
+ for (map = 0; map < Defines.MAXLIGHTMAPS && (surf.styles[map] != (byte) 255); map++) {
+ if (r_newrefdef.lightstyles[surf.styles[map] & 0xFF].white != surf.cached_light[map]) {
+ gotoDynamic = true;
+ break;
+ }
+ }
+
+ // this is a hack from cwei
+ if (map == 4) map--;
+
+ // dynamic this frame or dynamic previously
+ boolean is_dynamic = false;
+ if (gotoDynamic || (surf.dlightframe == r_framecount)) {
+ // label dynamic:
+ if (gl_dynamic.value != 0) {
+ if ((surf.texinfo.flags & (Defines.SURF_SKY | Defines.SURF_TRANS33 | Defines.SURF_TRANS66 | Defines.SURF_WARP)) == 0) {
+ is_dynamic = true;
+ }
+ }
+ }
+
+ glpoly_t p;
+ Image image = R_TextureAnimation(surf.texinfo);
+ int lmtex = surf.lightmaptexturenum;
+
+ if (is_dynamic) {
+ // ist raus gezogen worden int[] temp = new int[128*128];
+ int smax, tmax;
+
+ if (((surf.styles[map] & 0xFF) >= 32 || surf.styles[map] == 0) && (surf.dlightframe != r_framecount)) {
+ smax = (surf.extents[0] >> 4) + 1;
+ tmax = (surf.extents[1] >> 4) + 1;
+
+ R_BuildLightMap(surf, temp, smax);
+ R_SetCacheState(surf);
+
+ GL_MBind(GL_TEXTURE1, gl_state.lightmap_textures + surf.lightmaptexturenum);
+
+ lmtex = surf.lightmaptexturenum;
+
+ GL11.glTexSubImage2D(GL11.GL_TEXTURE_2D, 0,
+ surf.light_s, surf.light_t,
+ smax, tmax,
+ GL_LIGHTMAP_FORMAT,
+ GL11.GL_UNSIGNED_BYTE, temp);
+
+ } else {
+ smax = (surf.extents[0] >> 4) + 1;
+ tmax = (surf.extents[1] >> 4) + 1;
+
+ R_BuildLightMap(surf, temp, smax);
+
+ GL_MBind(GL_TEXTURE1, gl_state.lightmap_textures);
+
+ lmtex = 0;
+
+ GL11.glTexSubImage2D(GL11.GL_TEXTURE_2D, 0,
+ surf.light_s, surf.light_t,
+ smax, tmax,
+ GL_LIGHTMAP_FORMAT,
+ GL11.GL_UNSIGNED_BYTE, temp);
+
+ }
+
+ c_brush_polys++;
+
+ GL_MBind(GL_TEXTURE0, image.texnum);
+ GL_MBind(GL_TEXTURE1, gl_state.lightmap_textures + lmtex);
+
+ // ==========
+ // PGM
+ if ((surf.texinfo.flags & Defines.SURF_FLOWING) != 0) {
+ float scroll;
+
+ scroll = -64 * ((r_newrefdef.time / 40.0f) - (int) (r_newrefdef.time / 40.0f));
+ if (scroll == 0.0f)
+ scroll = -64.0f;
+
+ for (p = surf.polys; p != null; p = p.chain) {
+ p.beginScrolling(scroll);
+ GL11.glDrawArrays(GL11.GL_POLYGON, p.pos, p.numverts);
+ p.endScrolling();
+ }
+ } else {
+ for (p = surf.polys; p != null; p = p.chain) {
+ GL11.glDrawArrays(GL11.GL_POLYGON, p.pos, p.numverts);
+ }
+ }
+ // PGM
+ // ==========
+ } else {
+ c_brush_polys++;
+
+ GL_MBind(GL_TEXTURE0, image.texnum);
+ GL_MBind(GL_TEXTURE1, gl_state.lightmap_textures + lmtex);
+
+ // ==========
+ // PGM
+ if ((surf.texinfo.flags & Defines.SURF_FLOWING) != 0) {
+ float scroll;
+
+ scroll = -64 * ((r_newrefdef.time / 40.0f) - (int) (r_newrefdef.time / 40.0f));
+ if (scroll == 0.0)
+ scroll = -64.0f;
+
+ for (p = surf.polys; p != null; p = p.chain) {
+ p.beginScrolling(scroll);
+ GL11.glDrawArrays(GL11.GL_POLYGON, p.pos, p.numverts);
+ p.endScrolling();
+ }
+ } else {
+ // PGM
+ // ==========
+ for (p = surf.polys; p != null; p = p.chain) {
+ GL11.glDrawArrays(GL11.GL_POLYGON, p.pos, p.numverts);
+ }
+
+ // ==========
+ // PGM
+ }
+ // PGM
+ // ==========
+ }
+ }
+
+ /**
+ * R_DrawInlineBModel
+ */
+ void R_DrawInlineBModel() {
+ // calculate dynamic lighting for bmodel
+ if (gl_flashblend.value == 0) {
+ dlight_t lt;
+ for (int k = 0; k < r_newrefdef.num_dlights; k++) {
+ lt = r_newrefdef.dlights[k];
+ R_MarkLights(lt, 1 << k, currentmodel.nodes[currentmodel.firstnode]);
+ }
+ }
+
+ // psurf = ¤tmodel->surfaces[currentmodel->firstmodelsurface];
+ int psurfp = currentmodel.firstmodelsurface;
+ msurface_t[] surfaces = currentmodel.surfaces;
+ //psurf = surfaces[psurfp];
+
+ if ((currententity.flags & Defines.RF_TRANSLUCENT) != 0) {
+ GL11.glEnable(GL11.GL_BLEND);
+ GL11.glColor4f(1, 1, 1, 0.25f);
+ GL_TexEnv(GL11.GL_MODULATE);
+ }
+
+ //
+ // draw texture
+ //
+ msurface_t psurf;
+ cplane_t pplane;
+ float dot;
+ for (int i = 0; i < currentmodel.nummodelsurfaces; i++) {
+ psurf = surfaces[psurfp++];
+ // find which side of the node we are on
+ pplane = psurf.plane;
+
+ dot = Math3D.dotProduct(modelorg, pplane.normal) - pplane.dist;
+
+ // draw the polygon
+ if (((psurf.flags & Defines.SURF_PLANEBACK) != 0 && (dot < -BACKFACE_EPSILON)) ||
+ ((psurf.flags & Defines.SURF_PLANEBACK) == 0 && (dot > BACKFACE_EPSILON))) {
+ if ((psurf.texinfo.flags & (Defines.SURF_TRANS33 | Defines.SURF_TRANS66)) != 0) { // add to the translucent chain
+ psurf.texturechain = r_alpha_surfaces;
+ r_alpha_surfaces = psurf;
+ } else if ((psurf.flags & Defines.SURF_DRAWTURB) == 0) {
+ GL_RenderLightmappedPoly(psurf);
+ } else {
+ GL_EnableMultitexture(false);
+ R_RenderBrushPoly(psurf);
+ GL_EnableMultitexture(true);
+ }
+ }
+ }
+
+ if ((currententity.flags & Defines.RF_TRANSLUCENT) != 0) {
+ GL11.glDisable(GL11.GL_BLEND);
+ GL11.glColor4f(1, 1, 1, 1);
+ GL_TexEnv(GL11.GL_REPLACE);
+ }
+ }
+
+ /*
+ =============================================================================
+
+ LIGHTMAP ALLOCATION
+
+ =============================================================================
+ */
+
+ /**
+ * R_DrawBrushModel
+ */
+ void R_DrawBrushModel(entity_t e) {
+ if (currentmodel.nummodelsurfaces == 0)
+ return;
+
+ currententity = e;
+ gl_state.currenttextures[0] = gl_state.currenttextures[1] = -1;
+
+ boolean rotated;
+ if (e.angles[0] != 0 || e.angles[1] != 0 || e.angles[2] != 0) {
+ rotated = true;
+ for (int i = 0; i < 3; i++) {
+ mins[i] = e.origin[i] - currentmodel.radius;
+ maxs[i] = e.origin[i] + currentmodel.radius;
+ }
+ } else {
+ rotated = false;
+ Math3D.vectorAdd(e.origin, currentmodel.mins, mins);
+ Math3D.vectorAdd(e.origin, currentmodel.maxs, maxs);
+ }
+
+ if (R_CullBox(mins, maxs)) return;
+
+ GL11.glColor3f(1, 1, 1);
+
+ // memset (gl_lms.lightmap_surfaces, 0, sizeof(gl_lms.lightmap_surfaces));
+
+ // TODO wird beim multitexturing nicht gebraucht
+ //gl_lms.clearLightmapSurfaces();
+
+ Math3D.vectorSubtract(r_newrefdef.vieworg, e.origin, modelorg);
+ if (rotated) {
+ Math3D.vectorCopy(modelorg, org);
+ Math3D.angleVectors(e.angles, forward, right, up);
+ modelorg[0] = Math3D.dotProduct(org, forward);
+ modelorg[1] = -Math3D.dotProduct(org, right);
+ modelorg[2] = Math3D.dotProduct(org, up);
+ }
+
+ GL11.glPushMatrix();
+
+ e.angles[0] = -e.angles[0]; // stupid quake bug
+ e.angles[2] = -e.angles[2]; // stupid quake bug
+ R_RotateForEntity(e);
+ e.angles[0] = -e.angles[0]; // stupid quake bug
+ e.angles[2] = -e.angles[2]; // stupid quake bug
+
+ GL_EnableMultitexture(true);
+ GL_SelectTexture(GL_TEXTURE0);
+ GL_TexEnv(GL11.GL_REPLACE);
+ GL11.glInterleavedArrays(GL11.GL_T2F_V3F, Polygon.BYTE_STRIDE, globalPolygonInterleavedBuf);
+ GL_SelectTexture(GL_TEXTURE1);
+ GL_TexEnv(GL11.GL_MODULATE);
+ GL11.glTexCoordPointer(2, Polygon.BYTE_STRIDE, globalPolygonTexCoord1Buf);
+ GL11.glEnableClientState(GL11.GL_TEXTURE_COORD_ARRAY);
+
+ R_DrawInlineBModel();
+
+ ARBMultitexture.glClientActiveTextureARB(GL_TEXTURE1);
+ GL11.glDisableClientState(GL11.GL_TEXTURE_COORD_ARRAY);
+
+ GL_EnableMultitexture(false);
+
+ GL11.glPopMatrix();
+ }
+
+ /**
+ * R_RecursiveWorldNode
+ */
+ void R_RecursiveWorldNode(mnode_t node) {
+ if (node.contents == Defines.CONTENTS_SOLID)
+ return; // solid
+
+ if (node.visframe != r_visframecount)
+ return;
+
+ if (R_CullBox(node.mins, node.maxs))
+ return;
+
+ int c;
+ msurface_t mark;
+ // if a leaf node, draw stuff
+ if (node.contents != -1) {
+ mleaf_t pleaf = (mleaf_t) node;
+
+ // check for door connected areas
+ if (r_newrefdef.areabits != null) {
+ if (((r_newrefdef.areabits[pleaf.area >> 3] & 0xFF) & (1 << (pleaf.area & 7))) == 0)
+ return; // not visible
+ }
+
+ int markp = 0;
+
+ mark = pleaf.getMarkSurface(markp); // first marked surface
+ c = pleaf.nummarksurfaces;
+
+ if (c != 0) {
+ do {
+ mark.visframe = r_framecount;
+ mark = pleaf.getMarkSurface(++markp); // next surface
+ } while (--c != 0);
+ }
+
+ return;
+ }
+
+ // node is just a decision point, so go down the apropriate sides
+
+ // find which side of the node we are on
+ cplane_t plane = node.plane;
+ float dot;
+ switch (plane.type) {
+ case Defines.PLANE_X:
+ dot = modelorg[0] - plane.dist;
+ break;
+ case Defines.PLANE_Y:
+ dot = modelorg[1] - plane.dist;
+ break;
+ case Defines.PLANE_Z:
+ dot = modelorg[2] - plane.dist;
+ break;
+ default:
+ dot = Math3D.dotProduct(modelorg, plane.normal) - plane.dist;
+ break;
+ }
+
+ int side, sidebit;
+ if (dot >= 0.0f) {
+ side = 0;
+ sidebit = 0;
+ } else {
+ side = 1;
+ sidebit = Defines.SURF_PLANEBACK;
+ }
+
+ // recurse down the children, front side first
+ R_RecursiveWorldNode(node.children[side]);
+
+ // draw stuff
+ msurface_t surf;
+ Image image;
+ //for ( c = node.numsurfaces, surf = r_worldmodel.surfaces[node.firstsurface]; c != 0 ; c--, surf++)
+ for (c = 0; c < node.numsurfaces; c++) {
+ surf = r_worldmodel.surfaces[node.firstsurface + c];
+ if (surf.visframe != r_framecount)
+ continue;
+
+ if ((surf.flags & Defines.SURF_PLANEBACK) != sidebit)
+ continue; // wrong side
+
+ if ((surf.texinfo.flags & Defines.SURF_SKY) != 0) { // just adds to visible sky bounds
+ R_AddSkySurface(surf);
+ } else if ((surf.texinfo.flags & (Defines.SURF_TRANS33 | Defines.SURF_TRANS66)) != 0) { // add to the translucent chain
+ surf.texturechain = r_alpha_surfaces;
+ r_alpha_surfaces = surf;
+ } else {
+ if ((surf.flags & Defines.SURF_DRAWTURB) == 0) {
+ GL_RenderLightmappedPoly(surf);
+ } else {
+ // the polygon is visible, so add it to the texture
+ // sorted chain
+ // FIXME: this is a hack for animation
+ image = R_TextureAnimation(surf.texinfo);
+ surf.texturechain = image.texturechain;
+ image.texturechain = surf;
+ }
+ }
+ }
+ // recurse down the back side
+ R_RecursiveWorldNode(node.children[1 - side]);
+ }
+
+ /**
+ * R_DrawWorld
+ */
+ void R_DrawWorld() {
+ if (r_drawworld.value == 0)
+ return;
+
+ if ((r_newrefdef.rdflags & Defines.RDF_NOWORLDMODEL) != 0)
+ return;
+
+ currentmodel = r_worldmodel;
+
+ Math3D.vectorCopy(r_newrefdef.vieworg, modelorg);
+
+ entity_t ent = worldEntity;
+ // auto cycle the world frame for texture animation
+ ent.clear();
+ ent.frame = (int) (r_newrefdef.time * 2);
+ currententity = ent;
+
+ gl_state.currenttextures[0] = gl_state.currenttextures[1] = -1;
+
+ GL11.glColor3f(1, 1, 1);
+ // memset (gl_lms.lightmap_surfaces, 0, sizeof(gl_lms.lightmap_surfaces));
+ // TODO wird bei multitexture nicht gebraucht
+ //gl_lms.clearLightmapSurfaces();
+
+ R_ClearSkyBox();
+
+ GL_EnableMultitexture(true);
+
+ GL_SelectTexture(GL_TEXTURE0);
+ GL_TexEnv(GL11.GL_REPLACE);
+ GL11.glInterleavedArrays(GL11.GL_T2F_V3F, Polygon.BYTE_STRIDE, globalPolygonInterleavedBuf);
+ GL_SelectTexture(GL_TEXTURE1);
+ GL11.glTexCoordPointer(2, Polygon.BYTE_STRIDE, globalPolygonTexCoord1Buf);
+ GL11.glEnableClientState(GL11.GL_TEXTURE_COORD_ARRAY);
+
+ if (gl_lightmap.value != 0)
+ GL_TexEnv(GL11.GL_REPLACE);
+ else
+ GL_TexEnv(GL11.GL_MODULATE);
+
+ R_RecursiveWorldNode(r_worldmodel.nodes[0]); // root node
+
+ ARBMultitexture.glClientActiveTextureARB(GL_TEXTURE1);
+ GL11.glDisableClientState(GL11.GL_TEXTURE_COORD_ARRAY);
+
+ GL_EnableMultitexture(false);
+
+ DrawTextureChains();
+ R_DrawSkyBox();
+ R_DrawTriangleOutlines();
+ }
+
+ /**
+ * R_MarkLeaves
+ * Mark the leaves and nodes that are in the PVS for the current
+ * cluster
+ */
+ void R_MarkLeaves() {
+ if (r_oldviewcluster == r_viewcluster && r_oldviewcluster2 == r_viewcluster2 && r_novis.value == 0 && r_viewcluster != -1)
+ return;
+
+ // development aid to let you run around and see exactly where
+ // the pvs ends
+ if (gl_lockpvs.value != 0)
+ return;
+
+ r_visframecount++;
+ r_oldviewcluster = r_viewcluster;
+ r_oldviewcluster2 = r_viewcluster2;
+
+ int i;
+ if (r_novis.value != 0 || r_viewcluster == -1 || r_worldmodel.vis == null) {
+ // mark everything
+ for (i = 0; i < r_worldmodel.numleafs; i++)
+ r_worldmodel.leafs[i].visframe = r_visframecount;
+ for (i = 0; i < r_worldmodel.numnodes; i++)
+ r_worldmodel.nodes[i].visframe = r_visframecount;
+ return;
+ }
+
+ byte[] vis = Mod_ClusterPVS(r_viewcluster, r_worldmodel);
+ int c;
+ // may have to combine two clusters because of solid water boundaries
+ if (r_viewcluster2 != r_viewcluster) {
+ // memcpy (fatvis, vis, (r_worldmodel.numleafs+7)/8);
+ System.arraycopy(vis, 0, fatvis, 0, (r_worldmodel.numleafs + 7) >> 3);
+ vis = Mod_ClusterPVS(r_viewcluster2, r_worldmodel);
+ c = (r_worldmodel.numleafs + 31) >> 5;
+ c <<= 2;
+ for (int k = 0; k < c; k += 4) {
+ fatvis[k] |= vis[k];
+ fatvis[k + 1] |= vis[k + 1];
+ fatvis[k + 2] |= vis[k + 2];
+ fatvis[k + 3] |= vis[k + 3];
+ }
+
+ vis = fatvis;
+ }
+
+ mnode_t node;
+ mleaf_t leaf;
+ int cluster;
+ for (i = 0; i < r_worldmodel.numleafs; i++) {
+ leaf = r_worldmodel.leafs[i];
+ cluster = leaf.cluster;
+ if (cluster == -1)
+ continue;
+ if (((vis[cluster >> 3] & 0xFF) & (1 << (cluster & 7))) != 0) {
+ node = leaf;
+ do {
+ if (node.visframe == r_visframecount)
+ break;
+ node.visframe = r_visframecount;
+ node = node.parent;
+ } while (node != null);
+ }
+ }
+ }
+
+ /**
+ * LM_InitBlock
+ */
+ void LM_InitBlock() {
+ Arrays.fill(gl_lms.allocated, 0);
+ }
+
+ /**
+ * LM_UploadBlock
+ */
+ void LM_UploadBlock() {
+ int texture = gl_lms.current_lightmap_texture;
+
+ GL_Bind(gl_state.lightmap_textures + texture);
+ GL11.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR);
+ GL11.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR);
+
+ gl_lms.lightmap_buffer.rewind();
+ GL11.glTexImage2D(GL11.GL_TEXTURE_2D,
+ 0,
+ gl_lms.internal_format,
+ BLOCK_WIDTH, BLOCK_HEIGHT,
+ 0,
+ GL_LIGHTMAP_FORMAT,
+ GL11.GL_UNSIGNED_BYTE,
+ gl_lms.lightmap_buffer);
+ if (++gl_lms.current_lightmap_texture == MAX_LIGHTMAPS)
+ Com.Error(Defines.ERR_DROP, "LM_UploadBlock() - MAX_LIGHTMAPS exceeded\n");
+
+ //debugLightmap(gl_lms.lightmap_buffer, 128, 128, 4);
+ }
+
+ /**
+ * LM_AllocBlock
+ *
+ * @param w
+ * @param h
+ * @param pos
+ * @return a texture number and the position inside it
+ */
+ boolean LM_AllocBlock(int w, int h, pos_t pos) {
+ int best = BLOCK_HEIGHT;
+
+ int best2;
+ int i, j;
+ for (i = 0; i < BLOCK_WIDTH - w; i++) {
+ best2 = 0;
+
+ for (j = 0; j < w; j++) {
+ if (gl_lms.allocated[i + j] >= best)
+ break;
+ if (gl_lms.allocated[i + j] > best2)
+ best2 = gl_lms.allocated[i + j];
+ }
+ if (j == w) { // this is a valid spot
+ pos.x = i;
+ pos.y = best = best2;
+ }
+ }
+
+ if (best + h > BLOCK_HEIGHT)
+ return false;
+
+ for (i = 0; i < w; i++)
+ gl_lms.allocated[pos.x + i] = best + h;
+
+ return true;
+ }
+
+ /**
+ * GL_BuildPolygonFromSurface
+ */
+ void GL_BuildPolygonFromSurface(msurface_t fa) {
+ // reconstruct the polygon
+ medge_t[] pedges = currentmodel.edges;
+ int lnumverts = fa.numedges;
+ //
+ // draw texture
+ //
+ // poly = Hunk_Alloc (sizeof(glpoly_t) + (lnumverts-4) * VERTEXSIZE*sizeof(float));
+ glpoly_t poly = Polygon.create(lnumverts);
+
+ poly.next = fa.polys;
+ fa.polys = poly;
+
+ int lindex;
+ float[] vec;
+ medge_t r_pedge;
+ float s, t;
+ for (int i = 0; i < lnumverts; i++) {
+ lindex = currentmodel.surfedges[fa.firstedge + i];
+
+ if (lindex > 0) {
+ r_pedge = pedges[lindex];
+ vec = currentmodel.vertexes[r_pedge.v[0]].position;
+ } else {
+ r_pedge = pedges[-lindex];
+ vec = currentmodel.vertexes[r_pedge.v[1]].position;
+ }
+ s = Math3D.dotProduct(vec, fa.texinfo.vecs[0]) + fa.texinfo.vecs[0][3];
+ s /= fa.texinfo.image.width;
+
+ t = Math3D.dotProduct(vec, fa.texinfo.vecs[1]) + fa.texinfo.vecs[1][3];
+ t /= fa.texinfo.image.height;
+
+ poly.x(i, vec[0]);
+ poly.y(i, vec[1]);
+ poly.z(i, vec[2]);
+
+ poly.s1(i, s);
+ poly.t1(i, t);
+
+ //
+ // lightmap texture coordinates
+ //
+ s = Math3D.dotProduct(vec, fa.texinfo.vecs[0]) + fa.texinfo.vecs[0][3];
+ s -= fa.texturemins[0];
+ s += fa.light_s * 16;
+ s += 8;
+ s /= BLOCK_WIDTH * 16; //fa.texinfo.texture.width;
+
+ t = Math3D.dotProduct(vec, fa.texinfo.vecs[1]) + fa.texinfo.vecs[1][3];
+ t -= fa.texturemins[1];
+ t += fa.light_t * 16;
+ t += 8;
+ t /= BLOCK_HEIGHT * 16; //fa.texinfo.texture.height;
+
+ poly.s2(i, s);
+ poly.t2(i, t);
+ }
+ }
+
+ /**
+ * GL_CreateSurfaceLightmap
+ */
+ void GL_CreateSurfaceLightmap(msurface_t surf) {
+ if ((surf.flags & (Defines.SURF_DRAWSKY | Defines.SURF_DRAWTURB)) != 0)
+ return;
+
+ int smax = (surf.extents[0] >> 4) + 1;
+ int tmax = (surf.extents[1] >> 4) + 1;
+
+ pos_t lightPos = new pos_t(surf.light_s, surf.light_t);
+
+ if (!LM_AllocBlock(smax, tmax, lightPos)) {
+ LM_UploadBlock();
+ LM_InitBlock();
+ lightPos = new pos_t(surf.light_s, surf.light_t);
+ if (!LM_AllocBlock(smax, tmax, lightPos)) {
+ Com.Error(Defines.ERR_FATAL, "Consecutive calls to LM_AllocBlock(" + smax + "," + tmax + ") failed\n");
+ }
+ }
+
+ // kopiere die koordinaten zurueck
+ surf.light_s = lightPos.x;
+ surf.light_t = lightPos.y;
+
+ surf.lightmaptexturenum = gl_lms.current_lightmap_texture;
+
+ IntBuffer base = gl_lms.lightmap_buffer;
+ base.position(surf.light_t * BLOCK_WIDTH + surf.light_s);
+
+ R_SetCacheState(surf);
+ R_BuildLightMap(surf, base.slice(), BLOCK_WIDTH);
+ }
+
+ /**
+ * GL_BeginBuildingLightmaps
+ */
+ void GL_BeginBuildingLightmaps(Model m) {
+ // static lightstyle_t lightstyles[MAX_LIGHTSTYLES];
+ int i;
+
+ // init lightstyles
+ if (lightstyles == null) {
+ lightstyles = new lightstyle_t[Defines.MAX_LIGHTSTYLES];
+ for (i = 0; i < lightstyles.length; i++) {
+ lightstyles[i] = new lightstyle_t();
+ }
+ }
+
+ // memset( gl_lms.allocated, 0, sizeof(gl_lms.allocated) );
+ Arrays.fill(gl_lms.allocated, 0);
+
+ r_framecount = 1; // no dlightcache
+
+ GL_EnableMultitexture(true);
+ GL_SelectTexture(GL_TEXTURE1);
+
+ /*
+ ** setup the base lightstyles so the lightmaps won't have to be regenerated
+ ** the first time they're seen
+ */
+ for (i = 0; i < Defines.MAX_LIGHTSTYLES; i++) {
+ lightstyles[i].rgb[0] = 1;
+ lightstyles[i].rgb[1] = 1;
+ lightstyles[i].rgb[2] = 1;
+ lightstyles[i].white = 3;
+ }
+ r_newrefdef.lightstyles = lightstyles;
+
+ if (gl_state.lightmap_textures == 0) {
+ gl_state.lightmap_textures = TEXNUM_LIGHTMAPS;
+ }
+
+ gl_lms.current_lightmap_texture = 1;
+
+ /*
+ ** if mono lightmaps are enabled and we want to use alpha
+ ** blending (a,1-a) then we're likely running on a 3DLabs
+ ** Permedia2. In a perfect world we'd use a GL_ALPHA lightmap
+ ** in order to conserve space and maximize bandwidth, however
+ ** this isn't a perfect world.
+ **
+ ** So we have to use alpha lightmaps, but stored in GL_RGBA format,
+ ** which means we only get 1/16th the color resolution we should when
+ ** using alpha lightmaps. If we find another board that supports
+ ** only alpha lightmaps but that can at least support the GL_ALPHA
+ ** format then we should change this code to use real alpha maps.
+ */
+
+ char format = gl_monolightmap.string.toUpperCase().charAt(0);
+
+ if (format == 'A') {
+ gl_lms.internal_format = gl_tex_alpha_format;
+ }
+ /*
+ ** try to do hacked colored lighting with a blended texture
+ */
+ else if (format == 'C') {
+ gl_lms.internal_format = gl_tex_alpha_format;
+ } else if (format == 'I') {
+ gl_lms.internal_format = GL11.GL_INTENSITY8;
+ } else if (format == 'L') {
+ gl_lms.internal_format = GL11.GL_LUMINANCE8;
+ } else {
+ gl_lms.internal_format = gl_tex_solid_format;
+ }
+
+ /*
+ ** initialize the dynamic lightmap texture
+ */
+ GL_Bind(gl_state.lightmap_textures);
+ GL11.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR);
+ GL11.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR);
+ GL11.glTexImage2D(GL11.GL_TEXTURE_2D,
+ 0,
+ gl_lms.internal_format,
+ BLOCK_WIDTH, BLOCK_HEIGHT,
+ 0,
+ GL_LIGHTMAP_FORMAT,
+ GL11.GL_UNSIGNED_BYTE,
+ dummy);
+ }
+
+ /**
+ * GL_EndBuildingLightmaps
+ */
+ void GL_EndBuildingLightmaps() {
+ LM_UploadBlock();
+ GL_EnableMultitexture(false);
+ }
+
+ static class gllightmapstate_t {
+ final msurface_t[] lightmap_surfaces = new msurface_t[MAX_LIGHTMAPS];
+ final int[] allocated = new int[BLOCK_WIDTH];
+ // the lightmap texture data needs to be kept in
+ // main memory so texsubimage can update properly
+ //byte[] lightmap_buffer = new byte[4 * BLOCK_WIDTH * BLOCK_HEIGHT];
+ final IntBuffer lightmap_buffer = Lib.newIntBuffer(BLOCK_WIDTH * BLOCK_HEIGHT, ByteOrder.LITTLE_ENDIAN);
+ int internal_format;
+ int current_lightmap_texture;
+
+ public gllightmapstate_t() {
+ for (int i = 0; i < MAX_LIGHTMAPS; i++)
+ lightmap_surfaces[i] = new msurface_t();
+ }
+
+ public void clearLightmapSurfaces() {
+ for (int i = 0; i < MAX_LIGHTMAPS; i++)
+ // TODO lightmap_surfaces[i].clear();
+ lightmap_surfaces[i] = new msurface_t();
+ }
+
+ }
+
+ //ImageFrame frame;
+
+// void debugLightmap(byte[] buf, int w, int h, float scale) {
+// IntBuffer pix = ByteBuffer.wrap(buf).order(ByteOrder.LITTLE_ENDIAN).asIntBuffer();
+//
+// int[] pixel = new int[w * h];
+//
+// pix.get(pixel);
+//
+// BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_4BYTE_ABGR);
+// image.setRGB(0, 0, w, h, pixel, 0, w);
+// AffineTransformOp op = new AffineTransformOp(AffineTransform.getScaleInstance(scale, scale), AffineTransformOp.TYPE_NEAREST_NEIGHBOR);
+// BufferedImage tmp = op.filter(image, null);
+//
+// if (frame == null) {
+// frame = new ImageFrame(null);
+// frame.show();
+// }
+// frame.showImage(tmp);
+//
+// }
+
+}
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.render.lwjgl;
+
+import lwjake2.Defines;
+import lwjake2.Globals;
+import lwjake2.qcommon.Com;
+import lwjake2.render.Image;
+import lwjake2.render.glpoly_t;
+import lwjake2.render.msurface_t;
+import lwjake2.util.Math3D;
+import lwjake2.util.Vec3Cache;
+import org.lwjgl.opengl.GL11;
+
+/**
+ * Warp
+ *
+ * @author cwei
+ */
+public abstract class Warp extends Model {
+ // warpsin.h
+ public static final float[] SIN = {
+ 0f, 0.19633f, 0.392541f, 0.588517f, 0.784137f, 0.979285f, 1.17384f, 1.3677f,
+ 1.56072f, 1.75281f, 1.94384f, 2.1337f, 2.32228f, 2.50945f, 2.69512f, 2.87916f,
+ 3.06147f, 3.24193f, 3.42044f, 3.59689f, 3.77117f, 3.94319f, 4.11282f, 4.27998f,
+ 4.44456f, 4.60647f, 4.76559f, 4.92185f, 5.07515f, 5.22538f, 5.37247f, 5.51632f,
+ 5.65685f, 5.79398f, 5.92761f, 6.05767f, 6.18408f, 6.30677f, 6.42566f, 6.54068f,
+ 6.65176f, 6.75883f, 6.86183f, 6.9607f, 7.05537f, 7.14579f, 7.23191f, 7.31368f,
+ 7.39104f, 7.46394f, 7.53235f, 7.59623f, 7.65552f, 7.71021f, 7.76025f, 7.80562f,
+ 7.84628f, 7.88222f, 7.91341f, 7.93984f, 7.96148f, 7.97832f, 7.99036f, 7.99759f,
+ 8f, 7.99759f, 7.99036f, 7.97832f, 7.96148f, 7.93984f, 7.91341f, 7.88222f,
+ 7.84628f, 7.80562f, 7.76025f, 7.71021f, 7.65552f, 7.59623f, 7.53235f, 7.46394f,
+ 7.39104f, 7.31368f, 7.23191f, 7.14579f, 7.05537f, 6.9607f, 6.86183f, 6.75883f,
+ 6.65176f, 6.54068f, 6.42566f, 6.30677f, 6.18408f, 6.05767f, 5.92761f, 5.79398f,
+ 5.65685f, 5.51632f, 5.37247f, 5.22538f, 5.07515f, 4.92185f, 4.76559f, 4.60647f,
+ 4.44456f, 4.27998f, 4.11282f, 3.94319f, 3.77117f, 3.59689f, 3.42044f, 3.24193f,
+ 3.06147f, 2.87916f, 2.69512f, 2.50945f, 2.32228f, 2.1337f, 1.94384f, 1.75281f,
+ 1.56072f, 1.3677f, 1.17384f, 0.979285f, 0.784137f, 0.588517f, 0.392541f, 0.19633f,
+ 9.79717e-16f, -0.19633f, -0.392541f, -0.588517f, -0.784137f, -0.979285f, -1.17384f, -1.3677f,
+ -1.56072f, -1.75281f, -1.94384f, -2.1337f, -2.32228f, -2.50945f, -2.69512f, -2.87916f,
+ -3.06147f, -3.24193f, -3.42044f, -3.59689f, -3.77117f, -3.94319f, -4.11282f, -4.27998f,
+ -4.44456f, -4.60647f, -4.76559f, -4.92185f, -5.07515f, -5.22538f, -5.37247f, -5.51632f,
+ -5.65685f, -5.79398f, -5.92761f, -6.05767f, -6.18408f, -6.30677f, -6.42566f, -6.54068f,
+ -6.65176f, -6.75883f, -6.86183f, -6.9607f, -7.05537f, -7.14579f, -7.23191f, -7.31368f,
+ -7.39104f, -7.46394f, -7.53235f, -7.59623f, -7.65552f, -7.71021f, -7.76025f, -7.80562f,
+ -7.84628f, -7.88222f, -7.91341f, -7.93984f, -7.96148f, -7.97832f, -7.99036f, -7.99759f,
+ -8f, -7.99759f, -7.99036f, -7.97832f, -7.96148f, -7.93984f, -7.91341f, -7.88222f,
+ -7.84628f, -7.80562f, -7.76025f, -7.71021f, -7.65552f, -7.59623f, -7.53235f, -7.46394f,
+ -7.39104f, -7.31368f, -7.23191f, -7.14579f, -7.05537f, -6.9607f, -6.86183f, -6.75883f,
+ -6.65176f, -6.54068f, -6.42566f, -6.30677f, -6.18408f, -6.05767f, -5.92761f, -5.79398f,
+ -5.65685f, -5.51632f, -5.37247f, -5.22538f, -5.07515f, -4.92185f, -4.76559f, -4.60647f,
+ -4.44456f, -4.27998f, -4.11282f, -3.94319f, -3.77117f, -3.59689f, -3.42044f, -3.24193f,
+ -3.06147f, -2.87916f, -2.69512f, -2.50945f, -2.32228f, -2.1337f, -1.94384f, -1.75281f,
+ -1.56072f, -1.3677f, -1.17384f, -0.979285f, -0.784137f, -0.588517f, -0.392541f, -0.19633f
+ };
+ private static final int SUBDIVIDE_SIZE = 64;
+ // =========================================================
+ private static final float TURBSCALE = (float) (256.0f / (2 * Math.PI));
+ private static final float ON_EPSILON = 0.1f; // point on plane side epsilon
+ private static final int MAX_CLIP_VERTS = 64;
+ private static final int SIDE_BACK = 1;
+ private static final int SIDE_FRONT = 0;
+ private static final int SIDE_ON = 2;
+ private final float[][] tmpVerts = new float[64][3];
+ // stack variable
+ private final float[] v = {0, 0, 0};
+ private final float[] av = {0, 0, 0};
+ // stack variable
+ private final float[] v1 = {0, 0, 0};
+ private final float[] b = {0, 0, 0};
+ private final float[] skyaxis = {0, 0, 0};
+ private final Image[] sky_images = new Image[6];
+ private final float[][] skyclip = {
+ {1, 1, 0},
+ {1, -1, 0},
+ {0, -1, 1},
+ {0, 1, 1},
+ {1, 0, 1},
+ {-1, 0, 1}
+ };
+ // 1 = s, 2 = t, 3 = 2048
+ private final int[][] st_to_vec =
+ {
+ {3, -1, 2},
+ {-3, 1, 2},
+
+ {1, 3, 2},
+ {-1, -3, 2},
+
+ {-2, -1, 3}, // 0 degrees yaw, look straight up
+ {2, -1, -3} // look straight down
+
+ };
+ private final int[][] vec_to_st =
+ {
+ {-2, 3, 1},
+ {2, 3, -1},
+
+ {1, 3, 2},
+ {-1, 3, -2},
+
+ {-2, -1, 3},
+ {-2, 1, -3}
+
+ };
+ private final float[][] skymins = new float[2][6];
+ private final float[][] skymaxs = new float[2][6];
+ private final float[] dists = new float[MAX_CLIP_VERTS];
+ private final int[] sides = new int[MAX_CLIP_VERTS];
+ private final float[][][][] newv = new float[6][2][MAX_CLIP_VERTS][3];
+ private final float[][] verts = new float[MAX_CLIP_VERTS][3];
+ private final int[] skytexorder = {0, 2, 1, 3, 4, 5};
+ // 3dstudio environment map names
+ private final String[] suf = {"rt", "bk", "lf", "ft", "up", "dn"};
+ private float skyrotate;
+ private msurface_t warpface;
+ private int c_sky;
+ private float sky_min;
+ private float sky_max;
+
+ /**
+ * BoundPoly
+ *
+ * @param numverts
+ * @param verts
+ * @param mins
+ * @param maxs
+ */
+ private void BoundPoly(int numverts, float[][] verts, float[] mins, float[] maxs) {
+ mins[0] = mins[1] = mins[2] = 9999;
+ maxs[0] = maxs[1] = maxs[2] = -9999;
+
+ int j;
+ float[] v;
+ for (int i = 0; i < numverts; i++) {
+ v = verts[i];
+ for (j = 0; j < 3; j++) {
+ if (v[j] < mins[j])
+ mins[j] = v[j];
+ if (v[j] > maxs[j])
+ maxs[j] = v[j];
+ }
+ }
+ }
+
+ /**
+ * SubdividePolygon
+ *
+ * @param numverts
+ * @param verts
+ */
+ private void SubdividePolygon(int numverts, float[][] verts) {
+ int i, j, k;
+ float m;
+ float[][] front = new float[64][3];
+ float[][] back = new float[64][3];
+
+ int f, b;
+ float[] dist = new float[64];
+ float frac;
+
+ if (numverts > 60)
+ Com.Error(Defines.ERR_DROP, "numverts = " + numverts);
+
+ float[] mins = Vec3Cache.get();
+ float[] maxs = Vec3Cache.get();
+
+ BoundPoly(numverts, verts, mins, maxs);
+ float[] v;
+ // x,y und z
+ for (i = 0; i < 3; i++) {
+ m = (mins[i] + maxs[i]) * 0.5f;
+ m = SUBDIVIDE_SIZE * (float) Math.floor(m / SUBDIVIDE_SIZE + 0.5f);
+ if (maxs[i] - m < 8)
+ continue;
+ if (m - mins[i] < 8)
+ continue;
+
+ // cut it
+ for (j = 0; j < numverts; j++) {
+ dist[j] = verts[j][i] - m;
+ }
+
+ // wrap cases
+ dist[j] = dist[0];
+
+ Math3D.vectorCopy(verts[0], verts[numverts]);
+
+ f = b = 0;
+ for (j = 0; j < numverts; j++) {
+ v = verts[j];
+ if (dist[j] >= 0) {
+ Math3D.vectorCopy(v, front[f]);
+ f++;
+ }
+ if (dist[j] <= 0) {
+ Math3D.vectorCopy(v, back[b]);
+ b++;
+ }
+ if (dist[j] == 0 || dist[j + 1] == 0) continue;
+
+ if ((dist[j] > 0) != (dist[j + 1] > 0)) {
+ // clip point
+ frac = dist[j] / (dist[j] - dist[j + 1]);
+ for (k = 0; k < 3; k++)
+ front[f][k] = back[b][k] = v[k] + frac * (verts[j + 1][k] - v[k]);
+
+ f++;
+ b++;
+ }
+ }
+
+ SubdividePolygon(f, front);
+ SubdividePolygon(b, back);
+
+ Vec3Cache.release(2); // mins, maxs
+ return;
+ }
+
+ Vec3Cache.release(2); // mins, maxs
+
+ // add a point in the center to help keep warp valid
+
+ // wird im Konstruktor erschlagen
+ // poly = Hunk_Alloc (sizeof(glpoly_t) + ((numverts-4)+2) * VERTEXSIZE*sizeof(float));
+
+ // init polys
+ glpoly_t poly = Polygon.create(numverts + 2);
+
+ poly.next = warpface.polys;
+ warpface.polys = poly;
+
+ float[] total = Vec3Cache.get();
+ Math3D.vectorClear(total);
+ float total_s = 0;
+ float total_t = 0;
+ float s, t;
+ for (i = 0; i < numverts; i++) {
+ poly.x(i + 1, verts[i][0]);
+ poly.y(i + 1, verts[i][1]);
+ poly.z(i + 1, verts[i][2]);
+ s = Math3D.dotProduct(verts[i], warpface.texinfo.vecs[0]);
+ t = Math3D.dotProduct(verts[i], warpface.texinfo.vecs[1]);
+
+ total_s += s;
+ total_t += t;
+ Math3D.vectorAdd(total, verts[i], total);
+
+ poly.s1(i + 1, s);
+ poly.t1(i + 1, t);
+ }
+
+ float scale = 1.0f / numverts;
+ poly.x(0, total[0] * scale);
+ poly.y(0, total[1] * scale);
+ poly.z(0, total[2] * scale);
+ poly.s1(0, total_s * scale);
+ poly.t1(0, total_t * scale);
+
+ poly.x(i + 1, poly.x(1));
+ poly.y(i + 1, poly.y(1));
+ poly.z(i + 1, poly.z(1));
+ poly.s1(i + 1, poly.s1(1));
+ poly.t1(i + 1, poly.t1(1));
+ poly.s2(i + 1, poly.s2(1));
+ poly.t2(i + 1, poly.t2(1));
+
+ Vec3Cache.release(); // total
+ }
+
+ /**
+ * GL_SubdivideSurface
+ * Breaks a polygon up along axial 64 unit
+ * boundaries so that turbulent and sky warps
+ * can be done reasonably.
+ */
+ void GL_SubdivideSurface(msurface_t fa) {
+ float[][] verts = tmpVerts;
+ float[] vec;
+ warpface = fa;
+ //
+ // convert edges back to a normal polygon
+ //
+ int numverts = 0;
+ for (int i = 0; i < fa.numedges; i++) {
+ int lindex = loadmodel.surfedges[fa.firstedge + i];
+
+ if (lindex > 0)
+ vec = loadmodel.vertexes[loadmodel.edges[lindex].v[0]].position;
+ else
+ vec = loadmodel.vertexes[loadmodel.edges[-lindex].v[1]].position;
+ Math3D.vectorCopy(vec, verts[numverts]);
+ numverts++;
+ }
+ SubdividePolygon(numverts, verts);
+ }
+
+ /**
+ * EmitWaterPolys
+ * Does a water warp on the pre-fragmented glpoly_t chain
+ */
+ void EmitWaterPolys(msurface_t fa) {
+ float rdt = r_newrefdef.time;
+
+ float scroll;
+ if ((fa.texinfo.flags & Defines.SURF_FLOWING) != 0)
+ scroll = -64 * ((r_newrefdef.time * 0.5f) - (int) (r_newrefdef.time * 0.5f));
+ else
+ scroll = 0;
+
+ int i;
+ float s, t, os, ot;
+ glpoly_t p, bp;
+ for (bp = fa.polys; bp != null; bp = bp.next) {
+ p = bp;
+
+ GL11.glBegin(GL11.GL_TRIANGLE_FAN);
+ for (i = 0; i < p.numverts; i++) {
+ os = p.s1(i);
+ ot = p.t1(i);
+
+ s = os
+ + Warp.SIN[(int) ((ot * 0.125f + r_newrefdef.time) * TURBSCALE) & 255];
+ s += scroll;
+ s *= (1.0f / 64);
+
+ t = ot
+ + Warp.SIN[(int) ((os * 0.125f + rdt) * TURBSCALE) & 255];
+ t *= (1.0f / 64);
+
+ GL11.glTexCoord2f(s, t);
+ GL11.glVertex3f(p.x(i), p.y(i), p.z(i));
+ }
+ GL11.glEnd();
+ }
+ }
+
+ /**
+ * DrawSkyPolygon
+ *
+ * @param nump
+ * @param vecs
+ */
+ private void DrawSkyPolygon(int nump, float[][] vecs) {
+ c_sky++;
+ // decide which face it maps to
+ Math3D.vectorCopy(Globals.vec3_origin, v);
+ int i, axis;
+ for (i = 0; i < nump; i++) {
+ Math3D.vectorAdd(vecs[i], v, v);
+ }
+ av[0] = Math.abs(v[0]);
+ av[1] = Math.abs(v[1]);
+ av[2] = Math.abs(v[2]);
+ if (av[0] > av[1] && av[0] > av[2]) {
+ if (v[0] < 0)
+ axis = 1;
+ else
+ axis = 0;
+ } else if (av[1] > av[2] && av[1] > av[0]) {
+ if (v[1] < 0)
+ axis = 3;
+ else
+ axis = 2;
+ } else {
+ if (v[2] < 0)
+ axis = 5;
+ else
+ axis = 4;
+ }
+
+ // project new texture coords
+ float s, t, dv;
+ int j;
+ for (i = 0; i < nump; i++) {
+ j = vec_to_st[axis][2];
+ if (j > 0)
+ dv = vecs[i][j - 1];
+ else
+ dv = -vecs[i][-j - 1];
+ if (dv < 0.001f)
+ continue; // don't divide by zero
+ j = vec_to_st[axis][0];
+ if (j < 0)
+ s = -vecs[i][-j - 1] / dv;
+ else
+ s = vecs[i][j - 1] / dv;
+ j = vec_to_st[axis][1];
+ if (j < 0)
+ t = -vecs[i][-j - 1] / dv;
+ else
+ t = vecs[i][j - 1] / dv;
+
+ if (s < skymins[0][axis])
+ skymins[0][axis] = s;
+ if (t < skymins[1][axis])
+ skymins[1][axis] = t;
+ if (s > skymaxs[0][axis])
+ skymaxs[0][axis] = s;
+ if (t > skymaxs[1][axis])
+ skymaxs[1][axis] = t;
+ }
+ }
+
+ /**
+ * ClipSkyPolygon
+ *
+ * @param nump
+ * @param vecs
+ * @param stage
+ */
+ private void ClipSkyPolygon(int nump, float[][] vecs, int stage) {
+ if (nump > MAX_CLIP_VERTS - 2)
+ Com.Error(Defines.ERR_DROP, "ClipSkyPolygon: MAX_CLIP_VERTS");
+ if (stage == 6) { // fully clipped, so draw it
+ DrawSkyPolygon(nump, vecs);
+ return;
+ }
+
+ boolean front = false;
+ boolean back = false;
+ float[] norm = skyclip[stage];
+
+ int i;
+ float d;
+ for (i = 0; i < nump; i++) {
+ d = Math3D.dotProduct(vecs[i], norm);
+ if (d > ON_EPSILON) {
+ front = true;
+ sides[i] = SIDE_FRONT;
+ } else if (d < -ON_EPSILON) {
+ back = true;
+ sides[i] = SIDE_BACK;
+ } else
+ sides[i] = SIDE_ON;
+ dists[i] = d;
+ }
+
+ if (!front || !back) { // not clipped
+ ClipSkyPolygon(nump, vecs, stage + 1);
+ return;
+ }
+
+ // clip it
+ sides[i] = sides[0];
+ dists[i] = dists[0];
+ Math3D.vectorCopy(vecs[0], vecs[i]);
+
+ int newc0 = 0;
+ int newc1 = 0;
+ float[] v;
+ float e;
+ int j;
+ for (i = 0; i < nump; i++) {
+ v = vecs[i];
+ switch (sides[i]) {
+ case SIDE_FRONT:
+ Math3D.vectorCopy(v, newv[stage][0][newc0]);
+ newc0++;
+ break;
+ case SIDE_BACK:
+ Math3D.vectorCopy(v, newv[stage][1][newc1]);
+ newc1++;
+ break;
+ case SIDE_ON:
+ Math3D.vectorCopy(v, newv[stage][0][newc0]);
+ newc0++;
+ Math3D.vectorCopy(v, newv[stage][1][newc1]);
+ newc1++;
+ break;
+ }
+
+ if (sides[i] == SIDE_ON || sides[i + 1] == SIDE_ON || sides[i + 1] == sides[i])
+ continue;
+
+ d = dists[i] / (dists[i] - dists[i + 1]);
+ for (j = 0; j < 3; j++) {
+ e = v[j] + d * (vecs[i + 1][j] - v[j]);
+ newv[stage][0][newc0][j] = e;
+ newv[stage][1][newc1][j] = e;
+ }
+ newc0++;
+ newc1++;
+ }
+
+ // continue
+ ClipSkyPolygon(newc0, newv[stage][0], stage + 1);
+ ClipSkyPolygon(newc1, newv[stage][1], stage + 1);
+ }
+
+ /**
+ * R_AddSkySurface
+ */
+ void R_AddSkySurface(msurface_t fa) {
+ // calculate vertex values for sky box
+ for (glpoly_t p = fa.polys; p != null; p = p.next) {
+ for (int i = 0; i < p.numverts; i++) {
+ verts[i][0] = p.x(i) - r_origin[0];
+ verts[i][1] = p.y(i) - r_origin[1];
+ verts[i][2] = p.z(i) - r_origin[2];
+ }
+ ClipSkyPolygon(p.numverts, verts, 0);
+ }
+ }
+
+ /**
+ * R_ClearSkyBox
+ */
+ void R_ClearSkyBox() {
+ float[] skymins0 = skymins[0];
+ float[] skymins1 = skymins[1];
+ float[] skymaxs0 = skymaxs[0];
+ float[] skymaxs1 = skymaxs[1];
+
+ for (int i = 0; i < 6; i++) {
+ skymins0[i] = skymins1[i] = 9999;
+ skymaxs0[i] = skymaxs1[i] = -9999;
+ }
+ }
+
+ /**
+ * MakeSkyVec
+ *
+ * @param s
+ * @param t
+ * @param axis
+ */
+ private void MakeSkyVec(float s, float t, int axis) {
+ b[0] = s * 2300;
+ b[1] = t * 2300;
+ b[2] = 2300;
+
+ int j, k;
+ for (j = 0; j < 3; j++) {
+ k = st_to_vec[axis][j];
+ if (k < 0)
+ v1[j] = -b[-k - 1];
+ else
+ v1[j] = b[k - 1];
+ }
+
+ // avoid bilerp seam
+ s = (s + 1) * 0.5f;
+ t = (t + 1) * 0.5f;
+
+ if (s < sky_min)
+ s = sky_min;
+ else if (s > sky_max)
+ s = sky_max;
+ if (t < sky_min)
+ t = sky_min;
+ else if (t > sky_max)
+ t = sky_max;
+
+ t = 1.0f - t;
+ GL11.glTexCoord2f(s, t);
+ GL11.glVertex3f(v1[0], v1[1], v1[2]);
+ }
+
+ /**
+ * R_DrawSkyBox
+ */
+ void R_DrawSkyBox() {
+ int i;
+
+ if (skyrotate != 0) { // check for no sky at all
+ for (i = 0; i < 6; i++)
+ if (skymins[0][i] < skymaxs[0][i]
+ && skymins[1][i] < skymaxs[1][i])
+ break;
+ if (i == 6)
+ return; // nothing visible
+ }
+
+ GL11.glPushMatrix();
+ GL11.glTranslatef(r_origin[0], r_origin[1], r_origin[2]);
+ GL11.glRotatef(r_newrefdef.time * skyrotate, skyaxis[0], skyaxis[1], skyaxis[2]);
+
+ for (i = 0; i < 6; i++) {
+ if (skyrotate != 0) { // hack, forces full sky to draw when rotating
+ skymins[0][i] = -1;
+ skymins[1][i] = -1;
+ skymaxs[0][i] = 1;
+ skymaxs[1][i] = 1;
+ }
+
+ if (skymins[0][i] >= skymaxs[0][i]
+ || skymins[1][i] >= skymaxs[1][i])
+ continue;
+
+ GL_Bind(sky_images[skytexorder[i]].texnum);
+
+ GL11.glBegin(GL11.GL_QUADS);
+ MakeSkyVec(skymins[0][i], skymins[1][i], i);
+ MakeSkyVec(skymins[0][i], skymaxs[1][i], i);
+ MakeSkyVec(skymaxs[0][i], skymaxs[1][i], i);
+ MakeSkyVec(skymaxs[0][i], skymins[1][i], i);
+ GL11.glEnd();
+ }
+ GL11.glPopMatrix();
+ }
+
+ /**
+ * R_SetSky
+ *
+ * @param name
+ * @param rotate
+ * @param axis
+ */
+ protected void R_SetSky(String name, float rotate, float[] axis) {
+ assert (axis.length == 3) : "vec3_t bug";
+ String pathname;
+
+ skyrotate = rotate;
+ Math3D.vectorCopy(axis, skyaxis);
+
+ for (int i = 0; i < 6; i++) {
+ // chop down rotating skies for less memory
+ if (gl_skymip.value != 0 || skyrotate != 0)
+ gl_picmip.value++;
+
+ if (qglColorTableEXT && gl_ext_palettedtexture.value != 0) {
+ // Com_sprintf (pathname, sizeof(pathname), "env/%s%s.pcx", skyname, suf[i]);
+ pathname = "env/" + name + suf[i] + ".pcx";
+ } else {
+ // Com_sprintf (pathname, sizeof(pathname), "env/%s%s.tga", skyname, suf[i]);
+ pathname = "env/" + name + suf[i] + ".tga";
+ }
+
+ sky_images[i] = GL_FindImage(pathname, it_sky);
+
+ if (sky_images[i] == null)
+ sky_images[i] = r_notexture;
+
+ if (gl_skymip.value != 0 || skyrotate != 0) { // take less memory
+ gl_picmip.value--;
+ sky_min = 1.0f / 256;
+ sky_max = 255.0f / 256;
+ } else {
+ sky_min = 1.0f / 512;
+ sky_max = 511.0f / 512;
+ }
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.render;
+
+import lwjake2.Defines;
+
+import java.nio.ByteBuffer;
+
+public class medge_t {
+
+ public static final int DISK_SIZE = 2 * Defines.SIZE_OF_SHORT;
+
+ public static final int MEM_SIZE = 3 * Defines.SIZE_OF_INT;
+
+ // unsigned short
+ public final int[] v = new int[2];
+
+ public int cachededgeoffset;
+
+ public medge_t(ByteBuffer b) {
+ v[0] = b.getShort() & 0xFFFF;
+ v[1] = b.getShort() & 0xFFFF;
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.render;
+
+public class mleaf_t extends mnode_t {
+
+ // common with node
+ /*
+ public int contents; // wil be a negative contents number
+ public int visframe; // node needs to be traversed if current
+
+ public float minmaxs[] = new float[6]; // for bounding box culling
+
+ public mnode_t parent;
+ */
+
+ // leaf specific
+ public int cluster;
+ public int area;
+
+ //public msurface_t firstmarksurface;
+ public int nummarksurfaces;
+
+ // added by cwei
+ int markIndex;
+ msurface_t[] markSurfaces;
+
+ public void setMarkSurface(int markIndex, msurface_t[] markSurfaces) {
+ this.markIndex = markIndex;
+ this.markSurfaces = markSurfaces;
+ }
+
+ public msurface_t getMarkSurface(int index) {
+ assert (index >= 0 && index <= nummarksurfaces) : "mleaf: markSurface bug (index = " + index + "; num = " + nummarksurfaces + ")";
+ // TODO code in Surf.R_RecursiveWorldNode aendern (der Pointer wird wie in C zu weit gezaehlt)
+ return (index < nummarksurfaces) ? markSurfaces[markIndex + index] : null;
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.render;
+
+public class mmodel_t {
+ public final float[] mins = {0, 0, 0};
+ public final float[] maxs = {0, 0, 0};
+ public final float[] origin = {0, 0, 0}; // for sounds or lights
+ public float radius;
+ public int headnode;
+ public int visleafs; // not including the solid leaf 0
+ public int firstface, numfaces;
+}
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.render;
+
+import lwjake2.game.cplane_t;
+
+public class mnode_t {
+ //public float minmaxs[] = new float[6]; // for bounding box culling
+ public final float[] mins = new float[3]; // for bounding box culling
+ public final float[] maxs = new float[3]; // for bounding box culling
+ public final mnode_t[] children = new mnode_t[2];
+ // common with leaf
+ public int contents; // -1, to differentiate from leafs
+ public int visframe; // node needs to be traversed if current
+ public mnode_t parent;
+ // node specific
+ public cplane_t plane;
+ // unsigned short
+ public int firstsurface;
+ public int numsurfaces;
+
+}
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.render;
+
+import lwjake2.Defines;
+import lwjake2.game.cplane_t;
+
+import java.nio.ByteBuffer;
+
+public class msurface_t {
+
+ public final short[] texturemins = {0, 0};
+ public final short[] extents = {0, 0};
+ public final byte[] styles = new byte[Defines.MAXLIGHTMAPS];
+ public final float[] cached_light = new float[Defines.MAXLIGHTMAPS];
+ public int visframe; // should be drawn when node is crossed
+ public cplane_t plane;
+ public int flags;
+ public int firstedge; // look up in model->surfedges[], negative numbers
+ public int numedges; // are backwards edges
+ // gl lightmap coordinates for dynamic lightmaps
+ public int light_s, light_t; // gl lightmap coordinates
+ public int dlight_s, dlight_t;
+ public glpoly_t polys; // multiple if warped
+ public msurface_t texturechain;
+ public msurface_t lightmapchain;
+ // TODO check this
+ public mtexinfo_t texinfo = new mtexinfo_t();
+ // lighting info
+ public int dlightframe;
+ public int dlightbits;
+ public int lightmaptexturenum;
+ // values currently used in lightmap
+ //public byte samples[]; // [numstyles*surfsize]
+ public ByteBuffer samples; // [numstyles*surfsize]
+
+ public void clear() {
+ visframe = 0;
+ plane.clear();
+ flags = 0;
+
+ firstedge = 0;
+ numedges = 0;
+
+ texturemins[0] = texturemins[1] = -1;
+ extents[0] = extents[1] = 0;
+
+ light_s = light_t = 0;
+ dlight_s = dlight_t = 0;
+
+ polys = null;
+ texturechain = null;
+ lightmapchain = null;
+
+ //texinfo = new mtexinfo_t();
+ texinfo.clear();
+
+ dlightframe = 0;
+ dlightbits = 0;
+
+ lightmaptexturenum = 0;
+
+ for (int i = 0; i < styles.length; i++) {
+ styles[i] = 0;
+ }
+ for (int i = 0; i < cached_light.length; i++) {
+ cached_light[i] = 0;
+ }
+ if (samples != null) samples.clear();
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.render;
+
+import java.util.Arrays;
+
+public class mtexinfo_t {
+ // [s/t][xyz offset]
+ public float vecs[][] = {
+ {0, 0, 0, 0},
+ {0, 0, 0, 0}
+ };
+ public int flags;
+ public int numframes;
+ public mtexinfo_t next; // animation chain
+ public Image image;
+
+ public void clear() {
+ Arrays.fill(vecs[0], 0);
+ Arrays.fill(vecs[1], 0);
+
+ flags = 0;
+ numframes = 0;
+ next = null;
+ image = null;
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.render;
+
+import lwjake2.Defines;
+
+import java.nio.ByteBuffer;
+
+public class mvertex_t {
+ public static final int DISK_SIZE = 3 * Defines.SIZE_OF_FLOAT;
+
+ public static final int MEM_SIZE = 3 * Defines.SIZE_OF_FLOAT;
+
+ public final float[] position = {0, 0, 0};
+
+ public mvertex_t(ByteBuffer b) {
+ position[0] = b.getFloat();
+ position[1] = b.getFloat();
+ position[2] = b.getFloat();
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.server;
+
+import lwjake2.Defines;
+import lwjake2.Globals;
+import lwjake2.client.M;
+import lwjake2.game.EDict;
+import lwjake2.game.GameBase;
+import lwjake2.game.pushed_t;
+import lwjake2.game.trace_t;
+import lwjake2.qcommon.Com;
+import lwjake2.util.Math3D;
+
+/**
+ * SV
+ */
+public final class SV {
+
+ /**
+ * SV_FlyMove
+ * <p>
+ * The basic solid body movement clip that slides along multiple planes
+ * Returns the clipflags if the velocity was modified (hit something solid)
+ * 1 = floor 2 = wall / step 4 = dead stop
+ */
+ public final static int MAX_CLIP_PLANES = 5;
+
+ ///////////////////////////////////////
+ private static EDict[] SV_TestEntityPosition(EDict ent) {
+ trace_t trace;
+ int mask;
+
+ if (ent.clipmask != 0)
+ mask = ent.clipmask;
+ else
+ mask = Defines.MASK_SOLID;
+
+ trace = GameBase.gi.trace(ent.s.origin, ent.mins, ent.maxs,
+ ent.s.origin, ent, mask);
+
+ if (trace.startsolid)
+ return GameBase.g_edicts;
+
+ return null;
+ }
+
+ ///////////////////////////////////////
+ private static void SV_CheckVelocity(EDict ent) {
+ int i;
+
+ //
+ // bound velocity
+ //
+ for (i = 0; i < 3; i++) {
+ if (ent.velocity[i] > GameBase.sv_maxvelocity.value)
+ ent.velocity[i] = GameBase.sv_maxvelocity.value;
+ else if (ent.velocity[i] < -GameBase.sv_maxvelocity.value)
+ ent.velocity[i] = -GameBase.sv_maxvelocity.value;
+ }
+ }
+
+ /**
+ * Runs thinking code for this frame if necessary.
+ */
+ private static boolean SV_RunThink(EDict ent) {
+ float thinktime;
+
+ thinktime = ent.nextthink;
+ if (thinktime <= 0)
+ return true;
+ if (thinktime > GameBase.level.time + 0.001)
+ return true;
+
+ ent.nextthink = 0;
+
+ if (ent.think == null)
+ Com.Error(Defines.ERR_FATAL, "NULL ent.think");
+
+ ent.think.think(ent);
+
+ return false;
+ }
+
+ /**
+ * Two entities have touched, so run their touch functions.
+ */
+ private static void SV_Impact(EDict e1, trace_t trace) {
+ EDict e2;
+
+ e2 = trace.ent;
+
+ if (e1.touch != null && e1.solid != Defines.SOLID_NOT)
+ e1.touch.touch(e1, e2, trace.plane, trace.surface);
+
+ if (e2.touch != null && e2.solid != Defines.SOLID_NOT)
+ e2.touch.touch(e2, e1, GameBase.dummyplane, null);
+ }
+
+ private static void SV_FlyMove(EDict ent, int mask) {
+ EDict hit;
+ int bumpcount, numbumps;
+ float[] dir = {0.0f, 0.0f, 0.0f};
+ float d;
+ int numplanes;
+ float[][] planes = new float[MAX_CLIP_PLANES][3];
+ float[] primal_velocity = {0.0f, 0.0f, 0.0f};
+ float[] original_velocity = {0.0f, 0.0f, 0.0f};
+ float[] new_velocity = {0.0f, 0.0f, 0.0f};
+ int i, j;
+ trace_t trace;
+ float[] end = {0.0f, 0.0f, 0.0f};
+ float time_left;
+ int blocked;
+
+ numbumps = 4;
+
+ blocked = 0;
+ Math3D.vectorCopy(ent.velocity, original_velocity);
+ Math3D.vectorCopy(ent.velocity, primal_velocity);
+ numplanes = 0;
+
+ time_left = Defines.FRAMETIME;
+
+ ent.groundentity = null;
+ for (bumpcount = 0; bumpcount < numbumps; bumpcount++) {
+ for (i = 0; i < 3; i++)
+ end[i] = ent.s.origin[i] + time_left * ent.velocity[i];
+
+ trace = GameBase.gi.trace(ent.s.origin, ent.mins, ent.maxs, end,
+ ent, mask);
+
+ if (trace.allsolid) { // entity is trapped in another solid
+ Math3D.vectorCopy(Globals.vec3_origin, ent.velocity);
+ return;
+ }
+
+ if (trace.fraction > 0) { // actually covered some distance
+ Math3D.vectorCopy(trace.endpos, ent.s.origin);
+ Math3D.vectorCopy(ent.velocity, original_velocity);
+ numplanes = 0;
+ }
+
+ if (trace.fraction == 1)
+ break; // moved the entire distance
+
+ hit = trace.ent;
+
+ if (trace.plane.normal[2] > 0.7) {
+ blocked |= 1; // floor
+ if (hit.solid == Defines.SOLID_BSP) {
+ ent.groundentity = hit;
+ ent.groundentity_linkcount = hit.linkcount;
+ }
+ }
+ if (trace.plane.normal[2] == 0.0f) {
+ blocked |= 2; // step
+ }
+
+ //
+ // run the impact function
+ //
+ SV_Impact(ent, trace);
+ if (!ent.inuse)
+ break; // removed by the impact function
+
+ time_left -= time_left * trace.fraction;
+
+ // cliped to another plane
+ if (numplanes >= MAX_CLIP_PLANES) { // this shouldn't
+ // really happen
+ Math3D.vectorCopy(Globals.vec3_origin, ent.velocity);
+ return;
+ }
+
+ Math3D.vectorCopy(trace.plane.normal, planes[numplanes]);
+ numplanes++;
+
+ //
+ // modify original_velocity so it parallels all of the clip planes
+ //
+ for (i = 0; i < numplanes; i++) {
+ GameBase.ClipVelocity(original_velocity, planes[i],
+ new_velocity, 1);
+
+ for (j = 0; j < numplanes; j++)
+ if ((j != i)
+ && !Math3D.vectorEquals(planes[i], planes[j])) {
+ if (Math3D.dotProduct(new_velocity, planes[j]) < 0)
+ break; // not ok
+ }
+ if (j == numplanes)
+ break;
+ }
+
+ if (i != numplanes) { // go along this plane
+ Math3D.vectorCopy(new_velocity, ent.velocity);
+ } else { // go along the crease
+ if (numplanes != 2) {
+ // gi.dprintf ("clip velocity, numplanes ==
+ // %i\n",numplanes);
+ Math3D.vectorCopy(Globals.vec3_origin, ent.velocity);
+ return;
+ }
+ Math3D.crossProduct(planes[0], planes[1], dir);
+ d = Math3D.dotProduct(dir, ent.velocity);
+ Math3D.vectorScale(dir, d, ent.velocity);
+ }
+
+ //
+ // if original velocity is against the original velocity, stop dead
+ // to avoid tiny occilations in sloping corners
+ //
+ if (Math3D.dotProduct(ent.velocity, primal_velocity) <= 0) {
+ Math3D.vectorCopy(Globals.vec3_origin, ent.velocity);
+ return;
+ }
+ }
+
+ }
+
+ /**
+ * SV_AddGravity.
+ */
+ private static void SV_AddGravity(EDict ent) {
+ ent.velocity[2] -= ent.gravity * GameBase.sv_gravity.value
+ * Defines.FRAMETIME;
+ }
+
+ /**
+ * Does not change the entities velocity at all
+ */
+ private static trace_t SV_PushEntity(EDict ent, float[] push) {
+ trace_t trace;
+ float[] start = {0, 0, 0};
+ float[] end = {0, 0, 0};
+ int mask;
+
+ Math3D.vectorCopy(ent.s.origin, start);
+ Math3D.vectorAdd(start, push, end);
+
+ // FIXME: test this
+ // a goto statement was replaced.
+ boolean retry = false;
+
+ do {
+ if (ent.clipmask != 0)
+ mask = ent.clipmask;
+ else
+ mask = Defines.MASK_SOLID;
+
+ trace = GameBase.gi
+ .trace(start, ent.mins, ent.maxs, end, ent, mask);
+
+ Math3D.vectorCopy(trace.endpos, ent.s.origin);
+ GameBase.gi.linkentity(ent);
+
+ retry = false;
+ if (trace.fraction != 1.0) {
+ SV_Impact(ent, trace);
+
+ // if the pushed entity went away and the pusher is still there
+ if (!trace.ent.inuse && ent.inuse) {
+ // move the pusher back and try again
+ Math3D.vectorCopy(start, ent.s.origin);
+ GameBase.gi.linkentity(ent);
+ //goto retry;
+ retry = true;
+ }
+ }
+ } while (retry);
+
+ if (ent.inuse)
+ GameBase.G_TouchTriggers(ent);
+
+ return trace;
+ }
+
+ /**
+ * Objects need to be moved back on a failed push, otherwise riders would
+ * continue to slide.
+ */
+ private static boolean SV_Push(EDict pusher, float[] move, float[] amove) {
+ int i, e;
+ EDict check, block[];
+ float[] mins = {0, 0, 0};
+ float[] maxs = {0, 0, 0};
+ pushed_t p;
+ float[] org = {0, 0, 0};
+ float[] org2 = {0, 0, 0};
+ float[] move2 = {0, 0, 0};
+ float[] forward = {0, 0, 0};
+ float[] right = {0, 0, 0};
+ float[] up = {0, 0, 0};
+
+ // clamp the move to 1/8 units, so the position will
+ // be accurate for client side prediction
+ for (i = 0; i < 3; i++) {
+ float temp;
+ temp = move[i] * 8.0f;
+ if (temp > 0.0)
+ temp += 0.5;
+ else
+ temp -= 0.5;
+ move[i] = 0.125f * (int) temp;
+ }
+
+ // find the bounding box
+ for (i = 0; i < 3; i++) {
+ mins[i] = pusher.absmin[i] + move[i];
+ maxs[i] = pusher.absmax[i] + move[i];
+ }
+
+ // we need this for pushing things later
+ Math3D.vectorSubtract(Globals.vec3_origin, amove, org);
+ Math3D.angleVectors(org, forward, right, up);
+
+ // save the pusher's original position
+ GameBase.pushed[GameBase.pushed_p].ent = pusher;
+ Math3D.vectorCopy(pusher.s.origin,
+ GameBase.pushed[GameBase.pushed_p].origin);
+ Math3D.vectorCopy(pusher.s.angles,
+ GameBase.pushed[GameBase.pushed_p].angles);
+
+ if (pusher.client != null)
+ GameBase.pushed[GameBase.pushed_p].deltayaw = pusher.client.ps.pmove.delta_angles[Defines.YAW];
+
+ GameBase.pushed_p++;
+
+ // move the pusher to it's final position
+ Math3D.vectorAdd(pusher.s.origin, move, pusher.s.origin);
+ Math3D.vectorAdd(pusher.s.angles, amove, pusher.s.angles);
+ GameBase.gi.linkentity(pusher);
+
+ // see if any solid entities are inside the final position
+
+ //check= g_edicts + 1;
+ for (e = 1; e < GameBase.num_edicts; e++) {
+ check = GameBase.g_edicts[e];
+ if (!check.inuse)
+ continue;
+ if (check.movetype == Defines.MOVETYPE_PUSH
+ || check.movetype == Defines.MOVETYPE_STOP
+ || check.movetype == Defines.MOVETYPE_NONE
+ || check.movetype == Defines.MOVETYPE_NOCLIP)
+ continue;
+
+ if (check.area.prev == null)
+ continue; // not linked in anywhere
+
+ // if the entity is standing on the pusher, it will definitely be
+ // moved
+ if (check.groundentity != pusher) {
+ // see if the ent needs to be tested
+ if (check.absmin[0] >= maxs[0] || check.absmin[1] >= maxs[1]
+ || check.absmin[2] >= maxs[2]
+ || check.absmax[0] <= mins[0]
+ || check.absmax[1] <= mins[1]
+ || check.absmax[2] <= mins[2])
+ continue;
+
+ // see if the ent's bbox is inside the pusher's final position
+ if (SV_TestEntityPosition(check) == null)
+ continue;
+ }
+
+ if ((pusher.movetype == Defines.MOVETYPE_PUSH)
+ || (check.groundentity == pusher)) {
+ // move this entity
+ GameBase.pushed[GameBase.pushed_p].ent = check;
+ Math3D.vectorCopy(check.s.origin,
+ GameBase.pushed[GameBase.pushed_p].origin);
+ Math3D.vectorCopy(check.s.angles,
+ GameBase.pushed[GameBase.pushed_p].angles);
+ GameBase.pushed_p++;
+
+ // try moving the contacted entity
+ Math3D.vectorAdd(check.s.origin, move, check.s.origin);
+ if (check.client != null) { // FIXME: doesn't rotate monsters?
+ check.client.ps.pmove.delta_angles[Defines.YAW] += amove[Defines.YAW];
+ }
+
+ // figure movement due to the pusher's amove
+ Math3D.vectorSubtract(check.s.origin, pusher.s.origin, org);
+ org2[0] = Math3D.dotProduct(org, forward);
+ org2[1] = -Math3D.dotProduct(org, right);
+ org2[2] = Math3D.dotProduct(org, up);
+ Math3D.vectorSubtract(org2, org, move2);
+ Math3D.vectorAdd(check.s.origin, move2, check.s.origin);
+
+ // may have pushed them off an edge
+ if (check.groundentity != pusher)
+ check.groundentity = null;
+
+ block = SV_TestEntityPosition(check);
+ if (block == null) { // pushed ok
+ GameBase.gi.linkentity(check);
+ // impact?
+ continue;
+ }
+
+ // if it is ok to leave in the old position, do it
+ // this is only relevent for riding entities, not pushed
+ // FIXME: this doesn't acount for rotation
+ Math3D.vectorSubtract(check.s.origin, move, check.s.origin);
+ block = SV_TestEntityPosition(check);
+
+ if (block == null) {
+ GameBase.pushed_p--;
+ continue;
+ }
+ }
+
+ // save off the obstacle so we can call the block function
+ GameBase.obstacle = check;
+
+ // move back any entities we already moved
+ // go backwards, so if the same entity was pushed
+ // twice, it goes back to the original position
+ for (int ip = GameBase.pushed_p - 1; ip >= 0; ip--) {
+ p = GameBase.pushed[ip];
+ Math3D.vectorCopy(p.origin, p.ent.s.origin);
+ Math3D.vectorCopy(p.angles, p.ent.s.angles);
+ if (p.ent.client != null) {
+ p.ent.client.ps.pmove.delta_angles[Defines.YAW] = (short) p.deltayaw;
+ }
+ GameBase.gi.linkentity(p.ent);
+ }
+ return false;
+ }
+
+ // FIXME: is there a better way to handle this?
+ // see if anything we moved has touched a trigger
+ for (int ip = GameBase.pushed_p - 1; ip >= 0; ip--)
+ GameBase.G_TouchTriggers(GameBase.pushed[ip].ent);
+
+ return true;
+ }
+
+ /**
+ * Bmodel objects don't interact with each other, but push all box objects.
+ */
+ public static void SV_Physics_Pusher(EDict ent) {
+ float[] move = {0, 0, 0};
+ float[] amove = {0, 0, 0};
+ EDict part, mv;
+
+ // if not a team captain, so movement will be handled elsewhere
+ if ((ent.flags & Defines.FL_TEAMSLAVE) != 0)
+ return;
+
+ // make sure all team slaves can move before commiting
+ // any moves or calling any think functions
+ // if the move is blocked, all moved objects will be backed out
+ // retry:
+ GameBase.pushed_p = 0;
+ for (part = ent; part != null; part = part.teamchain) {
+ if (part.velocity[0] != 0 || part.velocity[1] != 0
+ || part.velocity[2] != 0 || part.avelocity[0] != 0
+ || part.avelocity[1] != 0 || part.avelocity[2] != 0) { // object
+ // is
+ // moving
+ Math3D.vectorScale(part.velocity, Defines.FRAMETIME, move);
+ Math3D.vectorScale(part.avelocity, Defines.FRAMETIME, amove);
+
+ if (!SV_Push(part, move, amove))
+ break; // move was blocked
+ }
+ }
+ if (GameBase.pushed_p > Defines.MAX_EDICTS)
+ SV_GAME.PF_error(Defines.ERR_FATAL,
+ "pushed_p > &pushed[MAX_EDICTS], memory corrupted");
+
+ if (part != null) {
+ // the move failed, bump all nextthink times and back out moves
+ for (mv = ent; mv != null; mv = mv.teamchain) {
+ if (mv.nextthink > 0)
+ mv.nextthink += Defines.FRAMETIME;
+ }
+
+ // if the pusher has a "blocked" function, call it
+ // otherwise, just stay in place until the obstacle is gone
+ if (part.blocked != null)
+ part.blocked.blocked(part, GameBase.obstacle);
+ } else { // the move succeeded, so call all think functions
+ for (part = ent; part != null; part = part.teamchain) {
+ SV_RunThink(part);
+ }
+ }
+ }
+
+ /**
+ * Non moving objects can only think.
+ */
+ public static void SV_Physics_None(EDict ent) {
+ // regular thinking
+ SV_RunThink(ent);
+ }
+
+ /**
+ * A moving object that doesn't obey physics.
+ */
+ public static void SV_Physics_Noclip(EDict ent) {
+ // regular thinking
+ if (!SV_RunThink(ent))
+ return;
+
+ Math3D.vectorMA(ent.s.angles, Defines.FRAMETIME, ent.avelocity,
+ ent.s.angles);
+ Math3D.vectorMA(ent.s.origin, Defines.FRAMETIME, ent.velocity,
+ ent.s.origin);
+
+ GameBase.gi.linkentity(ent);
+ }
+
+ /**
+ * Toss, bounce, and fly movement. When onground, do nothing.
+ */
+ public static void SV_Physics_Toss(EDict ent) {
+
+ trace_t trace;
+ float[] move = {0, 0, 0};
+ float backoff;
+ EDict slave;
+ boolean wasinwater;
+ boolean isinwater;
+ float[] old_origin = {0, 0, 0};
+
+ // regular thinking
+ SV_RunThink(ent);
+
+ // if not a team captain, so movement will be handled elsewhere
+ if ((ent.flags & Defines.FL_TEAMSLAVE) != 0)
+ return;
+
+ if (ent.velocity[2] > 0)
+ ent.groundentity = null;
+
+ // check for the groundentity going away
+ if (ent.groundentity != null)
+ if (!ent.groundentity.inuse)
+ ent.groundentity = null;
+
+ // if onground, return without moving
+ if (ent.groundentity != null)
+ return;
+
+ Math3D.vectorCopy(ent.s.origin, old_origin);
+
+ SV_CheckVelocity(ent);
+
+ // add gravity
+ if (ent.movetype != Defines.MOVETYPE_FLY
+ && ent.movetype != Defines.MOVETYPE_FLYMISSILE)
+ SV_AddGravity(ent);
+
+ // move angles
+ Math3D.vectorMA(ent.s.angles, Defines.FRAMETIME, ent.avelocity,
+ ent.s.angles);
+
+ // move origin
+ Math3D.vectorScale(ent.velocity, Defines.FRAMETIME, move);
+ trace = SV_PushEntity(ent, move);
+ if (!ent.inuse)
+ return;
+
+ if (trace.fraction < 1) {
+ if (ent.movetype == Defines.MOVETYPE_BOUNCE)
+ backoff = 1.5f;
+ else
+ backoff = 1;
+
+ GameBase.ClipVelocity(ent.velocity, trace.plane.normal,
+ ent.velocity, backoff);
+
+ // stop if on ground
+ if (trace.plane.normal[2] > 0.7) {
+ if (ent.velocity[2] < 60
+ || ent.movetype != Defines.MOVETYPE_BOUNCE) {
+ ent.groundentity = trace.ent;
+ ent.groundentity_linkcount = trace.ent.linkcount;
+ Math3D.vectorCopy(Globals.vec3_origin, ent.velocity);
+ Math3D.vectorCopy(Globals.vec3_origin, ent.avelocity);
+ }
+ }
+
+ // if (ent.touch)
+ // ent.touch (ent, trace.ent, &trace.plane, trace.surface);
+ }
+
+ // check for water transition
+ wasinwater = (ent.watertype & Defines.MASK_WATER) != 0;
+ ent.watertype = GameBase.gi.pointcontents.pointcontents(ent.s.origin);
+ isinwater = (ent.watertype & Defines.MASK_WATER) != 0;
+
+ if (isinwater)
+ ent.waterlevel = 1;
+ else
+ ent.waterlevel = 0;
+
+ if (!wasinwater && isinwater)
+ GameBase.gi.positioned_sound(old_origin, ent, Defines.CHAN_AUTO,
+ GameBase.gi.soundindex("misc/h2ohit1.wav"), 1, 1, 0);
+ else if (wasinwater && !isinwater)
+ GameBase.gi.positioned_sound(ent.s.origin, ent, Defines.CHAN_AUTO,
+ GameBase.gi.soundindex("misc/h2ohit1.wav"), 1, 1, 0);
+
+ // move teamslaves
+ for (slave = ent.teamchain; slave != null; slave = slave.teamchain) {
+ Math3D.vectorCopy(ent.s.origin, slave.s.origin);
+ GameBase.gi.linkentity(slave);
+ }
+ }
+
+ // FIXME: hacked in for E3 demo
+ private static void SV_AddRotationalFriction(EDict ent) {
+ int n;
+ float adjustment;
+
+ Math3D.vectorMA(ent.s.angles, Defines.FRAMETIME, ent.avelocity,
+ ent.s.angles);
+ adjustment = Defines.FRAMETIME * Defines.sv_stopspeed
+ * Defines.sv_friction;
+ for (n = 0; n < 3; n++) {
+ if (ent.avelocity[n] > 0) {
+ ent.avelocity[n] -= adjustment;
+ if (ent.avelocity[n] < 0)
+ ent.avelocity[n] = 0;
+ } else {
+ ent.avelocity[n] += adjustment;
+ if (ent.avelocity[n] > 0)
+ ent.avelocity[n] = 0;
+ }
+ }
+ }
+
+ /**
+ * Monsters freefall when they don't have a ground entity, otherwise all
+ * movement is done with discrete steps.
+ * <p>
+ * This is also used for objects that have become still on the ground, but
+ * will fall if the floor is pulled out from under them. FIXME: is this
+ * true?
+ */
+
+ public static void SV_Physics_Step(EDict ent) {
+ boolean wasonground;
+ boolean hitsound = false;
+ float vel[];
+ float speed, newspeed, control;
+ float friction;
+ EDict groundentity;
+ int mask;
+
+ // airborn monsters should always check for ground
+ if (ent.groundentity == null)
+ M.M_CheckGround(ent);
+
+ groundentity = ent.groundentity;
+
+ SV_CheckVelocity(ent);
+
+ wasonground = groundentity != null;
+
+ if (ent.avelocity[0] != 0 || ent.avelocity[1] != 0
+ || ent.avelocity[2] != 0)
+ SV_AddRotationalFriction(ent);
+
+ // add gravity except:
+ // flying monsters
+ // swimming monsters who are in the water
+ if (!wasonground)
+ if (0 == (ent.flags & Defines.FL_FLY))
+ if (!((ent.flags & Defines.FL_SWIM) != 0 && (ent.waterlevel > 2))) {
+ if (ent.velocity[2] < GameBase.sv_gravity.value * -0.1)
+ hitsound = true;
+ if (ent.waterlevel == 0)
+ SV_AddGravity(ent);
+ }
+
+ // friction for flying monsters that have been given vertical velocity
+ if ((ent.flags & Defines.FL_FLY) != 0 && (ent.velocity[2] != 0)) {
+ speed = Math.abs(ent.velocity[2]);
+ control = speed < Defines.sv_stopspeed ? Defines.sv_stopspeed
+ : speed;
+ friction = Defines.sv_friction / 3;
+ newspeed = speed - (Defines.FRAMETIME * control * friction);
+ if (newspeed < 0)
+ newspeed = 0;
+ newspeed /= speed;
+ ent.velocity[2] *= newspeed;
+ }
+
+ // friction for flying monsters that have been given vertical velocity
+ if ((ent.flags & Defines.FL_SWIM) != 0 && (ent.velocity[2] != 0)) {
+ speed = Math.abs(ent.velocity[2]);
+ control = speed < Defines.sv_stopspeed ? Defines.sv_stopspeed
+ : speed;
+ newspeed = speed
+ - (Defines.FRAMETIME * control * Defines.sv_waterfriction * ent.waterlevel);
+ if (newspeed < 0)
+ newspeed = 0;
+ newspeed /= speed;
+ ent.velocity[2] *= newspeed;
+ }
+
+ if (ent.velocity[2] != 0 || ent.velocity[1] != 0
+ || ent.velocity[0] != 0) {
+ // apply friction
+ // let dead monsters who aren't completely onground slide
+ if ((wasonground)
+ || 0 != (ent.flags & (Defines.FL_SWIM | Defines.FL_FLY)))
+ if (!(ent.health <= 0.0 && !M.M_CheckBottom(ent))) {
+ vel = ent.velocity;
+ speed = (float) Math
+ .sqrt(vel[0] * vel[0] + vel[1] * vel[1]);
+ if (speed != 0) {
+ friction = Defines.sv_friction;
+
+ control = speed < Defines.sv_stopspeed ? Defines.sv_stopspeed
+ : speed;
+ newspeed = speed - Defines.FRAMETIME * control
+ * friction;
+
+ if (newspeed < 0)
+ newspeed = 0;
+ newspeed /= speed;
+
+ vel[0] *= newspeed;
+ vel[1] *= newspeed;
+ }
+ }
+
+ if ((ent.svflags & Defines.SVF_MONSTER) != 0)
+ mask = Defines.MASK_MONSTERSOLID;
+ else
+ mask = Defines.MASK_SOLID;
+
+ SV_FlyMove(ent, mask);
+
+ GameBase.gi.linkentity(ent);
+ GameBase.G_TouchTriggers(ent);
+ if (!ent.inuse)
+ return;
+
+ if (ent.groundentity != null)
+ if (!wasonground)
+ if (hitsound)
+ GameBase.gi.sound(ent, 0,
+ GameBase.gi.soundindex("world/land.wav"), 1, 1, 0);
+ }
+
+ // regular thinking
+ SV_RunThink(ent);
+ }
+
+ /**
+ * Called by monster program code. The move will be adjusted for slopes and
+ * stairs, but if the move isn't possible, no move is done, false is
+ * returned, and pr_global_struct.trace_normal is set to the normal of the
+ * blocking wall.
+ */
+
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.server;
+
+import lwjake2.Defines;
+import lwjake2.Globals;
+import lwjake2.game.*;
+import lwjake2.qcommon.*;
+import lwjake2.sys.NET;
+import lwjake2.sys.Sys;
+import lwjake2.util.Lib;
+import lwjake2.util.QuakeFile;
+import lwjake2.util.Vargs;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.util.Calendar;
+
+class SV_CCMDS {
+
+ /*
+ ===============================================================================
+
+ OPERATOR CONSOLE ONLY COMMANDS
+
+ These commands can only be entered from stdin or by a remote operator datagram
+ ===============================================================================
+ */
+
+ /*
+ ====================
+ SV_SetMaster_f
+
+ Specify a list of master servers
+ ====================
+ */
+ private static void SV_SetMaster_f() {
+ int i, slot;
+
+ // only dedicated servers send heartbeats
+ if (Globals.dedicated.value == 0) {
+ Com.Printf("Only dedicated servers use masters.\n");
+ return;
+ }
+
+ // make sure the server is listed public
+ Cvar.set("public", "1");
+
+ for (i = 1; i < Defines.MAX_MASTERS; i++)
+ Server.master_adr[i] = new NetadrT();
+
+ slot = 1; // slot 0 will always contain the id master
+ for (i = 1; i < Cmd.Argc(); i++) {
+ if (slot == Defines.MAX_MASTERS)
+ break;
+
+ if (!NET.StringToAdr(Cmd.Argv(i), Server.master_adr[i])) {
+ Com.Printf("Bad address: " + Cmd.Argv(i) + "\n");
+ continue;
+ }
+ if (Server.master_adr[slot].port == 0)
+ Server.master_adr[slot].port = Defines.PORT_MASTER;
+
+ Com.Printf("Master server at " + NET.AdrToString(Server.master_adr[slot]) + "\n");
+ Com.Printf("Sending a ping.\n");
+
+ Netchan.OutOfBandPrint(Defines.NS_SERVER, Server.master_adr[slot], "ping");
+
+ slot++;
+ }
+
+ SV_INIT.svs.last_heartbeat = -9999999;
+ }
+
+ /*
+ ==================
+ SV_SetPlayer
+
+ Sets sv_client and sv_player to the player with idnum Cmd.Argv(1)
+ ==================
+ */
+ private static boolean SV_SetPlayer() {
+ client_t cl;
+ int i;
+ int idnum;
+ String s;
+
+ if (Cmd.Argc() < 2)
+ return false;
+
+ s = Cmd.Argv(1);
+
+ // numeric values are just slot numbers
+ if (s.charAt(0) >= '0' && s.charAt(0) <= '9') {
+ idnum = Lib.atoi(Cmd.Argv(1));
+ if (idnum < 0 || idnum >= Server.maxclients.value) {
+ Com.Printf("Bad client slot: " + idnum + "\n");
+ return false;
+ }
+
+ Server.sv_client = SV_INIT.svs.clients[idnum];
+ SV_USER.sv_player = Server.sv_client.edict;
+ if (0 == Server.sv_client.state) {
+ Com.Printf("Client " + idnum + " is not active\n");
+ return false;
+ }
+ return true;
+ }
+
+ // check for a name match
+ for (i = 0; i < Server.maxclients.value; i++) {
+ cl = SV_INIT.svs.clients[i];
+ if (0 == cl.state)
+ continue;
+ if (0 == Lib.strcmp(cl.name, s)) {
+ Server.sv_client = cl;
+ SV_USER.sv_player = Server.sv_client.edict;
+ return true;
+ }
+ }
+
+ Com.Printf("Userid " + s + " is not on the server\n");
+ return false;
+ }
+ /*
+ ===============================================================================
+
+ SAVEGAME FILES
+
+ ===============================================================================
+ */
+
+ private static void remove(String name) {
+ try {
+ new File(name).delete();
+ } catch (Exception ignored) {
+ }
+ }
+
+ /**
+ * Delete save files save/(number)/.
+ */
+ private static void SV_WipeSavegame(String savename) {
+
+ String name;
+
+ Com.DPrintf("SV_WipeSaveGame(" + savename + ")\n");
+
+ name = FS.Gamedir() + "/save/" + savename + "/server.ssv";
+ remove(name);
+
+ name = FS.Gamedir() + "/save/" + savename + "/game.ssv";
+ remove(name);
+
+ name = FS.Gamedir() + "/save/" + savename + "/*.sav";
+
+ File f = Sys.FindFirst(name, 0, 0);
+ while (f != null) {
+ f.delete();
+ f = Sys.FindNext();
+ }
+ Sys.FindClose();
+
+ name = FS.Gamedir() + "/save/" + savename + "/*.sv2";
+
+ f = Sys.FindFirst(name, 0, 0);
+
+ while (f != null) {
+ f.delete();
+ f = Sys.FindNext();
+ }
+ Sys.FindClose();
+ }
+
+ /*
+ ================
+ CopyFile
+ ================
+ */
+ private static void CopyFile(String src, String dst) {
+ RandomAccessFile f1, f2;
+ int l = -1;
+ byte buffer[] = new byte[65536];
+
+ //Com.DPrintf("CopyFile (" + src + ", " + dst + ")\n");
+ try {
+ f1 = new RandomAccessFile(src, "r");
+ } catch (Exception e) {
+ return;
+ }
+ try {
+ f2 = new RandomAccessFile(dst, "rw");
+ } catch (Exception e) {
+ try {
+ f1.close();
+ } catch (IOException e1) {
+ e1.printStackTrace();
+ }
+ return;
+ }
+
+ while (true) {
+
+ try {
+ l = f1.read(buffer, 0, 65536);
+ } catch (IOException e1) {
+
+ e1.printStackTrace();
+ }
+ if (l == -1)
+ break;
+ try {
+ f2.write(buffer, 0, l);
+ } catch (IOException e2) {
+
+ e2.printStackTrace();
+ }
+ }
+
+ try {
+ f1.close();
+ } catch (IOException e1) {
+
+ e1.printStackTrace();
+ }
+ try {
+ f2.close();
+ } catch (IOException e2) {
+
+ e2.printStackTrace();
+ }
+ }
+
+ /*
+ ================
+ SV_CopySaveGame
+ ================
+ */
+ private static void SV_CopySaveGame(String src, String dst) {
+ File found;
+
+ String name, name2;
+
+ Com.DPrintf("SV_CopySaveGame(" + src + "," + dst + ")\n");
+
+ SV_WipeSavegame(dst);
+
+ // copy the savegame over
+ name = FS.Gamedir() + "/save/" + src + "/server.ssv";
+ name2 = FS.Gamedir() + "/save/" + dst + "/server.ssv";
+ FS.CreatePath(name2);
+ CopyFile(name, name2);
+
+ name = FS.Gamedir() + "/save/" + src + "/game.ssv";
+ name2 = FS.Gamedir() + "/save/" + dst + "/game.ssv";
+ CopyFile(name, name2);
+
+ String name1 = FS.Gamedir() + "/save/" + src + "/";
+ name = FS.Gamedir() + "/save/" + src + "/*.sav";
+
+ found = Sys.FindFirst(name, 0, 0);
+
+ while (found != null) {
+ name = name1 + found.getName();
+ name2 = FS.Gamedir() + "/save/" + dst + "/" + found.getName();
+
+ CopyFile(name, name2);
+
+ // change sav to sv2
+ name = name.substring(0, name.length() - 3) + "sv2";
+ name2 = name2.substring(0, name2.length() - 3) + "sv2";
+
+ CopyFile(name, name2);
+
+ found = Sys.FindNext();
+ }
+ Sys.FindClose();
+ }
+
+ /*
+ ==============
+ SV_WriteLevelFile
+
+ ==============
+ */
+ private static void SV_WriteLevelFile() {
+
+ String name;
+ QuakeFile f;
+
+ Com.DPrintf("SV_WriteLevelFile()\n");
+
+ name = FS.Gamedir() + "/save/current/" + SV_INIT.sv.name + ".sv2";
+
+ try {
+ f = new QuakeFile(name, "rw");
+
+ for (int i = 0; i < Defines.MAX_CONFIGSTRINGS; i++)
+ f.writeString(SV_INIT.sv.configstrings[i]);
+
+ CM.CM_WritePortalState(f);
+ f.close();
+ } catch (Exception e) {
+ Com.Printf("Failed to open " + name + "\n");
+ e.printStackTrace();
+ }
+
+ name = FS.Gamedir() + "/save/current/" + SV_INIT.sv.name + ".sav";
+ GameSave.WriteLevel(name);
+ }
+
+ /*
+ ==============
+ SV_ReadLevelFile
+
+ ==============
+ */
+ public static void SV_ReadLevelFile() {
+ //char name[MAX_OSPATH];
+ String name;
+ QuakeFile f;
+
+ Com.DPrintf("SV_ReadLevelFile()\n");
+
+ name = FS.Gamedir() + "/save/current/" + SV_INIT.sv.name + ".sv2";
+ try {
+ f = new QuakeFile(name, "r");
+
+ for (int n = 0; n < Defines.MAX_CONFIGSTRINGS; n++)
+ SV_INIT.sv.configstrings[n] = f.readString();
+
+ CM.CM_ReadPortalState(f);
+
+ f.close();
+ } catch (IOException e1) {
+ Com.Printf("Failed to open " + name + "\n");
+ e1.printStackTrace();
+ }
+
+ name = FS.Gamedir() + "/save/current/" + SV_INIT.sv.name + ".sav";
+ GameSave.ReadLevel(name);
+ }
+
+ /*
+ ==============
+ SV_WriteServerFile
+
+ ==============
+ */
+ private static void SV_WriteServerFile(boolean autosave) {
+ QuakeFile f;
+ CvarT var;
+
+ String filename, name, string, comment;
+
+ Com.DPrintf("SV_WriteServerFile(" + (autosave ? "true" : "false") + ")\n");
+
+ filename = FS.Gamedir() + "/save/current/server.ssv";
+ try {
+ f = new QuakeFile(filename, "rw");
+
+ if (!autosave) {
+ Calendar c = Calendar.getInstance();
+ comment =
+ Com.sprintf(
+ "%2i:%2i %2i/%2i ",
+ new Vargs().add(c.get(Calendar.HOUR_OF_DAY)).add(c.get(Calendar.MINUTE)).add(
+ c.get(Calendar.MONTH) + 1).add(
+ c.get(Calendar.DAY_OF_MONTH)));
+ comment += SV_INIT.sv.configstrings[Defines.CS_NAME];
+ } else {
+ // autosaved
+ comment = "ENTERING " + SV_INIT.sv.configstrings[Defines.CS_NAME];
+ }
+
+ f.writeString(comment);
+ f.writeString(SV_INIT.svs.mapcmd);
+
+ // write the mapcmd
+
+ // write all CVAR_LATCH cvars
+ // these will be things like coop, skill, deathmatch, etc
+ for (var = Globals.cvar_vars; var != null; var = var.next) {
+ if (0 == (var.flags & Defines.CVAR_LATCH))
+ continue;
+ if (var.name.length() >= Defines.MAX_OSPATH - 1 || var.string.length() >= 128 - 1) {
+ Com.Printf("Cvar too long: " + var.name + " = " + var.string + "\n");
+ continue;
+ }
+
+ name = var.name;
+ string = var.string;
+ try {
+ f.writeString(name);
+ f.writeString(string);
+ } catch (IOException ignored) {
+ }
+
+ }
+ // rst: for termination.
+ f.writeString(null);
+ f.close();
+ } catch (Exception e) {
+ Com.Printf("Couldn't write " + filename + "\n");
+ }
+
+ // write game state
+ filename = FS.Gamedir() + "/save/current/game.ssv";
+ GameSave.WriteGame(filename, autosave);
+ }
+
+ /*
+ ==============
+ SV_ReadServerFile
+
+ ==============
+ */
+ private static void SV_ReadServerFile() {
+ String filename = "", name = "", string, mapcmd;
+ try {
+ QuakeFile f;
+
+ mapcmd = "";
+
+ Com.DPrintf("SV_ReadServerFile()\n");
+
+ filename = FS.Gamedir() + "/save/current/server.ssv";
+
+ f = new QuakeFile(filename, "r");
+
+ // read the mapcmd
+ mapcmd = f.readString();
+
+ // read all CVAR_LATCH cvars
+ // these will be things like coop, skill, deathmatch, etc
+ while (true) {
+ name = f.readString();
+ if (name == null)
+ break;
+ string = f.readString();
+
+ Com.DPrintf("Set " + name + " = " + string + "\n");
+ Cvar.forceSet(name, string);
+ }
+
+ f.close();
+
+ // start a new game fresh with new cvars
+ SV_INIT.SV_InitGame();
+
+ SV_INIT.svs.mapcmd = mapcmd;
+
+ // read game state
+ filename = FS.Gamedir() + "/save/current/game.ssv";
+ GameSave.ReadGame(filename);
+ } catch (Exception e) {
+ Com.Printf("Couldn't read file " + filename + "\n");
+ e.printStackTrace();
+ }
+ }
+ //=========================================================
+
+ /*
+ ==================
+ SV_DemoMap_f
+
+ Puts the server in demo mode on a specific map/cinematic
+ ==================
+ */
+ private static void SV_DemoMap_f() {
+ SV_INIT.SV_Map(true, Cmd.Argv(1), false);
+ }
+
+ /*
+ ==================
+ SV_GameMap_f
+
+ Saves the state of the map just being exited and goes to a new map.
+
+ If the initial character of the map string is '*', the next map is
+ in a new unit, so the current savegame directory is cleared of
+ map files.
+
+ Example:
+
+ *inter.cin+jail
+
+ Clears the archived maps, plays the inter.cin cinematic, then
+ goes to map jail.bsp.
+ ==================
+ */
+ private static void SV_GameMap_f() {
+ String map;
+ int i;
+ client_t cl;
+ boolean savedInuse[];
+
+ if (Cmd.Argc() != 2) {
+ Com.Printf("USAGE: gamemap <map>\n");
+ return;
+ }
+
+ Com.DPrintf("SV_GameMap(" + Cmd.Argv(1) + ")\n");
+
+ FS.CreatePath(FS.Gamedir() + "/save/current/");
+
+ // check for clearing the current savegame
+ map = Cmd.Argv(1);
+ if (map.charAt(0) == '*') {
+ // wipe all the *.sav files
+ SV_WipeSavegame("current");
+ } else { // save the map just exited
+ if (SV_INIT.sv.state == Defines.ss_game) {
+ // clear all the client inuse flags before saving so that
+ // when the level is re-entered, the clients will spawn
+ // at spawn points instead of occupying body shells
+ savedInuse = new boolean[(int) Server.maxclients.value];
+ for (i = 0; i < Server.maxclients.value; i++) {
+ cl = SV_INIT.svs.clients[i];
+ savedInuse[i] = cl.edict.inuse;
+ cl.edict.inuse = false;
+ }
+
+ SV_WriteLevelFile();
+
+ // we must restore these for clients to transfer over correctly
+ for (i = 0; i < Server.maxclients.value; i++) {
+ cl = SV_INIT.svs.clients[i];
+ cl.edict.inuse = savedInuse[i];
+
+ }
+ savedInuse = null;
+ }
+ }
+
+ // start up the next map
+ SV_INIT.SV_Map(false, Cmd.Argv(1), false);
+
+ // archive server state
+ SV_INIT.svs.mapcmd = Cmd.Argv(1);
+
+ // copy off the level to the autosave slot
+ if (0 == Globals.dedicated.value) {
+ SV_WriteServerFile(true);
+ SV_CopySaveGame("current", "save0");
+ }
+ }
+
+ /*
+ ==================
+ SV_Map_f
+
+ Goes directly to a given map without any savegame archiving.
+ For development work
+ ==================
+ */
+ private static void SV_Map_f() {
+ String map;
+ //char expanded[MAX_QPATH];
+ String expanded;
+
+ // if not a pcx, demo, or cinematic, check to make sure the level exists
+ map = Cmd.Argv(1);
+ if (!map.contains(".")) {
+ expanded = "maps/" + map + ".bsp";
+ if (FS.LoadFile(expanded) == null) {
+
+ Com.Printf("Can't find " + expanded + "\n");
+ return;
+ }
+ }
+
+ SV_INIT.sv.state = Defines.ss_dead; // don't save current level when changing
+
+ SV_WipeSavegame("current");
+ SV_GameMap_f();
+ }
+ /*
+ =====================================================================
+
+ SAVEGAMES
+
+ =====================================================================
+ */
+
+ /*
+ ==============
+ SV_Loadgame_f
+
+ ==============
+ */
+ private static void SV_Loadgame_f() {
+
+ String name;
+ RandomAccessFile f;
+ String dir;
+
+ if (Cmd.Argc() != 2) {
+ Com.Printf("USAGE: loadgame <directory>\n");
+ return;
+ }
+
+ Com.Printf("Loading game...\n");
+
+ dir = Cmd.Argv(1);
+ if ((dir.contains("..")) || (dir.contains("/")) || (dir.contains("\\"))) {
+ Com.Printf("Bad savedir.\n");
+ }
+
+ // make sure the server.ssv file exists
+ name = FS.Gamedir() + "/save/" + Cmd.Argv(1) + "/server.ssv";
+ try {
+ f = new RandomAccessFile(name, "r");
+ } catch (FileNotFoundException e) {
+ Com.Printf("No such savegame: " + name + "\n");
+ return;
+ }
+
+ try {
+ f.close();
+ } catch (IOException e1) {
+ e1.printStackTrace();
+ }
+
+ SV_CopySaveGame(Cmd.Argv(1), "current");
+ SV_ReadServerFile();
+
+ // go to the map
+ SV_INIT.sv.state = Defines.ss_dead; // don't save current level when changing
+ SV_INIT.SV_Map(false, SV_INIT.svs.mapcmd, true);
+ }
+
+ /*
+ ==============
+ SV_Savegame_f
+
+ ==============
+ */
+ private static void SV_Savegame_f() {
+ String dir;
+
+ if (SV_INIT.sv.state != Defines.ss_game) {
+ Com.Printf("You must be in a game to save.\n");
+ return;
+ }
+
+ if (Cmd.Argc() != 2) {
+ Com.Printf("USAGE: savegame <directory>\n");
+ return;
+ }
+
+ if (Cvar.variableValue("deathmatch") != 0) {
+ Com.Printf("Can't savegame in a deathmatch\n");
+ return;
+ }
+
+ if (0 == Lib.strcmp(Cmd.Argv(1), "current")) {
+ Com.Printf("Can't save to 'current'\n");
+ return;
+ }
+
+ if (Server.maxclients.value == 1 && SV_INIT.svs.clients[0].edict.client.ps.stats[Defines.STAT_HEALTH] <= 0) {
+ Com.Printf("\nCan't savegame while dead!\n");
+ return;
+ }
+
+ dir = Cmd.Argv(1);
+ if ((dir.contains("..")) || (dir.contains("/")) || (dir.contains("\\"))) {
+ Com.Printf("Bad savedir.\n");
+ }
+
+ Com.Printf("Saving game...\n");
+
+ // archive current level, including all client edicts.
+ // when the level is reloaded, they will be shells awaiting
+ // a connecting client
+ SV_WriteLevelFile();
+
+ // save server state
+ try {
+ SV_WriteServerFile(false);
+ } catch (Exception e) {
+ Com.Printf("IOError in SV_WriteServerFile: " + e);
+ }
+
+ // copy it off
+ SV_CopySaveGame("current", dir);
+ Com.Printf("Done.\n");
+ }
+
+ //===============================================================
+ /*
+ ==================
+ SV_Kick_f
+
+ Kick a user off of the server
+ ==================
+ */
+ private static void SV_Kick_f() {
+ if (!SV_INIT.svs.initialized) {
+ Com.Printf("No server running.\n");
+ return;
+ }
+
+ if (Cmd.Argc() != 2) {
+ Com.Printf("Usage: kick <userid>\n");
+ return;
+ }
+
+ if (!SV_SetPlayer())
+ return;
+
+ SV_SEND.SV_BroadcastPrintf(Defines.PRINT_HIGH, Server.sv_client.name + " was kicked\n");
+ // print directly, because the dropped client won't get the
+ // SV_BroadcastPrintf message
+ SV_SEND.SV_ClientPrintf(Server.sv_client, Defines.PRINT_HIGH, "You were kicked from the game\n");
+ Server.SV_DropClient(Server.sv_client);
+ Server.sv_client.lastmessage = SV_INIT.svs.realtime; // min case there is a funny zombie
+ }
+
+ /*
+ ================
+ SV_Status_f
+ ================
+ */
+ private static void SV_Status_f() {
+ int i, j, l;
+ client_t cl;
+ String s;
+ int ping;
+ if (SV_INIT.svs.clients == null) {
+ Com.Printf("No server running.\n");
+ return;
+ }
+ Com.Printf("map : " + SV_INIT.sv.name + "\n");
+
+ Com.Printf("num score ping name lastmsg address qport \n");
+ Com.Printf("--- ----- ---- --------------- ------- --------------------- ------\n");
+ for (i = 0; i < Server.maxclients.value; i++) {
+ cl = SV_INIT.svs.clients[i];
+ if (0 == cl.state)
+ continue;
+
+ Com.Printf("%3i ", new Vargs().add(i));
+ Com.Printf("%5i ", new Vargs().add(cl.edict.client.ps.stats[Defines.STAT_FRAGS]));
+
+ if (cl.state == Defines.cs_connected)
+ Com.Printf("CNCT ");
+ else if (cl.state == Defines.cs_zombie)
+ Com.Printf("ZMBI ");
+ else {
+ ping = cl.ping < 9999 ? cl.ping : 9999;
+ Com.Printf("%4i ", new Vargs().add(ping));
+ }
+
+ Com.Printf("%s", new Vargs().add(cl.name));
+ l = 16 - cl.name.length();
+ for (j = 0; j < l; j++)
+ Com.Printf(" ");
+
+ Com.Printf("%7i ", new Vargs().add(SV_INIT.svs.realtime - cl.lastmessage));
+
+ s = NET.AdrToString(cl.netchan.remote_address);
+ Com.Printf(s);
+ l = 22 - s.length();
+ for (j = 0; j < l; j++)
+ Com.Printf(" ");
+
+ Com.Printf("%5i", new Vargs().add(cl.netchan.qport));
+
+ Com.Printf("\n");
+ }
+ Com.Printf("\n");
+ }
+
+ /*
+ ==================
+ SV_ConSay_f
+ ==================
+ */
+ private static void SV_ConSay_f() {
+ client_t client;
+ int j;
+ String p;
+ String text; // char[1024];
+
+ if (Cmd.Argc() < 2)
+ return;
+
+ text = "console: ";
+ p = Cmd.Args();
+
+ if (p.charAt(0) == '"') {
+ p = p.substring(1, p.length() - 1);
+ }
+
+ text += p;
+
+ for (j = 0; j < Server.maxclients.value; j++) {
+ client = SV_INIT.svs.clients[j];
+ if (client.state != Defines.cs_spawned)
+ continue;
+ SV_SEND.SV_ClientPrintf(client, Defines.PRINT_CHAT, text + "\n");
+ }
+ }
+
+ /*
+ ==================
+ SV_Heartbeat_f
+ ==================
+ */
+ private static void SV_Heartbeat_f() {
+ SV_INIT.svs.last_heartbeat = -9999999;
+ }
+
+ /*
+ ===========
+ SV_Serverinfo_f
+
+ Examine or change the serverinfo string
+ ===========
+ */
+ private static void SV_Serverinfo_f() {
+ Com.Printf("Server info settings:\n");
+ Info.Print(Cvar.serverinfo());
+ }
+
+ /*
+ ===========
+ SV_DumpUser_f
+
+ Examine all a users info strings
+ ===========
+ */
+ private static void SV_DumpUser_f() {
+ if (Cmd.Argc() != 2) {
+ Com.Printf("Usage: info <userid>\n");
+ return;
+ }
+
+ if (!SV_SetPlayer())
+ return;
+
+ Com.Printf("userinfo\n");
+ Com.Printf("--------\n");
+ Info.Print(Server.sv_client.userinfo);
+
+ }
+
+ /*
+ ==============
+ SV_ServerRecord_f
+
+ Begins server demo recording. Every entity and every message will be
+ recorded, but no playerinfo will be stored. Primarily for demo merging.
+ ==============
+ */
+ private static void SV_ServerRecord_f() {
+ //char name[MAX_OSPATH];
+ String name;
+ byte buf_data[] = new byte[32768];
+ sizebuf_t buf = new sizebuf_t();
+ int len;
+ int i;
+
+ if (Cmd.Argc() != 2) {
+ Com.Printf("serverrecord <demoname>\n");
+ return;
+ }
+
+ if (SV_INIT.svs.demofile != null) {
+ Com.Printf("Already recording.\n");
+ return;
+ }
+
+ if (SV_INIT.sv.state != Defines.ss_game) {
+ Com.Printf("You must be in a level to record.\n");
+ return;
+ }
+
+ //
+ // open the demo file
+ //
+ name = FS.Gamedir() + "/demos/" + Cmd.Argv(1) + ".dm2";
+
+ Com.Printf("recording to " + name + ".\n");
+ FS.CreatePath(name);
+ try {
+ SV_INIT.svs.demofile = new RandomAccessFile(name, "rw");
+ } catch (Exception e) {
+ Com.Printf("ERROR: couldn't open.\n");
+ return;
+ }
+
+ // setup a buffer to catch all multicasts
+ SZ.Init(SV_INIT.svs.demo_multicast, SV_INIT.svs.demo_multicast_buf, SV_INIT.svs.demo_multicast_buf.length);
+
+ //
+ // write a single giant fake message with all the startup info
+ //
+ SZ.Init(buf, buf_data, buf_data.length);
+
+ //
+ // serverdata needs to go over for all types of servers
+ // to make sure the protocol is right, and to set the gamedir
+ //
+ // send the serverdata
+ MSG.WriteByte(buf, Defines.svc_serverdata);
+ MSG.WriteLong(buf, Defines.PROTOCOL_VERSION);
+ MSG.WriteLong(buf, SV_INIT.svs.spawncount);
+ // 2 means server demo
+ MSG.WriteByte(buf, 2); // demos are always attract loops
+ MSG.WriteString(buf, Cvar.variableString("gamedir"));
+ MSG.WriteShort(buf, -1);
+ // send full levelname
+ MSG.WriteString(buf, SV_INIT.sv.configstrings[Defines.CS_NAME]);
+
+ for (i = 0; i < Defines.MAX_CONFIGSTRINGS; i++)
+ if (SV_INIT.sv.configstrings[i].length() == 0) {
+ MSG.WriteByte(buf, Defines.svc_configstring);
+ MSG.WriteShort(buf, i);
+ MSG.WriteString(buf, SV_INIT.sv.configstrings[i]);
+ }
+
+ // write it to the demo file
+ Com.DPrintf("signon message length: " + buf.cursize + "\n");
+ len = EndianHandler.swapInt(buf.cursize);
+ //fwrite(len, 4, 1, svs.demofile);
+ //fwrite(buf.data, buf.cursize, 1, svs.demofile);
+ try {
+ SV_INIT.svs.demofile.writeInt(len);
+ SV_INIT.svs.demofile.write(buf.data, 0, buf.cursize);
+ } catch (IOException e1) {
+ // TODO: do quake2 error handling!
+ e1.printStackTrace();
+ }
+
+ // the rest of the demo file will be individual frames
+ }
+
+ /*
+ ==============
+ SV_ServerStop_f
+
+ Ends server demo recording
+ ==============
+ */
+ private static void SV_ServerStop_f() {
+ if (SV_INIT.svs.demofile == null) {
+ Com.Printf("Not doing a serverrecord.\n");
+ return;
+ }
+ try {
+ SV_INIT.svs.demofile.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ SV_INIT.svs.demofile = null;
+ Com.Printf("Recording completed.\n");
+ }
+
+ /*
+ ===============
+ SV_KillServer_f
+
+ Kick everyone off, possibly in preparation for a new game
+
+ ===============
+ */
+ private static void SV_KillServer_f() {
+ if (!SV_INIT.svs.initialized)
+ return;
+ Server.SV_Shutdown("Server was killed.\n", false);
+ NET.Config(false); // close network sockets
+ }
+
+ /*
+ ===============
+ SV_ServerCommand_f
+
+ Let the game dll handle a command
+ ===============
+ */
+ private static void SV_ServerCommand_f() {
+
+ GameSVCmds.ServerCommand();
+ }
+ //===========================================================
+
+ /*
+ ==================
+ SV_InitOperatorCommands
+ ==================
+ */
+ public static void SV_InitOperatorCommands() {
+ Cmd.AddCommand("heartbeat", new xcommand_t() {
+ public void execute() {
+ SV_Heartbeat_f();
+ }
+ });
+ Cmd.AddCommand("kick", new xcommand_t() {
+ public void execute() {
+ SV_Kick_f();
+ }
+ });
+ Cmd.AddCommand("status", new xcommand_t() {
+ public void execute() {
+ SV_Status_f();
+ }
+ });
+ Cmd.AddCommand("serverinfo", new xcommand_t() {
+ public void execute() {
+ SV_Serverinfo_f();
+ }
+ });
+ Cmd.AddCommand("dumpuser", new xcommand_t() {
+ public void execute() {
+ SV_DumpUser_f();
+ }
+ });
+
+ Cmd.AddCommand("map", new xcommand_t() {
+ public void execute() {
+ SV_Map_f();
+ }
+ });
+ Cmd.AddCommand("demomap", new xcommand_t() {
+ public void execute() {
+ SV_DemoMap_f();
+ }
+ });
+ Cmd.AddCommand("gamemap", new xcommand_t() {
+ public void execute() {
+ SV_GameMap_f();
+ }
+ });
+ Cmd.AddCommand("setmaster", new xcommand_t() {
+ public void execute() {
+ SV_SetMaster_f();
+ }
+ });
+
+ if (Globals.dedicated.value != 0)
+ Cmd.AddCommand("say", new xcommand_t() {
+ public void execute() {
+ SV_ConSay_f();
+ }
+ });
+
+ Cmd.AddCommand("serverrecord", new xcommand_t() {
+ public void execute() {
+ SV_ServerRecord_f();
+ }
+ });
+ Cmd.AddCommand("serverstop", new xcommand_t() {
+ public void execute() {
+ SV_ServerStop_f();
+ }
+ });
+
+ Cmd.AddCommand("save", new xcommand_t() {
+ public void execute() {
+ SV_Savegame_f();
+ }
+ });
+ Cmd.AddCommand("load", new xcommand_t() {
+ public void execute() {
+ SV_Loadgame_f();
+ }
+ });
+
+ Cmd.AddCommand("killserver", new xcommand_t() {
+ public void execute() {
+ SV_KillServer_f();
+ }
+ });
+
+ Cmd.AddCommand("sv", new xcommand_t() {
+ public void execute() {
+ SV_ServerCommand_f();
+ }
+ });
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.server;
+
+import lwjake2.Defines;
+import lwjake2.game.*;
+import lwjake2.qcommon.*;
+
+import java.io.IOException;
+
+public class SV_ENTS {
+
+ /**
+ * =============================================================================
+ * <p>
+ * Build a client frame structure
+ * <p>
+ * =============================================================================
+ */
+
+ public static final byte[] fatpvs = new byte[65536 / 8]; // 32767 is MAX_MAP_LEAFS
+
+ /*
+ * =============================================================================
+ *
+ * Encode a client frame onto the network channel
+ *
+ * =============================================================================
+ */
+
+ /**
+ * Writes a delta update of an entity_state_t list to the message.
+ */
+ static void SV_EmitPacketEntities(client_frame_t from, client_frame_t to,
+ sizebuf_t msg) {
+ entity_state_t oldent = null, newent = null;
+ int oldindex, newindex;
+ int oldnum, newnum;
+ int from_num_entities;
+ int bits;
+
+ MSG.WriteByte(msg, Defines.svc_packetentities);
+
+ if (from == null)
+ from_num_entities = 0;
+ else
+ from_num_entities = from.num_entities;
+
+ newindex = 0;
+ oldindex = 0;
+ while (newindex < to.num_entities || oldindex < from_num_entities) {
+ if (newindex >= to.num_entities)
+ newnum = 9999;
+ else {
+ newent = SV_INIT.svs.client_entities[(to.first_entity + newindex)
+ % SV_INIT.svs.num_client_entities];
+ newnum = newent.number;
+ }
+
+ if (oldindex >= from_num_entities)
+ oldnum = 9999;
+ else {
+ oldent = SV_INIT.svs.client_entities[(from.first_entity + oldindex)
+ % SV_INIT.svs.num_client_entities];
+ oldnum = oldent.number;
+ }
+
+ if (newnum == oldnum) {
+ // delta update from old position
+ // because the force parm is false, this will not result
+ // in any bytes being emited if the entity has not changed at
+ // all note that players are always 'newentities', this updates
+ // their oldorigin always
+ // and prevents warping
+ MSG.WriteDeltaEntity(oldent, newent, msg, false,
+ newent.number <= Server.maxclients.value);
+ oldindex++;
+ newindex++;
+ continue;
+ }
+
+ if (newnum < oldnum) {
+ // this is a new entity, send it from the baseline
+ MSG.WriteDeltaEntity(SV_INIT.sv.baselines[newnum], newent, msg,
+ true, true);
+ newindex++;
+ continue;
+ }
+
+ if (newnum > oldnum) {
+ // the old entity isn't present in the new message
+ bits = Defines.U_REMOVE;
+ if (oldnum >= 256)
+ bits |= Defines.U_NUMBER16 | Defines.U_MOREBITS1;
+
+ MSG.WriteByte(msg, bits & 255);
+ if ((bits & 0x0000ff00) != 0)
+ MSG.WriteByte(msg, (bits >> 8) & 255);
+
+ if ((bits & Defines.U_NUMBER16) != 0)
+ MSG.WriteShort(msg, oldnum);
+ else
+ MSG.WriteByte(msg, oldnum);
+
+ oldindex++;
+ }
+ }
+
+ MSG.WriteShort(msg, 0); // end of packetentities
+
+ }
+
+ /**
+ * Writes the status of a player to a client system.
+ */
+ static void SV_WritePlayerstateToClient(client_frame_t from,
+ client_frame_t to, sizebuf_t msg) {
+ int i;
+ int pflags;
+ // ptr
+ player_state_t ps, ops;
+ // mem
+ player_state_t dummy;
+ int statbits;
+
+ ps = to.ps;
+ if (from == null) {
+ //memset (dummy, 0, sizeof(dummy));
+ dummy = new player_state_t();
+ ops = dummy;
+ } else
+ ops = from.ps;
+
+ // determine what needs to be sent
+ pflags = 0;
+
+ if (ps.pmove.pm_type != ops.pmove.pm_type)
+ pflags |= Defines.PS_M_TYPE;
+
+ if (ps.pmove.origin[0] != ops.pmove.origin[0]
+ || ps.pmove.origin[1] != ops.pmove.origin[1]
+ || ps.pmove.origin[2] != ops.pmove.origin[2])
+ pflags |= Defines.PS_M_ORIGIN;
+
+ if (ps.pmove.velocity[0] != ops.pmove.velocity[0]
+ || ps.pmove.velocity[1] != ops.pmove.velocity[1]
+ || ps.pmove.velocity[2] != ops.pmove.velocity[2])
+ pflags |= Defines.PS_M_VELOCITY;
+
+ if (ps.pmove.pm_time != ops.pmove.pm_time)
+ pflags |= Defines.PS_M_TIME;
+
+ if (ps.pmove.pm_flags != ops.pmove.pm_flags)
+ pflags |= Defines.PS_M_FLAGS;
+
+ if (ps.pmove.gravity != ops.pmove.gravity)
+ pflags |= Defines.PS_M_GRAVITY;
+
+ if (ps.pmove.delta_angles[0] != ops.pmove.delta_angles[0]
+ || ps.pmove.delta_angles[1] != ops.pmove.delta_angles[1]
+ || ps.pmove.delta_angles[2] != ops.pmove.delta_angles[2])
+ pflags |= Defines.PS_M_DELTA_ANGLES;
+
+ if (ps.viewoffset[0] != ops.viewoffset[0]
+ || ps.viewoffset[1] != ops.viewoffset[1]
+ || ps.viewoffset[2] != ops.viewoffset[2])
+ pflags |= Defines.PS_VIEWOFFSET;
+
+ if (ps.viewangles[0] != ops.viewangles[0]
+ || ps.viewangles[1] != ops.viewangles[1]
+ || ps.viewangles[2] != ops.viewangles[2])
+ pflags |= Defines.PS_VIEWANGLES;
+
+ if (ps.kick_angles[0] != ops.kick_angles[0]
+ || ps.kick_angles[1] != ops.kick_angles[1]
+ || ps.kick_angles[2] != ops.kick_angles[2])
+ pflags |= Defines.PS_KICKANGLES;
+
+ if (ps.blend[0] != ops.blend[0] || ps.blend[1] != ops.blend[1]
+ || ps.blend[2] != ops.blend[2] || ps.blend[3] != ops.blend[3])
+ pflags |= Defines.PS_BLEND;
+
+ if (ps.fov != ops.fov)
+ pflags |= Defines.PS_FOV;
+
+ if (ps.rdflags != ops.rdflags)
+ pflags |= Defines.PS_RDFLAGS;
+
+
+ pflags |= Defines.PS_WEAPONINDEX;
+
+ // write it
+ MSG.WriteByte(msg, Defines.svc_playerinfo);
+ MSG.WriteShort(msg, pflags);
+
+ // write the pmove_state_t
+ if ((pflags & Defines.PS_M_TYPE) != 0)
+ MSG.WriteByte(msg, ps.pmove.pm_type);
+
+ if ((pflags & Defines.PS_M_ORIGIN) != 0) {
+ MSG.WriteShort(msg, ps.pmove.origin[0]);
+ MSG.WriteShort(msg, ps.pmove.origin[1]);
+ MSG.WriteShort(msg, ps.pmove.origin[2]);
+ }
+
+ if ((pflags & Defines.PS_M_VELOCITY) != 0) {
+ MSG.WriteShort(msg, ps.pmove.velocity[0]);
+ MSG.WriteShort(msg, ps.pmove.velocity[1]);
+ MSG.WriteShort(msg, ps.pmove.velocity[2]);
+ }
+
+ if ((pflags & Defines.PS_M_TIME) != 0)
+ MSG.WriteByte(msg, ps.pmove.pm_time);
+
+ if ((pflags & Defines.PS_M_FLAGS) != 0)
+ MSG.WriteByte(msg, ps.pmove.pm_flags);
+
+ if ((pflags & Defines.PS_M_GRAVITY) != 0)
+ MSG.WriteShort(msg, ps.pmove.gravity);
+
+ if ((pflags & Defines.PS_M_DELTA_ANGLES) != 0) {
+ MSG.WriteShort(msg, ps.pmove.delta_angles[0]);
+ MSG.WriteShort(msg, ps.pmove.delta_angles[1]);
+ MSG.WriteShort(msg, ps.pmove.delta_angles[2]);
+ }
+
+ // write the rest of the player_state_t
+ if ((pflags & Defines.PS_VIEWOFFSET) != 0) {
+ MSG.WriteChar(msg, ps.viewoffset[0] * 4);
+ MSG.WriteChar(msg, ps.viewoffset[1] * 4);
+ MSG.WriteChar(msg, ps.viewoffset[2] * 4);
+ }
+
+ if ((pflags & Defines.PS_VIEWANGLES) != 0) {
+ MSG.WriteAngle16(msg, ps.viewangles[0]);
+ MSG.WriteAngle16(msg, ps.viewangles[1]);
+ MSG.WriteAngle16(msg, ps.viewangles[2]);
+ }
+
+ if ((pflags & Defines.PS_KICKANGLES) != 0) {
+ MSG.WriteChar(msg, ps.kick_angles[0] * 4);
+ MSG.WriteChar(msg, ps.kick_angles[1] * 4);
+ MSG.WriteChar(msg, ps.kick_angles[2] * 4);
+ }
+
+
+ if ((pflags & Defines.PS_BLEND) != 0) {
+ MSG.WriteByte(msg, ps.blend[0] * 255);
+ MSG.WriteByte(msg, ps.blend[1] * 255);
+ MSG.WriteByte(msg, ps.blend[2] * 255);
+ MSG.WriteByte(msg, ps.blend[3] * 255);
+ }
+ if ((pflags & Defines.PS_FOV) != 0)
+ MSG.WriteByte(msg, ps.fov);
+ if ((pflags & Defines.PS_RDFLAGS) != 0)
+ MSG.WriteByte(msg, ps.rdflags);
+
+ // send stats
+ statbits = 0;
+ for (i = 0; i < Defines.MAX_STATS; i++)
+ if (ps.stats[i] != ops.stats[i])
+ statbits |= 1 << i;
+ MSG.WriteLong(msg, statbits);
+ for (i = 0; i < Defines.MAX_STATS; i++)
+ if ((statbits & (1 << i)) != 0)
+ MSG.WriteShort(msg, ps.stats[i]);
+ }
+
+ /**
+ * Writes a frame to a client system.
+ */
+ public static void SV_WriteFrameToClient(client_t client, sizebuf_t msg) {
+ //ptr
+ client_frame_t frame, oldframe;
+ int lastframe;
+
+ //Com.Printf ("%i . %i\n", new
+ // Vargs().add(client.lastframe).add(sv.framenum));
+ // this is the frame we are creating
+ frame = client.frames[SV_INIT.sv.framenum & Defines.UPDATE_MASK];
+ if (client.lastframe <= 0) { // client is asking for a retransmit
+ oldframe = null;
+ lastframe = -1;
+ } else if (SV_INIT.sv.framenum - client.lastframe >= (Defines.UPDATE_BACKUP - 3)) {
+ // client hasn't gotten a good message through in a long time
+ // Com_Printf ("%s: Delta request from out-of-date packet.\n",
+ // client.name);
+ oldframe = null;
+ lastframe = -1;
+ } else { // we have a valid message to delta from
+ oldframe = client.frames[client.lastframe & Defines.UPDATE_MASK];
+ lastframe = client.lastframe;
+ }
+
+ MSG.WriteByte(msg, Defines.svc_frame);
+ MSG.WriteLong(msg, SV_INIT.sv.framenum);
+ MSG.WriteLong(msg, lastframe); // what we are delta'ing from
+ MSG.WriteByte(msg, client.surpressCount); // rate dropped packets
+ client.surpressCount = 0;
+
+ // send over the areabits
+ MSG.WriteByte(msg, frame.areabytes);
+ SZ.Write(msg, frame.areabits, frame.areabytes);
+
+ // delta encode the playerstate
+ SV_WritePlayerstateToClient(oldframe, frame, msg);
+
+ // delta encode the entities
+ SV_EmitPacketEntities(oldframe, frame, msg);
+ }
+
+ /**
+ * The client will interpolate the view position, so we can't use a single
+ * PVS point.
+ */
+ public static void SV_FatPVS(float[] org) {
+ int leafs[] = new int[64];
+ int i, j, count;
+ int longs;
+ byte src[];
+ float[] mins = {0, 0, 0}, maxs = {0, 0, 0};
+
+ for (i = 0; i < 3; i++) {
+ mins[i] = org[i] - 8;
+ maxs[i] = org[i] + 8;
+ }
+
+ count = CM.CM_BoxLeafnums(mins, maxs, leafs, 64, null);
+
+ if (count < 1)
+ Com.Error(Defines.ERR_FATAL, "SV_FatPVS: count < 1");
+
+ longs = (CM.CM_NumClusters() + 31) >> 5;
+
+ // convert leafs to clusters
+ for (i = 0; i < count; i++)
+ leafs[i] = CM.CM_LeafCluster(leafs[i]);
+
+ System.arraycopy(CM.CM_ClusterPVS(leafs[0]), 0, SV_ENTS.fatpvs, 0,
+ longs << 2);
+ // or in all the other leaf bits
+ for (i = 1; i < count; i++) {
+ for (j = 0; j < i; j++)
+ if (leafs[i] == leafs[j])
+ break;
+ if (j != i)
+ continue; // already have the cluster we want
+
+ src = CM.CM_ClusterPVS(leafs[i]);
+
+ //for (j=0 ; j<longs ; j++)
+ // ((long *)fatpvs)[j] |= ((long *)src)[j];
+ int k = 0;
+ for (j = 0; j < longs; j++) {
+ SV_ENTS.fatpvs[k] |= src[k++];
+ SV_ENTS.fatpvs[k] |= src[k++];
+ SV_ENTS.fatpvs[k] |= src[k++];
+ SV_ENTS.fatpvs[k] |= src[k++];
+ }
+ }
+ }
+
+ /**
+ * Decides which entities are going to be visible to the client, and copies
+ * off the playerstat and areabits.
+ */
+ public static void SV_BuildClientFrame(client_t client) {
+ int e, i;
+ float[] org = {0, 0, 0};
+ EDict ent;
+ EDict clent;
+ client_frame_t frame;
+ entity_state_t state;
+ int l;
+ int clientarea, clientcluster;
+ int leafnum;
+ byte clientphs[];
+ byte bitvector[];
+
+ clent = client.edict;
+ if (clent.client == null)
+ return; // not in game yet
+
+ // this is the frame we are creating
+ frame = client.frames[SV_INIT.sv.framenum & Defines.UPDATE_MASK];
+
+ frame.senttime = SV_INIT.svs.realtime; // save it for ping calc later
+
+ // find the client's PVS
+ for (i = 0; i < 3; i++)
+ org[i] = clent.client.ps.pmove.origin[i] * 0.125f
+ + clent.client.ps.viewoffset[i];
+
+ leafnum = CM.CM_PointLeafnum(org);
+ clientarea = CM.CM_LeafArea(leafnum);
+ clientcluster = CM.CM_LeafCluster(leafnum);
+
+ // calculate the visible areas
+ frame.areabytes = CM.CM_WriteAreaBits(frame.areabits, clientarea);
+
+ // grab the current player_state_t
+ frame.ps.set(clent.client.ps);
+
+ SV_FatPVS(org);
+ clientphs = CM.CM_ClusterPHS(clientcluster);
+
+ // build up the list of visible entities
+ frame.num_entities = 0;
+ frame.first_entity = SV_INIT.svs.next_client_entities;
+
+ for (e = 1; e < GameBase.num_edicts; e++) {
+ ent = GameBase.g_edicts[e];
+
+ // ignore ents without visible models
+ if ((ent.svflags & Defines.SVF_NOCLIENT) != 0)
+ continue;
+
+ // ignore ents without visible models unless they have an effect
+ if (0 == ent.s.modelindex && 0 == ent.s.effects && 0 == ent.s.sound
+ && 0 == ent.s.event)
+ continue;
+
+ // ignore if not touching a PV leaf
+ // check area
+ if (ent != clent) {
+ if (!CM.CM_AreasConnected(clientarea, ent.areanum)) {
+ // doors can legally straddle two areas, so we may need to check another one
+ if (0 == ent.areanum2 || !CM.CM_AreasConnected(clientarea, ent.areanum2))
+ continue; // blocked by a door
+ }
+
+ }
+
+ // add it to the circular client_entities array
+ int ix = SV_INIT.svs.next_client_entities
+ % SV_INIT.svs.num_client_entities;
+ state = SV_INIT.svs.client_entities[ix];
+ if (ent.s.number != e) {
+ Com.DPrintf("FIXING ENT.S.NUMBER!!!\n");
+ ent.s.number = e;
+ }
+
+ //*state = ent.s;
+ SV_INIT.svs.client_entities[ix].set(ent.s);
+
+ // don't mark players missiles as solid
+ if (ent.owner == client.edict)
+ state.solid = 0;
+
+ SV_INIT.svs.next_client_entities++;
+ frame.num_entities++;
+ }
+ }
+
+ /**
+ * Save everything in the world out without deltas. Used for recording
+ * footage for merged or assembled demos.
+ */
+ public static void SV_RecordDemoMessage() {
+ int e;
+ EDict ent;
+ entity_state_t nostate = new entity_state_t(null);
+ sizebuf_t buf = new sizebuf_t();
+ byte buf_data[] = new byte[32768];
+ int len;
+
+ if (SV_INIT.svs.demofile == null)
+ return;
+
+ //memset (nostate, 0, sizeof(nostate));
+ SZ.Init(buf, buf_data, buf_data.length);
+
+ // write a frame message that doesn't contain a player_state_t
+ MSG.WriteByte(buf, Defines.svc_frame);
+ MSG.WriteLong(buf, SV_INIT.sv.framenum);
+
+ MSG.WriteByte(buf, Defines.svc_packetentities);
+
+ e = 1;
+ ent = GameBase.g_edicts[e];
+
+ while (e < GameBase.num_edicts) {
+ // ignore ents without visible models unless they have an effect
+ if (ent.inuse
+ && ent.s.number != 0
+ && (ent.s.modelindex != 0 || ent.s.effects != 0
+ || ent.s.sound != 0 || ent.s.event != 0)
+ && 0 == (ent.svflags & Defines.SVF_NOCLIENT))
+ MSG.WriteDeltaEntity(nostate, ent.s, buf, false, true);
+
+ e++;
+ ent = GameBase.g_edicts[e];
+ }
+
+ MSG.WriteShort(buf, 0); // end of packetentities
+
+ // now add the accumulated multicast information
+ SZ.Write(buf, SV_INIT.svs.demo_multicast.data,
+ SV_INIT.svs.demo_multicast.cursize);
+ SZ.Clear(SV_INIT.svs.demo_multicast);
+
+ // now write the entire message to the file, prefixed by the length
+ len = EndianHandler.swapInt(buf.cursize);
+
+ try {
+ //fwrite (len, 4, 1, svs.demofile);
+ SV_INIT.svs.demofile.writeInt(len);
+ //fwrite (buf.data, buf.cursize, 1, svs.demofile);
+ SV_INIT.svs.demofile.write(buf.data, 0, buf.cursize);
+ } catch (IOException e1) {
+ Com.Printf("Error writing demo file:" + e);
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.server;
+
+import lwjake2.Defines;
+import lwjake2.Globals;
+import lwjake2.game.*;
+import lwjake2.qcommon.CM;
+import lwjake2.qcommon.Com;
+import lwjake2.qcommon.MSG;
+import lwjake2.qcommon.SZ;
+import lwjake2.util.Math3D;
+
+public class SV_GAME {
+
+ /**
+ * PF_Unicast
+ * <p>
+ * Sends the contents of the mutlicast buffer to a single client.
+ */
+ public static void PF_Unicast(EDict ent, boolean reliable) {
+ int p;
+ client_t client;
+
+ if (ent == null)
+ return;
+
+ p = ent.index;
+ if (p < 1 || p > Server.maxclients.value)
+ return;
+
+ client = SV_INIT.svs.clients[p - 1];
+
+ if (reliable)
+ SZ.Write(client.netchan.message, SV_INIT.sv.multicast.data,
+ SV_INIT.sv.multicast.cursize);
+ else
+ SZ.Write(client.datagram, SV_INIT.sv.multicast.data,
+ SV_INIT.sv.multicast.cursize);
+
+ SZ.Clear(SV_INIT.sv.multicast);
+ }
+
+ /**
+ * PF_dprintf
+ * <p>
+ * Debug print to server console.
+ */
+ public static void PF_dprintf(String fmt) {
+ Com.Printf(fmt);
+ }
+
+
+ /**
+ * Centerprintf for critical messages.
+ */
+ public static void PF_cprintfhigh(EDict ent, String fmt) {
+ PF_cprintf(ent, Defines.PRINT_HIGH, fmt);
+ }
+
+ /**
+ * PF_cprintf
+ * <p>
+ * Print to a single client.
+ */
+ public static void PF_cprintf(EDict ent, int level, String fmt) {
+
+ int n = 0;
+
+ if (ent != null) {
+ n = ent.index;
+ if (n < 1 || n > Server.maxclients.value)
+ Com.Error(Defines.ERR_DROP, "cprintf to a non-client");
+ }
+
+ if (ent != null)
+ SV_SEND.SV_ClientPrintf(SV_INIT.svs.clients[n - 1], level, fmt);
+ else
+ Com.Printf(fmt);
+ }
+
+ /**
+ * PF_centerprintf
+ * <p>
+ * centerprint to a single client.
+ */
+ public static void PF_centerprintf(EDict ent, String fmt) {
+ int n;
+
+ n = ent.index;
+ if (n < 1 || n > Server.maxclients.value)
+ return; // Com_Error (ERR_DROP, "centerprintf to a non-client");
+
+ MSG.WriteByte(SV_INIT.sv.multicast, Defines.svc_centerprint);
+ MSG.WriteString(SV_INIT.sv.multicast, fmt);
+ PF_Unicast(ent, true);
+ }
+
+ public static void PF_error(int level, String fmt) {
+ Com.Error(level, fmt);
+ }
+
+ /**
+ * PF_setmodel
+ * <p>
+ * Also sets mins and maxs for inline bmodels.
+ */
+ public static void PF_setmodel(EDict ent, String name) {
+ int i;
+ cmodel_t mod;
+
+ if (name == null)
+ Com.Error(Defines.ERR_DROP, "PF_setmodel: NULL");
+
+ i = SV_INIT.SV_ModelIndex(name);
+
+ ent.s.modelindex = i;
+
+ // if it is an inline model, get the size information for it
+ if (name.startsWith("*")) {
+ mod = CM.InlineModel(name);
+ Math3D.vectorCopy(mod.mins, ent.mins);
+ Math3D.vectorCopy(mod.maxs, ent.maxs);
+ SV_WORLD.SV_LinkEdict(ent);
+ }
+ }
+
+ /**
+ * PF_Configstring
+ */
+ public static void PF_Configstring(int index, String val) {
+ if (index < 0 || index >= Defines.MAX_CONFIGSTRINGS)
+ Com.Error(Defines.ERR_DROP, "configstring: bad index " + index
+ + "\n");
+
+ if (val == null)
+ val = "";
+
+ // change the string in sv
+ SV_INIT.sv.configstrings[index] = val;
+
+ if (SV_INIT.sv.state != Defines.ss_loading) { // send the update to
+ // everyone
+ SZ.Clear(SV_INIT.sv.multicast);
+ MSG.WriteChar(SV_INIT.sv.multicast, Defines.svc_configstring);
+ MSG.WriteShort(SV_INIT.sv.multicast, index);
+ MSG.WriteString(SV_INIT.sv.multicast, val);
+
+ SV_SEND.SV_Multicast(Globals.vec3_origin, Defines.MULTICAST_ALL_R);
+ }
+ }
+
+ public static void PF_WriteByte(int c) {
+ MSG.WriteByte(SV_INIT.sv.multicast, c);
+ }
+
+ public static void PF_WriteShort(int c) {
+ MSG.WriteShort(SV_INIT.sv.multicast, c);
+ }
+
+ public static void PF_WriteString(String s) {
+ MSG.WriteString(SV_INIT.sv.multicast, s);
+ }
+
+ public static void PF_WritePos(float[] pos) {
+ MSG.WritePos(SV_INIT.sv.multicast, pos);
+ }
+
+ public static void PF_WriteDir(float[] dir) {
+ MSG.WriteDir(SV_INIT.sv.multicast, dir);
+ }
+
+ /**
+ * PF_inPHS.
+ * <p>
+ * Also checks portalareas so that doors block sound.
+ */
+ public static boolean PF_inPHS(float[] p1, float[] p2) {
+ int leafnum;
+ int cluster;
+ int area1, area2;
+ byte mask[];
+
+ leafnum = CM.CM_PointLeafnum(p1);
+ cluster = CM.CM_LeafCluster(leafnum);
+ area1 = CM.CM_LeafArea(leafnum);
+ mask = CM.CM_ClusterPHS(cluster);
+
+ leafnum = CM.CM_PointLeafnum(p2);
+ cluster = CM.CM_LeafCluster(leafnum);
+ area2 = CM.CM_LeafArea(leafnum);
+
+ // quake2 bugfix
+ if (cluster == -1)
+ return false;
+ // more than one bounce away
+ return !(mask != null && (0 == (mask[cluster >> 3] & (1 << (cluster & 7))))) && CM.CM_AreasConnected(area1, area2);
+
+ }
+
+ public static void PF_StartSound(EDict entity, int channel,
+ int sound_num, float volume, float attenuation, float timeofs) {
+
+ if (null == entity)
+ return;
+ SV_SEND.SV_StartSound(null, entity, channel, sound_num, volume,
+ attenuation, timeofs);
+
+ }
+
+
+ /**
+ * SV_ShutdownGameProgs
+ * <p>
+ * Called when either the entire server is being killed, or it is changing
+ * to a different game directory.
+ */
+ public static void SV_ShutdownGameProgs() {
+ GameBase.ShutdownGame();
+ }
+
+ /**
+ * SV_InitGameProgs
+ * <p>
+ * Init the game subsystem for a new map.
+ */
+
+ public static void SV_InitGameProgs() {
+
+ // unload anything we have now
+ SV_ShutdownGameProgs();
+
+ game_import_t gimport = new game_import_t();
+
+ // all functions set in game_export_t (rst)
+ GameBase.GetGameApi(gimport);
+
+ GameSave.InitGame();
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.server;
+
+import lwjake2.Defines;
+import lwjake2.Globals;
+import lwjake2.client.Client;
+import lwjake2.client.SCR;
+import lwjake2.game.*;
+import lwjake2.qcommon.*;
+import lwjake2.sys.NET;
+import lwjake2.util.Lib;
+import lwjake2.util.Math3D;
+
+import java.io.IOException;
+import java.io.RandomAccessFile;
+
+public class SV_INIT {
+
+ public static server_static_t svs = new server_static_t(); // persistant
+ public static server_t sv = new server_t(); // local server
+ private static String firstmap = "";
+
+ /**
+ * SV_FindIndex.
+ */
+ private static int SV_FindIndex(String name, int start, int max) {
+ int i;
+
+ if (name == null || name.length() == 0)
+ return 0;
+
+ for (i = 1; i < max && sv.configstrings[start + i] != null; i++)
+ if (0 == Lib.strcmp(sv.configstrings[start + i], name))
+ return i;
+
+ if (i == max)
+ Com.Error(Defines.ERR_DROP, "*Index: overflow");
+
+ sv.configstrings[start + i] = name;
+
+ if (sv.state != Defines.ss_loading) {
+ // send the update to everyone
+ SZ.Clear(sv.multicast);
+ MSG.WriteChar(sv.multicast, Defines.svc_configstring);
+ MSG.WriteShort(sv.multicast, start + i);
+ MSG.WriteString(sv.multicast, name);
+ SV_SEND.SV_Multicast(Globals.vec3_origin, Defines.MULTICAST_ALL_R);
+ }
+
+ return i;
+ }
+
+ public static int SV_ModelIndex(String name) {
+ return SV_FindIndex(name, Defines.CS_MODELS, Defines.MAX_MODELS);
+ }
+
+ public static int SV_SoundIndex(String name) {
+ return SV_FindIndex(name, Defines.CS_SOUNDS, Defines.MAX_SOUNDS);
+ }
+
+ public static int SV_ImageIndex(String name) {
+ return SV_FindIndex(name, Defines.CS_IMAGES, Defines.MAX_IMAGES);
+ }
+
+ /**
+ * SV_CreateBaseline
+ * <p>
+ * Entity baselines are used to compress the update messages to the clients --
+ * only the fields that differ from the baseline will be transmitted.
+ */
+ private static void SV_CreateBaseline() {
+ EDict svent;
+ int entnum;
+
+ for (entnum = 1; entnum < GameBase.num_edicts; entnum++) {
+ svent = GameBase.g_edicts[entnum];
+
+ if (!svent.inuse)
+ continue;
+ if (0 == svent.s.modelindex && 0 == svent.s.sound
+ && 0 == svent.s.effects)
+ continue;
+
+ svent.s.number = entnum;
+
+ // take current state as baseline
+ Math3D.vectorCopy(svent.s.origin, svent.s.old_origin);
+ sv.baselines[entnum].set(svent.s);
+ }
+ }
+
+ /**
+ * SV_CheckForSavegame.
+ */
+ private static void SV_CheckForSavegame() {
+
+ String name;
+ RandomAccessFile f;
+
+ int i;
+
+ if (Server.sv_noreload.value != 0)
+ return;
+
+ if (Cvar.variableValue("deathmatch") != 0)
+ return;
+
+ name = FS.Gamedir() + "/save/current/" + sv.name + ".sav";
+ try {
+ f = new RandomAccessFile(name, "r");
+ } catch (Exception e) {
+ return;
+ }
+
+ try {
+ f.close();
+ } catch (IOException e1) {
+ e1.printStackTrace();
+ }
+
+ SV_WORLD.SV_ClearWorld();
+
+ // get configstrings and areaportals
+ SV_CCMDS.SV_ReadLevelFile();
+
+ if (!sv.loadgame) {
+ // coming back to a level after being in a different
+ // level, so run it for ten seconds
+
+ // rlava2 was sending too many lightstyles, and overflowing the
+ // reliable data. temporarily changing the server state to loading
+ // prevents these from being passed down.
+ int previousState; // PGM
+
+ previousState = sv.state; // PGM
+ sv.state = Defines.ss_loading; // PGM
+ for (i = 0; i < 100; i++)
+ GameBase.G_RunFrame();
+
+ sv.state = previousState; // PGM
+ }
+ }
+
+ /**
+ * SV_SpawnServer.
+ * <p>
+ * Change the server to a new map, taking all connected clients along with
+ * it.
+ */
+ private static void SV_SpawnServer(String server, String spawnpoint,
+ int serverstate, boolean attractloop, boolean loadgame) {
+ int i;
+ int checksum = 0;
+
+ if (attractloop)
+ Cvar.set("paused", "0");
+
+ Com.Printf("------- Server Initialization -------\n");
+
+ Com.DPrintf("SpawnServer: " + server + "\n");
+ if (sv.demofile != null)
+ try {
+ sv.demofile.close();
+ } catch (Exception ignored) {
+ }
+
+ // any partially connected client will be restarted
+ svs.spawncount++;
+
+ sv.state = Defines.ss_dead;
+
+ Globals.server_state = sv.state;
+
+ // wipe the entire per-level structure
+ sv = new server_t();
+
+ svs.realtime = 0;
+ sv.loadgame = loadgame;
+ sv.attractloop = attractloop;
+
+ // save name for levels that don't set message
+ sv.configstrings[Defines.CS_NAME] = server;
+
+ if (Cvar.variableValue("deathmatch") != 0) {
+ sv.configstrings[Defines.CS_AIRACCEL] = ""
+ + Server.sv_airaccelerate.value;
+ PMove.pm_airaccelerate = Server.sv_airaccelerate.value;
+ } else {
+ sv.configstrings[Defines.CS_AIRACCEL] = "0";
+ PMove.pm_airaccelerate = 0;
+ }
+
+ SZ.Init(sv.multicast, sv.multicast_buf, sv.multicast_buf.length);
+
+ sv.name = server;
+
+ // leave slots at start for clients only
+ for (i = 0; i < Server.maxclients.value; i++) {
+ // needs to reconnect
+ if (svs.clients[i].state > Defines.cs_connected)
+ svs.clients[i].state = Defines.cs_connected;
+ svs.clients[i].lastframe = -1;
+ }
+
+ sv.time = 1000;
+
+ sv.name = server;
+ sv.configstrings[Defines.CS_NAME] = server;
+
+ int iw[] = {checksum};
+
+ if (serverstate != Defines.ss_game) {
+ sv.models[1] = CM.CM_LoadMap("", false, iw); // no real map
+ } else {
+ sv.configstrings[Defines.CS_MODELS + 1] = "maps/" + server + ".bsp";
+ sv.models[1] = CM.CM_LoadMap(
+ sv.configstrings[Defines.CS_MODELS + 1], false, iw);
+ }
+ checksum = iw[0];
+ sv.configstrings[Defines.CS_MAPCHECKSUM] = "" + checksum;
+
+
+ // clear physics interaction links
+
+ SV_WORLD.SV_ClearWorld();
+
+ for (i = 1; i < CM.CM_NumInlineModels(); i++) {
+ sv.configstrings[Defines.CS_MODELS + 1 + i] = "*" + i;
+
+ // copy references
+ sv.models[i + 1] = CM.InlineModel(sv.configstrings[Defines.CS_MODELS + 1 + i]);
+ }
+
+
+ // spawn the rest of the entities on the map
+
+ // precache and static commands can be issued during
+ // map initialization
+
+ sv.state = Defines.ss_loading;
+ Globals.server_state = sv.state;
+
+ // load and spawn all other entities
+ GameSpawn.SpawnEntities(sv.name, CM.CM_EntityString(), spawnpoint);
+
+ // run two frames to allow everything to settle
+ GameBase.G_RunFrame();
+ GameBase.G_RunFrame();
+
+ // all precaches are complete
+ sv.state = serverstate;
+ Globals.server_state = sv.state;
+
+ // create a baseline for more efficient communications
+ SV_CreateBaseline();
+
+ // check for a savegame
+ SV_CheckForSavegame();
+
+ // set serverinfo variable
+ Cvar.fullSet("mapname", sv.name, Defines.CVAR_SERVERINFO
+ | Defines.CVAR_NOSET);
+ }
+
+ /**
+ * SV_InitGame.
+ * <p>
+ * A brand new game has been started.
+ */
+ public static void SV_InitGame() {
+ int i;
+ EDict ent;
+ //char idmaster[32];
+ String idmaster;
+
+ if (svs.initialized) {
+ // cause any connected clients to reconnect
+ Server.SV_Shutdown("Server restarted\n", true);
+ } else {
+ // make sure the client is down
+ Client.Drop();
+ SCR.BeginLoadingPlaque();
+ }
+
+ // get any latched variable changes (maxclients, etc)
+ Cvar.getLatchedVars();
+
+ svs.initialized = true;
+
+ if (Cvar.variableValue("coop") != 0
+ && Cvar.variableValue("deathmatch") != 0) {
+ Com.Printf("Deathmatch and Coop both set, disabling Coop\n");
+ Cvar.fullSet("coop", "0", Defines.CVAR_SERVERINFO
+ | Defines.CVAR_LATCH);
+ }
+
+ // dedicated servers are can't be single player and are usually DM
+ // so unless they explicity set coop, force it to deathmatch
+ if (Globals.dedicated.value != 0) {
+ if (0 == Cvar.variableValue("coop"))
+ Cvar.fullSet("deathmatch", "1", Defines.CVAR_SERVERINFO
+ | Defines.CVAR_LATCH);
+ }
+
+ // init clients
+ if (Cvar.variableValue("deathmatch") != 0) {
+ if (Server.maxclients.value <= 1)
+ Cvar.fullSet("maxclients", "8", Defines.CVAR_SERVERINFO
+ | Defines.CVAR_LATCH);
+ else if (Server.maxclients.value > Defines.MAX_CLIENTS)
+ Cvar.fullSet("maxclients", "" + Defines.MAX_CLIENTS,
+ Defines.CVAR_SERVERINFO | Defines.CVAR_LATCH);
+ } else if (Cvar.variableValue("coop") != 0) {
+ if (Server.maxclients.value <= 1 || Server.maxclients.value > 4)
+ Cvar.fullSet("maxclients", "4", Defines.CVAR_SERVERINFO
+ | Defines.CVAR_LATCH);
+
+ } else // non-deathmatch, non-coop is one player
+ {
+ Cvar.fullSet("maxclients", "1", Defines.CVAR_SERVERINFO
+ | Defines.CVAR_LATCH);
+ }
+
+ svs.spawncount = Lib.rand();
+ svs.clients = new client_t[(int) Server.maxclients.value];
+ for (int n = 0; n < svs.clients.length; n++) {
+ svs.clients[n] = new client_t();
+ svs.clients[n].serverindex = n;
+ }
+ svs.num_client_entities = ((int) Server.maxclients.value)
+ * Defines.UPDATE_BACKUP * 64; //ok.
+
+ svs.client_entities = new entity_state_t[svs.num_client_entities];
+ for (int n = 0; n < svs.client_entities.length; n++)
+ svs.client_entities[n] = new entity_state_t(null);
+
+ // init network stuff
+ NET.Config((Server.maxclients.value > 1));
+
+ // heartbeats will always be sent to the id master
+ svs.last_heartbeat = -99999; // send immediately
+ idmaster = "192.246.40.37:" + Defines.PORT_MASTER;
+ NET.StringToAdr(idmaster, Server.master_adr[0]);
+
+ // init game
+ SV_GAME.SV_InitGameProgs();
+
+ for (i = 0; i < Server.maxclients.value; i++) {
+ ent = GameBase.g_edicts[i + 1];
+ svs.clients[i].edict = ent;
+ svs.clients[i].lastcmd = new usercmd_t();
+ }
+ }
+ // server info
+
+ /**
+ * SV_Map
+ * <p>
+ * the full syntax is:
+ * <p>
+ * map [*] <map>$ <startspot>+ <nextserver>
+ * <p>
+ * command from the console or progs. Map can also be a.cin, .pcx, or .dm2 file.
+ * <p>
+ * Nextserver is used to allow a cinematic to play, then proceed to
+ * another level:
+ * <p>
+ * map tram.cin+jail_e3
+ */
+ public static void SV_Map(boolean attractloop, String levelstring, boolean loadgame) {
+
+ int l;
+ String level, spawnpoint;
+
+ sv.loadgame = loadgame;
+ sv.attractloop = attractloop;
+
+ if (sv.state == Defines.ss_dead && !sv.loadgame)
+ SV_InitGame(); // the game is just starting
+
+ level = levelstring; // bis hier her ok.
+
+ // if there is a + in the map, set nextserver to the remainder
+
+ int c = level.indexOf('+');
+ if (c != -1) {
+ Cvar.set("nextserver", "gamemap \"" + level.substring(c + 1) + "\"");
+ level = level.substring(0, c);
+ } else {
+ Cvar.set("nextserver", "");
+ }
+
+ // rst: base1 works for full, damo1 works for demo, so we need to store first map.
+ if (firstmap.length() == 0) {
+ if (!levelstring.endsWith(".cin") && !levelstring.endsWith(".pcx") && !levelstring.endsWith(".dm2")) {
+ int pos = levelstring.indexOf('+');
+ firstmap = levelstring.substring(pos + 1);
+ }
+ }
+
+ // ZOID: special hack for end game screen in coop mode
+ if (Cvar.variableValue("coop") != 0 && level.equals("victory.pcx"))
+ Cvar.set("nextserver", "gamemap \"*" + firstmap + "\"");
+
+ // if there is a $, use the remainder as a spawnpoint
+ int pos = level.indexOf('$');
+ if (pos != -1) {
+ spawnpoint = level.substring(pos + 1);
+ level = level.substring(0, pos);
+
+ } else
+ spawnpoint = "";
+
+ // skip the end-of-unit flag * if necessary
+ if (level.charAt(0) == '*')
+ level = level.substring(1);
+
+ l = level.length();
+ if (l > 4 && level.endsWith(".cin")) {
+ SCR.BeginLoadingPlaque(); // for local system
+ SV_SEND.SV_BroadcastCommand("changing\n");
+ SV_SpawnServer(level, spawnpoint, Defines.ss_cinematic,
+ attractloop, loadgame);
+ } else if (l > 4 && level.endsWith(".dm2")) {
+ SCR.BeginLoadingPlaque(); // for local system
+ SV_SEND.SV_BroadcastCommand("changing\n");
+ SV_SpawnServer(level, spawnpoint, Defines.ss_demo, attractloop,
+ loadgame);
+ } else if (l > 4 && level.endsWith(".pcx")) {
+ SCR.BeginLoadingPlaque(); // for local system
+ SV_SEND.SV_BroadcastCommand("changing\n");
+ SV_SpawnServer(level, spawnpoint, Defines.ss_pic, attractloop,
+ loadgame);
+ } else {
+ SCR.BeginLoadingPlaque(); // for local system
+ SV_SEND.SV_BroadcastCommand("changing\n");
+ SV_SEND.SV_SendClientMessages();
+ SV_SpawnServer(level, spawnpoint, Defines.ss_game, attractloop,
+ loadgame);
+ CommandBuffer.CopyToDefer();
+ }
+
+ SV_SEND.SV_BroadcastCommand("reconnect\n");
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.server;
+
+import lwjake2.Defines;
+import lwjake2.Globals;
+import lwjake2.game.EDict;
+import lwjake2.game.EndianHandler;
+import lwjake2.qcommon.*;
+import lwjake2.util.Lib;
+import lwjake2.util.Math3D;
+
+import java.io.IOException;
+
+public class SV_SEND {
+ /*
+ =============================================================================
+
+ Com_Printf redirection
+
+ =============================================================================
+ */
+
+ public static final StringBuffer sv_outputbuf = new StringBuffer();
+ private static final float[] origin_v = {0, 0, 0};
+ private static final sizebuf_t msg = new sizebuf_t();
+ /*
+ =============================================================================
+
+ EVENT MESSAGES
+
+ =============================================================================
+ */
+ private static final byte msgbuf[] = new byte[Defines.MAX_MSGLEN];
+ private static final byte[] NULLBYTE = {0};
+
+ public static void SV_FlushRedirect(int sv_redirected, byte outputbuf[]) {
+ if (sv_redirected == Defines.RD_PACKET) {
+ String s = ("print\n" + Lib.CtoJava(outputbuf));
+ Netchan.Netchan_OutOfBand(Defines.NS_SERVER, Globals.net_from, s.length(), Lib.stringToBytes(s));
+ } else if (sv_redirected == Defines.RD_CLIENT) {
+ MSG.WriteByte(Server.sv_client.netchan.message, Defines.svc_print);
+ MSG.WriteByte(Server.sv_client.netchan.message, Defines.PRINT_HIGH);
+ MSG.WriteString(Server.sv_client.netchan.message, outputbuf);
+ }
+ }
+
+ /*
+ =================
+ SV_ClientPrintf
+
+ Sends text across to be displayed if the level passes
+ =================
+ */
+ public static void SV_ClientPrintf(client_t cl, int level, String s) {
+
+ if (level < cl.messagelevel)
+ return;
+
+ MSG.WriteByte(cl.netchan.message, Defines.svc_print);
+ MSG.WriteByte(cl.netchan.message, level);
+ MSG.WriteString(cl.netchan.message, s);
+ }
+
+ /*
+ =================
+ SV_BroadcastPrintf
+
+ Sends text to all active clients
+ =================
+ */
+ public static void SV_BroadcastPrintf(int level, String s) {
+
+ client_t cl;
+
+ // echo to console
+ if (Globals.dedicated.value != 0) {
+
+ Com.Printf(s);
+ }
+
+ for (int i = 0; i < Server.maxclients.value; i++) {
+ cl = SV_INIT.svs.clients[i];
+ if (level < cl.messagelevel)
+ continue;
+ if (cl.state != Defines.cs_spawned)
+ continue;
+ MSG.WriteByte(cl.netchan.message, Defines.svc_print);
+ MSG.WriteByte(cl.netchan.message, level);
+ MSG.WriteString(cl.netchan.message, s);
+ }
+ }
+ /*
+ ===============================================================================
+
+ FRAME UPDATES
+
+ ===============================================================================
+ */
+
+ /*
+ =================
+ SV_BroadcastCommand
+
+ Sends text to all active clients
+ =================
+ */
+ public static void SV_BroadcastCommand(String s) {
+
+ if (SV_INIT.sv.state == 0)
+ return;
+
+ MSG.WriteByte(SV_INIT.sv.multicast, Defines.svc_stufftext);
+ MSG.WriteString(SV_INIT.sv.multicast, s);
+ SV_Multicast(null, Defines.MULTICAST_ALL_R);
+ }
+
+ /*
+ =================
+ SV_Multicast
+
+ Sends the contents of sv.multicast to a subset of the clients,
+ then clears sv.multicast.
+
+ MULTICAST_ALL same as broadcast (origin can be null)
+ MULTICAST_PVS send to clients potentially visible from org
+ MULTICAST_PHS send to clients potentially hearable from org
+ =================
+ */
+ public static void SV_Multicast(float[] origin, int to) {
+ client_t client;
+ byte mask[];
+ int leafnum, cluster;
+ int j;
+ boolean reliable;
+ int area1, area2;
+
+ reliable = false;
+
+ if (to != Defines.MULTICAST_ALL_R && to != Defines.MULTICAST_ALL) {
+ leafnum = CM.CM_PointLeafnum(origin);
+ area1 = CM.CM_LeafArea(leafnum);
+ } else {
+ leafnum = 0; // just to avoid compiler warnings
+ area1 = 0;
+ }
+
+ // if doing a serverrecord, store everything
+ if (SV_INIT.svs.demofile != null)
+ SZ.Write(SV_INIT.svs.demo_multicast, SV_INIT.sv.multicast.data, SV_INIT.sv.multicast.cursize);
+
+ switch (to) {
+ case Defines.MULTICAST_ALL_R:
+ reliable = true; // intentional fallthrough, no break here
+ case Defines.MULTICAST_ALL:
+ leafnum = 0;
+ mask = null;
+ break;
+
+ case Defines.MULTICAST_PHS_R:
+ reliable = true; // intentional fallthrough
+ case Defines.MULTICAST_PHS:
+ leafnum = CM.CM_PointLeafnum(origin);
+ cluster = CM.CM_LeafCluster(leafnum);
+ mask = CM.CM_ClusterPHS(cluster);
+ break;
+
+ case Defines.MULTICAST_PVS_R:
+ reliable = true; // intentional fallthrough
+ case Defines.MULTICAST_PVS:
+ leafnum = CM.CM_PointLeafnum(origin);
+ cluster = CM.CM_LeafCluster(leafnum);
+ mask = CM.CM_ClusterPVS(cluster);
+ break;
+
+ default:
+ mask = null;
+ Com.Error(Defines.ERR_FATAL, "SV_Multicast: bad to:" + to + "\n");
+ }
+
+ // send the data to all relevent clients
+ for (j = 0; j < Server.maxclients.value; j++) {
+ client = SV_INIT.svs.clients[j];
+
+ if (client.state == Defines.cs_free || client.state == Defines.cs_zombie)
+ continue;
+ if (client.state != Defines.cs_spawned && !reliable)
+ continue;
+
+ if (mask != null) {
+ leafnum = CM.CM_PointLeafnum(client.edict.s.origin);
+ cluster = CM.CM_LeafCluster(leafnum);
+ area2 = CM.CM_LeafArea(leafnum);
+ if (!CM.CM_AreasConnected(area1, area2))
+ continue;
+
+ // quake2 bugfix
+ if (cluster == -1)
+ continue;
+ if (mask != null && (0 == (mask[cluster >> 3] & (1 << (cluster & 7)))))
+ continue;
+ }
+
+ if (reliable)
+ SZ.Write(client.netchan.message, SV_INIT.sv.multicast.data, SV_INIT.sv.multicast.cursize);
+ else
+ SZ.Write(client.datagram, SV_INIT.sv.multicast.data, SV_INIT.sv.multicast.cursize);
+ }
+
+ SZ.Clear(SV_INIT.sv.multicast);
+ }
+
+ /*
+ ==================
+ SV_StartSound
+
+ Each entity can have eight independant sound sources, like voice,
+ weapon, feet, etc.
+
+ If cahnnel & 8, the sound will be sent to everyone, not just
+ things in the PHS.
+
+ FIXME: if entity isn't in PHS, they must be forced to be sent or
+ have the origin explicitly sent.
+
+ Channel 0 is an auto-allocate channel, the others override anything
+ already running on that entity/channel pair.
+
+ An attenuation of 0 will play full volume everywhere in the level.
+ Larger attenuations will drop off. (max 4 attenuation)
+
+ Timeofs can range from 0.0 to 0.1 to cause sounds to be started
+ later in the frame than they normally would.
+
+ If origin is null, the origin is determined from the entity origin
+ or the midpoint of the entity box for bmodels.
+ ==================
+ */
+ public static void SV_StartSound(
+ float[] origin,
+ EDict entity,
+ int channel,
+ int soundindex,
+ float volume,
+ float attenuation,
+ float timeofs) {
+ int sendchan;
+ int flags;
+ int i;
+ int ent;
+ boolean use_phs;
+
+ if (volume < 0 || volume > 1.0)
+ Com.Error(Defines.ERR_FATAL, "SV_StartSound: volume = " + volume);
+
+ if (attenuation < 0 || attenuation > 4)
+ Com.Error(Defines.ERR_FATAL, "SV_StartSound: attenuation = " + attenuation);
+
+ // if (channel < 0 || channel > 15)
+ // Com_Error (ERR_FATAL, "SV_StartSound: channel = %i", channel);
+
+ if (timeofs < 0 || timeofs > 0.255)
+ Com.Error(Defines.ERR_FATAL, "SV_StartSound: timeofs = " + timeofs);
+
+ ent = entity.index;
+
+ // no PHS flag
+ if ((channel & 8) != 0) {
+ use_phs = false;
+ channel &= 7;
+ } else
+ use_phs = true;
+
+ sendchan = (ent << 3) | (channel & 7);
+
+ flags = 0;
+ if (volume != Defines.DEFAULT_SOUND_PACKET_VOLUME)
+ flags |= Defines.SND_VOLUME;
+ if (attenuation != Defines.DEFAULT_SOUND_PACKET_ATTENUATION)
+ flags |= Defines.SND_ATTENUATION;
+
+ // the client doesn't know that bmodels have weird origins
+ // the origin can also be explicitly set
+ if ((entity.svflags & Defines.SVF_NOCLIENT) != 0 || (entity.solid == Defines.SOLID_BSP) || origin != null)
+ flags |= Defines.SND_POS;
+
+ // always send the entity number for channel overrides
+ flags |= Defines.SND_ENT;
+
+ if (timeofs != 0)
+ flags |= Defines.SND_OFFSET;
+
+ // use the entity origin unless it is a bmodel or explicitly specified
+ if (origin == null) {
+ origin = origin_v;
+ if (entity.solid == Defines.SOLID_BSP) {
+ for (i = 0; i < 3; i++)
+ origin_v[i] = entity.s.origin[i] + 0.5f * (entity.mins[i] + entity.maxs[i]);
+ } else {
+ Math3D.vectorCopy(entity.s.origin, origin_v);
+ }
+ }
+
+ MSG.WriteByte(SV_INIT.sv.multicast, Defines.svc_sound);
+ MSG.WriteByte(SV_INIT.sv.multicast, flags);
+ MSG.WriteByte(SV_INIT.sv.multicast, soundindex);
+
+ if ((flags & Defines.SND_VOLUME) != 0)
+ MSG.WriteByte(SV_INIT.sv.multicast, volume * 255);
+ if ((flags & Defines.SND_ATTENUATION) != 0)
+ MSG.WriteByte(SV_INIT.sv.multicast, attenuation * 64);
+ if ((flags & Defines.SND_OFFSET) != 0)
+ MSG.WriteByte(SV_INIT.sv.multicast, timeofs * 1000);
+
+ if ((flags & Defines.SND_ENT) != 0)
+ MSG.WriteShort(SV_INIT.sv.multicast, sendchan);
+
+ if ((flags & Defines.SND_POS) != 0)
+ MSG.WritePos(SV_INIT.sv.multicast, origin);
+
+ // if the sound doesn't attenuate,send it to everyone
+ // (global radio chatter, voiceovers, etc)
+ if (attenuation == Defines.ATTN_NONE)
+ use_phs = false;
+
+ if ((channel & Defines.CHAN_RELIABLE) != 0) {
+ if (use_phs)
+ SV_Multicast(origin, Defines.MULTICAST_PHS_R);
+ else
+ SV_Multicast(origin, Defines.MULTICAST_ALL_R);
+ } else {
+ if (use_phs)
+ SV_Multicast(origin, Defines.MULTICAST_PHS);
+ else
+ SV_Multicast(origin, Defines.MULTICAST_ALL);
+ }
+ }
+
+ /*
+ =======================
+ SV_SendClientDatagram
+ =======================
+ */
+ private static void SV_SendClientDatagram(client_t client) {
+ //byte msg_buf[] = new byte[Defines.MAX_MSGLEN];
+
+ SV_ENTS.SV_BuildClientFrame(client);
+
+ SZ.Init(msg, msgbuf, msgbuf.length);
+ msg.allowoverflow = true;
+
+ // send over all the relevant entity_state_t
+ // and the player_state_t
+ SV_ENTS.SV_WriteFrameToClient(client, msg);
+
+ // copy the accumulated multicast datagram
+ // for this client out to the message
+ // it is necessary for this to be after the WriteEntities
+ // so that entity references will be current
+ if (client.datagram.overflowed)
+ Com.Printf("WARNING: datagram overflowed for " + client.name + "\n");
+ else
+ SZ.Write(msg, client.datagram.data, client.datagram.cursize);
+ SZ.Clear(client.datagram);
+
+ if (msg.overflowed) { // must have room left for the packet header
+ Com.Printf("WARNING: msg overflowed for " + client.name + "\n");
+ SZ.Clear(msg);
+ }
+
+ // send the datagram
+ Netchan.Transmit(client.netchan, msg.cursize, msg.data);
+
+ // record the size for rate estimation
+ client.message_size[SV_INIT.sv.framenum % Defines.RATE_MESSAGES] = msg.cursize;
+
+ }
+
+ /*
+ ==================
+ SV_DemoCompleted
+ ==================
+ */
+ private static void SV_DemoCompleted() {
+ if (SV_INIT.sv.demofile != null) {
+ try {
+ SV_INIT.sv.demofile.close();
+ } catch (IOException e) {
+ Com.Printf("IOError closing d9emo fiele:" + e);
+ }
+ SV_INIT.sv.demofile = null;
+ }
+ SV_USER.SV_Nextserver();
+ }
+
+ /*
+ =======================
+ SV_RateDrop
+
+ Returns true if the client is over its current
+ bandwidth estimation and should not be sent another packet
+ =======================
+ */
+ private static boolean SV_RateDrop(client_t c) {
+ int total;
+ int i;
+
+ // never drop over the loopback
+ if (c.netchan.remote_address.type == Defines.NA_LOOPBACK)
+ return false;
+
+ total = 0;
+
+ for (i = 0; i < Defines.RATE_MESSAGES; i++) {
+ total += c.message_size[i];
+ }
+
+ if (total > c.rate) {
+ c.surpressCount++;
+ c.message_size[SV_INIT.sv.framenum % Defines.RATE_MESSAGES] = 0;
+ return true;
+ }
+
+ return false;
+ }
+
+ /*
+ =======================
+ SV_SendClientMessages
+ =======================
+ */
+ public static void SV_SendClientMessages() {
+ int i;
+ client_t c;
+ int msglen;
+ int r;
+
+ msglen = 0;
+
+ // read the next demo message if needed
+ if (SV_INIT.sv.state == Defines.ss_demo && SV_INIT.sv.demofile != null) {
+ if (Server.sv_paused.value != 0)
+ msglen = 0;
+ else {
+ // get the next message
+ //r = fread (&msglen, 4, 1, sv.demofile);
+ try {
+ msglen = EndianHandler.swapInt(SV_INIT.sv.demofile.readInt());
+ } catch (Exception e) {
+ SV_DemoCompleted();
+ return;
+ }
+
+ //msglen = LittleLong (msglen);
+ if (msglen == -1) {
+ SV_DemoCompleted();
+ return;
+ }
+ if (msglen > Defines.MAX_MSGLEN)
+ Com.Error(Defines.ERR_DROP, "SV_SendClientMessages: msglen > MAX_MSGLEN");
+
+ //r = fread (msgbuf, msglen, 1, sv.demofile);
+ r = 0;
+ try {
+ r = SV_INIT.sv.demofile.read(msgbuf, 0, msglen);
+ } catch (IOException e1) {
+ Com.Printf("IOError: reading demo file, " + e1);
+ }
+ if (r != msglen) {
+ SV_DemoCompleted();
+ return;
+ }
+ }
+ }
+
+ // send a message to each connected client
+ for (i = 0; i < Server.maxclients.value; i++) {
+ c = SV_INIT.svs.clients[i];
+
+ if (c.state == 0)
+ continue;
+ // if the reliable message overflowed,
+ // drop the client
+ if (c.netchan.message.overflowed) {
+ SZ.Clear(c.netchan.message);
+ SZ.Clear(c.datagram);
+ SV_BroadcastPrintf(Defines.PRINT_HIGH, c.name + " overflowed\n");
+ Server.SV_DropClient(c);
+ }
+
+ if (SV_INIT.sv.state == Defines.ss_cinematic
+ || SV_INIT.sv.state == Defines.ss_demo
+ || SV_INIT.sv.state == Defines.ss_pic)
+ Netchan.Transmit(c.netchan, msglen, msgbuf);
+ else if (c.state == Defines.cs_spawned) {
+ // don't overrun bandwidth
+ if (SV_RateDrop(c))
+ continue;
+
+ SV_SendClientDatagram(c);
+ } else {
+ // just update reliable if needed
+ if (c.netchan.message.cursize != 0 || Globals.curtime - c.netchan.last_sent > 1000)
+ Netchan.Transmit(c.netchan, 0, NULLBYTE);
+ }
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.server;
+
+import lwjake2.Defines;
+import lwjake2.Globals;
+import lwjake2.game.*;
+import lwjake2.qcommon.*;
+import lwjake2.util.Lib;
+
+import java.io.IOException;
+
+public class SV_USER {
+
+ public static final int MAX_STRINGCMDS = 8;
+ static EDict sv_player;
+ static final ucmd_t[] ucmds = {
+ // auto issued
+ new ucmd_t("new", SV_USER::SV_New_f), new ucmd_t("configstrings", SV_USER::SV_Configstrings_f), new ucmd_t("baselines", SV_USER::SV_Baselines_f), new ucmd_t("begin", SV_USER::SV_Begin_f), new ucmd_t("nextserver", SV_USER::SV_Nextserver_f), new ucmd_t("disconnect", SV_USER::SV_Disconnect_f),
+
+ // issued by hand at client consoles
+ new ucmd_t("info", SV_USER::SV_ShowServerinfo_f), new ucmd_t("download", SV_USER::SV_BeginDownload_f), new ucmd_t("nextdl", SV_USER::SV_NextDownload_f)};
+
+ /*
+ * ================== SV_BeginDemoServer ==================
+ */
+ public static void SV_BeginDemoserver() {
+ String name;
+
+ name = "demos/" + SV_INIT.sv.name;
+ try {
+ SV_INIT.sv.demofile = FS.FOpenFile(name);
+ } catch (IOException e) {
+ Com.Error(Defines.ERR_DROP, "Couldn't open " + name + "\n");
+ }
+ if (SV_INIT.sv.demofile == null)
+ Com.Error(Defines.ERR_DROP, "Couldn't open " + name + "\n");
+ }
+
+ /*
+ * ============================================================
+ *
+ * USER STRINGCMD EXECUTION
+ *
+ * sv_client and sv_player will be valid.
+ * ============================================================
+ */
+
+ /*
+ * ================ SV_New_f
+ *
+ * Sends the first message from the server to a connected client. This will
+ * be sent on the initial connection and upon each server load.
+ * ================
+ */
+ public static void SV_New_f() {
+ String gamedir;
+ int playernum;
+ EDict ent;
+
+ Com.DPrintf("New() from " + Server.sv_client.name + "\n");
+
+ if (Server.sv_client.state != Defines.cs_connected) {
+ Com.Printf("New not valid -- already spawned\n");
+ return;
+ }
+
+ // demo servers just dump the file message
+ if (SV_INIT.sv.state == Defines.ss_demo) {
+ SV_BeginDemoserver();
+ return;
+ }
+
+ //
+ // serverdata needs to go over for all types of servers
+ // to make sure the protocol is right, and to set the gamedir
+ //
+ gamedir = Cvar.variableString("gamedir");
+
+ // send the serverdata
+ MSG.WriteByte(Server.sv_client.netchan.message,
+ Defines.svc_serverdata);
+ MSG.WriteInt(Server.sv_client.netchan.message,
+ Defines.PROTOCOL_VERSION);
+
+ MSG.WriteLong(Server.sv_client.netchan.message,
+ SV_INIT.svs.spawncount);
+ MSG.WriteByte(Server.sv_client.netchan.message,
+ SV_INIT.sv.attractloop ? 1 : 0);
+ MSG.WriteString(Server.sv_client.netchan.message, gamedir);
+
+ if (SV_INIT.sv.state == Defines.ss_cinematic
+ || SV_INIT.sv.state == Defines.ss_pic)
+ playernum = -1;
+ else
+ //playernum = sv_client - svs.clients;
+ playernum = Server.sv_client.serverindex;
+
+ MSG.WriteShort(Server.sv_client.netchan.message, playernum);
+
+ // send full levelname
+ MSG.WriteString(Server.sv_client.netchan.message,
+ SV_INIT.sv.configstrings[Defines.CS_NAME]);
+
+ //
+ // game server
+ //
+ if (SV_INIT.sv.state == Defines.ss_game) {
+ // set up the entity for the client
+ ent = GameBase.g_edicts[playernum + 1];
+ ent.s.number = playernum + 1;
+ Server.sv_client.edict = ent;
+ Server.sv_client.lastcmd = new usercmd_t();
+
+ // begin fetching configstrings
+ MSG.WriteByte(Server.sv_client.netchan.message,
+ Defines.svc_stufftext);
+ MSG.WriteString(Server.sv_client.netchan.message,
+ "cmd configstrings " + SV_INIT.svs.spawncount + " 0\n");
+ }
+
+ }
+
+ /*
+ * ================== SV_Configstrings_f ==================
+ */
+ public static void SV_Configstrings_f() {
+ int start;
+
+ Com.DPrintf("Configstrings() from " + Server.sv_client.name + "\n");
+
+ if (Server.sv_client.state != Defines.cs_connected) {
+ Com.Printf("configstrings not valid -- already spawned\n");
+ return;
+ }
+
+ // handle the case of a level changing while a client was connecting
+ if (Lib.atoi(Cmd.Argv(1)) != SV_INIT.svs.spawncount) {
+ Com.Printf("SV_Configstrings_f from different level\n");
+ SV_New_f();
+ return;
+ }
+
+ start = Lib.atoi(Cmd.Argv(2));
+
+ // write a packet full of data
+
+ while (Server.sv_client.netchan.message.cursize < Defines.MAX_MSGLEN / 2
+ && start < Defines.MAX_CONFIGSTRINGS) {
+ if (SV_INIT.sv.configstrings[start] != null
+ && SV_INIT.sv.configstrings[start].length() != 0) {
+ MSG.WriteByte(Server.sv_client.netchan.message,
+ Defines.svc_configstring);
+ MSG.WriteShort(Server.sv_client.netchan.message, start);
+ MSG.WriteString(Server.sv_client.netchan.message,
+ SV_INIT.sv.configstrings[start]);
+ }
+ start++;
+ }
+
+ // send next command
+
+ if (start == Defines.MAX_CONFIGSTRINGS) {
+ MSG.WriteByte(Server.sv_client.netchan.message,
+ Defines.svc_stufftext);
+ MSG.WriteString(Server.sv_client.netchan.message, "cmd baselines "
+ + SV_INIT.svs.spawncount + " 0\n");
+ } else {
+ MSG.WriteByte(Server.sv_client.netchan.message,
+ Defines.svc_stufftext);
+ MSG.WriteString(Server.sv_client.netchan.message,
+ "cmd configstrings " + SV_INIT.svs.spawncount + " " + start
+ + "\n");
+ }
+ }
+
+ /*
+ * ================== SV_Baselines_f ==================
+ */
+ public static void SV_Baselines_f() {
+ int start;
+ entity_state_t nullstate;
+ entity_state_t base;
+
+ Com.DPrintf("Baselines() from " + Server.sv_client.name + "\n");
+
+ if (Server.sv_client.state != Defines.cs_connected) {
+ Com.Printf("baselines not valid -- already spawned\n");
+ return;
+ }
+
+ // handle the case of a level changing while a client was connecting
+ if (Lib.atoi(Cmd.Argv(1)) != SV_INIT.svs.spawncount) {
+ Com.Printf("SV_Baselines_f from different level\n");
+ SV_New_f();
+ return;
+ }
+
+ start = Lib.atoi(Cmd.Argv(2));
+
+ //memset (&nullstate, 0, sizeof(nullstate));
+ nullstate = new entity_state_t(null);
+
+ // write a packet full of data
+
+ while (Server.sv_client.netchan.message.cursize < Defines.MAX_MSGLEN / 2
+ && start < Defines.MAX_EDICTS) {
+ base = SV_INIT.sv.baselines[start];
+ if (base.modelindex != 0 || base.sound != 0 || base.effects != 0) {
+ MSG.WriteByte(Server.sv_client.netchan.message,
+ Defines.svc_spawnbaseline);
+ MSG.WriteDeltaEntity(nullstate, base,
+ Server.sv_client.netchan.message, true, true);
+ }
+ start++;
+ }
+
+ // send next command
+
+ if (start == Defines.MAX_EDICTS) {
+ MSG.WriteByte(Server.sv_client.netchan.message,
+ Defines.svc_stufftext);
+ MSG.WriteString(Server.sv_client.netchan.message, "precache "
+ + SV_INIT.svs.spawncount + "\n");
+ } else {
+ MSG.WriteByte(Server.sv_client.netchan.message,
+ Defines.svc_stufftext);
+ MSG.WriteString(Server.sv_client.netchan.message, "cmd baselines "
+ + SV_INIT.svs.spawncount + " " + start + "\n");
+ }
+ }
+
+ /*
+ * ================== SV_Begin_f ==================
+ */
+ public static void SV_Begin_f() {
+ Com.DPrintf("Begin() from " + Server.sv_client.name + "\n");
+
+ // handle the case of a level changing while a client was connecting
+ if (Lib.atoi(Cmd.Argv(1)) != SV_INIT.svs.spawncount) {
+ Com.Printf("SV_Begin_f from different level\n");
+ SV_New_f();
+ return;
+ }
+
+ Server.sv_client.state = Defines.cs_spawned;
+
+ // call the game begin function
+ PlayerClient.ClientBegin(SV_USER.sv_player);
+
+ CommandBuffer.InsertFromDefer();
+ }
+
+ /*
+ * ================== SV_NextDownload_f ==================
+ */
+ public static void SV_NextDownload_f() {
+ int r;
+ int percent;
+ int size;
+
+ if (Server.sv_client.download == null)
+ return;
+
+ r = Server.sv_client.downloadsize - Server.sv_client.downloadcount;
+ if (r > 1024)
+ r = 1024;
+
+ MSG.WriteByte(Server.sv_client.netchan.message, Defines.svc_download);
+ MSG.WriteShort(Server.sv_client.netchan.message, r);
+
+ Server.sv_client.downloadcount += r;
+ size = Server.sv_client.downloadsize;
+ if (size == 0)
+ size = 1;
+ percent = Server.sv_client.downloadcount * 100 / size;
+ MSG.WriteByte(Server.sv_client.netchan.message, percent);
+ SZ.Write(Server.sv_client.netchan.message, Server.sv_client.download,
+ Server.sv_client.downloadcount - r, r);
+
+ if (Server.sv_client.downloadcount != Server.sv_client.downloadsize)
+ return;
+
+ FS.FreeFile();
+ Server.sv_client.download = null;
+ }
+
+ //=============================================================================
+
+ /*
+ * ================== SV_BeginDownload_f ==================
+ */
+ public static void SV_BeginDownload_f() {
+ String name;
+ int offset = 0;
+
+ name = Cmd.Argv(1);
+
+ if (Cmd.Argc() > 2)
+ offset = Lib.atoi(Cmd.Argv(2)); // downloaded offset
+
+ // hacked by zoid to allow more conrol over download
+ // first off, no .. or global allow check
+
+ if (name.contains("..")
+ || Server.allow_download.value == 0 // leading dot is no good
+ || name.charAt(0) == '.' // leading slash bad as well, must be
+ // in subdir
+ || name.charAt(0) == '/' // next up, skin check
+ || (name.startsWith("players/") && 0 == Server.allow_download_players.value) // now
+ // models
+ || (name.startsWith("models/") && 0 == Server.allow_download_models.value) // now
+ // sounds
+ || (name.startsWith("sound/") && 0 == Server.allow_download_sounds.value)
+ // now maps (note special case for maps, must not be in pak)
+ || (name.startsWith("maps/") && 0 == Server.allow_download_maps.value) // MUST
+ // be
+ // in a
+ // subdirectory
+ || name.indexOf('/') == -1) { // don't allow anything with ..
+ // path
+ MSG.WriteByte(Server.sv_client.netchan.message,
+ Defines.svc_download);
+ MSG.WriteShort(Server.sv_client.netchan.message, -1);
+ MSG.WriteByte(Server.sv_client.netchan.message, 0);
+ return;
+ }
+
+ if (Server.sv_client.download != null)
+ FS.FreeFile();
+
+ Server.sv_client.download = FS.LoadFile(name);
+
+ // rst: this handles loading errors, no message yet visible
+ if (Server.sv_client.download == null) {
+ return;
+ }
+
+ Server.sv_client.downloadsize = Server.sv_client.download.length;
+ Server.sv_client.downloadcount = offset;
+
+ if (offset > Server.sv_client.downloadsize)
+ Server.sv_client.downloadcount = Server.sv_client.downloadsize;
+
+ if (Server.sv_client.download == null // special check for maps, if it
+ // came from a pak file, don't
+ // allow
+ // download ZOID
+ || (name.startsWith("maps/") && FS.file_from_pak != 0)) {
+ Com.DPrintf("Couldn't download " + name + " to "
+ + Server.sv_client.name + "\n");
+ if (Server.sv_client.download != null) {
+ FS.FreeFile();
+ Server.sv_client.download = null;
+ }
+
+ MSG.WriteByte(Server.sv_client.netchan.message,
+ Defines.svc_download);
+ MSG.WriteShort(Server.sv_client.netchan.message, -1);
+ MSG.WriteByte(Server.sv_client.netchan.message, 0);
+ return;
+ }
+
+ SV_NextDownload_f();
+ Com.DPrintf("Downloading " + name + " to " + Server.sv_client.name
+ + "\n");
+ }
+
+ /*
+ * ================= SV_Disconnect_f
+ *
+ * The client is going to disconnect, so remove the connection immediately
+ * =================
+ */
+ public static void SV_Disconnect_f() {
+ // SV_EndRedirect ();
+ Server.SV_DropClient(Server.sv_client);
+ }
+
+ //============================================================================
+
+ /*
+ * ================== SV_ShowServerinfo_f
+ *
+ * Dumps the serverinfo info string ==================
+ */
+ public static void SV_ShowServerinfo_f() {
+ Info.Print(Cvar.serverinfo());
+ }
+
+ public static void SV_Nextserver() {
+ String v;
+
+ //ZOID, ss_pic can be nextserver'd in coop mode
+ if (SV_INIT.sv.state == Defines.ss_game
+ || (SV_INIT.sv.state == Defines.ss_pic &&
+ 0 == Cvar.variableValue("coop")))
+ return; // can't nextserver while playing a normal game
+
+ SV_INIT.svs.spawncount++; // make sure another doesn't sneak in
+ v = Cvar.variableString("nextserver");
+ //if (!v[0])
+ if (v.length() == 0)
+ CommandBuffer.AddText("killserver\n");
+ else {
+ CommandBuffer.AddText(v);
+ CommandBuffer.AddText("\n");
+ }
+ Cvar.set("nextserver", "");
+ }
+
+ /*
+ * ================== SV_Nextserver_f
+ *
+ * A cinematic has completed or been aborted by a client, so move to the
+ * next server, ==================
+ */
+ public static void SV_Nextserver_f() {
+ if (Lib.atoi(Cmd.Argv(1)) != SV_INIT.svs.spawncount) {
+ Com.DPrintf("Nextserver() from wrong level, from "
+ + Server.sv_client.name + "\n");
+ return; // leftover from last server
+ }
+
+ Com.DPrintf("Nextserver() from " + Server.sv_client.name + "\n");
+
+ SV_Nextserver();
+ }
+
+ /*
+ * ================== SV_ExecuteUserCommand ==================
+ */
+ public static void SV_ExecuteUserCommand(String s) {
+
+ Com.dprintln("SV_ExecuteUserCommand:" + s);
+ SV_USER.ucmd_t u = null;
+
+ Cmd.TokenizeString(s.toCharArray(), true);
+ SV_USER.sv_player = Server.sv_client.edict;
+
+ // SV_BeginRedirect (RD_CLIENT);
+
+ int i = 0;
+ for (; i < SV_USER.ucmds.length; i++) {
+ u = SV_USER.ucmds[i];
+ if (Cmd.Argv(0).equals(u.name)) {
+ u.r.run();
+ break;
+ }
+ }
+
+ if (i == SV_USER.ucmds.length && SV_INIT.sv.state == Defines.ss_game)
+ Cmd.ClientCommand(SV_USER.sv_player);
+
+ // SV_EndRedirect ();
+ }
+
+ public static void SV_ClientThink(client_t cl, usercmd_t cmd) {
+ cl.commandMsec -= cmd.msec & 0xFF;
+
+ if (cl.commandMsec < 0 && Server.sv_enforcetime.value != 0) {
+ Com.DPrintf("commandMsec underflow from " + cl.name + "\n");
+ return;
+ }
+
+ PlayerClient.ClientThink(cl.edict, cmd);
+ }
+
+ /*
+ * ===========================================================================
+ *
+ * USER CMD EXECUTION
+ *
+ * ===========================================================================
+ */
+
+ /*
+ * =================== SV_ExecuteClientMessage
+ *
+ * The current net_message is parsed for the given client
+ * ===================
+ */
+ public static void SV_ExecuteClientMessage(client_t cl) {
+ int c;
+ String s;
+
+ usercmd_t nullcmd = new usercmd_t();
+ usercmd_t oldest = new usercmd_t(), oldcmd = new usercmd_t(), newcmd = new usercmd_t();
+ int net_drop;
+ int stringCmdCount;
+ int checksum, calculatedChecksum;
+ int checksumIndex;
+ boolean move_issued;
+ int lastframe;
+
+ Server.sv_client = cl;
+ SV_USER.sv_player = Server.sv_client.edict;
+
+ // only allow one move command
+ move_issued = false;
+ stringCmdCount = 0;
+
+ while (true) {
+ if (Globals.net_message.readcount > Globals.net_message.cursize) {
+ Com.Printf("SV_ReadClientMessage: bad read:\n");
+ Com.Printf(Lib.hexDump(Globals.net_message.data, 32, false));
+ Server.SV_DropClient(cl);
+ return;
+ }
+
+ c = MSG.ReadByte(Globals.net_message);
+ if (c == -1)
+ break;
+
+ switch (c) {
+ default:
+ Com.Printf("SV_ReadClientMessage: unknown command char\n");
+ Server.SV_DropClient(cl);
+ return;
+
+ case Defines.clc_nop:
+ break;
+
+ case Defines.clc_userinfo:
+ cl.userinfo = MSG.ReadString(Globals.net_message);
+ Server.SV_UserinfoChanged(cl);
+ break;
+
+ case Defines.clc_move:
+ if (move_issued)
+ return; // someone is trying to cheat...
+
+ move_issued = true;
+ checksumIndex = Globals.net_message.readcount;
+ checksum = MSG.ReadByte(Globals.net_message);
+ lastframe = MSG.ReadLong(Globals.net_message);
+
+ if (lastframe != cl.lastframe) {
+ cl.lastframe = lastframe;
+ if (cl.lastframe > 0) {
+ cl.frame_latency[cl.lastframe
+ & (Defines.LATENCY_COUNTS - 1)] = SV_INIT.svs.realtime
+ - cl.frames[cl.lastframe & Defines.UPDATE_MASK].senttime;
+ }
+ }
+
+ //memset (nullcmd, 0, sizeof(nullcmd));
+ nullcmd = new usercmd_t();
+ MSG.ReadDeltaUsercmd(Globals.net_message, nullcmd, oldest);
+ MSG.ReadDeltaUsercmd(Globals.net_message, oldest, oldcmd);
+ MSG.ReadDeltaUsercmd(Globals.net_message, oldcmd, newcmd);
+
+ if (cl.state != Defines.cs_spawned) {
+ cl.lastframe = -1;
+ break;
+ }
+
+ // if the checksum fails, ignore the rest of the packet
+
+ calculatedChecksum = Com.BlockSequenceCRCByte(
+ Globals.net_message.data, checksumIndex + 1,
+ Globals.net_message.readcount - checksumIndex - 1,
+ cl.netchan.incoming_sequence);
+
+ if ((calculatedChecksum & 0xff) != checksum) {
+ Com.DPrintf("Failed command checksum for " + cl.name + " ("
+ + calculatedChecksum + " != " + checksum + ")/"
+ + cl.netchan.incoming_sequence + "\n");
+ return;
+ }
+
+ if (0 == Server.sv_paused.value) {
+ net_drop = cl.netchan.dropped;
+ if (net_drop < 20) {
+
+ //if (net_drop > 2)
+
+ // Com.Printf ("drop %i\n", net_drop);
+ while (net_drop > 2) {
+ SV_ClientThink(cl, cl.lastcmd);
+
+ net_drop--;
+ }
+ if (net_drop > 1)
+ SV_ClientThink(cl, oldest);
+
+ if (net_drop > 0)
+ SV_ClientThink(cl, oldcmd);
+
+ }
+ SV_ClientThink(cl, newcmd);
+ }
+
+ // copy.
+ cl.lastcmd.set(newcmd);
+ break;
+
+ case Defines.clc_stringcmd:
+ s = MSG.ReadString(Globals.net_message);
+
+ // malicious users may try using too many string commands
+ if (++stringCmdCount < SV_USER.MAX_STRINGCMDS)
+ SV_ExecuteUserCommand(s);
+
+ if (cl.state == Defines.cs_zombie)
+ return; // disconnect command
+ break;
+ }
+ }
+ }
+
+ public static class ucmd_t {
+ final String name;
+ final Runnable r;
+
+ public ucmd_t(String n, Runnable r) {
+ name = n;
+ this.r = r;
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.server;
+
+import lwjake2.Defines;
+import lwjake2.Globals;
+import lwjake2.game.*;
+import lwjake2.qcommon.CM;
+import lwjake2.qcommon.Com;
+import lwjake2.util.Math3D;
+
+public class SV_WORLD {
+ private static final int MAX_TOTAL_ENT_LEAFS = 128;
+ // world.c -- world query functions
+ //
+ //
+ //===============================================================================
+ //
+ //ENTITY AREA CHECKING
+ //
+ //FIXME: this use of "area" is different from the bsp file use
+ //===============================================================================
+ private static final areanode_t[] sv_areanodes = new areanode_t[Defines.AREA_NODES];
+ private static final int[] leafs = new int[MAX_TOTAL_ENT_LEAFS];
+ private static final int[] clusters = new int[MAX_TOTAL_ENT_LEAFS];
+ //===========================================================================
+ private static final EDict[] touch = new EDict[Defines.MAX_EDICTS];
+ //===========================================================================
+ private static final EDict[] touchlist = new EDict[Defines.MAX_EDICTS];
+ private static int sv_numareanodes;
+ private static float[] area_mins;
+ private static float[] area_maxs;
+ private static EDict[] area_list;
+ private static int area_count;
+ private static int area_maxcount;
+ private static int area_type;
+
+ static {
+ SV_WORLD.initNodes();
+ }
+
+ private static void initNodes() {
+ for (int n = 0; n < Defines.AREA_NODES; n++)
+ SV_WORLD.sv_areanodes[n] = new areanode_t();
+ }
+
+ // ClearLink is used for new headnodes
+ private static void ClearLink(link_t l) {
+ l.prev = l.next = l;
+ }
+
+ private static void RemoveLink(link_t l) {
+ l.next.prev = l.prev;
+ l.prev.next = l.next;
+ }
+
+ private static void InsertLinkBefore(link_t l, link_t before) {
+ l.next = before;
+ l.prev = before.prev;
+ l.prev.next = l;
+ l.next.prev = l;
+ }
+
+ /*
+ * =============== SV_CreateAreaNode
+ *
+ * Builds a uniformly subdivided tree for the given world size
+ * ===============
+ */
+ private static areanode_t SV_CreateAreaNode(int depth, float[] mins,
+ float[] maxs) {
+ areanode_t anode;
+ float[] size = {0, 0, 0};
+ float[] mins1 = {0, 0, 0}, maxs1 = {0, 0, 0}, mins2 = {0, 0, 0}, maxs2 = {
+ 0, 0, 0};
+ anode = SV_WORLD.sv_areanodes[SV_WORLD.sv_numareanodes];
+ // just for debugging (rst)
+ Math3D.vectorCopy(mins, anode.mins_rst);
+ Math3D.vectorCopy(maxs, anode.maxs_rst);
+ SV_WORLD.sv_numareanodes++;
+ ClearLink(anode.trigger_edicts);
+ ClearLink(anode.solid_edicts);
+ if (depth == Defines.AREA_DEPTH) {
+ anode.axis = -1;
+ anode.children[0] = anode.children[1] = null;
+ return anode;
+ }
+ Math3D.vectorSubtract(maxs, mins, size);
+ if (size[0] > size[1])
+ anode.axis = 0;
+ else
+ anode.axis = 1;
+ anode.dist = 0.5f * (maxs[anode.axis] + mins[anode.axis]);
+ Math3D.vectorCopy(mins, mins1);
+ Math3D.vectorCopy(mins, mins2);
+ Math3D.vectorCopy(maxs, maxs1);
+ Math3D.vectorCopy(maxs, maxs2);
+ maxs1[anode.axis] = mins2[anode.axis] = anode.dist;
+ anode.children[0] = SV_CreateAreaNode(depth + 1, mins2, maxs2);
+ anode.children[1] = SV_CreateAreaNode(depth + 1, mins1, maxs1);
+ return anode;
+ }
+
+ /*
+ * =============== SV_ClearWorld
+ *
+ * ===============
+ */
+ public static void SV_ClearWorld() {
+ initNodes();
+ SV_WORLD.sv_numareanodes = 0;
+ SV_CreateAreaNode(0, SV_INIT.sv.models[1].mins,
+ SV_INIT.sv.models[1].maxs);
+ /*
+ * Com.p("areanodes:" + sv_numareanodes + " (sollten 32 sein)."); for
+ * (int n = 0; n < sv_numareanodes; n++) { Com.Printf( "|%3i|%2i|%8.2f
+ * |%8.2f|%8.2f|%8.2f| %8.2f|%8.2f|%8.2f|\n", new Vargs() .add(n)
+ * .add(sv_areanodes[n].axis) .add(sv_areanodes[n].dist)
+ * .add(sv_areanodes[n].mins_rst[0]) .add(sv_areanodes[n].mins_rst[1])
+ * .add(sv_areanodes[n].mins_rst[2]) .add(sv_areanodes[n].maxs_rst[0])
+ * .add(sv_areanodes[n].maxs_rst[1]) .add(sv_areanodes[n].maxs_rst[2])); }
+ */
+ }
+
+ /*
+ * =============== SV_UnlinkEdict ===============
+ */
+ public static void SV_UnlinkEdict(EDict ent) {
+ if (null == ent.area.prev)
+ return; // not linked in anywhere
+ RemoveLink(ent.area);
+ ent.area.prev = ent.area.next = null;
+ }
+
+ public static void SV_LinkEdict(EDict ent) {
+ areanode_t node;
+ int num_leafs;
+ int j, k;
+ int area;
+ int topnode = 0;
+ if (ent.area.prev != null)
+ SV_UnlinkEdict(ent); // unlink from old position
+ if (ent == GameBase.g_edicts[0])
+ return; // don't add the world
+ if (!ent.inuse)
+ return;
+ // set the size
+ Math3D.vectorSubtract(ent.maxs, ent.mins, ent.size);
+ // encode the size into the entity_state for client prediction
+ if (ent.solid == Defines.SOLID_BBOX
+ && 0 == (ent.svflags & Defines.SVF_DEADMONSTER)) {
+ // assume that x/y are equal and symetric
+ int i = (int) (ent.maxs[0] / 8);
+ if (i < 1)
+ i = 1;
+ if (i > 31)
+ i = 31;
+ // z is not symetric
+ j = (int) ((-ent.mins[2]) / 8);
+ if (j < 1)
+ j = 1;
+ if (j > 31)
+ j = 31;
+ // and z maxs can be negative...
+ k = (int) ((ent.maxs[2] + 32) / 8);
+ if (k < 1)
+ k = 1;
+ if (k > 63)
+ k = 63;
+ ent.s.solid = (k << 10) | (j << 5) | i;
+ } else if (ent.solid == Defines.SOLID_BSP) {
+ ent.s.solid = 31; // a solid_bbox will never create this value
+ } else
+ ent.s.solid = 0;
+ // set the abs box
+ if (ent.solid == Defines.SOLID_BSP
+ && (ent.s.angles[0] != 0 || ent.s.angles[1] != 0 || ent.s.angles[2] != 0)) {
+ // expand for rotation
+ float max, v;
+ max = 0;
+ for (int i = 0; i < 3; i++) {
+ v = Math.abs(ent.mins[i]);
+ if (v > max)
+ max = v;
+ v = Math.abs(ent.maxs[i]);
+ if (v > max)
+ max = v;
+ }
+ for (int i = 0; i < 3; i++) {
+ ent.absmin[i] = ent.s.origin[i] - max;
+ ent.absmax[i] = ent.s.origin[i] + max;
+ }
+ } else {
+ // normal
+ Math3D.vectorAdd(ent.s.origin, ent.mins, ent.absmin);
+ Math3D.vectorAdd(ent.s.origin, ent.maxs, ent.absmax);
+ }
+ // because movement is clipped an epsilon away from an actual edge,
+ // we must fully check even when bounding boxes don't quite touch
+ ent.absmin[0]--;
+ ent.absmin[1]--;
+ ent.absmin[2]--;
+ ent.absmax[0]++;
+ ent.absmax[1]++;
+ ent.absmax[2]++;
+ // link to PVS leafs
+ ent.num_clusters = 0;
+ ent.areanum = 0;
+ ent.areanum2 = 0;
+ // get all leafs, including solids
+ int iw[] = {topnode};
+ num_leafs = CM.CM_BoxLeafnums(ent.absmin, ent.absmax, SV_WORLD.leafs,
+ SV_WORLD.MAX_TOTAL_ENT_LEAFS, iw);
+ topnode = iw[0];
+ // set areas
+ for (int i = 0; i < num_leafs; i++) {
+ SV_WORLD.clusters[i] = CM.CM_LeafCluster(SV_WORLD.leafs[i]);
+ area = CM.CM_LeafArea(SV_WORLD.leafs[i]);
+ if (area != 0) {
+ // doors may legally straggle two areas,
+ // but nothing should evern need more than that
+ if (ent.areanum != 0 && ent.areanum != area) {
+ if (ent.areanum2 != 0 && ent.areanum2 != area
+ && SV_INIT.sv.state == Defines.ss_loading)
+ Com.DPrintf("Object touching 3 areas at "
+ + ent.absmin[0] + " " + ent.absmin[1] + " "
+ + ent.absmin[2] + "\n");
+ ent.areanum2 = area;
+ } else
+ ent.areanum = area;
+ }
+ }
+ if (num_leafs >= SV_WORLD.MAX_TOTAL_ENT_LEAFS) {
+ // assume we missed some leafs, and mark by headnode
+ ent.num_clusters = -1;
+ ent.headnode = topnode;
+ } else {
+ ent.num_clusters = 0;
+ for (int i = 0; i < num_leafs; i++) {
+ if (SV_WORLD.clusters[i] == -1)
+ continue; // not a visible leaf
+ for (j = 0; j < i; j++)
+ if (SV_WORLD.clusters[j] == SV_WORLD.clusters[i])
+ break;
+ if (j == i) {
+ if (ent.num_clusters == Defines.MAX_ENT_CLUSTERS) {
+ // assume we missed some leafs, and mark by headnode
+ ent.num_clusters = -1;
+ ent.headnode = topnode;
+ break;
+ }
+ ent.clusternums[ent.num_clusters++] = SV_WORLD.clusters[i];
+ }
+ }
+ }
+ // if first time, make sure old_origin is valid
+ if (0 == ent.linkcount) {
+ Math3D.vectorCopy(ent.s.origin, ent.s.old_origin);
+ }
+ ent.linkcount++;
+ if (ent.solid == Defines.SOLID_NOT)
+ return;
+ // find the first node that the ent's box crosses
+ node = SV_WORLD.sv_areanodes[0];
+ while (true) {
+ if (node.axis == -1)
+ break;
+ if (ent.absmin[node.axis] > node.dist)
+ node = node.children[0];
+ else if (ent.absmax[node.axis] < node.dist)
+ node = node.children[1];
+ else
+ break; // crosses the node
+ }
+ // link it in
+ if (ent.solid == Defines.SOLID_TRIGGER)
+ InsertLinkBefore(ent.area, node.trigger_edicts);
+ else
+ InsertLinkBefore(ent.area, node.solid_edicts);
+ }
+
+ /*
+ * ==================== SV_AreaEdicts_r
+ *
+ * ====================
+ */
+ private static void SV_AreaEdicts_r(areanode_t node) {
+ link_t l, next, start;
+ EDict check;
+ // touch linked edicts
+ if (SV_WORLD.area_type == Defines.AREA_SOLID)
+ start = node.solid_edicts;
+ else
+ start = node.trigger_edicts;
+ for (l = start.next; l != start; l = next) {
+ next = l.next;
+ check = (EDict) l.o;
+ if (check.solid == Defines.SOLID_NOT)
+ continue; // deactivated
+ if (check.absmin[0] > SV_WORLD.area_maxs[0]
+ || check.absmin[1] > SV_WORLD.area_maxs[1]
+ || check.absmin[2] > SV_WORLD.area_maxs[2]
+ || check.absmax[0] < SV_WORLD.area_mins[0]
+ || check.absmax[1] < SV_WORLD.area_mins[1]
+ || check.absmax[2] < SV_WORLD.area_mins[2])
+ continue; // not touching
+ if (SV_WORLD.area_count == SV_WORLD.area_maxcount) {
+ Com.Printf("SV_AreaEdicts: MAXCOUNT\n");
+ return;
+ }
+ SV_WORLD.area_list[SV_WORLD.area_count] = check;
+ SV_WORLD.area_count++;
+ }
+ if (node.axis == -1)
+ return; // terminal node
+ // recurse down both sides
+ if (SV_WORLD.area_maxs[node.axis] > node.dist)
+ SV_AreaEdicts_r(node.children[0]);
+ if (SV_WORLD.area_mins[node.axis] < node.dist)
+ SV_AreaEdicts_r(node.children[1]);
+ }
+
+ /*
+ * ================ SV_AreaEdicts ================
+ */
+ public static int SV_AreaEdicts(float[] mins, float[] maxs, EDict list[],
+ int maxcount, int areatype) {
+ SV_WORLD.area_mins = mins;
+ SV_WORLD.area_maxs = maxs;
+ SV_WORLD.area_list = list;
+ SV_WORLD.area_count = 0;
+ SV_WORLD.area_maxcount = maxcount;
+ SV_WORLD.area_type = areatype;
+ SV_AreaEdicts_r(SV_WORLD.sv_areanodes[0]);
+ return SV_WORLD.area_count;
+ }
+
+ /*
+ * ============= SV_PointContents =============
+ */
+ public static int SV_PointContents(float[] p) {
+ EDict hit;
+ int i, num;
+ int contents, c2;
+ int headnode;
+ // get base contents from world
+ contents = CM.PointContents(p, SV_INIT.sv.models[1].headnode);
+ // or in contents from all the other entities
+ num = SV_AreaEdicts(p, p, SV_WORLD.touch, Defines.MAX_EDICTS,
+ Defines.AREA_SOLID);
+ for (i = 0; i < num; i++) {
+ hit = SV_WORLD.touch[i];
+ // might intersect, so do an exact clip
+ headnode = SV_HullForEntity(hit);
+ c2 = CM.TransformedPointContents(p, headnode, hit.s.origin,
+ hit.s.angles);
+ contents |= c2;
+ }
+ return contents;
+ }
+
+ /*
+ * ================ SV_HullForEntity
+ *
+ * Returns a headnode that can be used for testing or clipping an object of
+ * mins/maxs size. Offset is filled in to contain the adjustment that must
+ * be added to the testing object's origin to get a point to use with the
+ * returned hull. ================
+ */
+ private static int SV_HullForEntity(EDict ent) {
+ cmodel_t model;
+ // decide which clipping hull to use, based on the size
+ if (ent.solid == Defines.SOLID_BSP) {
+ // explicit hulls in the BSP model
+ model = SV_INIT.sv.models[ent.s.modelindex];
+ if (null == model)
+ Com.Error(Defines.ERR_FATAL,
+ "MOVETYPE_PUSH with a non bsp model");
+ return model.headnode;
+ }
+ // create a temp hull from bounding box sizes
+ return CM.HeadnodeForBox(ent.mins, ent.maxs);
+ }
+
+ private static void SV_ClipMoveToEntities(moveclip_t clip) {
+ int i, num;
+ EDict touch;
+ trace_t trace;
+ int headnode;
+ float angles[];
+ num = SV_AreaEdicts(clip.boxmins, clip.boxmaxs, SV_WORLD.touchlist,
+ Defines.MAX_EDICTS, Defines.AREA_SOLID);
+ // be careful, it is possible to have an entity in this
+ // list removed before we get to it (killtriggered)
+ for (i = 0; i < num; i++) {
+ touch = SV_WORLD.touchlist[i];
+ if (touch.solid == Defines.SOLID_NOT)
+ continue;
+ if (touch == clip.passedict)
+ continue;
+ if (clip.trace.allsolid)
+ return;
+ if (clip.passedict != null) {
+ if (touch.owner == clip.passedict)
+ continue; // don't clip against own missiles
+ if (clip.passedict.owner == touch)
+ continue; // don't clip against owner
+ }
+ if (0 == (clip.contentmask & Defines.CONTENTS_DEADMONSTER)
+ && 0 != (touch.svflags & Defines.SVF_DEADMONSTER))
+ continue;
+ // might intersect, so do an exact clip
+ headnode = SV_HullForEntity(touch);
+ angles = touch.s.angles;
+ if (touch.solid != Defines.SOLID_BSP)
+ angles = Globals.vec3_origin; // boxes don't rotate
+ if ((touch.svflags & Defines.SVF_MONSTER) != 0)
+ trace = CM.TransformedBoxTrace(clip.start, clip.end,
+ clip.mins2, clip.maxs2, headnode, clip.contentmask,
+ touch.s.origin, angles);
+ else
+ trace = CM.TransformedBoxTrace(clip.start, clip.end, clip.mins,
+ clip.maxs, headnode, clip.contentmask, touch.s.origin,
+ angles);
+ if (trace.allsolid || trace.startsolid
+ || trace.fraction < clip.trace.fraction) {
+ trace.ent = touch;
+ if (clip.trace.startsolid) {
+ clip.trace = trace;
+ clip.trace.startsolid = true;
+ } else
+ clip.trace.set(trace);
+ } else if (trace.startsolid)
+ clip.trace.startsolid = true;
+ }
+ }
+
+ /*
+ * ================== SV_TraceBounds ==================
+ */
+ private static void SV_TraceBounds(float[] start, float[] mins,
+ float[] maxs, float[] end, float[] boxmins, float[] boxmaxs) {
+ int i;
+ for (i = 0; i < 3; i++) {
+ if (end[i] > start[i]) {
+ boxmins[i] = start[i] + mins[i] - 1;
+ boxmaxs[i] = end[i] + maxs[i] + 1;
+ } else {
+ boxmins[i] = end[i] + mins[i] - 1;
+ boxmaxs[i] = start[i] + maxs[i] + 1;
+ }
+ }
+ }
+
+ /*
+ * ================== SV_Trace
+ *
+ * Moves the given mins/maxs volume through the world from start to end.
+ *
+ * Passedict and edicts owned by passedict are explicitly not checked.
+ *
+ * ==================
+ */
+ public static trace_t SV_Trace(float[] start, float[] mins, float[] maxs,
+ float[] end, EDict passedict, int contentmask) {
+ moveclip_t clip = new moveclip_t();
+ if (mins == null)
+ mins = Globals.vec3_origin;
+ if (maxs == null)
+ maxs = Globals.vec3_origin;
+
+ // clip to world
+ clip.trace = CM.boxTrace(start, end, mins, maxs, 0, contentmask);
+ clip.trace.ent = GameBase.g_edicts[0];
+ if (clip.trace.fraction == 0)
+ return clip.trace; // blocked by the world
+ clip.contentmask = contentmask;
+ clip.start = start;
+ clip.end = end;
+ clip.mins = mins;
+ clip.maxs = maxs;
+ clip.passedict = passedict;
+ Math3D.vectorCopy(mins, clip.mins2);
+ Math3D.vectorCopy(maxs, clip.maxs2);
+ // create the bounding box of the entire move
+ SV_TraceBounds(start, clip.mins2, clip.maxs2, end, clip.boxmins,
+ clip.boxmaxs);
+ // clip to other solid entities
+ SV_ClipMoveToEntities(clip);
+ return clip.trace;
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.server;
+
+import lwjake2.Defines;
+import lwjake2.Globals;
+import lwjake2.game.*;
+import lwjake2.qcommon.*;
+import lwjake2.sys.NET;
+import lwjake2.util.Lib;
+
+import java.io.IOException;
+
+public class Server {
+
+ /**
+ * Address of group servers.
+ */
+ public static final NetadrT[] master_adr = new NetadrT[Defines.MAX_MASTERS];
+ /**
+ * Send a message to the master every few minutes to let it know we are
+ * alive, and log information.
+ */
+ private static final int HEARTBEAT_SECONDS = 300;
+ public static client_t sv_client; // current client
+
+ public static CvarT sv_paused;
+ public static CvarT sv_enforcetime;
+ public static CvarT allow_download;
+ public static CvarT allow_download_players;
+ public static CvarT allow_download_models;
+ // disconnect
+ public static CvarT allow_download_sounds;
+ public static CvarT allow_download_maps;
+ public static CvarT sv_airaccelerate;
+ public static CvarT sv_noreload; // don't reload level state when
+ public static CvarT maxclients; // FIXME: rename sv_maxclients
+ private static CvarT sv_timedemo;
+ private static CvarT timeout; // seconds without any message
+ private static CvarT zombietime; // seconds to sink messages after
+ // reentering
+ private static CvarT rcon_password; // password for remote server commands
+ private static CvarT sv_showclamp;
+
+ private static CvarT hostname;
+
+ private static CvarT public_server; // should heartbeats be sent
+
+ private static CvarT sv_reconnect_limit; // minimum seconds between connect
+ // messages
+
+ static {
+ for (int i = 0; i < Defines.MAX_MASTERS; i++) {
+ master_adr[i] = new NetadrT();
+ }
+ }
+
+ /**
+ * Called when the player is totally leaving the server, either willingly or
+ * unwillingly. This is NOT called if the entire server is quiting or
+ * crashing.
+ */
+ public static void SV_DropClient(client_t drop) {
+ // add the disconnect
+ MSG.WriteByte(drop.netchan.message, Defines.svc_disconnect);
+
+ if (drop.state == Defines.cs_spawned) {
+ // call the prog function for removing a client
+ // this will remove the body, among other things
+ PlayerClient.ClientDisconnect(drop.edict);
+ }
+
+ if (drop.download != null) {
+ FS.FreeFile();
+ drop.download = null;
+ }
+
+ drop.state = Defines.cs_zombie; // become free in a few seconds
+ drop.name = "";
+ }
+
+
+ /* ==============================================================================
+ *
+ * CONNECTIONLESS COMMANDS
+ *
+ * ==============================================================================*/
+
+ /**
+ * Builds the string that is sent as heartbeats and status replies.
+ */
+ private static String SV_StatusString() {
+ String player;
+ StringBuilder status = new StringBuilder();
+ int i;
+ client_t cl;
+ int statusLength;
+ int playerLength;
+
+ status = new StringBuilder(Cvar.serverinfo() + "\n");
+
+ for (i = 0; i < Server.maxclients.value; i++) {
+ cl = SV_INIT.svs.clients[i];
+ if (cl.state == Defines.cs_connected
+ || cl.state == Defines.cs_spawned) {
+ player = "" + cl.edict.client.ps.stats[Defines.STAT_FRAGS]
+ + " " + cl.ping + "\"" + cl.name + "\"\n";
+
+ playerLength = player.length();
+ statusLength = status.length();
+
+ if (statusLength + playerLength >= 1024)
+ break; // can't hold any more
+
+ status.append(player);
+ }
+ }
+
+ return status.toString();
+ }
+
+ /**
+ * Responds with all the info that qplug or qspy can see
+ */
+ private static void SVC_Status() {
+ Netchan.OutOfBandPrint(Defines.NS_SERVER, Globals.net_from, "print\n"
+ + SV_StatusString());
+ }
+
+ /**
+ * SVC_Ack
+ */
+ private static void SVC_Ack() {
+ Com.Printf("Ping acknowledge from " + NET.AdrToString(Globals.net_from)
+ + "\n");
+ }
+
+ /**
+ * SVC_Info, responds with short info for broadcast scans The second parameter should
+ * be the current protocol version number.
+ */
+ private static void SVC_Info() {
+ String string;
+ int i, count;
+ int version;
+
+ if (Server.maxclients.value == 1)
+ return; // ignore in single player
+
+ version = Lib.atoi(Cmd.Argv(1));
+
+ if (version != Defines.PROTOCOL_VERSION)
+ string = Server.hostname.string + ": wrong version\n";
+ else {
+ count = 0;
+ for (i = 0; i < Server.maxclients.value; i++)
+ if (SV_INIT.svs.clients[i].state >= Defines.cs_connected)
+ count++;
+
+ string = Server.hostname.string + " " + SV_INIT.sv.name + " "
+ + count + "/" + (int) Server.maxclients.value + "\n";
+ }
+
+ Netchan.OutOfBandPrint(Defines.NS_SERVER, Globals.net_from, "info\n"
+ + string);
+ }
+
+ /**
+ * SVC_Ping, Just responds with an acknowledgement.
+ */
+ private static void SVC_Ping() {
+ Netchan.OutOfBandPrint(Defines.NS_SERVER, Globals.net_from, "ack");
+ }
+
+ /**
+ * Returns a challenge number that can be used in a subsequent
+ * client_connect command. We do this to prevent denial of service attacks
+ * that flood the server with invalid connection IPs. With a challenge, they
+ * must give a valid IP address.
+ */
+ private static void SVC_GetChallenge() {
+ int i;
+ int oldest;
+ int oldestTime;
+
+ oldest = 0;
+ oldestTime = 0x7fffffff;
+
+ // see if we already have a challenge for this ip
+ for (i = 0; i < Defines.MAX_CHALLENGES; i++) {
+ if (NET.CompareBaseAdr(Globals.net_from,
+ SV_INIT.svs.challenges[i].adr))
+ break;
+ if (SV_INIT.svs.challenges[i].time < oldestTime) {
+ oldestTime = SV_INIT.svs.challenges[i].time;
+ oldest = i;
+ }
+ }
+
+ if (i == Defines.MAX_CHALLENGES) {
+ // overwrite the oldest
+ SV_INIT.svs.challenges[oldest].challenge = Lib.rand() & 0x7fff;
+ SV_INIT.svs.challenges[oldest].adr = Globals.net_from;
+ SV_INIT.svs.challenges[oldest].time = Globals.curtime;
+ i = oldest;
+ }
+
+ // send it back
+ Netchan.OutOfBandPrint(Defines.NS_SERVER, Globals.net_from,
+ "challenge " + SV_INIT.svs.challenges[i].challenge);
+ }
+
+ /**
+ * A connection request that did not come from the master.
+ */
+ private static void SVC_DirectConnect() {
+ String userinfo;
+ NetadrT adr;
+ int i;
+ client_t cl;
+
+ int version;
+ int qport;
+
+ adr = Globals.net_from;
+
+ Com.DPrintf("SVC_DirectConnect ()\n");
+
+ version = Lib.atoi(Cmd.Argv(1));
+ if (version != Defines.PROTOCOL_VERSION) {
+ Netchan.OutOfBandPrint(Defines.NS_SERVER, adr,
+ "print\nServer is version " + Globals.VERSION + "\n");
+ Com.DPrintf(" rejected connect from version " + version + "\n");
+ return;
+ }
+
+ qport = Lib.atoi(Cmd.Argv(2));
+ int challenge = Lib.atoi(Cmd.Argv(3));
+ userinfo = Cmd.Argv(4);
+
+ // force the IP key/value pair so the game can filter based on ip
+ userinfo = Info.Info_SetValueForKey(userinfo, "ip", NET.AdrToString(Globals.net_from));
+
+ // attractloop servers are ONLY for local clients
+ if (SV_INIT.sv.attractloop) {
+ if (!NET.IsLocalAddress(adr)) {
+ Com.Printf("Remote connect in attract loop. Ignored.\n");
+ Netchan.OutOfBandPrint(Defines.NS_SERVER, adr,
+ "print\nConnection refused.\n");
+ return;
+ }
+ }
+
+ // see if the challenge is valid
+ if (!NET.IsLocalAddress(adr)) {
+ for (i = 0; i < Defines.MAX_CHALLENGES; i++) {
+ if (NET.CompareBaseAdr(Globals.net_from,
+ SV_INIT.svs.challenges[i].adr)) {
+ if (challenge == SV_INIT.svs.challenges[i].challenge)
+ break; // good
+ Netchan.OutOfBandPrint(Defines.NS_SERVER, adr,
+ "print\nBad challenge.\n");
+ return;
+ }
+ }
+ if (i == Defines.MAX_CHALLENGES) {
+ Netchan.OutOfBandPrint(Defines.NS_SERVER, adr,
+ "print\nNo challenge for address.\n");
+ return;
+ }
+ }
+
+ // if there is already a slot for this ip, reuse it
+ for (i = 0; i < Server.maxclients.value; i++) {
+ cl = SV_INIT.svs.clients[i];
+
+ if (cl.state == Defines.cs_free)
+ continue;
+ if (NET.CompareBaseAdr(adr, cl.netchan.remote_address)
+ && (cl.netchan.qport == qport || adr.port == cl.netchan.remote_address.port)) {
+ if (!NET.IsLocalAddress(adr)
+ && (SV_INIT.svs.realtime - cl.lastconnect) < ((int) Server.sv_reconnect_limit.value * 1000)) {
+ Com.DPrintf(NET.AdrToString(adr)
+ + ":reconnect rejected : too soon\n");
+ return;
+ }
+ Com.Printf(NET.AdrToString(adr) + ":reconnect\n");
+
+ gotnewcl(i, challenge, userinfo, adr, qport);
+ return;
+ }
+ }
+
+ // find a client slot
+ //newcl = null;
+ int index = -1;
+ for (i = 0; i < Server.maxclients.value; i++) {
+ cl = SV_INIT.svs.clients[i];
+ if (cl.state == Defines.cs_free) {
+ index = i;
+ break;
+ }
+ }
+ if (index == -1) {
+ Netchan.OutOfBandPrint(Defines.NS_SERVER, adr,
+ "print\nServer is full.\n");
+ Com.DPrintf("Rejected a connection.\n");
+ return;
+ }
+ gotnewcl(index, challenge, userinfo, adr, qport);
+ }
+
+ /**
+ * Initializes player structures after successfull connection.
+ */
+ private static void gotnewcl(int i, int challenge, String userinfo,
+ NetadrT adr, int qport) {
+ // build a new connection
+ // accept the new client
+ // this is the only place a client_t is ever initialized
+
+ Server.sv_client = SV_INIT.svs.clients[i];
+
+ int edictnum = i + 1;
+
+ EDict ent = GameBase.g_edicts[edictnum];
+ SV_INIT.svs.clients[i].edict = ent;
+
+ // save challenge for checksumming
+ SV_INIT.svs.clients[i].challenge = challenge;
+
+
+ // get the game a chance to reject this connection or modify the
+ // userinfo
+ if (!(PlayerClient.ClientConnect(ent, userinfo))) {
+ if (Info.Info_ValueForKey(userinfo, "rejmsg") != null)
+ Netchan.OutOfBandPrint(Defines.NS_SERVER, adr, "print\n"
+ + Info.Info_ValueForKey(userinfo, "rejmsg")
+ + "\nConnection refused.\n");
+ else
+ Netchan.OutOfBandPrint(Defines.NS_SERVER, adr,
+ "print\nConnection refused.\n");
+ Com.DPrintf("Game rejected a connection.\n");
+ return;
+ }
+
+ // parse some info from the info strings
+ SV_INIT.svs.clients[i].userinfo = userinfo;
+ SV_UserinfoChanged(SV_INIT.svs.clients[i]);
+
+ // send the connect packet to the client
+ Netchan.OutOfBandPrint(Defines.NS_SERVER, adr, "client_connect");
+
+ Netchan.Setup(Defines.NS_SERVER, SV_INIT.svs.clients[i].netchan, adr, qport);
+
+ SV_INIT.svs.clients[i].state = Defines.cs_connected;
+
+ SZ.Init(SV_INIT.svs.clients[i].datagram,
+ SV_INIT.svs.clients[i].datagram_buf,
+ SV_INIT.svs.clients[i].datagram_buf.length);
+
+ SV_INIT.svs.clients[i].datagram.allowoverflow = true;
+ SV_INIT.svs.clients[i].lastmessage = SV_INIT.svs.realtime; // don't timeout
+ SV_INIT.svs.clients[i].lastconnect = SV_INIT.svs.realtime;
+ Com.DPrintf("new client added.\n");
+ }
+
+
+ /**
+ * Checks if the rcon password is corect.
+ */
+ private static int Rcon_Validate() {
+ if (0 == Server.rcon_password.string.length())
+ return 0;
+
+ if (0 != Lib.strcmp(Cmd.Argv(1), Server.rcon_password.string))
+ return 0;
+
+ return 1;
+ }
+
+ /**
+ * A client issued an rcon command. Shift down the remaining args Redirect
+ * all printfs fromt hte server to the client.
+ */
+ private static void SVC_RemoteCommand() {
+ int i;
+ StringBuilder remaining;
+
+ i = Rcon_Validate();
+
+ String msg = Lib.CtoJava(Globals.net_message.data, 4, 1024);
+
+ if (i == 0)
+ Com.Printf("Bad rcon from " + NET.AdrToString(Globals.net_from)
+ + ":\n" + msg + "\n");
+ else
+ Com.Printf("Rcon from " + NET.AdrToString(Globals.net_from) + ":\n"
+ + msg + "\n");
+
+ Com.BeginRedirect(Defines.RD_PACKET, SV_SEND.sv_outputbuf,
+ Defines.SV_OUTPUTBUF_LENGTH, new Com.RD_Flusher() {
+ public void rd_flush(int target, StringBuffer buffer) {
+ SV_SEND.SV_FlushRedirect(target, Lib.stringToBytes(buffer.toString()));
+ }
+ });
+
+ if (0 == Rcon_Validate()) {
+ Com.Printf("Bad rcon_password.\n");
+ } else {
+ remaining = new StringBuilder();
+
+ for (i = 2; i < Cmd.Argc(); i++) {
+ remaining.append(Cmd.Argv(i));
+ remaining.append(" ");
+ }
+
+ Cmd.ExecuteString(remaining.toString());
+ }
+
+ Com.EndRedirect();
+ }
+
+ /**
+ * A connectionless packet has four leading 0xff characters to distinguish
+ * it from a game channel. Clients that are in the game can still send
+ * connectionless packets. It is used also by rcon commands.
+ */
+ private static void SV_ConnectionlessPacket() {
+ String s;
+ String c;
+
+ MSG.BeginReading(Globals.net_message);
+ MSG.ReadLong(Globals.net_message); // skip the -1 marker
+
+ s = MSG.ReadStringLine(Globals.net_message);
+
+ Cmd.TokenizeString(s.toCharArray(), false);
+
+ c = Cmd.Argv(0);
+
+ //for debugging purposes
+ //Com.Printf("Packet " + NET.AdrToString(Netchan.net_from) + " : " + c + "\n");
+ //Com.Printf(Lib.hexDump(net_message.data, 64, false) + "\n");
+
+ if (0 == Lib.strcmp(c, "ping"))
+ SVC_Ping();
+ else if (0 == Lib.strcmp(c, "ack"))
+ SVC_Ack();
+ else if (0 == Lib.strcmp(c, "status"))
+ SVC_Status();
+ else if (0 == Lib.strcmp(c, "info"))
+ SVC_Info();
+ else if (0 == Lib.strcmp(c, "getchallenge"))
+ SVC_GetChallenge();
+ else if (0 == Lib.strcmp(c, "connect"))
+ SVC_DirectConnect();
+ else if (0 == Lib.strcmp(c, "rcon"))
+ SVC_RemoteCommand();
+ else {
+ Com.Printf("bad connectionless packet from "
+ + NET.AdrToString(Globals.net_from) + "\n");
+ Com.Printf("[" + s + "]\n");
+ Com.Printf("" + Lib.hexDump(Globals.net_message.data, 128, false));
+ }
+ }
+
+ /**
+ * Updates the cl.ping variables.
+ */
+ private static void SV_CalcPings() {
+ int i, j;
+ client_t cl;
+ int total, count;
+
+ for (i = 0; i < Server.maxclients.value; i++) {
+ cl = SV_INIT.svs.clients[i];
+ if (cl.state != Defines.cs_spawned)
+ continue;
+
+ total = 0;
+ count = 0;
+ for (j = 0; j < Defines.LATENCY_COUNTS; j++) {
+ if (cl.frame_latency[j] > 0) {
+ count++;
+ total += cl.frame_latency[j];
+ }
+ }
+ if (0 == count)
+ cl.ping = 0;
+ else
+ cl.ping = total / count;
+
+ // let the game dll know about the ping
+ cl.edict.client.ping = cl.ping;
+ }
+ }
+
+ /**
+ * Every few frames, gives all clients an allotment of milliseconds for
+ * their command moves. If they exceed it, assume cheating.
+ */
+ private static void SV_GiveMsec() {
+ int i;
+ client_t cl;
+
+ if ((SV_INIT.sv.framenum & 15) != 0)
+ return;
+
+ for (i = 0; i < Server.maxclients.value; i++) {
+ cl = SV_INIT.svs.clients[i];
+ if (cl.state == Defines.cs_free)
+ continue;
+
+ cl.commandMsec = 1800; // 1600 + some slop
+ }
+ }
+
+ /**
+ * Reads packets from the network or loopback.
+ */
+ private static void SV_ReadPackets() {
+ int i;
+ client_t cl;
+ int qport = 0;
+
+ while (NET.GetPacket(Defines.NS_SERVER, Globals.net_from,
+ Globals.net_message)) {
+
+ // check for connectionless packet (0xffffffff) first
+ if ((Globals.net_message.data[0] == -1)
+ && (Globals.net_message.data[1] == -1)
+ && (Globals.net_message.data[2] == -1)
+ && (Globals.net_message.data[3] == -1)) {
+ SV_ConnectionlessPacket();
+ continue;
+ }
+
+ // read the qport out of the message so we can fix up
+ // stupid address translating routers
+ MSG.BeginReading(Globals.net_message);
+ MSG.ReadLong(Globals.net_message); // sequence number
+ MSG.ReadLong(Globals.net_message); // sequence number
+ qport = MSG.ReadShort(Globals.net_message) & 0xffff;
+
+ // check for packets from connected clients
+ for (i = 0; i < Server.maxclients.value; i++) {
+ cl = SV_INIT.svs.clients[i];
+ if (cl.state == Defines.cs_free)
+ continue;
+ if (!NET.CompareBaseAdr(Globals.net_from,
+ cl.netchan.remote_address))
+ continue;
+ if (cl.netchan.qport != qport)
+ continue;
+ if (cl.netchan.remote_address.port != Globals.net_from.port) {
+ Com.Printf("SV_ReadPackets: fixing up a translated port\n");
+ cl.netchan.remote_address.port = Globals.net_from.port;
+ }
+
+ if (Netchan.Process(cl.netchan, Globals.net_message)) {
+ // this is a valid, sequenced packet, so process it
+ if (cl.state != Defines.cs_zombie) {
+ cl.lastmessage = SV_INIT.svs.realtime; // don't timeout
+ SV_USER.SV_ExecuteClientMessage(cl);
+ }
+ }
+ break;
+ }
+
+ if (i != Server.maxclients.value) {
+ }
+ }
+ }
+
+ /**
+ * If a packet has not been received from a client for timeout.value
+ * seconds, drop the conneciton. Server frames are used instead of realtime
+ * to avoid dropping the local client while debugging.
+ * <p>
+ * When a client is normally dropped, the client_t goes into a zombie state
+ * for a few seconds to make sure any final reliable message gets resent if
+ * necessary.
+ */
+ private static void SV_CheckTimeouts() {
+ int i;
+ client_t cl;
+ int droppoint;
+ int zombiepoint;
+
+ droppoint = (int) (SV_INIT.svs.realtime - 1000 * Server.timeout.value);
+ zombiepoint = (int) (SV_INIT.svs.realtime - 1000 * Server.zombietime.value);
+
+ for (i = 0; i < Server.maxclients.value; i++) {
+ cl = SV_INIT.svs.clients[i];
+ // message times may be wrong across a changelevel
+ if (cl.lastmessage > SV_INIT.svs.realtime)
+ cl.lastmessage = SV_INIT.svs.realtime;
+
+ if (cl.state == Defines.cs_zombie && cl.lastmessage < zombiepoint) {
+ cl.state = Defines.cs_free; // can now be reused
+ continue;
+ }
+ if ((cl.state == Defines.cs_connected || cl.state == Defines.cs_spawned)
+ && cl.lastmessage < droppoint) {
+ SV_SEND.SV_BroadcastPrintf(Defines.PRINT_HIGH, cl.name
+ + " timed out\n");
+ SV_DropClient(cl);
+ cl.state = Defines.cs_free; // don't bother with zombie state
+ }
+ }
+ }
+
+ /**
+ * SV_PrepWorldFrame
+ * <p>
+ * This has to be done before the world logic, because player processing
+ * happens outside RunWorldFrame.
+ */
+ private static void SV_PrepWorldFrame() {
+ EDict ent;
+ int i;
+
+ for (i = 0; i < GameBase.num_edicts; i++) {
+ ent = GameBase.g_edicts[i];
+ // events only last for a single message
+ ent.s.event = 0;
+ }
+
+ }
+
+ /**
+ * SV_RunGameFrame.
+ */
+ private static void SV_RunGameFrame() {
+
+ // we always need to bump framenum, even if we
+ // don't run the world, otherwise the delta
+ // compression can get confused when a client
+ // has the "current" frame
+ SV_INIT.sv.framenum++;
+ SV_INIT.sv.time = SV_INIT.sv.framenum * 100;
+
+ // don't run if paused
+ if (0 == Server.sv_paused.value || Server.maxclients.value > 1) {
+ GameBase.G_RunFrame();
+
+ // never get more than one tic behind
+ if (SV_INIT.sv.time < SV_INIT.svs.realtime) {
+ if (Server.sv_showclamp.value != 0)
+ Com.Printf("sv highclamp\n");
+ SV_INIT.svs.realtime = SV_INIT.sv.time;
+ }
+ }
+
+
+ }
+
+ /**
+ * doFrame.
+ */
+ public static void doFrame(long msec) {
+ Globals.time_before_game = Globals.time_after_game = 0;
+
+ // if server is not active, do nothing
+ if (!SV_INIT.svs.initialized)
+ return;
+
+ SV_INIT.svs.realtime += msec;
+
+ // keep the random time dependent
+ Lib.rand();
+
+ // check timeouts
+ SV_CheckTimeouts();
+
+ // get packets from clients
+ SV_ReadPackets();
+
+ //if (Game.g_edicts[1] !=null)
+ // Com.p("player at:" + Lib.vtofsbeaty(Game.g_edicts[1].s.origin ));
+
+ // move autonomous things around if enough time has passed
+ if (0 == Server.sv_timedemo.value
+ && SV_INIT.svs.realtime < SV_INIT.sv.time) {
+ // never let the time get too far off
+ if (SV_INIT.sv.time - SV_INIT.svs.realtime > 100) {
+ if (Server.sv_showclamp.value != 0)
+ Com.Printf("sv lowclamp\n");
+ SV_INIT.svs.realtime = SV_INIT.sv.time - 100;
+ }
+ NET.Sleep(SV_INIT.sv.time - SV_INIT.svs.realtime);
+ return;
+ }
+
+ // update ping based on the last known frame from all clients
+ SV_CalcPings();
+
+ // give the clients some timeslices
+ SV_GiveMsec();
+
+ // let everything in the world think and move
+ SV_RunGameFrame();
+
+ // send messages back to the clients that had packets read this frame
+ SV_SEND.SV_SendClientMessages();
+
+ // save the entire world state if recording a serverdemo
+ SV_ENTS.SV_RecordDemoMessage();
+
+ // send a heartbeat to the master if needed
+ Master_Heartbeat();
+
+ // clear teleport flags, etc for next frame
+ SV_PrepWorldFrame();
+
+ }
+
+ private static void Master_Heartbeat() {
+ String string;
+ int i;
+
+ // pgm post3.19 change, cvar pointer not validated before dereferencing
+ if (Globals.dedicated == null || 0 == Globals.dedicated.value)
+ return; // only dedicated servers send heartbeats
+
+ // pgm post3.19 change, cvar pointer not validated before dereferencing
+ if (null == Server.public_server || 0 == Server.public_server.value)
+ return; // a private dedicated game
+
+ // check for time wraparound
+ if (SV_INIT.svs.last_heartbeat > SV_INIT.svs.realtime)
+ SV_INIT.svs.last_heartbeat = SV_INIT.svs.realtime;
+
+ if (SV_INIT.svs.realtime - SV_INIT.svs.last_heartbeat < Server.HEARTBEAT_SECONDS * 1000)
+ return; // not time to send yet
+
+ SV_INIT.svs.last_heartbeat = SV_INIT.svs.realtime;
+
+ // send the same string that we would give for a status OOB command
+ string = SV_StatusString();
+
+ // send to group master
+ for (i = 0; i < Defines.MAX_MASTERS; i++)
+ if (Server.master_adr[i].port != 0) {
+ Com.Printf("Sending heartbeat to "
+ + NET.AdrToString(Server.master_adr[i]) + "\n");
+ Netchan.OutOfBandPrint(Defines.NS_SERVER,
+ Server.master_adr[i], "heartbeat\n" + string);
+ }
+ }
+
+
+ /**
+ * Master_Shutdown, Informs all masters that this server is going down.
+ */
+ private static void Master_Shutdown() {
+ int i;
+
+ // pgm post3.19 change, cvar pointer not validated before dereferencing
+ if (null == Globals.dedicated || 0 == Globals.dedicated.value)
+ return; // only dedicated servers send heartbeats
+
+ // pgm post3.19 change, cvar pointer not validated before dereferencing
+ if (null == Server.public_server || 0 == Server.public_server.value)
+ return; // a private dedicated game
+
+ // send to group master
+ for (i = 0; i < Defines.MAX_MASTERS; i++)
+ if (Server.master_adr[i].port != 0) {
+ if (i > 0)
+ Com.Printf("Sending heartbeat to "
+ + NET.AdrToString(Server.master_adr[i]) + "\n");
+ Netchan.OutOfBandPrint(Defines.NS_SERVER,
+ Server.master_adr[i], "shutdown");
+ }
+ }
+
+
+ /**
+ * Pull specific info from a newly changed userinfo string into a more C
+ * freindly form.
+ */
+ public static void SV_UserinfoChanged(client_t cl) {
+ String val;
+ int i;
+
+ // call prog code to allow overrides
+ PlayerClient.ClientUserinfoChanged(cl.edict, cl.userinfo);
+
+ // name for C code
+ cl.name = Info.Info_ValueForKey(cl.userinfo, "name");
+
+ // mask off high bit
+ //TODO: masking for german umlaute
+ //for (i=0 ; i<sizeof(cl.name) ; i++)
+ // cl.name[i] &= 127;
+
+ // rate command
+ val = Info.Info_ValueForKey(cl.userinfo, "rate");
+ if (val.length() > 0) {
+ i = Lib.atoi(val);
+ cl.rate = i;
+ if (cl.rate < 100)
+ cl.rate = 100;
+ if (cl.rate > 15000)
+ cl.rate = 15000;
+ } else
+ cl.rate = 5000;
+
+ // msg command
+ val = Info.Info_ValueForKey(cl.userinfo, "msg");
+ if (val.length() > 0) {
+ cl.messagelevel = Lib.atoi(val);
+ }
+
+ }
+
+ /**
+ * Only called at quake2.exe startup, not for each game
+ */
+ public static void SV_Init() {
+ SV_CCMDS.SV_InitOperatorCommands(); //ok.
+
+ Server.rcon_password = Cvar.get("rcon_password", "", 0);
+ Cvar.get("skill", "1", 0);
+ Cvar.get("deathmatch", "0", Defines.CVAR_LATCH);
+ Cvar.get("coop", "0", Defines.CVAR_LATCH);
+ Cvar.get("dmflags", "" + Defines.DF_INSTANT_ITEMS,
+ Defines.CVAR_SERVERINFO);
+ Cvar.get("fraglimit", "0", Defines.CVAR_SERVERINFO);
+ Cvar.get("timelimit", "0", Defines.CVAR_SERVERINFO);
+ Cvar.get("cheats", "0", Defines.CVAR_SERVERINFO | Defines.CVAR_LATCH);
+ Cvar.get("protocol", "" + Defines.PROTOCOL_VERSION,
+ Defines.CVAR_SERVERINFO | Defines.CVAR_NOSET);
+
+ Server.maxclients = Cvar.get("maxclients", "1",
+ Defines.CVAR_SERVERINFO | Defines.CVAR_LATCH);
+ Server.hostname = Cvar.get("hostname", "noname",
+ Defines.CVAR_SERVERINFO | Defines.CVAR_ARCHIVE);
+ Server.timeout = Cvar.get("timeout", "125", 0);
+ Server.zombietime = Cvar.get("zombietime", "2", 0);
+ Server.sv_showclamp = Cvar.get("showclamp", "0", 0);
+ Server.sv_paused = Cvar.get("paused", "0", 0);
+ Server.sv_timedemo = Cvar.get("timedemo", "0", 0);
+ Server.sv_enforcetime = Cvar.get("sv_enforcetime", "0", 0);
+
+ Server.allow_download = Cvar.get("allow_download", "1",
+ Defines.CVAR_ARCHIVE);
+ Server.allow_download_players = Cvar.get("allow_download_players",
+ "0", Defines.CVAR_ARCHIVE);
+ Server.allow_download_models = Cvar.get("allow_download_models", "1",
+ Defines.CVAR_ARCHIVE);
+ Server.allow_download_sounds = Cvar.get("allow_download_sounds", "1",
+ Defines.CVAR_ARCHIVE);
+ Server.allow_download_maps = Cvar.get("allow_download_maps", "1",
+ Defines.CVAR_ARCHIVE);
+
+ Server.sv_noreload = Cvar.get("sv_noreload", "0", 0);
+ Server.sv_airaccelerate = Cvar.get("sv_airaccelerate", "0",
+ Defines.CVAR_LATCH);
+ Server.public_server = Cvar.get("public", "0", 0);
+ Server.sv_reconnect_limit = Cvar.get("sv_reconnect_limit", "3",
+ Defines.CVAR_ARCHIVE);
+
+ SZ.Init(Globals.net_message, Globals.net_message_buffer,
+ Globals.net_message_buffer.length);
+ }
+
+ /**
+ * Used by SV_Shutdown to send a final message to all connected clients
+ * before the server goes down. The messages are sent immediately, not just
+ * stuck on the outgoing message list, because the server is going to
+ * totally exit after returning from this function.
+ */
+ private static void SV_FinalMessage(String message, boolean reconnect) {
+ int i;
+ client_t cl;
+
+ SZ.Clear(Globals.net_message);
+ MSG.WriteByte(Globals.net_message, Defines.svc_print);
+ MSG.WriteByte(Globals.net_message, Defines.PRINT_HIGH);
+ MSG.WriteString(Globals.net_message, message);
+
+ if (reconnect)
+ MSG.WriteByte(Globals.net_message, Defines.svc_reconnect);
+ else
+ MSG.WriteByte(Globals.net_message, Defines.svc_disconnect);
+
+ // send it twice
+ // stagger the packets to crutch operating system limited buffers
+
+ for (i = 0; i < SV_INIT.svs.clients.length; i++) {
+ cl = SV_INIT.svs.clients[i];
+ if (cl.state >= Defines.cs_connected)
+ Netchan.Transmit(cl.netchan, Globals.net_message.cursize,
+ Globals.net_message.data);
+ }
+ for (i = 0; i < SV_INIT.svs.clients.length; i++) {
+ cl = SV_INIT.svs.clients[i];
+ if (cl.state >= Defines.cs_connected)
+ Netchan.Transmit(cl.netchan, Globals.net_message.cursize,
+ Globals.net_message.data);
+ }
+ }
+
+ /**
+ * Called when each game quits, before Sys_Quit or Sys_Error.
+ */
+ public static void SV_Shutdown(String finalmsg, boolean reconnect) {
+ if (SV_INIT.svs.clients != null)
+ SV_FinalMessage(finalmsg, reconnect);
+
+ Master_Shutdown();
+
+ SV_GAME.SV_ShutdownGameProgs();
+
+ // free current level
+ if (SV_INIT.sv.demofile != null)
+ try {
+ SV_INIT.sv.demofile.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ SV_INIT.sv = new server_t();
+
+ Globals.server_state = SV_INIT.sv.state;
+
+ if (SV_INIT.svs.demofile != null)
+ try {
+ SV_INIT.svs.demofile.close();
+ } catch (IOException e1) {
+ e1.printStackTrace();
+ }
+
+ SV_INIT.svs = new server_static_t();
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.server;
+
+import lwjake2.game.link_t;
+
+public class areanode_t {
+ final areanode_t[] children = new areanode_t[2];
+ final link_t trigger_edicts = new link_t(this);
+ final link_t solid_edicts = new link_t(this);
+ // used for debugging
+ final float[] mins_rst = {0, 0, 0};
+ final float[] maxs_rst = {0, 0, 0};
+ int axis; // -1 = leaf node
+ float dist;
+}
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.server;
+
+import lwjake2.qcommon.NetadrT;
+
+class challenge_t {
+ //mem
+ NetadrT adr = new NetadrT();
+ int challenge;
+ int time;
+}
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.server;
+
+import lwjake2.Defines;
+import lwjake2.game.player_state_t;
+
+class client_frame_t {
+
+ final byte[] areabits = new byte[Defines.MAX_MAP_AREAS / 8]; // portalarea visibility bits
+ final player_state_t ps = new player_state_t();
+ int areabytes;
+ int num_entities;
+ int first_entity; // into the circular sv_packet_entities[]
+ int senttime; // for ping calculations
+}
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.server;
+
+import lwjake2.Defines;
+import lwjake2.game.EDict;
+import lwjake2.game.usercmd_t;
+import lwjake2.qcommon.netchan_t;
+import lwjake2.qcommon.sizebuf_t;
+
+public class client_t {
+
+ public static final int LATENCY_COUNTS = 16;
+ public static final int RATE_MESSAGES = 10;
+ final int[] frame_latency = new int[LATENCY_COUNTS];
+ final int[] message_size = new int[RATE_MESSAGES]; // used to rate drop packets
+ // The datagram is written to by sound calls, prints, temp ents, etc.
+ // It can be harmlessly overflowed.
+ final sizebuf_t datagram = new sizebuf_t();
+ final byte[] datagram_buf = new byte[Defines.MAX_MSGLEN];
+ final client_frame_t[] frames = new client_frame_t[Defines.UPDATE_BACKUP]; // updates can be delta'd from here
+ final netchan_t netchan = new netchan_t();
+ int state;
+ String userinfo = "";
+ int lastframe; // for delta compression
+ usercmd_t lastcmd = new usercmd_t(); // for filling in big drops
+ int commandMsec; // every seconds this is reset, if user
+ // commands exhaust it, assume time cheating
+ int ping;
+ int rate;
+ int surpressCount; // number of messages rate supressed
+ // pointer
+ EDict edict; // EDICT_NUM(clientnum+1)
+ //char name[32]; // extracted from userinfo, high bits masked
+ String name = ""; // extracted from userinfo, high bits masked
+ int messagelevel; // for filtering printed messages
+ byte download[]; // file being downloaded
+ int downloadsize; // total bytes (can't use EOF because of paks)
+ int downloadcount; // bytes sent
+ int lastmessage; // sv.framenum when packet was last received
+ int lastconnect;
+ int challenge; // challenge of this user, randomly generated
+ //this was introduced by rst, since java can't calculate the index out of the address.
+ int serverindex;
+
+ public client_t() {
+ for (int n = 0; n < Defines.UPDATE_BACKUP; n++) {
+ frames[n] = new client_frame_t();
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.server;
+
+import lwjake2.game.EDict;
+import lwjake2.game.trace_t;
+
+public class moveclip_t {
+ final float[] boxmins = {0, 0, 0};
+ final float[] boxmaxs = {0, 0, 0};// enclose the test object along entire move
+ final float[] mins2 = {0, 0, 0};
+ final float[] maxs2 = {0, 0, 0}; // size when clipping against mosnters
+ float[] mins, maxs; // size of the moving object
+ float[] start, end;
+ // mem
+ trace_t trace = new trace_t();
+ EDict passedict;
+ int contentmask;
+
+}
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.server;
+
+import lwjake2.Defines;
+import lwjake2.game.entity_state_t;
+import lwjake2.qcommon.sizebuf_t;
+
+import java.io.RandomAccessFile;
+
+public class server_static_t {
+ final challenge_t[] challenges = new challenge_t[Defines.MAX_CHALLENGES]; // to
+ // prevent
+ // invalid
+ // IPs
+ // from
+ // connecting
+ final sizebuf_t demo_multicast = new sizebuf_t();
+ final byte[] demo_multicast_buf = new byte[Defines.MAX_MSGLEN];
+ boolean initialized; // sv_init has completed
+ int realtime; // always increasing, no clamping, etc
+ String mapcmd = ""; // ie: *intro.cin+base
+ int spawncount; // incremented each server start
+ client_t clients[]; // [maxclients->value];
+ // used to check late spawns
+ int num_client_entities; // maxclients->value*UPDATE_BACKUP*MAX_PACKET_ENTITIES
+ int next_client_entities; // next client_entity to use
+ entity_state_t client_entities[]; // [num_client_entities]
+ int last_heartbeat;
+ // serverrecord values
+ RandomAccessFile demofile;
+
+ public server_static_t() {
+ for (int n = 0; n < Defines.MAX_CHALLENGES; n++) {
+ challenges[n] = new challenge_t();
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.server;
+
+import lwjake2.Defines;
+import lwjake2.game.cmodel_t;
+import lwjake2.game.entity_state_t;
+import lwjake2.qcommon.sizebuf_t;
+
+import java.io.RandomAccessFile;
+
+public class server_t {
+
+ final cmodel_t[] models;
+ final String[] configstrings = new String[Defines.MAX_CONFIGSTRINGS];
+ final entity_state_t[] baselines = new entity_state_t[Defines.MAX_EDICTS];
+ // the multicast buffer is used to send a message to a set of clients
+ // it is only used to marshall data until SV_Multicast is called
+ final sizebuf_t multicast = new sizebuf_t();
+ final byte[] multicast_buf = new byte[Defines.MAX_MSGLEN];
+ int state; // precache commands are only valid during load
+ boolean attractloop; // running cinematics and demos for the local system
+ boolean loadgame; // client begins should reuse existing entity
+ // only
+ int time; // always sv.framenum * 100 msec
+ int framenum;
+ String name = ""; // map name, or cinematic name
+ // demo server information
+ RandomAccessFile demofile;
+
+ public server_t() {
+ models = new cmodel_t[Defines.MAX_MODELS];
+ for (int n = 0; n < Defines.MAX_MODELS; n++)
+ models[n] = new cmodel_t();
+
+ for (int n = 0; n < Defines.MAX_EDICTS; n++)
+ baselines[n] = new entity_state_t(null);
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.sound;
+
+import java.nio.ByteBuffer;
+
+/**
+ * DummyDriver
+ *
+ * @author cwei
+ */
+public final class DummyDriver implements Sound {
+
+ static {
+ S.register(new DummyDriver());
+ }
+
+ private DummyDriver() {
+ }
+
+ /* (non-Javadoc)
+ * @see jake2.sound.Sound#Init()
+ */
+ public boolean Init() {
+ return true;
+ }
+
+ /* (non-Javadoc)
+ * @see jake2.sound.Sound#Shutdown()
+ */
+ public void Shutdown() {
+ }
+
+ /* (non-Javadoc)
+ * @see jake2.sound.Sound#BeginRegistration()
+ */
+ public void BeginRegistration() {
+ }
+
+ /* (non-Javadoc)
+ * @see jake2.sound.Sound#RegisterSound(java.lang.String)
+ */
+ public sfx_t RegisterSound(String sample) {
+ return null;
+ }
+
+ /* (non-Javadoc)
+ * @see jake2.sound.Sound#EndRegistration()
+ */
+ public void EndRegistration() {
+ }
+
+ /* (non-Javadoc)
+ * @see jake2.sound.Sound#StartLocalSound(java.lang.String)
+ */
+ public void StartLocalSound(String sound) {
+ }
+
+ /* (non-Javadoc)
+ * @see jake2.sound.Sound#StartSound(float[], int, int, jake2.sound.sfx_t, float, float, float)
+ */
+ public void StartSound(float[] origin, int entnum, int entchannel, sfx_t sfx, float fvol, float attenuation, float timeofs) {
+ }
+
+ /* (non-Javadoc)
+ * @see jake2.sound.Sound#Update(float[], float[], float[], float[])
+ */
+ public void Update(float[] origin, float[] forward, float[] right, float[] up) {
+ }
+
+ /* (non-Javadoc)
+ * @see jake2.sound.Sound#RawSamples(int, int, int, int, byte[])
+ */
+ public void RawSamples(int samples, int rate, int width, int channels, ByteBuffer data) {
+ }
+
+ public void disableStreaming() {
+ }
+
+ /* (non-Javadoc)
+ * @see jake2.sound.Sound#StopAllSounds()
+ */
+ public void StopAllSounds() {
+ }
+
+ /* (non-Javadoc)
+ * @see jake2.sound.Sound#getName()
+ */
+ public String getName() {
+ return "dummy";
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.sound;
+
+import lwjake2.Defines;
+import lwjake2.game.CvarT;
+import lwjake2.qcommon.Com;
+import lwjake2.qcommon.Cvar;
+
+import java.nio.ByteBuffer;
+import java.util.Vector;
+
+public class S {
+
+ static final Vector<Sound> drivers = new Vector<>(1);
+ static Sound impl;
+ static CvarT s_impl;
+
+ /**
+ * Searches for and initializes all known sound drivers.
+ */
+ static {
+ // dummy driver (no sound)
+ try {
+ Class.forName("lwjake2.sound.DummyDriver");
+ // initialize impl with the default value
+ // this is necessary for dedicated mode
+ useDriver("dummy");
+ } catch (Throwable e) {
+ Com.DPrintf("could not init dummy sound driver class.");
+ }
+
+ try {
+ Class.forName("org.lwjgl.openal.AL");
+ Class.forName("lwjake2.sound.lwjgl.LWJGLSoundImpl");
+ } catch (Throwable e) {
+ // ignore the lwjgl driver if runtime not in classpath
+ Com.DPrintf("could not init lwjgl sound driver class.");
+ }
+ }
+
+ /**
+ * Registers a new Sound Implementor.
+ */
+ public static void register(Sound driver) {
+ if (driver == null) {
+ throw new IllegalArgumentException("Sound implementation can't be null");
+ }
+ if (!drivers.contains(driver)) {
+ drivers.add(driver);
+ }
+ }
+
+ /**
+ * Switches to the specific sound driver.
+ */
+ public static void useDriver(String driverName) {
+ Sound driver = null;
+ int count = drivers.size();
+ for (Sound driver1 : drivers) {
+ driver = driver1;
+ if (driver.getName().equals(driverName)) {
+ impl = driver;
+ return;
+ }
+ }
+ // if driver not found use dummy
+ impl = drivers.lastElement();
+ }
+
+ /**
+ * Initializes the sound module.
+ */
+ public static void Init() {
+
+ Com.Printf("\n------- sound initialization -------\n");
+
+ CvarT cv = Cvar.get("s_initsound", "1", 0);
+ if (cv.value == 0.0f) {
+ Com.Printf("not initializing.\n");
+ useDriver("dummy");
+ return;
+ }
+
+ // set the last registered driver as default
+ String defaultDriver = "dummy";
+ if (drivers.size() > 1) {
+ defaultDriver = (drivers.lastElement()).getName();
+ }
+
+ s_impl = Cvar.get("s_impl", defaultDriver, Defines.CVAR_ARCHIVE);
+ useDriver(s_impl.string);
+
+ if (impl.Init()) {
+ // driver ok
+ Cvar.set("s_impl", impl.getName());
+ } else {
+ // fallback
+ useDriver("dummy");
+ }
+
+ Com.Printf("\n------- use sound driver \"" + impl.getName() + "\" -------\n");
+ StopAllSounds();
+ }
+
+ public static void Shutdown() {
+ impl.Shutdown();
+ }
+
+ /**
+ * Called before the sounds are to be loaded and registered.
+ */
+ public static void BeginRegistration() {
+ impl.BeginRegistration();
+ }
+
+ /**
+ * Registers and loads a sound.
+ */
+ public static sfx_t RegisterSound(String sample) {
+ return impl.RegisterSound(sample);
+ }
+
+ /**
+ * Called after all sounds are registered and loaded.
+ */
+ public static void EndRegistration() {
+ impl.EndRegistration();
+ }
+
+ /**
+ * Starts a local sound.
+ */
+ public static void StartLocalSound(String sound) {
+ impl.StartLocalSound(sound);
+ }
+
+ /**
+ * StartSound - Validates the parms and ques the sound up
+ * if pos is NULL, the sound will be dynamically sourced from the entity
+ * Entchannel 0 will never override a playing sound
+ */
+ public static void StartSound(float[] origin, int entnum, int entchannel, sfx_t sfx, float fvol, float attenuation, float timeofs) {
+ impl.StartSound(origin, entnum, entchannel, sfx, fvol, attenuation, timeofs);
+ }
+
+ /**
+ * Updates the sound renderer according to the changes in the environment,
+ * called once each time through the main loop.
+ */
+ public static void Update(float[] origin, float[] forward, float[] right, float[] up) {
+ impl.Update(origin, forward, right, up);
+ }
+
+ /**
+ * Cinematic streaming and voice over network.
+ */
+ public static void RawSamples(int samples, int rate, int width, int channels, ByteBuffer data) {
+ impl.RawSamples(samples, rate, width, channels, data);
+ }
+
+ /**
+ * Switches off the sound streaming.
+ */
+ public static void disableStreaming() {
+ impl.disableStreaming();
+ }
+
+ /**
+ * Stops all sounds.
+ */
+ public static void StopAllSounds() {
+ impl.StopAllSounds();
+ }
+
+ public static String getDriverName() {
+ return impl.getName();
+ }
+
+ /**
+ * Returns a string array containing all sound driver names.
+ */
+ public static String[] getDriverNames() {
+ String[] names = new String[drivers.size()];
+ for (int i = 0; i < names.length; i++) {
+ names[i] = (drivers.get(i)).getName();
+ }
+ return names;
+ }
+
+ /**
+ * This is used, when resampling to this default sampling rate is activated
+ * in the wavloader. It is placed here that sound implementors can override
+ * this one day.
+ */
+ public static int getDefaultSampleRate() {
+ return 44100;
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.sound;
+
+import lwjake2.Defines;
+
+import java.nio.ByteBuffer;
+
+/**
+ * Sound
+ *
+ * @author cwei
+ */
+public interface Sound {
+
+ int MAX_SFX = Defines.MAX_SOUNDS * 2;
+ int STREAM_QUEUE = 8;
+
+ String getName();
+
+ boolean Init();
+
+ void Shutdown();
+
+ /*
+ =====================
+ S_BeginRegistration
+ =====================
+ */
+ void BeginRegistration();
+
+ /*
+ =====================
+ S_RegisterSound
+ =====================
+ */
+ sfx_t RegisterSound(String sample);
+
+ /*
+ =====================
+ S_EndRegistration
+ =====================
+ */
+ void EndRegistration();
+
+ /*
+ ==================
+ S_StartLocalSound
+ ==================
+ */
+ void StartLocalSound(String sound);
+
+ /*
+ ====================
+ S_StartSound
+
+ Validates the parms and ques the sound up
+ if pos is NULL, the sound will be dynamically sourced from the entity
+ Entchannel 0 will never override a playing sound
+ ====================
+ */
+ void StartSound(float[] origin, int entnum, int entchannel, sfx_t sfx, float fvol, float attenuation, float timeofs);
+
+ /*
+ ============
+ S_Update
+
+ Called once each time through the main loop
+ ============
+ */
+ void Update(float[] origin, float[] forward, float[] right, float[] up);
+
+ /*
+ ============
+ S_RawSamples
+
+ Cinematic streaming and voice over network
+ ============
+ */
+ void RawSamples(int samples, int rate, int width, int channels, ByteBuffer data);
+
+ void disableStreaming();
+
+ /*
+ ==================
+ S_StopAllSounds
+ ==================
+ */
+ void StopAllSounds();
+
+}
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.sound;
+
+import lwjake2.Defines;
+import lwjake2.qcommon.Com;
+import lwjake2.qcommon.FS;
+
+/**
+ * SND_MEM
+ */
+public class WaveLoader {
+
+ /**
+ * The ResampleSfx can squeeze and stretch samples to a default sample rate.
+ * Since Joal and lwjgl sound drivers support this, we don't need it and the samples
+ * can keep their original sample rate. Use this switch for reactivating resampling.
+ */
+ private static final boolean DONT_DO_A_RESAMPLING_FOR_JOAL_AND_LWJGL = true;
+ static byte[] data_b;
+ static int data_p;
+ static int iff_end;
+ static int last_chunk;
+ static int iff_data;
+ static int iff_chunk_len;
+
+ /**
+ * Loads a sound from a wav file.
+ */
+ public static sfxcache_t LoadSound(sfx_t s) {
+ if (s.name.charAt(0) == '*')
+ return null;
+
+ // see if still in memory
+ sfxcache_t sc = s.cache;
+ if (sc != null)
+ return sc;
+
+ String name;
+ // load it in
+ if (s.truename != null)
+ name = s.truename;
+ else
+ name = s.name;
+
+ String namebuffer;
+ if (name.charAt(0) == '#')
+ namebuffer = name.substring(1);
+ else
+ namebuffer = "sound/" + name;
+
+ byte[] data = FS.LoadFile(namebuffer);
+
+ if (data == null) {
+ Com.DPrintf("Couldn't load " + namebuffer + "\n");
+ return null;
+ }
+
+ int size = data.length;
+
+ wavinfo_t info = GetWavinfo(s.name, data, size);
+
+ if (info.channels != 1) {
+ Com.Printf(s.name + " is a stereo sample - ignoring\n");
+ return null;
+ }
+
+ float stepscale;
+ if (DONT_DO_A_RESAMPLING_FOR_JOAL_AND_LWJGL)
+ stepscale = 1;
+ else
+ stepscale = (float) info.rate / S.getDefaultSampleRate();
+
+ int len = (int) (info.samples / stepscale);
+ len = len * info.width * info.channels;
+
+ // TODO: handle max sample bytes with a cvar
+ /*
+ This is the maximum sample length in bytes which has to be replaced by
+ a configurable variable.
+ */
+ int maxsamplebytes = 2048 * 1024;
+ if (len >= maxsamplebytes) {
+ Com.Printf(s.name + " is too long: " + len + " bytes?! ignoring.\n");
+ return null;
+ }
+
+ sc = s.cache = new sfxcache_t(len);
+
+ sc.length = info.samples;
+ sc.loopstart = info.loopstart;
+ sc.speed = info.rate;
+ sc.width = info.width;
+ sc.stereo = info.channels;
+
+ ResampleSfx(s, sc.speed, sc.width, data, info.dataofs);
+ data = null;
+
+ return sc;
+ }
+
+ /**
+ * Converts sample data with respect to the endianess and adjusts
+ * the sample rate of a loaded sample, see flag DONT_DO_A_RESAMPLING_FOR_JOAL_AND_LWJGL.
+ */
+ public static void ResampleSfx(sfx_t sfx, int inrate, int inwidth, byte data[], int offset) {
+ int outcount;
+ int srcsample;
+ int i;
+ int sample, samplefrac, fracstep;
+ sfxcache_t sc;
+
+ sc = sfx.cache;
+
+ if (sc == null)
+ return;
+
+ // again calculate the stretching factor.
+ // this is usually 0.5, 1, or 2
+
+ float stepscale;
+ if (DONT_DO_A_RESAMPLING_FOR_JOAL_AND_LWJGL)
+ stepscale = 1;
+ else
+ stepscale = (float) inrate / S.getDefaultSampleRate();
+ outcount = (int) (sc.length / stepscale);
+ sc.length = outcount;
+
+ if (sc.loopstart != -1)
+ sc.loopstart = (int) (sc.loopstart / stepscale);
+
+ // if resampled, sample has now the default sample rate
+ if (!DONT_DO_A_RESAMPLING_FOR_JOAL_AND_LWJGL)
+ sc.speed = S.getDefaultSampleRate();
+
+ sc.width = inwidth;
+ sc.stereo = 0;
+ samplefrac = 0;
+ fracstep = (int) (stepscale * 256);
+
+ for (i = 0; i < outcount; i++) {
+ srcsample = samplefrac >> 8;
+ samplefrac += fracstep;
+
+ if (inwidth == 2) {
+ sample = (data[offset + srcsample * 2] & 0xff)
+ + (data[offset + srcsample * 2 + 1] << 8);
+ } else {
+ sample = ((data[offset + srcsample] & 0xff) - 128) << 8;
+ }
+
+ if (sc.width == 2) {
+ if (Defines.LITTLE_ENDIAN) {
+ sc.data[i * 2] = (byte) (sample & 0xff);
+ sc.data[i * 2 + 1] = (byte) ((sample >>> 8) & 0xff);
+ } else {
+ sc.data[i * 2] = (byte) ((sample >>> 8) & 0xff);
+ sc.data[i * 2 + 1] = (byte) (sample & 0xff);
+ }
+ } else {
+ sc.data[i] = (byte) (sample >> 8);
+ }
+ }
+ }
+
+ static short GetLittleShort() {
+ int val = 0;
+ val = data_b[data_p] & 0xFF;
+ data_p++;
+ val |= ((data_b[data_p] & 0xFF) << 8);
+ data_p++;
+ return (short) val;
+ }
+
+ static int GetLittleLong() {
+ int val = 0;
+ val = data_b[data_p] & 0xFF;
+ data_p++;
+ val |= ((data_b[data_p] & 0xFF) << 8);
+ data_p++;
+ val |= ((data_b[data_p] & 0xFF) << 16);
+ data_p++;
+ val |= ((data_b[data_p] & 0xFF) << 24);
+ data_p++;
+ return val;
+ }
+
+ static void FindNextChunk(String name) {
+ while (true) {
+ data_p = last_chunk;
+
+ if (data_p >= iff_end) { // didn't find the chunk
+ data_p = 0;
+ return;
+ }
+
+ data_p += 4;
+
+ iff_chunk_len = GetLittleLong();
+
+ if (iff_chunk_len < 0) {
+ data_p = 0;
+ return;
+ }
+ if (iff_chunk_len > 1024 * 1024) {
+ Com.Println(" Warning: FindNextChunk: length is past the 1 meg sanity limit");
+ }
+ data_p -= 8;
+ last_chunk = data_p + 8 + ((iff_chunk_len + 1) & ~1);
+ String s = new String(data_b, data_p, 4);
+ if (s.equals(name))
+ return;
+ }
+ }
+
+ static void FindChunk(String name) {
+ last_chunk = iff_data;
+ FindNextChunk(name);
+ }
+
+ /*
+ ============
+ GetWavinfo
+ ============
+ */
+ static wavinfo_t GetWavinfo(String name, byte[] wav, int wavlength) {
+ wavinfo_t info = new wavinfo_t();
+ int i;
+ int format;
+ int samples;
+
+ if (wav == null)
+ return info;
+
+ iff_data = 0;
+ iff_end = wavlength;
+ data_b = wav;
+
+ // find "RIFF" chunk
+ FindChunk("RIFF");
+ String s = new String(data_b, data_p + 8, 4);
+ if (!s.equals("WAVE")) {
+ Com.Printf("Missing RIFF/WAVE chunks\n");
+ return info;
+ }
+
+ // get "fmt " chunk
+ iff_data = data_p + 12;
+ // DumpChunks ();
+
+ FindChunk("fmt ");
+ if (data_p == 0) {
+ Com.Printf("Missing fmt chunk\n");
+ return info;
+ }
+ data_p += 8;
+ format = GetLittleShort();
+ if (format != 1) {
+ Com.Printf("Microsoft PCM format only\n");
+ return info;
+ }
+
+ info.channels = GetLittleShort();
+ info.rate = GetLittleLong();
+ data_p += 4 + 2;
+ info.width = GetLittleShort() / 8;
+
+ // get cue chunk
+ FindChunk("cue ");
+ if (data_p != 0) {
+ data_p += 32;
+ info.loopstart = GetLittleLong();
+ // Com_Printf("loopstart=%d\n", sfx->loopstart);
+
+ // if the next chunk is a LIST chunk, look for a cue length marker
+ FindNextChunk("LIST");
+ if (data_p != 0) {
+ if (data_b.length >= data_p + 32) {
+ s = new String(data_b, data_p + 28, 4);
+ if (s.equals("MARK")) { // this is not a proper parse, but
+ // it works with cooledit...
+ data_p += 24;
+ i = GetLittleLong(); // samples in loop
+ info.samples = info.loopstart + i;
+ // Com_Printf("looped length: %i\n", i);
+ }
+ }
+ }
+ } else
+ info.loopstart = -1;
+
+ // find data chunk
+ FindChunk("data");
+ if (data_p == 0) {
+ Com.Printf("Missing data chunk\n");
+ return info;
+ }
+
+ data_p += 4;
+ samples = GetLittleLong() / info.width;
+
+ if (info.samples != 0) {
+ if (samples < info.samples)
+ Com.Error(Defines.ERR_DROP, "Sound " + name + " has a bad loop length");
+ } else {
+ info.samples = samples;
+ if (info.loopstart > 0) info.samples -= info.loopstart;
+ }
+
+ info.dataofs = data_p;
+
+ return info;
+ }
+
+ static class wavinfo_t {
+ int rate;
+ int width;
+ int channels;
+ int loopstart;
+ int samples;
+ int dataofs; // chunk starts this many bytes from file start
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.sound.lwjgl;
+
+import lwjake2.Defines;
+import lwjake2.Globals;
+import lwjake2.client.CL_ents;
+import lwjake2.game.entity_state_t;
+import lwjake2.qcommon.Com;
+import lwjake2.sound.Sound;
+import lwjake2.sound.sfx_t;
+import lwjake2.sound.sfxcache_t;
+import lwjake2.util.Lib;
+import lwjake2.util.Math3D;
+import org.lwjgl.openal.AL10;
+import org.lwjgl.openal.AL11;
+import org.lwjgl.openal.EFX10;
+
+import java.nio.ByteBuffer;
+import java.nio.FloatBuffer;
+import java.nio.IntBuffer;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ * Channel
+ *
+ * @author dsanders/cwei
+ */
+public class Channel {
+
+ final static int LISTENER = 0;
+ final static int FIXED = 1;
+ final static int DYNAMIC = 2;
+ final static int MAX_CHANNELS = 128;
+
+ private static final Channel[] channels = new Channel[MAX_CHANNELS];
+ private static final IntBuffer sources = Lib.newIntBuffer(MAX_CHANNELS);
+ private static final Map<Integer, Channel> looptable = new Hashtable<>(MAX_CHANNELS);
+ private static final IntBuffer tmp = Lib.newIntBuffer(1);
+ private static final FloatBuffer sourceOriginBuffer = Lib.newFloatBuffer(3);
+ //stack variable
+ private static final float[] entityOrigin = {0, 0, 0};
+ // a reference of LWJGLSoundImpl.buffers
+ private static IntBuffer buffers;
+ private static int numChannels;
+ // stream handling
+ private static boolean streamingEnabled = false;
+ private static int streamQueue = 0;
+ private final int sourceId;
+ private final float[] origin = {0, 0, 0};
+ // sound attributes
+ private int type;
+ private int entnum;
+ private int entchannel;
+ private int bufferId;
+ private float volume;
+ private float rolloff;
+ // update flags
+ private boolean autosound;
+ private boolean active;
+ private boolean modified;
+ private boolean bufferChanged;
+ private boolean volumeChanged;
+
+ private Channel(int sourceId) {
+ this.sourceId = sourceId;
+ clear();
+ volumeChanged = false;
+ volume = 1.0f;
+ }
+
+ static int init(IntBuffer buffers) {
+ Channel.buffers = buffers;
+ // create channels
+ int sourceId;
+ for (int i = 0; i < MAX_CHANNELS; i++) {
+
+ AL10.alGenSources(tmp);
+ sourceId = tmp.get(0);
+
+ // can't generate more sources
+ if (sourceId <= 0) break;
+
+ sources.put(i, sourceId);
+
+ channels[i] = new Channel(sourceId);
+ numChannels++;
+
+ // set default values for AL sources
+ AL10.alSourcef(sourceId, AL10.AL_GAIN, 1.0f);
+ AL10.alSourcef(sourceId, AL10.AL_PITCH, 1.0f);
+ AL10.alSourcei(sourceId, AL10.AL_SOURCE_ABSOLUTE, AL10.AL_TRUE);
+ AL10.alSource3f(sourceId, AL10.AL_VELOCITY, 0, 0, 0);
+ AL10.alSourcei(sourceId, AL10.AL_LOOPING, AL10.AL_FALSE);
+ AL10.alSourcef(sourceId, AL10.AL_REFERENCE_DISTANCE, 200.0f);
+ AL10.alSourcef(sourceId, AL10.AL_MIN_GAIN, 0.0005f);
+ AL10.alSourcef(sourceId, AL10.AL_MAX_GAIN, 1.0f);
+ }
+ return numChannels;
+ }
+
+ static void reset() {
+ for (int i = 0; i < numChannels; i++) {
+ AL10.alSourceStop(sources.get(i));
+ AL10.alSourcei(sources.get(i), AL10.AL_BUFFER, 0);
+ channels[i].clear();
+ }
+ }
+
+ static void shutdown() {
+ AL10.alDeleteSources(sources);
+ numChannels = 0;
+ }
+
+ static void enableStreaming() {
+ if (streamingEnabled) return;
+
+ // use the last source
+ numChannels--;
+ streamingEnabled = true;
+ streamQueue = 0;
+
+ int source = channels[numChannels].sourceId;
+ AL10.alSourcei(source, AL10.AL_SOURCE_RELATIVE, AL10.AL_TRUE);
+ AL10.alSourcef(source, AL10.AL_GAIN, 1.0f);
+ channels[numChannels].volumeChanged = true;
+
+ Com.DPrintf("streaming enabled\n");
+ }
+
+ static void disableStreaming() {
+ if (!streamingEnabled) return;
+ unqueueStreams();
+ int source = channels[numChannels].sourceId;
+ AL10.alSourcei(source, AL10.AL_SOURCE_ABSOLUTE, AL10.AL_TRUE);
+
+ // free the last source
+ //numChannels++;
+ streamingEnabled = false;
+ Com.DPrintf("streaming disabled\n");
+ }
+
+ static void unqueueStreams() {
+ if (!streamingEnabled) return;
+ int source = channels[numChannels].sourceId;
+
+ // stop streaming
+ AL10.alSourceStop(source);
+ int count = AL10.alGetSourcei(source, AL10.AL_BUFFERS_QUEUED);
+ Com.DPrintf("unqueue " + count + " buffers\n");
+ while (count-- > 0) {
+ AL10.alSourceUnqueueBuffers(source, tmp);
+ }
+ streamQueue = 0;
+ }
+
+ static void updateStream(ByteBuffer samples, int count, int format, int rate) {
+ enableStreaming();
+ int source = channels[numChannels].sourceId;
+ int processed = AL10.alGetSourcei(source, AL10.AL_BUFFERS_PROCESSED);
+
+ boolean playing = (AL10.alGetSourcei(source, AL10.AL_SOURCE_STATE) == AL10.AL_PLAYING);
+ boolean interupted = !playing && streamQueue > 2;
+
+ IntBuffer buffer = tmp;
+ if (interupted) {
+ unqueueStreams();
+ buffer.put(0, buffers.get(Sound.MAX_SFX + streamQueue++));
+ Com.DPrintf("queue " + (streamQueue - 1) + '\n');
+ } else if (processed < 2) {
+ // check queue overrun
+ if (streamQueue >= Sound.STREAM_QUEUE) return;
+ buffer.put(0, buffers.get(Sound.MAX_SFX + streamQueue++));
+ Com.DPrintf("queue " + (streamQueue - 1) + '\n');
+ } else {
+ // reuse the buffer
+ AL10.alSourceUnqueueBuffers(source, buffer);
+ }
+
+ samples.position(0);
+ samples.limit(count);
+ AL10.alBufferData(buffer.get(0), format, samples, rate);
+ AL10.alSourceQueueBuffers(source, buffer);
+
+ if (streamQueue > 1 && !playing) {
+ Com.DPrintf("start sound\n");
+ AL10.alSourcePlay(source);
+ }
+ }
+
+ static void addPlaySounds() {
+ while (Channel.assign(PlaySound.nextPlayableSound())) ;
+ }
+
+ private static boolean assign(PlaySound ps) {
+ if (ps == null) return false;
+ Channel ch = null;
+ int i;
+ for (i = 0; i < numChannels; i++) {
+ ch = channels[i];
+
+ if (ps.entchannel != 0 && ch.entnum == ps.entnum && ch.entchannel == ps.entchannel) {
+ // always override sound from same entity
+ if (ch.bufferId != ps.bufferId) {
+ AL10.alSourceStop(ch.sourceId);
+ }
+ break;
+ }
+
+ // don't let monster sounds override player sounds
+ if ((ch.entnum == Globals.clientStateT.playernum + 1) && (ps.entnum != Globals.clientStateT.playernum + 1) && ch.bufferId != -1)
+ continue;
+
+ // looking for a free AL source
+ if (!ch.active) {
+ break;
+ }
+ }
+
+ if (i == numChannels)
+ return false;
+
+ ch.type = ps.type;
+ if (ps.type == Channel.FIXED)
+ Math3D.vectorCopy(ps.origin, ch.origin);
+ ch.entnum = ps.entnum;
+ ch.entchannel = ps.entchannel;
+ ch.bufferChanged = (ch.bufferId != ps.bufferId);
+ ch.bufferId = ps.bufferId;
+ ch.rolloff = ps.attenuation * 2;
+ ch.volumeChanged = (ch.volume != ps.volume);
+ ch.volume = ps.volume;
+ ch.active = true;
+ ch.modified = true;
+ return true;
+ }
+
+ private static Channel pickForLoop(int bufferId) {
+ Channel ch;
+ for (int i = 0; i < numChannels; i++) {
+ ch = channels[i];
+ // looking for a free AL source
+ if (!ch.active) {
+ ch.entnum = 0;
+ ch.entchannel = 0;
+ ch.bufferChanged = (ch.bufferId != bufferId);
+ ch.bufferId = bufferId;
+ ch.volumeChanged = (ch.volume != 1.0f);
+ ch.volume = 1.0f;
+ ch.rolloff = (float) 6 * 2;
+ ch.active = true;
+ ch.modified = true;
+ return ch;
+ }
+ }
+ return null;
+ }
+
+ static void playAllSounds(FloatBuffer listenerOrigin, int currentEffectIndex, int currentFilterIndex) {
+ FloatBuffer sourceOrigin = sourceOriginBuffer;
+ Channel ch;
+ int sourceId;
+ int state;
+
+ for (int i = 0; i < numChannels; i++) {
+ ch = channels[i];
+ if (ch.active) {
+ sourceId = ch.sourceId;
+ switch (ch.type) {
+ case Channel.LISTENER:
+ sourceOrigin.put(0, listenerOrigin.get(0));
+ sourceOrigin.put(1, listenerOrigin.get(1));
+ sourceOrigin.put(2, listenerOrigin.get(2));
+ break;
+ case Channel.DYNAMIC:
+ CL_ents.GetEntitySoundOrigin(ch.entnum, entityOrigin);
+ convertVector(entityOrigin, sourceOrigin);
+ break;
+ case Channel.FIXED:
+ convertVector(ch.origin, sourceOrigin);
+ break;
+ }
+
+ if (ch.modified) {
+ if (ch.bufferChanged) {
+ AL10.alSourcei(sourceId, AL10.AL_BUFFER, ch.bufferId);
+ }
+ if (ch.volumeChanged) {
+ AL10.alSourcef(sourceId, AL10.AL_GAIN, ch.volume);
+ }
+ AL10.alSourcef(sourceId, AL10.AL_ROLLOFF_FACTOR, ch.rolloff);
+ AL10.alSource3f(sourceId, AL10.AL_POSITION, sourceOrigin.get(), sourceOrigin.get(), sourceOrigin.get());
+ AL11.alSource3i(sourceId, EFX10.AL_AUXILIARY_SEND_FILTER, currentEffectIndex, 0, EFX10.AL_FILTER_NULL);
+ AL10.alSourcei(sourceId, EFX10.AL_DIRECT_FILTER, currentFilterIndex);
+ AL10.alSourcePlay(sourceId);
+ sourceOrigin.rewind();
+ ch.modified = false;
+ } else {
+ state = AL10.alGetSourcei(sourceId, AL10.AL_SOURCE_STATE);
+ if (state == AL10.AL_PLAYING) {
+ AL10.alSource3f(sourceId, AL10.AL_POSITION, sourceOrigin.get(), sourceOrigin.get(), sourceOrigin.get());
+ sourceOrigin.rewind();
+ } else {
+ ch.clear();
+ }
+ }
+ ch.autosound = false;
+ }
+ }
+ }
+
+ /*
+ * adddLoopSounds
+ * Entities with a ->sound field will generated looped sounds
+ * that are automatically started, stopped, and merged together
+ * as the entities are sent to the client
+ */
+ static void addLoopSounds() {
+
+ if ((Globals.cl_paused.value != 0.0f) || (Globals.clientStaticT.state != Globals.ca_active) || !Globals.clientStateT.sound_prepped) {
+ removeUnusedLoopSounds();
+ return;
+ }
+
+ Channel ch;
+ sfx_t sfx;
+ sfxcache_t sc;
+ int num;
+ entity_state_t ent;
+ int key;
+ int sound = 0;
+
+ for (int i = 0; i < Globals.clientStateT.frame.num_entities; i++) {
+ num = (Globals.clientStateT.frame.parse_entities + i) & (Defines.MAX_PARSE_ENTITIES - 1);
+ ent = Globals.cl_parse_entities[num];
+ sound = ent.sound;
+
+ if (sound == 0) continue;
+
+ key = ent.number;
+ ch = looptable.get(key);
+
+ if (ch != null) {
+ // keep on looping
+ ch.autosound = true;
+ Math3D.vectorCopy(ent.origin, ch.origin);
+ continue;
+ }
+
+ sfx = Globals.clientStateT.sound_precache[sound];
+ if (sfx == null)
+ continue; // bad sound effect
+
+ sc = sfx.cache;
+ if (sc == null)
+ continue;
+
+ // allocate a channel
+ ch = Channel.pickForLoop(buffers.get(sfx.bufferId));
+ if (ch == null)
+ break;
+
+ ch.type = FIXED;
+ Math3D.vectorCopy(ent.origin, ch.origin);
+ ch.autosound = true;
+
+ looptable.put(key, ch);
+ AL10.alSourcei(ch.sourceId, AL10.AL_LOOPING, AL10.AL_TRUE);
+ }
+
+ removeUnusedLoopSounds();
+
+ }
+
+ private static void removeUnusedLoopSounds() {
+ Channel ch;
+ // stop unused loopsounds
+ for (Iterator<Channel> iter = looptable.values().iterator(); iter.hasNext(); ) {
+ ch = iter.next();
+ if (!ch.autosound) {
+ AL10.alSourceStop(ch.sourceId);
+ AL10.alSourcei(ch.sourceId, AL10.AL_LOOPING, AL10.AL_FALSE);
+ iter.remove();
+ ch.clear();
+ }
+ }
+ }
+
+ static void convertVector(float[] from, FloatBuffer to) {
+ to.put(0, from[0]);
+ to.put(1, from[2]);
+ to.put(2, -from[1]);
+ }
+
+ static void convertOrientation(float[] forward, float[] up, FloatBuffer orientation) {
+ orientation.put(0, forward[0]);
+ orientation.put(1, forward[2]);
+ orientation.put(2, -forward[1]);
+ orientation.put(3, up[0]);
+ orientation.put(4, up[2]);
+ orientation.put(5, -up[1]);
+ }
+
+ private void clear() {
+ entnum = entchannel = bufferId = -1;
+ bufferChanged = false;
+ rolloff = 0;
+ autosound = false;
+ active = false;
+ modified = false;
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.sound.lwjgl;
+
+import lwjake2.Defines;
+import lwjake2.EFX.EFXFilterLowPass;
+import lwjake2.Globals;
+import lwjake2.game.Cmd;
+import lwjake2.game.CvarT;
+import lwjake2.game.GameBase;
+import lwjake2.game.entity_state_t;
+import lwjake2.qcommon.Com;
+import lwjake2.qcommon.Cvar;
+import lwjake2.qcommon.FS;
+import lwjake2.qcommon.xcommand_t;
+import lwjake2.sound.*;
+import lwjake2.util.Lib;
+import lwjake2.util.Vargs;
+import org.lwjgl.LWJGLException;
+import org.lwjgl.openal.*;
+
+import java.nio.*;
+
+/**
+ * LWJGLSoundImpl
+ *
+ * @author dsanders/cwei
+ */
+public final class LWJGLSoundImpl implements Sound {
+
+ static final sfx_t[] known_sfx = new sfx_t[MAX_SFX];
+ static int num_sfx;
+
+ static {
+ S.register(new LWJGLSoundImpl());
+ }
+
+ static {
+ for (int i = 0; i < known_sfx.length; i++)
+ known_sfx[i] = new sfx_t();
+ }
+
+ // the last 4 buffers are used for cinematics streaming
+ private final IntBuffer buffers = Lib.newIntBuffer(MAX_SFX + STREAM_QUEUE);
+ // TODO check the sfx direct buffer size
+ // 2MB sfx buffer
+ private final ByteBuffer sfxDataBuffer = Lib.newByteBuffer(2 * 1024 * 1024);
+ private final FloatBuffer listenerOrigin = Lib.newFloatBuffer(3);
+ private final FloatBuffer listenerOrientation = Lib.newFloatBuffer(6);
+ private final ShortBuffer streamBuffer = sfxDataBuffer.slice().order(ByteOrder.BIG_ENDIAN).asShortBuffer();
+ int s_registration_sequence;
+ boolean s_registering;
+ private CvarT s_volume;
+ /**
+ * EFX Variables
+ */
+ private int currentEffectIndex;
+ private EFXFilterLowPass underwaterFilter;
+
+ // singleton
+ private LWJGLSoundImpl() {
+ }
+
+ /* (non-Javadoc)
+ * @see jake2.sound.SoundImpl#Init()
+ */
+ public boolean Init() {
+
+ try {
+ initOpenAL();
+ checkError();
+ initOpenALExtensions();
+ } catch (OpenALException e) {
+ Com.Printf(e.getMessage() + '\n');
+ return false;
+ } catch (Exception e) {
+ Com.DPrintf(e.getMessage() + '\n');
+ return false;
+ }
+
+ // set the listerner (master) volume
+ s_volume = Cvar.get("s_volume", "0.7", Defines.CVAR_ARCHIVE);
+ AL10.alGenBuffers(buffers);
+ int count = Channel.init(buffers);
+ Com.Printf("... using " + count + " channels\n");
+ AL10.alDistanceModel(AL10.AL_INVERSE_DISTANCE_CLAMPED);
+ Cmd.AddCommand("play", new xcommand_t() {
+ public void execute() {
+ Play();
+ }
+ });
+ Cmd.AddCommand("stopsound", new xcommand_t() {
+ public void execute() {
+ StopAllSounds();
+ }
+ });
+ Cmd.AddCommand("soundlist", new xcommand_t() {
+ public void execute() {
+ SoundList();
+ }
+ });
+ Cmd.AddCommand("soundinfo", new xcommand_t() {
+ public void execute() {
+ SoundInfo_f();
+ }
+ });
+
+ num_sfx = 0;
+
+ Com.Printf("sound sampling rate: 44100Hz\n");
+
+ StopAllSounds();
+ Com.Printf("------------------------------------\n");
+ return true;
+ }
+
+ private void initOpenAL() throws OpenALException {
+ try {
+ AL.create();
+ } catch (LWJGLException e) {
+ throw new OpenALException(e);
+ }
+ String deviceName = null;
+
+ String os = System.getProperty("os.name");
+ if (os.startsWith("Windows")) {
+ deviceName = "DirectSound3D";
+ }
+
+ String defaultSpecifier = ALC10.alcGetString(AL.getDevice(), ALC10.ALC_DEFAULT_DEVICE_SPECIFIER);
+
+ Com.Printf(os + " using " + ((deviceName == null) ? defaultSpecifier : deviceName) + '\n');
+
+ // Check for an error.
+ if (ALC10.alcGetError(AL.getDevice()) != ALC10.ALC_NO_ERROR) {
+ Com.DPrintf("Error with SoundDevice");
+ }
+ }
+
+ /**
+ * Initializes OpenAL EFX effects.
+ */
+ private void initOpenALExtensions() {
+ Com.Printf("... using EFX effects:\n");
+ underwaterFilter = new EFXFilterLowPass();
+ underwaterFilter.setGain(1.0f);
+ underwaterFilter.setGainHF(0.0f);
+ }
+
+ void exitOpenAL() {
+ // Unload EFX Effects
+ underwaterFilter.killFilter();
+
+ // Release the context and the device.
+ AL.destroy();
+ }
+
+ /* (non-Javadoc)
+ * @see jake2.sound.SoundImpl#RegisterSound(jake2.sound.sfx_t)
+ */
+ private void initBuffer(byte[] samples, int bufferId, int freq) {
+ ByteBuffer data = sfxDataBuffer.slice();
+ data.put(samples).flip();
+ AL10.alBufferData(buffers.get(bufferId), AL10.AL_FORMAT_MONO16,
+ data, freq);
+ }
+
+ private void checkError() {
+ Com.DPrintf("AL Error: " + alErrorString() + '\n');
+ }
+
+ private String alErrorString() {
+ int error;
+ String message = "";
+ if ((error = AL10.alGetError()) != AL10.AL_NO_ERROR) {
+ switch (error) {
+ case AL10.AL_INVALID_OPERATION:
+ message = "invalid operation";
+ break;
+ case AL10.AL_INVALID_VALUE:
+ message = "invalid value";
+ break;
+ case AL10.AL_INVALID_ENUM:
+ message = "invalid enum";
+ break;
+ case AL10.AL_INVALID_NAME:
+ message = "invalid name";
+ break;
+ default:
+ message = "" + error;
+ }
+ }
+ return message;
+ }
+
+ /* (non-Javadoc)
+ * @see jake2.sound.SoundImpl#Shutdown()
+ */
+ public void Shutdown() {
+ StopAllSounds();
+ Channel.shutdown();
+ AL10.alDeleteBuffers(buffers);
+ exitOpenAL();
+
+ Cmd.RemoveCommand("play");
+ Cmd.RemoveCommand("stopsound");
+ Cmd.RemoveCommand("soundlist");
+ Cmd.RemoveCommand("soundinfo");
+
+ // free all sounds
+ for (int i = 0; i < num_sfx; i++) {
+ if (known_sfx[i].name == null)
+ continue;
+ known_sfx[i].clear();
+ }
+ num_sfx = 0;
+ }
+
+ /* (non-Javadoc)
+ * @see jake2.sound.SoundImpl#StartSound(float[], int, int, jake2.sound.sfx_t, float, float, float)
+ */
+ public void StartSound(float[] origin, int entnum, int entchannel, sfx_t sfx, float fvol, float attenuation, float timeofs) {
+
+ if (sfx == null)
+ return;
+
+ if (sfx.name.charAt(0) == '*')
+ sfx = RegisterSexedSound(Globals.cl_entities[entnum].current, sfx.name);
+
+ if (LoadSound(sfx) == null)
+ return; // can't load sound
+
+ if (attenuation != Defines.ATTN_STATIC)
+ attenuation *= 0.5f;
+
+ PlaySound.allocate(origin, entnum, entchannel, buffers.get(sfx.bufferId), fvol, attenuation, timeofs);
+ }
+
+ /* (non-Javadoc)
+ * @see jake2.sound.SoundImpl#Update(float[], float[], float[], float[])
+ */
+ public void Update(float[] origin, float[] forward, float[] right, float[] up) {
+
+ Channel.convertVector(origin, listenerOrigin);
+ AL10.alListener(AL10.AL_POSITION, listenerOrigin);
+
+ Channel.convertOrientation(forward, up, listenerOrientation);
+ AL10.alListener(AL10.AL_ORIENTATION, listenerOrientation);
+
+ // set the master volume
+ AL10.alListenerf(AL10.AL_GAIN, s_volume.value);
+
+ // Detect EFX Conditions
+ int currentFilterIndex;
+ if ((GameBase.gi.pointcontents.pointcontents(origin) & Defines.MASK_WATER) != 0) {
+ currentFilterIndex = underwaterFilter.getIndex();
+ } else {
+ currentEffectIndex = EFX10.AL_EFFECTSLOT_NULL;
+ currentFilterIndex = EFX10.AL_FILTER_NULL;
+ }
+
+ Channel.addLoopSounds();
+ Channel.addPlaySounds();
+ Channel.playAllSounds(listenerOrigin, currentEffectIndex, currentFilterIndex);
+ }
+
+ /* (non-Javadoc)
+ * @see jake2.sound.SoundImpl#StopAllSounds()
+ */
+ public void StopAllSounds() {
+ // mute the listener (master)
+ AL10.alListenerf(AL10.AL_GAIN, 0);
+ PlaySound.reset();
+ Channel.reset();
+ }
+
+ /* (non-Javadoc)
+ * @see jake2.sound.Sound#getName()
+ */
+ public String getName() {
+ return "lwjgl";
+ }
+
+ /* (non-Javadoc)
+ * @see jake2.sound.Sound#BeginRegistration()
+ */
+ public void BeginRegistration() {
+ s_registration_sequence++;
+ s_registering = true;
+ }
+
+ /* (non-Javadoc)
+ * @see jake2.sound.Sound#RegisterSound(java.lang.String)
+ */
+ public sfx_t RegisterSound(String name) {
+ sfx_t sfx = FindName(name, true);
+ sfx.registration_sequence = s_registration_sequence;
+
+ if (!s_registering)
+ LoadSound(sfx);
+
+ return sfx;
+ }
+
+ /* (non-Javadoc)
+ * @see jake2.sound.Sound#EndRegistration()
+ */
+ public void EndRegistration() {
+ int i;
+ sfx_t sfx;
+
+ // free any sounds not from this registration sequence
+ for (i = 0; i < num_sfx; i++) {
+ sfx = known_sfx[i];
+ if (sfx.name == null)
+ continue;
+ if (sfx.registration_sequence != s_registration_sequence) {
+ // don't need this sound
+ sfx.clear();
+ }
+ }
+
+ // load everything in
+ for (i = 0; i < num_sfx; i++) {
+ sfx = known_sfx[i];
+ if (sfx.name == null)
+ continue;
+ LoadSound(sfx);
+ }
+
+ s_registering = false;
+ }
+
+ sfx_t RegisterSexedSound(entity_state_t ent, String base) {
+
+ sfx_t sfx = null;
+
+ // determine what model the client is using
+ String model = null;
+ int n = Globals.CS_PLAYERSKINS + ent.number - 1;
+ if (Globals.clientStateT.configstrings[n] != null) {
+ int p = Globals.clientStateT.configstrings[n].indexOf('\\');
+ if (p >= 0) {
+ p++;
+ model = Globals.clientStateT.configstrings[n].substring(p);
+ //strcpy(model, p);
+ p = model.indexOf('/');
+ if (p > 0)
+ model = model.substring(0, p);
+ }
+ }
+ // if we can't figure it out, they're male
+ if (model == null || model.length() == 0)
+ model = "male";
+
+ // see if we already know of the model specific sound
+ String sexedFilename = "#players/" + model + "/" + base.substring(1);
+ //Com_sprintf (sexedFilename, sizeof(sexedFilename), "#players/%s/%s", model, base+1);
+ sfx = FindName(sexedFilename, false);
+
+ if (sfx != null) return sfx;
+
+ //
+ // fall back strategies
+ //
+ // not found , so see if it exists
+ if (FS.FileLength(sexedFilename.substring(1)) > 0) {
+ // yes, register it
+ return RegisterSound(sexedFilename);
+ }
+ // try it with the female sound in the pak0.pak
+ if (model.equalsIgnoreCase("female")) {
+ String femaleFilename = "player/female/" + base.substring(1);
+ if (FS.FileLength("sound/" + femaleFilename) > 0)
+ return AliasName(sexedFilename, femaleFilename);
+ }
+ // no chance, revert to the male sound in the pak0.pak
+ String maleFilename = "player/male/" + base.substring(1);
+ return AliasName(sexedFilename, maleFilename);
+ }
+
+ sfx_t FindName(String name, boolean create) {
+ int i;
+ sfx_t sfx = null;
+
+ if (name == null)
+ Com.Error(Defines.ERR_FATAL, "S_FindName: NULL\n");
+ if (name.length() == 0)
+ Com.Error(Defines.ERR_FATAL, "S_FindName: empty name\n");
+
+ if (name.length() >= Defines.MAX_QPATH)
+ Com.Error(Defines.ERR_FATAL, "Sound name too long: " + name);
+
+ // see if already loaded
+ for (i = 0; i < num_sfx; i++)
+ if (name.equals(known_sfx[i].name)) {
+ return known_sfx[i];
+ }
+
+ if (!create)
+ return null;
+
+ // find a free sfx
+ for (i = 0; i < num_sfx; i++)
+ if (known_sfx[i].name == null)
+ // registration_sequence < s_registration_sequence)
+ break;
+
+ if (i == num_sfx) {
+ if (num_sfx == MAX_SFX)
+ Com.Error(Defines.ERR_FATAL, "S_FindName: out of sfx_t");
+ num_sfx++;
+ }
+
+ sfx = known_sfx[i];
+ sfx.clear();
+ sfx.name = name;
+ sfx.registration_sequence = s_registration_sequence;
+ sfx.bufferId = i;
+
+ return sfx;
+ }
+
+ /*
+ ==================
+ S_AliasName
+
+ ==================
+ */
+ sfx_t AliasName(String aliasname, String truename) {
+ sfx_t sfx = null;
+ String s;
+ int i;
+
+ s = truename;
+
+ // find a free sfx
+ for (i = 0; i < num_sfx; i++)
+ if (known_sfx[i].name == null)
+ break;
+
+ if (i == num_sfx) {
+ if (num_sfx == MAX_SFX)
+ Com.Error(Defines.ERR_FATAL, "S_FindName: out of sfx_t");
+ num_sfx++;
+ }
+
+ sfx = known_sfx[i];
+ sfx.clear();
+ sfx.name = aliasname;
+ sfx.registration_sequence = s_registration_sequence;
+ sfx.truename = s;
+ // set the AL bufferId
+ sfx.bufferId = i;
+
+ return sfx;
+ }
+
+ /*
+ ==============
+ S_LoadSound
+ ==============
+ */
+ public sfxcache_t LoadSound(sfx_t s) {
+ if (s.isCached) return s.cache;
+ sfxcache_t sc = WaveLoader.LoadSound(s);
+ if (sc != null) {
+ initBuffer(sc.data, s.bufferId, sc.speed);
+ s.isCached = true;
+ // free samples for GC
+ s.cache.data = null;
+ }
+ return sc;
+ }
+
+ /* (non-Javadoc)
+ * @see jake2.sound.Sound#StartLocalSound(java.lang.String)
+ */
+ public void StartLocalSound(String sound) {
+ sfx_t sfx;
+
+ sfx = RegisterSound(sound);
+ if (sfx == null) {
+ Com.Printf("S_StartLocalSound: can't cache " + sound + "\n");
+ return;
+ }
+ StartSound(null, Globals.clientStateT.playernum + 1, 0, sfx, 1, 1, 0);
+ }
+
+ /* (non-Javadoc)
+ * @see jake2.sound.Sound#RawSamples(int, int, int, int, byte[])
+ */
+ public void RawSamples(int samples, int rate, int width, int channels, ByteBuffer data) {
+ int format;
+ if (channels == 2) {
+ format = (width == 2) ? AL10.AL_FORMAT_STEREO16
+ : AL10.AL_FORMAT_STEREO8;
+ } else {
+ format = (width == 2) ? AL10.AL_FORMAT_MONO16
+ : AL10.AL_FORMAT_MONO8;
+ }
+
+ // convert to signed 16 bit samples
+ if (format == AL10.AL_FORMAT_MONO8) {
+ int value;
+ for (int i = 0; i < samples; i++) {
+ value = (data.get(i) & 0xFF) - 128;
+ streamBuffer.put(i, (short) value);
+ }
+ format = AL10.AL_FORMAT_MONO16;
+ width = 2;
+ data = sfxDataBuffer.slice();
+ }
+
+ Channel.updateStream(data, samples * channels * width, format, rate);
+ }
+
+ public void disableStreaming() {
+ Channel.disableStreaming();
+ }
+ /*
+ ===============================================================================
+
+ console functions
+
+ ===============================================================================
+ */
+
+ void Play() {
+ int i;
+ String name;
+ sfx_t sfx;
+
+ i = 1;
+ while (i < Cmd.Argc()) {
+ name = Cmd.Argv(i);
+ if (name.indexOf('.') == -1)
+ name += ".wav";
+
+ sfx = RegisterSound(name);
+ StartSound(null, Globals.clientStateT.playernum + 1, 0, sfx, 1.0f, 1.0f, 0.0f);
+ i++;
+ }
+ }
+
+ void SoundList() {
+ int i;
+ sfx_t sfx;
+ sfxcache_t sc;
+ int size, total;
+
+ total = 0;
+ for (i = 0; i < num_sfx; i++) {
+ sfx = known_sfx[i];
+ if (sfx.registration_sequence == 0)
+ continue;
+ sc = sfx.cache;
+ if (sc != null) {
+ size = sc.length * sc.width * (sc.stereo + 1);
+ total += size;
+ if (sc.loopstart >= 0)
+ Com.Printf("L");
+ else
+ Com.Printf(" ");
+ Com.Printf("(%2db) %6i : %s\n", new Vargs(3).add(sc.width * 8).add(size).add(sfx.name));
+ } else {
+ if (sfx.name.charAt(0) == '*')
+ Com.Printf(" placeholder : " + sfx.name + "\n");
+ else
+ Com.Printf(" not loaded : " + sfx.name + "\n");
+ }
+ }
+ Com.Printf("Total resident: " + total + "\n");
+ }
+
+ void SoundInfo_f() {
+
+ Com.Printf("%5d stereo\n", new Vargs(1).add(1));
+ Com.Printf("%5d samples\n", new Vargs(1).add(22050));
+ Com.Printf("%5d samplebits\n", new Vargs(1).add(16));
+ Com.Printf("%5d speed\n", new Vargs(1).add(44100));
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.sound.lwjgl;
+
+import lwjake2.Globals;
+import lwjake2.util.Math3D;
+
+/**
+ * PlaySound
+ *
+ * @author cwei
+ */
+public class PlaySound {
+
+ final static int MAX_PLAYSOUNDS = 128;
+
+ // list with sentinel
+ private static final PlaySound freeList;
+ private static final PlaySound playableList;
+
+ private static final PlaySound[] backbuffer = new PlaySound[MAX_PLAYSOUNDS];
+
+ static {
+ for (int i = 0; i < backbuffer.length; i++) {
+ backbuffer[i] = new PlaySound();
+ }
+ // init the sentinels
+ freeList = new PlaySound();
+ playableList = new PlaySound();
+ // reset the lists
+ reset();
+ }
+
+ final float[] origin = {0, 0, 0};
+ // sound attributes
+ int type;
+ int entnum;
+ int entchannel;
+ int bufferId;
+ float volume;
+ float attenuation;
+ // begin time in ms
+ private long beginTime;
+
+ // for linked list
+ private PlaySound prev, next;
+
+ private PlaySound() {
+ prev = next = null;
+ this.clear();
+ }
+
+ static void reset() {
+ // init the sentinels
+ freeList.next = freeList.prev = freeList;
+ playableList.next = playableList.prev = playableList;
+
+ // concat the the freeList
+ PlaySound ps;
+ for (PlaySound aBackbuffer : backbuffer) {
+ ps = aBackbuffer;
+ ps.clear();
+ ps.prev = freeList;
+ ps.next = freeList.next;
+ ps.prev.next = ps;
+ ps.next.prev = ps;
+ }
+ }
+
+ static PlaySound nextPlayableSound() {
+ PlaySound ps = null;
+ ps = playableList.next;
+ if (ps == playableList || ps.beginTime > Globals.clientStateT.time)
+ return null;
+ PlaySound.release(ps);
+ return ps;
+ }
+
+ private static PlaySound get() {
+ PlaySound ps = freeList.next;
+ if (ps == freeList)
+ return null;
+
+ ps.prev.next = ps.next;
+ ps.next.prev = ps.prev;
+ return ps;
+ }
+
+ private static void add(PlaySound ps) {
+
+ PlaySound sort = playableList.next;
+
+ for (; sort != playableList && sort.beginTime < ps.beginTime; sort = sort.next) ;
+ ps.next = sort;
+ ps.prev = sort.prev;
+ ps.next.prev = ps;
+ ps.prev.next = ps;
+ }
+
+ private static void release(PlaySound ps) {
+ ps.prev.next = ps.next;
+ ps.next.prev = ps.prev;
+ // add to free list
+ ps.next = freeList.next;
+ freeList.next.prev = ps;
+ ps.prev = freeList;
+ freeList.next = ps;
+ }
+
+ static void allocate(float[] origin, int entnum, int entchannel,
+ int bufferId, float volume, float attenuation, float timeoffset) {
+
+ PlaySound ps = PlaySound.get();
+
+ if (ps != null) {
+ // find the right sound type
+ if (entnum == Globals.clientStateT.playernum + 1) {
+ ps.type = Channel.LISTENER;
+ } else if (origin != null) {
+ ps.type = Channel.FIXED;
+ Math3D.vectorCopy(origin, ps.origin);
+ } else {
+ ps.type = Channel.DYNAMIC;
+ }
+ ps.entnum = entnum;
+ ps.entchannel = entchannel;
+ ps.bufferId = bufferId;
+ ps.volume = volume;
+ ps.attenuation = attenuation;
+ ps.beginTime = Globals.clientStateT.time + (long) (timeoffset * 1000);
+ PlaySound.add(ps);
+ } else {
+ System.err.println("PlaySounds out of Limit");
+ }
+ }
+
+ private void clear() {
+ type = bufferId = entnum = entchannel = -1;
+ // volume = attenuation = beginTime = 0;
+ attenuation = beginTime = 0;
+ // Math3D.VectorClear(origin);
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.sound;
+
+public class sfx_t {
+ public String name;
+ public int registration_sequence;
+ public sfxcache_t cache;
+ public String truename;
+
+ // is used for AL buffers
+ public int bufferId = -1;
+ public boolean isCached = false;
+
+ public void clear() {
+ name = truename = null;
+ cache = null;
+ registration_sequence = 0;
+ bufferId = -1;
+ isCached = false;
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.sound;
+
+public class sfxcache_t {
+ public int length;
+ public int loopstart;
+ public int speed; // not needed, because converted on load?
+ public int width;
+ public int stereo;
+ public byte data[]; // variable sized
+
+ public sfxcache_t(int size) {
+ data = new byte[size];
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.sound;
+
+/**
+ * wavinfo_t
+ */
+public class wavinfo_t {
+}
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.sys;
+
+import java.awt.event.*;
+import java.util.LinkedList;
+
+/**
+ * InputListener
+ */
+public final class InputListener implements KeyListener, MouseListener,
+ MouseMotionListener, ComponentListener, MouseWheelListener {
+
+ // modifications of eventQueue must be thread safe!
+ private static final LinkedList<LWJake2InputEvent> eventQueue = new LinkedList<>();
+
+ private static void addEvent(LWJake2InputEvent ev) {
+ synchronized (eventQueue) {
+ eventQueue.addLast(ev);
+ }
+ }
+
+ static LWJake2InputEvent nextEvent() {
+ LWJake2InputEvent ev;
+ synchronized (eventQueue) {
+ ev = (!eventQueue.isEmpty()) ? eventQueue.removeFirst() : null;
+ }
+ return ev;
+ }
+
+ public void keyPressed(KeyEvent e) {
+ if (!((e.getModifiersEx() & InputEvent.ALT_GRAPH_DOWN_MASK) != 0)) {
+ addEvent(new LWJake2InputEvent(LWJake2InputEvent.KeyPress, e));
+ }
+ }
+
+ public void keyReleased(KeyEvent e) {
+ addEvent(new LWJake2InputEvent(LWJake2InputEvent.KeyRelease, e));
+ }
+
+ public void keyTyped(KeyEvent e) {
+ if ((e.getModifiersEx() & InputEvent.ALT_GRAPH_DOWN_MASK) != 0) {
+ addEvent(new LWJake2InputEvent(LWJake2InputEvent.KeyPress, e));
+ addEvent(new LWJake2InputEvent(LWJake2InputEvent.KeyRelease, e));
+ }
+ }
+
+ public void mouseClicked(MouseEvent e) {
+ }
+
+ public void mouseEntered(MouseEvent e) {
+ }
+
+ public void mouseExited(MouseEvent e) {
+ }
+
+ public void mousePressed(MouseEvent e) {
+ addEvent(new LWJake2InputEvent(LWJake2InputEvent.ButtonPress, e));
+ }
+
+ public void mouseReleased(MouseEvent e) {
+ addEvent(new LWJake2InputEvent(LWJake2InputEvent.ButtonRelease, e));
+ }
+
+ public void mouseDragged(MouseEvent e) {
+ addEvent(new LWJake2InputEvent(LWJake2InputEvent.MotionNotify, e));
+ }
+
+ public void mouseMoved(MouseEvent e) {
+ addEvent(new LWJake2InputEvent(LWJake2InputEvent.MotionNotify, e));
+ }
+
+ public void componentHidden(ComponentEvent e) {
+ }
+
+ public void componentMoved(ComponentEvent e) {
+ addEvent(new LWJake2InputEvent(LWJake2InputEvent.ConfigureNotify, e));
+ }
+
+ public void componentResized(ComponentEvent e) {
+ addEvent(new LWJake2InputEvent(LWJake2InputEvent.ConfigureNotify, e));
+ }
+
+ public void componentShown(ComponentEvent e) {
+ JOGLKBD.c = e.getComponent();
+ addEvent(new LWJake2InputEvent(LWJake2InputEvent.CreateNotify, e));
+ }
+
+ public void mouseWheelMoved(MouseWheelEvent e) {
+ addEvent(new LWJake2InputEvent(LWJake2InputEvent.WheelMoved, e));
+ }
+
+}
+
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.sys;
+
+import lwjake2.client.Key;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.ComponentEvent;
+import java.awt.event.KeyEvent;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseWheelEvent;
+
+final public class JOGLKBD extends KBD {
+ static Component c = null;
+ private static Robot robot;
+ private static Cursor emptyCursor = null;
+ private static int win_w2 = 0;
+ private static int win_h2 = 0;
+
+ static {
+ try {
+ robot = new Robot();
+ } catch (AWTException e) {
+ System.exit(1);
+ }
+ }
+
+ private static int XLateKey(KeyEvent ev) {
+
+ int key = 0;
+ int code = ev.getKeyCode();
+
+ switch (code) {
+// 00626 case XK_KP_Page_Up: key = K_KP_PGUP; break;
+ case KeyEvent.VK_PAGE_UP:
+ key = Key.K_PGUP;
+ break;
+
+// 00629 case XK_KP_Page_Down: key = K_KP_PGDN; break;
+ case KeyEvent.VK_PAGE_DOWN:
+ key = Key.K_PGDN;
+ break;
+
+// 00632 case XK_KP_Home: key = K_KP_HOME; break;
+ case KeyEvent.VK_HOME:
+ key = Key.K_HOME;
+ break;
+
+// 00635 case XK_KP_End: key = K_KP_END; break;
+ case KeyEvent.VK_END:
+ key = Key.K_END;
+ break;
+
+ case KeyEvent.VK_KP_LEFT:
+ key = Key.K_KP_LEFTARROW;
+ break;
+ case KeyEvent.VK_LEFT:
+ key = Key.K_LEFTARROW;
+ break;
+
+ case KeyEvent.VK_KP_RIGHT:
+ key = Key.K_KP_RIGHTARROW;
+ break;
+ case KeyEvent.VK_RIGHT:
+ key = Key.K_RIGHTARROW;
+ break;
+
+ case KeyEvent.VK_KP_DOWN:
+ key = Key.K_KP_DOWNARROW;
+ break;
+ case KeyEvent.VK_DOWN:
+ key = Key.K_DOWNARROW;
+ break;
+
+ case KeyEvent.VK_KP_UP:
+ key = Key.K_KP_UPARROW;
+ break;
+ case KeyEvent.VK_UP:
+ key = Key.K_UPARROW;
+ break;
+
+ case KeyEvent.VK_ESCAPE:
+ key = Key.K_ESCAPE;
+ break;
+
+
+ case KeyEvent.VK_ENTER:
+ key = Key.K_ENTER;
+ break;
+// 00652 case XK_KP_Enter: key = K_KP_ENTER; break;
+
+ case KeyEvent.VK_TAB:
+ key = Key.K_TAB;
+ break;
+
+ case KeyEvent.VK_F1:
+ key = Key.K_F1;
+ break;
+ case KeyEvent.VK_F2:
+ key = Key.K_F2;
+ break;
+ case KeyEvent.VK_F3:
+ key = Key.K_F3;
+ break;
+ case KeyEvent.VK_F4:
+ key = Key.K_F4;
+ break;
+ case KeyEvent.VK_F5:
+ key = Key.K_F5;
+ break;
+ case KeyEvent.VK_F6:
+ key = Key.K_F6;
+ break;
+ case KeyEvent.VK_F7:
+ key = Key.K_F7;
+ break;
+ case KeyEvent.VK_F8:
+ key = Key.K_F8;
+ break;
+ case KeyEvent.VK_F9:
+ key = Key.K_F9;
+ break;
+ case KeyEvent.VK_F10:
+ key = Key.K_F10;
+ break;
+ case KeyEvent.VK_F11:
+ key = Key.K_F11;
+ break;
+ case KeyEvent.VK_F12:
+ key = Key.K_F12;
+ break;
+
+ case KeyEvent.VK_BACK_SPACE:
+ key = Key.K_BACKSPACE;
+ break;
+
+ case KeyEvent.VK_DELETE:
+ key = Key.K_DEL;
+ break;
+// 00683 case XK_KP_Delete: key = K_KP_DEL; break;
+
+ case KeyEvent.VK_PAUSE:
+ key = Key.K_PAUSE;
+ break;
+
+ case KeyEvent.VK_SHIFT:
+ key = Key.K_SHIFT;
+ break;
+ case KeyEvent.VK_CONTROL:
+ key = Key.K_CTRL;
+ break;
+
+ case KeyEvent.VK_ALT:
+ case KeyEvent.VK_ALT_GRAPH:
+ key = Key.K_ALT;
+ break;
+
+// 00700 case XK_KP_Begin: key = K_KP_5; break;
+// 00701
+ case KeyEvent.VK_INSERT:
+ key = Key.K_INS;
+ break;
+ // toggle console for DE and US keyboards
+ case KeyEvent.VK_DEAD_ACUTE:
+ case KeyEvent.VK_CIRCUMFLEX:
+ case KeyEvent.VK_DEAD_CIRCUMFLEX:
+ key = '`';
+ break;
+
+ default:
+ key = ev.getKeyChar();
+
+ if (key >= 'A' && key <= 'Z')
+ key = key - 'A' + 'a';
+ break;
+ }
+ if (key > 255) key = 0;
+
+ return key;
+ }
+
+ public void Init() {
+ }
+
+ public void Update() {
+ // get events
+ HandleEvents();
+ }
+
+ public void Close() {
+ }
+
+ private void HandleEvents() {
+ int key;
+
+ LWJake2InputEvent event;
+ while ((event = InputListener.nextEvent()) != null) {
+ switch (event.type) {
+ case LWJake2InputEvent.KeyPress:
+ case LWJake2InputEvent.KeyRelease:
+ Do_Key_Event(XLateKey((KeyEvent) event.ev), event.type == LWJake2InputEvent.KeyPress);
+ break;
+
+ case LWJake2InputEvent.MotionNotify:
+// if (IN.ignorefirst) {
+// IN.ignorefirst = false;
+// break;
+// }
+ if (UserInputHandler.mouse_active) {
+ mx = (((MouseEvent) event.ev).getX() - win_w2) * 2;
+ my = (((MouseEvent) event.ev).getY() - win_h2) * 2;
+ } else {
+ mx = 0;
+ my = 0;
+ }
+ break;
+ // see java.awt.MouseEvent
+ case LWJake2InputEvent.ButtonPress:
+ key = mouseEventToKey((MouseEvent) event.ev);
+ Do_Key_Event(key, true);
+ break;
+
+ case LWJake2InputEvent.ButtonRelease:
+ key = mouseEventToKey((MouseEvent) event.ev);
+ Do_Key_Event(key, false);
+ break;
+
+ case LWJake2InputEvent.WheelMoved:
+ int dir = ((MouseWheelEvent) event.ev).getWheelRotation();
+ if (dir > 0) {
+ Do_Key_Event(Key.K_MWHEELDOWN, true);
+ Do_Key_Event(Key.K_MWHEELDOWN, false);
+ } else {
+ Do_Key_Event(Key.K_MWHEELUP, true);
+ Do_Key_Event(Key.K_MWHEELUP, false);
+ }
+ break;
+
+ case LWJake2InputEvent.CreateNotify:
+ case LWJake2InputEvent.ConfigureNotify:
+ Component c = ((ComponentEvent) event.ev).getComponent();
+ win_x = 0;
+ win_y = 0;
+ win_w2 = c.getWidth() / 2;
+ win_h2 = c.getHeight() / 2;
+ while (c != null) {
+ if (c instanceof Container) {
+ Insets insets = ((Container) c).getInsets();
+ win_x += insets.left;
+ win_y += insets.top;
+ }
+ win_x += c.getX();
+ win_y += c.getY();
+ c = c.getParent();
+ }
+ break;
+ }
+ }
+
+ if (mx != 0 || my != 0) {
+ // move the mouse to the window center again
+ robot.mouseMove(win_x + win_w2, win_y + win_h2);
+ }
+ }
+
+ // strange button numbering in java.awt.MouseEvent
+ // BUTTON1(left) BUTTON2(center) BUTTON3(right)
+ // K_MOUSE1 K_MOUSE3 K_MOUSE2
+ private int mouseEventToKey(MouseEvent ev) {
+ switch (ev.getButton()) {
+ case MouseEvent.BUTTON3:
+ return Key.K_MOUSE2;
+ case MouseEvent.BUTTON2:
+ return Key.K_MOUSE3;
+ default:
+ return Key.K_MOUSE1;
+ }
+ }
+
+ public void Do_Key_Event(int key, boolean down) {
+ Key.Event(key, down, Timer.getCurrentTimeMillis());
+ }
+
+ private void centerMouse() {
+ robot.mouseMove(win_x + win_w2, win_y + win_h2);
+ }
+
+ public void installGrabs() {
+ if (emptyCursor == null) {
+ ImageIcon emptyIcon = new ImageIcon(new byte[0]);
+ emptyCursor = c.getToolkit().createCustomCursor(emptyIcon.getImage(), new Point(0, 0), "emptyCursor");
+ }
+ c.setCursor(emptyCursor);
+ centerMouse();
+ }
+
+ public void uninstallGrabs() {
+ c.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.sys;
+
+/**
+ * KBD
+ */
+abstract public class KBD {
+
+ // motion values
+ public static int mx = 0;
+ public static int my = 0;
+ static int win_x = 0;
+ static int win_y = 0;
+
+ abstract public void Init();
+
+ abstract public void Update();
+
+ abstract public void Close();
+
+ abstract public void Do_Key_Event(int key, boolean down);
+
+ abstract public void installGrabs();
+
+ abstract public void uninstallGrabs();
+ //abstract public void centerMouse();
+}
+
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.sys;
+
+import lwjake2.Defines;
+import lwjake2.Globals;
+import lwjake2.client.Key;
+import lwjake2.qcommon.CommandBuffer;
+import org.lwjgl.input.Keyboard;
+import org.lwjgl.input.Mouse;
+import org.lwjgl.opengl.Display;
+
+/**
+ * @author dsanders
+ */
+public class LWJGLKBD extends KBD {
+
+ private static int lastRepeat;
+ private char[] lwjglKeycodeMap = null;
+ private int pressed[] = null;
+
+ public void Init() {
+ try {
+ if (!Keyboard.isCreated()) Keyboard.create();
+ if (!Mouse.isCreated()) Mouse.create();
+
+ // Old code from old LWJGL, not sure if it's needed - flibit
+
+ // if (!Keyboard.isBuffered()) Keyboard.enableBuffer();
+ // if (!Keyboard.isTranslationEnabled()) Keyboard.enableTranslation();
+ // if (!Mouse.isBuffered()) Mouse.enableBuffer();
+
+ if (lwjglKeycodeMap == null) lwjglKeycodeMap = new char[256];
+ if (pressed == null) pressed = new int[256];
+
+ lastRepeat = Timer.getCurrentTimeMillis();
+ } catch (Exception ignored) {
+ }
+ }
+
+ public void Update() {
+ // get events
+ HandleEvents();
+ }
+
+ public void Close() {
+ Keyboard.destroy();
+ Mouse.destroy();
+ // free the memory for GC
+ lwjglKeycodeMap = null;
+ pressed = null;
+ }
+
+ private void HandleEvents() {
+ Keyboard.poll();
+
+ if (Display.isCloseRequested()) {
+ CommandBuffer.ExecuteText(Defines.EXEC_APPEND, "quit");
+ }
+
+ while (Keyboard.next()) {
+ int key = Keyboard.getEventKey();
+ char ch = Keyboard.getEventCharacter();
+ boolean down = Keyboard.getEventKeyState();
+
+ // fill the character translation table
+ // this is needed because the getEventCharacter() returns \0 if a key is released
+ // keycode is correct but the charachter value is not
+ if (down) {
+ lwjglKeycodeMap[key] = ch;
+ pressed[key] = Globals.sys_frame_time;
+ } else {
+ pressed[key] = 0;
+ }
+
+ Do_Key_Event(XLateKey(key, ch), down);
+ }
+
+ generateRepeats();
+
+ if (UserInputHandler.mouse_active) {
+ mx = Mouse.getDX() << 1;
+ my = -Mouse.getDY() << 1;
+ } else {
+ mx = 0;
+ my = 0;
+ }
+
+ while (Mouse.next()) {
+ int button = Mouse.getEventButton();
+ if (button >= 0) {
+ Do_Key_Event(Key.K_MOUSE1 + button, Mouse.getEventButtonState());
+ } else {
+ button = Mouse.getEventDWheel();
+ if (button > 0) {
+ Do_Key_Event(Key.K_MWHEELUP, true);
+ Do_Key_Event(Key.K_MWHEELUP, false);
+ } else if (button < 0) {
+ Do_Key_Event(Key.K_MWHEELDOWN, true);
+ Do_Key_Event(Key.K_MWHEELDOWN, false);
+ }
+ }
+ }
+ }
+
+ private void generateRepeats() {
+ int time = Globals.sys_frame_time;
+ if (time - lastRepeat > 50) {
+ for (int i = 0; i < pressed.length; i++) {
+ if (pressed[i] > 0 && time - pressed[i] > 500)
+ Do_Key_Event(XLateKey(i, lwjglKeycodeMap[i]), true);
+ }
+ lastRepeat = time;
+ }
+ }
+
+ private int XLateKey(int code, int ch) {
+ int key = 0;
+
+ switch (code) {
+// 00626 case XK_KP_Page_Up: key = K_KP_PGUP; break;
+ case Keyboard.KEY_PRIOR:
+ key = Key.K_PGUP;
+ break;
+
+// 00629 case XK_KP_Page_Down: key = K_KP_PGDN; break;
+ case Keyboard.KEY_NEXT:
+ key = Key.K_PGDN;
+ break;
+
+// 00632 case XK_KP_Home: key = K_KP_HOME; break;
+ case Keyboard.KEY_HOME:
+ key = Key.K_HOME;
+ break;
+
+// 00635 case XK_KP_End: key = K_KP_END; break;
+ case Keyboard.KEY_END:
+ key = Key.K_END;
+ break;
+
+ // case Keyboard.KEY_LEFT: key = Key.K_KP_LEFTARROW; break;
+ case Keyboard.KEY_LEFT:
+ key = Key.K_LEFTARROW;
+ break;
+
+ // case Keyboard.KEY_RIGHT: key = Key.K_KP_RIGHTARROW; break;
+ case Keyboard.KEY_RIGHT:
+ key = Key.K_RIGHTARROW;
+ break;
+
+ // case Keyboard.KEY_DOWN: key = Key.K_KP_DOWNARROW; break;
+ case Keyboard.KEY_DOWN:
+ key = Key.K_DOWNARROW;
+ break;
+
+ // case Keyboard.KEY_UP: key = Key.K_KP_UPARROW; break;
+ case Keyboard.KEY_UP:
+ key = Key.K_UPARROW;
+ break;
+
+ case Keyboard.KEY_ESCAPE:
+ key = Key.K_ESCAPE;
+ break;
+
+
+ case Keyboard.KEY_RETURN:
+ key = Key.K_ENTER;
+ break;
+// 00652 case XK_KP_Enter: key = K_KP_ENTER; break;
+
+ case Keyboard.KEY_TAB:
+ key = Key.K_TAB;
+ break;
+
+ case Keyboard.KEY_F1:
+ key = Key.K_F1;
+ break;
+ case Keyboard.KEY_F2:
+ key = Key.K_F2;
+ break;
+ case Keyboard.KEY_F3:
+ key = Key.K_F3;
+ break;
+ case Keyboard.KEY_F4:
+ key = Key.K_F4;
+ break;
+ case Keyboard.KEY_F5:
+ key = Key.K_F5;
+ break;
+ case Keyboard.KEY_F6:
+ key = Key.K_F6;
+ break;
+ case Keyboard.KEY_F7:
+ key = Key.K_F7;
+ break;
+ case Keyboard.KEY_F8:
+ key = Key.K_F8;
+ break;
+ case Keyboard.KEY_F9:
+ key = Key.K_F9;
+ break;
+ case Keyboard.KEY_F10:
+ key = Key.K_F10;
+ break;
+ case Keyboard.KEY_F11:
+ key = Key.K_F11;
+ break;
+ case Keyboard.KEY_F12:
+ key = Key.K_F12;
+ break;
+
+ case Keyboard.KEY_BACK:
+ key = Key.K_BACKSPACE;
+ break;
+
+ case Keyboard.KEY_DELETE:
+ key = Key.K_DEL;
+ break;
+// 00683 case XK_KP_Delete: key = K_KP_DEL; break;
+
+ case Keyboard.KEY_PAUSE:
+ key = Key.K_PAUSE;
+ break;
+
+ case Keyboard.KEY_RSHIFT:
+ case Keyboard.KEY_LSHIFT:
+ key = Key.K_SHIFT;
+ break;
+
+ case Keyboard.KEY_RCONTROL:
+ case Keyboard.KEY_LCONTROL:
+ key = Key.K_CTRL;
+ break;
+
+ case Keyboard.KEY_LMENU:
+ case Keyboard.KEY_RMENU:
+ key = Key.K_ALT;
+ break;
+
+// 00700 case XK_KP_Begin: key = K_KP_5; break;
+// 00701
+ case Keyboard.KEY_INSERT:
+ key = Key.K_INS;
+ break;
+ // toggle console for DE and US keyboards
+ case Keyboard.KEY_GRAVE:
+ case Keyboard.KEY_CIRCUMFLEX:
+ key = '`';
+ break;
+
+ default:
+ key = lwjglKeycodeMap[code];
+ if (key >= 'A' && key <= 'Z')
+ key = key - 'A' + 'a';
+ break;
+ }
+ if (key > 255) key = 0;
+ return key;
+ }
+
+ public void Do_Key_Event(int key, boolean down) {
+ Key.Event(key, down, Timer.getCurrentTimeMillis());
+ }
+
+ public void installGrabs() {
+ Mouse.setGrabbed(true);
+ }
+
+ public void uninstallGrabs() {
+ Mouse.setGrabbed(false);
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.sys;
+
+import java.awt.*;
+
+/**
+ * LWJake2InputEvent
+ */
+class LWJake2InputEvent {
+ static final int KeyPress = 0;
+ static final int KeyRelease = 1;
+ static final int MotionNotify = 2;
+ static final int ButtonPress = 3;
+ static final int ButtonRelease = 4;
+ static final int CreateNotify = 5;
+ static final int ConfigureNotify = 6;
+ static final int WheelMoved = 7;
+ final int type;
+ final AWTEvent ev;
+
+ LWJake2InputEvent(int type, AWTEvent ev) {
+ this.type = type;
+ this.ev = ev;
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.sys;
+
+import lwjake2.Defines;
+import lwjake2.Globals;
+import lwjake2.game.CvarT;
+import lwjake2.qcommon.Com;
+import lwjake2.qcommon.Cvar;
+import lwjake2.qcommon.NetadrT;
+import lwjake2.qcommon.sizebuf_t;
+import lwjake2.util.Lib;
+
+import java.io.IOException;
+import java.net.DatagramSocket;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.nio.ByteBuffer;
+import java.nio.channels.DatagramChannel;
+
+public final class NET {
+
+ private static final loopback_t[] loopbacks = new loopback_t[2];
+ private final static int MAX_LOOPBACK = 4;
+ /**
+ * Local loopback adress.
+ */
+ private static final NetadrT net_local_adr = new NetadrT();
+ private static final DatagramChannel[] ip_channels = {null, null};
+ private static final DatagramSocket[] ip_sockets = {null, null};
+
+ static {
+ loopbacks[0] = new loopback_t();
+ loopbacks[1] = new loopback_t();
+ }
+
+ /**
+ * Compares ip address and port.
+ */
+ public static boolean CompareAdr(NetadrT a, NetadrT b) {
+ return (a.ip[0] == b.ip[0] && a.ip[1] == b.ip[1] && a.ip[2] == b.ip[2]
+ && a.ip[3] == b.ip[3] && a.port == b.port);
+ }
+
+ /**
+ * Compares ip address without the port.
+ */
+ public static boolean CompareBaseAdr(NetadrT a, NetadrT b) {
+ if (a.type != b.type)
+ return false;
+
+ if (a.type == Defines.NA_LOOPBACK)
+ return true;
+
+ if (a.type == Defines.NA_IP) {
+ return (a.ip[0] == b.ip[0] && a.ip[1] == b.ip[1]
+ && a.ip[2] == b.ip[2] && a.ip[3] == b.ip[3]);
+ }
+ return false;
+ }
+
+ /**
+ * Returns a string holding ip address and port like "ip0.ip1.ip2.ip3:port".
+ */
+ public static String AdrToString(NetadrT a) {
+ return String.valueOf(a.ip[0] & 0xFF) + '.' + (a.ip[1] & 0xFF) +
+ '.' +
+ (a.ip[2] & 0xFF) + '.' + (a.ip[3] & 0xFF) +
+ ':' + a.port;
+ }
+
+ /**
+ * Returns IP address without the port as string.
+ */
+ public static String BaseAdrToString(NetadrT a) {
+ return String.valueOf(a.ip[0] & 0xFF) + '.' + (a.ip[1] & 0xFF) +
+ '.' +
+ (a.ip[2] & 0xFF) + '.' + (a.ip[3] & 0xFF);
+ }
+
+ /**
+ * Creates an NetadrT from an string.
+ */
+ public static boolean StringToAdr(String s, NetadrT a) {
+ if (s.equalsIgnoreCase("localhost") || s.equalsIgnoreCase("loopback")) {
+ a.set(net_local_adr);
+ return true;
+ }
+ try {
+ String[] address = s.split(":");
+ InetAddress ia = InetAddress.getByName(address[0]);
+ a.ip = ia.getAddress();
+ a.type = Defines.NA_IP;
+ if (address.length == 2)
+ a.port = Lib.atoi(address[1]);
+ return true;
+ } catch (Exception e) {
+ Com.Println(e.getMessage());
+ return false;
+ }
+ }
+
+ /**
+ * Seems to return true, if the address is is on 127.0.0.1.
+ */
+ public static boolean IsLocalAddress(NetadrT adr) {
+ return CompareAdr(adr, net_local_adr);
+ }
+
+ /**
+ * Gets a packet from internal loopback.
+ */
+ private static boolean GetLoopPacket(int sock, NetadrT net_from,
+ sizebuf_t net_message) {
+ loopback_t loop;
+ loop = loopbacks[sock];
+
+ if (loop.send - loop.get > MAX_LOOPBACK)
+ loop.get = loop.send - MAX_LOOPBACK;
+
+ if (loop.get >= loop.send)
+ return false;
+
+ int i = loop.get & (MAX_LOOPBACK - 1);
+ loop.get++;
+
+ System.arraycopy(loop.msgs[i].data, 0, net_message.data, 0,
+ loop.msgs[i].datalen);
+ net_message.cursize = loop.msgs[i].datalen;
+
+ net_from.set(net_local_adr);
+ return true;
+ }
+
+ /**
+ * Sends a packet via internal loopback.
+ */
+ private static void SendLoopPacket(int sock, int length, byte[] data,
+ NetadrT to) {
+ int i;
+ loopback_t loop;
+
+ loop = loopbacks[sock ^ 1];
+
+ // modulo 4
+ i = loop.send & (MAX_LOOPBACK - 1);
+ loop.send++;
+
+ System.arraycopy(data, 0, loop.msgs[i].data, 0, length);
+ loop.msgs[i].datalen = length;
+ }
+
+ /*
+ * ==================================================
+ *
+ * LOOPBACK BUFFERS FOR LOCAL PLAYER
+ *
+ * ==================================================
+ */
+
+ /**
+ * Gets a packet from a network channel
+ */
+ public static boolean GetPacket(int sock, NetadrT net_from,
+ sizebuf_t net_message) {
+
+ if (GetLoopPacket(sock, net_from, net_message)) {
+ return true;
+ }
+
+ if (ip_sockets[sock] == null)
+ return false;
+
+ try {
+ ByteBuffer receiveBuffer = ByteBuffer.wrap(net_message.data);
+
+ InetSocketAddress srcSocket = (InetSocketAddress) ip_channels[sock]
+ .receive(receiveBuffer);
+ if (srcSocket == null)
+ return false;
+
+ net_from.ip = srcSocket.getAddress().getAddress();
+ net_from.port = srcSocket.getPort();
+ net_from.type = Defines.NA_IP;
+
+ int packetLength = receiveBuffer.position();
+
+ if (packetLength > net_message.maxsize) {
+ Com.Println("Oversize packet from " + AdrToString(net_from));
+ return false;
+ }
+
+ // set the size
+ net_message.cursize = packetLength;
+ // set the sentinel
+ net_message.data[packetLength] = 0;
+ return true;
+
+ } catch (IOException e) {
+ Com.DPrintf("NET_GetPacket: " + e + " from "
+ + AdrToString(net_from) + "\n");
+ return false;
+ }
+ }
+
+ /**
+ * Sends a Packet.
+ */
+ public static void SendPacket(int sock, int length, byte[] data, NetadrT to) {
+ if (to.type == Defines.NA_LOOPBACK) {
+ SendLoopPacket(sock, length, data, to);
+ return;
+ }
+
+ if (ip_sockets[sock] == null)
+ return;
+
+ if (to.type != Defines.NA_BROADCAST && to.type != Defines.NA_IP) {
+ Com.Error(Defines.ERR_FATAL, "NET_SendPacket: bad address type");
+ return;
+ }
+
+ try {
+ SocketAddress dstSocket = new InetSocketAddress(to.getInetAddress(), to.port);
+ ip_channels[sock].send(ByteBuffer.wrap(data, 0, length), dstSocket);
+ } catch (Exception e) {
+ Com.Println("NET_SendPacket ERROR: " + e + " to " + AdrToString(to));
+ }
+ }
+
+ /**
+ * OpenIP, creates the network sockets.
+ */
+ private static void OpenIP() {
+ CvarT port, ip, clientport;
+
+ port = Cvar.get("port", "" + Defines.PORT_SERVER, Defines.CVAR_NOSET);
+ ip = Cvar.get("ip", "localhost", Defines.CVAR_NOSET);
+ clientport = Cvar.get("clientport", "" + Defines.PORT_CLIENT, Defines.CVAR_NOSET);
+
+ if (ip_sockets[Defines.NS_SERVER] == null)
+ ip_sockets[Defines.NS_SERVER] = Socket(Defines.NS_SERVER,
+ ip.string, (int) port.value);
+
+ if (ip_sockets[Defines.NS_CLIENT] == null)
+ ip_sockets[Defines.NS_CLIENT] = Socket(Defines.NS_CLIENT,
+ ip.string, (int) clientport.value);
+ if (ip_sockets[Defines.NS_CLIENT] == null)
+ ip_sockets[Defines.NS_CLIENT] = Socket(Defines.NS_CLIENT,
+ ip.string, Defines.PORT_ANY);
+ }
+
+ /**
+ * Config multi or singlepalyer - A single player game will only use the loopback code.
+ */
+ public static void Config(boolean multiplayer) {
+ if (!multiplayer) {
+ // shut down any existing sockets
+ for (int i = 0; i < 2; i++) {
+ if (ip_sockets[i] != null) {
+ ip_sockets[i].close();
+ ip_sockets[i] = null;
+ }
+ }
+ } else {
+ // open sockets
+ OpenIP();
+ }
+ }
+
+ /**
+ * Init
+ */
+ public static void Init() {
+ // nothing to do
+ }
+
+ /*
+ * Socket
+ */
+ private static DatagramSocket Socket(int sock, String ip, int port) {
+
+ DatagramSocket newsocket = null;
+ try {
+ if (ip_channels[sock] == null || !ip_channels[sock].isOpen())
+ ip_channels[sock] = DatagramChannel.open();
+
+ if (ip == null || ip.length() == 0 || ip.equals("localhost")) {
+ if (port == Defines.PORT_ANY) {
+ newsocket = ip_channels[sock].socket();
+ newsocket.bind(new InetSocketAddress(0));
+ } else {
+ newsocket = ip_channels[sock].socket();
+ newsocket.bind(new InetSocketAddress(port));
+ }
+ } else {
+ InetAddress ia = InetAddress.getByName(ip);
+ newsocket = ip_channels[sock].socket();
+ newsocket.bind(new InetSocketAddress(ia, port));
+ }
+
+ // nonblocking channel
+ ip_channels[sock].configureBlocking(false);
+ // the socket have to be broadcastable
+ newsocket.setBroadcast(true);
+ } catch (Exception e) {
+ Com.Println("Error: " + e.toString());
+ newsocket = null;
+ }
+ return newsocket;
+ }
+
+ /**
+ * Shutdown - closes the sockets
+ */
+ public static void Shutdown() {
+ // close sockets
+ Config(false);
+ }
+
+ /**
+ * Sleeps msec or until net socket is ready.
+ */
+ public static void Sleep(int msec) {
+ if (ip_sockets[Defines.NS_SERVER] == null
+ || (Globals.dedicated != null && Globals.dedicated.value == 0))
+ return; // we're not a server, just run full speed
+
+ try {
+ //TODO: check for timeout
+ Thread.sleep(msec);
+ } catch (InterruptedException ignored) {
+ }
+ //ip_sockets[NS_SERVER].
+
+ // this should wait up to 100ms until a packet
+ /*
+ * struct timeval timeout;
+ * fd_set fdset;
+ * extern CvarT *dedicated;
+ * extern qboolean stdin_active;
+ *
+ * if (!ip_sockets[NS_SERVER] || (dedicated && !dedicated.value))
+ * return; // we're not a server, just run full speed
+ *
+ * FD_ZERO(&fdset);
+ *
+ * if (stdin_active)
+ * FD_SET(0, &fdset); // stdin is processed too
+ *
+ * FD_SET(ip_sockets[NS_SERVER], &fdset); // network socket
+ *
+ * timeout.tv_sec = msec/1000;
+ * timeout.tv_usec = (msec%1000)*1000;
+ *
+ * select(ip_sockets[NS_SERVER]+1, &fdset, NULL, NULL, &timeout);
+ */
+ }
+
+ static class loopmsg_t {
+ final byte[] data = new byte[Defines.MAX_MSGLEN];
+
+ int datalen;
+ }
+
+ public static class loopback_t {
+ final loopmsg_t[] msgs;
+ int get, send;
+
+ loopback_t() {
+ msgs = new loopmsg_t[MAX_LOOPBACK];
+ for (int n = 0; n < MAX_LOOPBACK; n++) {
+ msgs[n] = new loopmsg_t();
+ }
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.sys;
+
+public class NanoTimer extends Timer {
+
+ private final long base;
+
+ NanoTimer() {
+ base = System.nanoTime();
+ }
+
+ public long currentTimeMillis() {
+ long time = System.nanoTime();
+ long delta = time - base;
+ if (delta < 0) {
+ delta += Long.MAX_VALUE + 1;
+ }
+ return (long) (delta * 0.000001);
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.sys;
+
+class StandardTimer extends Timer {
+
+ private final long base;
+
+ StandardTimer() {
+ base = System.currentTimeMillis();
+ }
+
+ public long currentTimeMillis() {
+ long time = System.currentTimeMillis();
+ long delta = time - base;
+ if (delta < 0) {
+ delta += Long.MAX_VALUE + 1;
+ }
+ return delta;
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.sys;
+
+import lwjake2.Defines;
+import lwjake2.Globals;
+import lwjake2.client.Client;
+import lwjake2.qcommon.Com;
+
+import java.io.File;
+import java.io.FilenameFilter;
+import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
+
+/**
+ * Sys
+ */
+public final class Sys extends Defines {
+
+ private static File[] fdir;
+ private static int fileindex;
+
+
+ //============================================
+
+ public static void Error(String error) {
+
+ Client.Shutdown();
+ //StackTrace();
+ new Exception(error).printStackTrace();
+ System.exit(1);
+ }
+
+ public static void Quit() {
+ Client.Shutdown();
+
+ System.exit(0);
+ }
+
+ //ok!
+ public static File[] FindAll(String path, int musthave, int canthave) {
+
+ int index = path.lastIndexOf('/');
+
+ String findpattern;
+ String findbase;
+ if (index != -1) {
+ findbase = path.substring(0, index);
+ findpattern = path.substring(index + 1, path.length());
+ } else {
+ findbase = path;
+ findpattern = "*";
+ }
+
+ if (findpattern.equals("*.*")) {
+ findpattern = "*";
+ }
+
+ File fdir = new File(findbase);
+
+ if (!fdir.exists())
+ return null;
+
+ FilenameFilter filter = new FileFilter(findpattern, musthave, canthave);
+
+ return fdir.listFiles(filter);
+ }
+
+ // ok.
+ public static File FindFirst(String path, int musthave, int canthave) {
+
+ if (fdir != null)
+ Sys.Error("Sys_BeginFind without close");
+
+ // COM_FilePath (path, findbase);
+
+ fdir = FindAll(path, canthave, musthave);
+ fileindex = 0;
+
+ if (fdir == null)
+ return null;
+
+ return FindNext();
+ }
+
+ public static File FindNext() {
+
+ if (fileindex >= fdir.length)
+ return null;
+
+ return fdir[fileindex++];
+ }
+
+ public static void FindClose() {
+ fdir = null;
+ }
+
+ public static void SendKeyEvents() {
+ Globals.re.getKeyboardHandler().Update();
+
+ // grab frame time
+ Globals.sys_frame_time = Timer.getCurrentTimeMillis();
+ }
+
+ public static String GetClipboardData() {
+ // TODO: implement GetClipboardData
+ return null;
+ }
+
+ public static void ConsoleOutput(String msg) {
+ if (Globals.nostdout != null && Globals.nostdout.value != 0)
+ return;
+
+ System.out.print(msg);
+ }
+
+ /**
+ * Match the pattern findpattern against the filename.
+ * <p>
+ * In the pattern string, `*' matches any sequence of characters, `?'
+ * matches any character, [SET] matches any character in the specified set,
+ * [!SET] matches any character not in the specified set. A set is composed
+ * of characters or ranges; a range looks like character hyphen character
+ * (as in 0-9 or A-Z). [0-9a-zA-Z_] is the set of characters allowed in C
+ * identifiers. Any other character in the pattern must be matched exactly.
+ * To suppress the special syntactic significance of any of `[]*?!-\', and
+ * match the character exactly, precede it with a `\'.
+ */
+ static class FileFilter implements FilenameFilter {
+
+ final String regexpr;
+
+ final int musthave;
+ final int canthave;
+
+ FileFilter(String findpattern, int musthave, int canthave) {
+ this.regexpr = convert2regexpr(findpattern);
+ this.musthave = musthave;
+ this.canthave = canthave;
+
+ }
+
+ /*
+ * @see java.io.FilenameFilter#accept(java.io.File, java.lang.String)
+ */
+ public boolean accept(File dir, String name) {
+ return name.matches(regexpr) && CompareAttributes(dir, musthave, canthave);
+ }
+
+ String convert2regexpr(String pattern) {
+
+ StringBuilder sb = new StringBuilder();
+
+ char c;
+ boolean escape = false;
+
+ String subst;
+
+ // convert pattern
+ for (int i = 0; i < pattern.length(); i++) {
+ c = pattern.charAt(i);
+ subst = null;
+ switch (c) {
+ case '*':
+ subst = (!escape) ? ".*" : "*";
+ break;
+ case '.':
+ subst = (!escape) ? "\\." : ".";
+ break;
+ case '!':
+ subst = (!escape) ? "^" : "!";
+ break;
+ case '?':
+ subst = (!escape) ? "." : "?";
+ break;
+ case '\\':
+ escape = !escape;
+ break;
+ default:
+ escape = false;
+ }
+ if (subst != null) {
+ sb.append(subst);
+ escape = false;
+ } else
+ sb.append(c);
+ }
+
+ // the converted pattern
+ String regexpr = sb.toString();
+
+ //Com.DPrintf("pattern: " + pattern + " regexpr: " + regexpr +
+ // '\n');
+ try {
+ Pattern.compile(regexpr);
+ } catch (PatternSyntaxException e) {
+ Com.Printf("invalid file pattern ( *.* is used instead )\n");
+ return ".*"; // the default
+ }
+ return regexpr;
+ }
+
+ boolean CompareAttributes(File dir, int musthave, int canthave) {
+ // . and .. never match
+ String name = dir.getName();
+
+ return !(name.equals(".") || name.equals(".."));
+
+ }
+
+ }
+
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.sys;
+
+import lwjake2.Globals;
+import lwjake2.qcommon.Com;
+
+public abstract class Timer {
+
+ private static Timer timer;
+
+ static {
+ try {
+ timer = new NanoTimer();
+ } catch (Throwable e) {
+ timer = new StandardTimer();
+ }
+ Com.Println("using " + timer.getClass().getName());
+ }
+
+ public static int getCurrentTimeMillis() {
+ return Globals.curtime = (int) (timer.currentTimeMillis());
+ }
+
+ protected abstract long currentTimeMillis();
+}
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.sys;
+
+import lwjake2.Globals;
+import lwjake2.client.CL_input;
+import lwjake2.client.Key;
+import lwjake2.game.Cmd;
+import lwjake2.game.usercmd_t;
+import lwjake2.qcommon.Cvar;
+import lwjake2.qcommon.xcommand_t;
+import lwjake2.util.Math3D;
+
+/**
+ * IN
+ */
+public final class UserInputHandler extends Globals {
+
+ static boolean mouse_active = false;
+ private static boolean mouse_avail = true;
+ private static int mouse_buttonstate;
+
+ private static int mouse_oldbuttonstate;
+
+ private static int old_mouse_x;
+
+ private static int old_mouse_y;
+
+ private static boolean mlooking;
+
+ private static void ActivateMouse() {
+ if (!mouse_avail)
+ return;
+ if (!mouse_active) {
+ KBD.mx = KBD.my = 0; // don't spazz
+ install_grabs();
+ mouse_active = true;
+ }
+ }
+
+ private static void DeactivateMouse() {
+ // if (!mouse_avail || c == null) return;
+ if (mouse_active) {
+ uninstall_grabs();
+ mouse_active = false;
+ }
+ }
+
+ private static void install_grabs() {
+ Globals.re.getKeyboardHandler().installGrabs();
+ boolean ignorefirst = true;
+ }
+
+ private static void uninstall_grabs() {
+ Globals.re.getKeyboardHandler().uninstallGrabs();
+ }
+
+ private static void toggleMouse() {
+ if (mouse_avail) {
+ mouse_avail = false;
+ DeactivateMouse();
+ } else {
+ mouse_avail = true;
+ ActivateMouse();
+ }
+ }
+
+ public static void Init() {
+ in_mouse = Cvar.get("in_mouse", "1", CVAR_ARCHIVE);
+ in_joystick = Cvar.get("in_joystick", "0", CVAR_ARCHIVE);
+ }
+
+ public static void Shutdown() {
+ mouse_avail = false;
+ }
+
+ public static void Real_IN_Init() {
+ // mouse variables
+ Globals.m_filter = Cvar.get("m_filter", "0", 0);
+ Globals.in_mouse = Cvar.get("in_mouse", "1", CVAR_ARCHIVE);
+ Globals.freelook = Cvar.get("freelook", "1", 0);
+ Globals.lookstrafe = Cvar.get("lookstrafe", "0", 0);
+ Globals.sensitivity = Cvar.get("sensitivity", "3", 0);
+ Globals.m_pitch = Cvar.get("m_pitch", "0.022", 0);
+ Globals.m_yaw = Cvar.get("m_yaw", "0.022", 0);
+ Globals.m_forward = Cvar.get("m_forward", "1", 0);
+ Globals.m_side = Cvar.get("m_side", "0.8", 0);
+
+ Cmd.AddCommand("+mlook", new xcommand_t() {
+ public void execute() {
+ MLookDown();
+ }
+ });
+ Cmd.AddCommand("-mlook", new xcommand_t() {
+ public void execute() {
+ MLookUp();
+ }
+ });
+
+ Cmd.AddCommand("force_centerview", new xcommand_t() {
+ public void execute() {
+ Force_CenterView_f();
+ }
+ });
+
+ Cmd.AddCommand("togglemouse", new xcommand_t() {
+ public void execute() {
+ toggleMouse();
+ }
+ });
+
+ UserInputHandler.mouse_avail = true;
+ }
+
+ public static void Commands() {
+ int i;
+
+ if (!UserInputHandler.mouse_avail)
+ return;
+
+ KBD kbd = Globals.re.getKeyboardHandler();
+ for (i = 0; i < 3; i++) {
+ if ((UserInputHandler.mouse_buttonstate & (1 << i)) != 0 && (UserInputHandler.mouse_oldbuttonstate & (1 << i)) == 0)
+ kbd.Do_Key_Event(Key.K_MOUSE1 + i, true);
+
+ if ((UserInputHandler.mouse_buttonstate & (1 << i)) == 0 && (UserInputHandler.mouse_oldbuttonstate & (1 << i)) != 0)
+ kbd.Do_Key_Event(Key.K_MOUSE1 + i, false);
+ }
+ UserInputHandler.mouse_oldbuttonstate = UserInputHandler.mouse_buttonstate;
+ }
+
+ public static void doFrame() {
+
+ if (!clientStateT.refresh_prepped || clientStaticT.key_dest == key_console
+ || clientStaticT.key_dest == key_menu)
+ DeactivateMouse();
+ else
+ ActivateMouse();
+ }
+
+ public static void CenterView() {
+ clientStateT.viewangles[PITCH] = -Math3D
+ .short2Angle(clientStateT.frame.playerstate.pmove.delta_angles[PITCH]);
+ }
+
+ public static void Move(usercmd_t cmd) {
+ if (!UserInputHandler.mouse_avail)
+ return;
+
+ if (Globals.m_filter.value != 0.0f) {
+ KBD.mx = (KBD.mx + UserInputHandler.old_mouse_x) / 2;
+ KBD.my = (KBD.my + UserInputHandler.old_mouse_y) / 2;
+ }
+
+ UserInputHandler.old_mouse_x = KBD.mx;
+ UserInputHandler.old_mouse_y = KBD.my;
+
+ KBD.mx = (int) (KBD.mx * Globals.sensitivity.value);
+ KBD.my = (int) (KBD.my * Globals.sensitivity.value);
+
+ // add mouse X/Y movement to cmd
+ if ((CL_input.in_strafe.state & 1) != 0
+ || ((Globals.lookstrafe.value != 0) && UserInputHandler.mlooking)) {
+ cmd.sidemove += Globals.m_side.value * KBD.mx;
+ } else {
+ Globals.clientStateT.viewangles[YAW] -= Globals.m_yaw.value * KBD.mx;
+ }
+
+ if ((UserInputHandler.mlooking || Globals.freelook.value != 0.0f)
+ && (CL_input.in_strafe.state & 1) == 0) {
+ Globals.clientStateT.viewangles[PITCH] += Globals.m_pitch.value * KBD.my;
+ } else {
+ cmd.forwardmove -= Globals.m_forward.value * KBD.my;
+ }
+ KBD.mx = KBD.my = 0;
+ }
+
+ private static void MLookDown() {
+ mlooking = true;
+ }
+
+ private static void MLookUp() {
+ mlooking = false;
+ CenterView();
+ }
+
+ private static void Force_CenterView_f() {
+ Globals.clientStateT.viewangles[PITCH] = 0;
+ }
+
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.util;
+
+import lwjake2.Globals;
+import lwjake2.qcommon.Com;
+
+import java.io.File;
+import java.io.RandomAccessFile;
+import java.io.UnsupportedEncodingException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.FloatBuffer;
+import java.nio.IntBuffer;
+
+public class Lib {
+
+
+ public static final int SIZEOF_FLOAT = 4;
+ public static final int SIZEOF_INT = 4;
+ static final byte nullfiller[] = new byte[8192];
+
+ /**
+ * Converts a vector to a string.
+ */
+ public static String vtos(float[] v) {
+ return (int) v[0] + " " + (int) v[1] + " " + (int) v[2];
+ }
+
+ /**
+ * Converts a vector to a beatiful string.
+ */
+ public static String vtofsbeaty(float[] v) {
+ return Com.sprintf("%8.2f %8.2f %8.2f", new Vargs().add(v[0]).add(v[1]).add(v[2]));
+ }
+
+ /**
+ * Like in libc.
+ */
+ public static short rand() {
+ return (short) Globals.rnd.nextInt(Short.MAX_VALUE + 1);
+ }
+
+ /**
+ * Like in libc.
+ */
+ public static float crandom() {
+ return (Globals.rnd.nextFloat() - 0.5f) * 2.0f;
+ }
+
+ /**
+ * Like in libc.
+ */
+ public static float crand() {
+ return (Globals.rnd.nextFloat() - 0.5f) * 2.0f;
+ }
+
+ /**
+ * Like in libc.
+ */
+ public static int strcmp(String in1, String in2) {
+ return in1.compareTo(in2);
+ }
+
+ /**
+ * Like in libc.
+ */
+ public static float atof(String in) {
+ float res = 0;
+
+ try {
+ res = Float.parseFloat(in);
+ } catch (Exception ignored) {
+ }
+
+ return res;
+ }
+
+ /**
+ * Like in quake2.
+ */
+ public static int Q_stricmp(String in1, String in2) {
+ return in1.compareToIgnoreCase(in2);
+ }
+
+ /**
+ * Like in libc.
+ */
+ public static int atoi(String in) {
+ try {
+ return Integer.parseInt(in);
+ } catch (Exception e) {
+ try {
+ return (int) Double.parseDouble(in);
+ } catch (Exception e1) {
+ return 0;
+ }
+ }
+ }
+
+ /**
+ * Converts a string to a vector. Needs improvement.
+ */
+ public static float[] atov(String v) {
+ float[] res = {0, 0, 0};
+ String strres[] = v.split(" ");
+ for (int n = 0; n < 3 && n < strres.length; n++) {
+ res[n] = atof(strres[n]);
+ }
+ return res;
+ }
+
+ /**
+ * Like in libc.
+ */
+ public static int strlen(char in[]) {
+ for (int i = 0; i < in.length; i++)
+ if (in[i] == 0)
+ return i;
+ return in.length;
+ }
+
+ /**
+ * Like in libc.
+ */
+ public static int strlen(byte in[]) {
+ for (int i = 0; i < in.length; i++)
+ if (in[i] == 0)
+ return i;
+ return in.length;
+ }
+
+ /**
+ * Converts memory to a memory dump string.
+ */
+ public static String hexDump(byte data1[], int len, boolean showAddress) {
+ StringBuilder result = new StringBuilder();
+ StringBuilder charfield = new StringBuilder();
+ int i = 0;
+ while (i < len) {
+ if ((i & 0xf) == 0) {
+ if (showAddress) {
+ String address = Integer.toHexString(i);
+ address = ("0000".substring(0, 4 - address.length()) + address).toUpperCase();
+ result.append(address).append(": ");
+ }
+ }
+ int v = data1[i];
+
+ result.append(hex2(v));
+ result.append(" ");
+
+ charfield.append(readableChar(v));
+ i++;
+
+ // nach dem letzten, newline einfuegen
+ if ((i & 0xf) == 0) {
+ result.append(charfield);
+ result.append("\n");
+ charfield.setLength(0);
+ }
+ // in der Mitte ein Luecke einfuegen ?
+ else if ((i & 0xf) == 8) {
+ result.append(" ");
+ }
+ }
+ return result.toString();
+ }
+
+ /**
+ * Formats an hex byte.
+ */
+ public static String hex2(int i) {
+ String val = Integer.toHexString(i & 0xff);
+ return ("00".substring(0, 2 - val.length()) + val).toUpperCase();
+ }
+
+ /**
+ * Returns true if the char is alphanumeric.
+ */
+ public static char readableChar(int i) {
+ if ((i < 0x20) || (i > 0x7f))
+ return '.';
+ else
+ return (char) i;
+ }
+
+ /**
+ * Like in libc
+ */
+ public static RandomAccessFile fopen(String name, String mode) {
+ try {
+ return new RandomAccessFile(name, mode);
+ } catch (Exception e) {
+ Com.DPrintf("Could not open file:" + name);
+ return null;
+ }
+ }
+
+ /**
+ * Like in libc
+ */
+ public static void fclose(RandomAccessFile f) {
+ try {
+ f.close();
+ } catch (Exception ignored) {
+ }
+ }
+
+ /**
+ * Returns the right part of the string from the last occruence of c.
+ */
+ public static String rightFrom(String in, char c) {
+ int pos = in.lastIndexOf(c);
+ if (pos == -1)
+ return "";
+ else if (pos < in.length())
+ return in.substring(pos + 1, in.length());
+ return "";
+ }
+
+ /**
+ * Returns the left part of the string from the last occruence of c.
+ */
+ public static String leftFrom(String in, char c) {
+ int pos = in.lastIndexOf(c);
+ if (pos == -1)
+ return "";
+ else if (pos < in.length())
+ return in.substring(0, pos);
+ return "";
+ }
+
+ /**
+ * Renames a file.
+ */
+ public static int rename(String oldn, String newn) {
+ try {
+ File f1 = new File(oldn);
+ File f2 = new File(newn);
+ f1.renameTo(f2);
+ return 0;
+ } catch (Exception e) {
+ return 1;
+ }
+ }
+
+ /**
+ * Converts an int to 4 bytes java representation.
+ */
+ public static byte[] getIntBytes(int c) {
+ byte b[] = new byte[4];
+ b[0] = (byte) ((c & 0xff));
+ b[1] = (byte) ((c >>> 8) & 0xff);
+ b[2] = (byte) ((c >>> 16) & 0xff);
+ b[3] = (byte) ((c >>> 24) & 0xff);
+ return b;
+ }
+
+ /**
+ * Duplicates a float array.
+ */
+ public static float[] clone(float in[]) {
+ float out[] = new float[in.length];
+
+ if (in.length != 0)
+ System.arraycopy(in, 0, out, 0, in.length);
+
+ return out;
+ }
+
+ /**
+ * convert a java string to byte[] with 8bit latin 1
+ * <p>
+ * avoid String.getBytes() because it is using system specific character encoding.
+ */
+ public static byte[] stringToBytes(String value) {
+ try {
+ return value.getBytes("ISO-8859-1");
+ } catch (UnsupportedEncodingException e) {
+ // can't happen: Latin 1 is a standard encoding
+ return null;
+ }
+ }
+
+ /**
+ * convert a byte[] with 8bit latin 1 to java string
+ * <p>
+ * avoid new String(bytes) because it is using system specific character encoding.
+ */
+ public static String bytesToString(byte[] value) {
+ try {
+ return new String(value, "ISO-8859-1");
+ } catch (UnsupportedEncodingException e) {
+ // can't happen: Latin 1 is a standard encoding
+ return null;
+ }
+ }
+
+
+ /* java.nio.* Buffer util functions */
+
+ /**
+ * Helper method that savely handles the null termination of old C String data.
+ */
+ public static String CtoJava(byte[] old) {
+ return CtoJava(old, 0, old.length);
+ }
+
+ /**
+ * Helper method that savely handles the null termination of old C String data.
+ */
+ public static String CtoJava(byte[] old, int offset, int maxLenght) {
+ if (old.length == 0 || old[0] == 0) return "";
+ int i;
+ for (i = offset; (i - offset) < maxLenght && old[i] != 0; i++) ;
+ return new String(old, offset, i - offset);
+ }
+
+ public static FloatBuffer newFloatBuffer(int numElements) {
+ ByteBuffer bb = newByteBuffer(numElements * SIZEOF_FLOAT);
+ return bb.asFloatBuffer();
+ }
+
+ public static IntBuffer newIntBuffer(int numElements) {
+ ByteBuffer bb = newByteBuffer(numElements * SIZEOF_INT);
+ return bb.asIntBuffer();
+ }
+
+ public static IntBuffer newIntBuffer(int numElements, ByteOrder order) {
+ ByteBuffer bb = newByteBuffer(numElements * SIZEOF_INT, order);
+ return bb.asIntBuffer();
+ }
+
+ public static ByteBuffer newByteBuffer(int numElements) {
+ ByteBuffer bb = ByteBuffer.allocateDirect(numElements);
+ bb.order(ByteOrder.nativeOrder());
+ return bb;
+ }
+
+ public static ByteBuffer newByteBuffer(int numElements, ByteOrder order) {
+ ByteBuffer bb = ByteBuffer.allocateDirect(numElements);
+ bb.order(order);
+ return bb;
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.util;
+
+import lwjake2.Defines;
+import lwjake2.game.cplane_t;
+import lwjake2.qcommon.Com;
+
+public class Math3D {
+
+ static final float shortratio = 360.0f / 65536.0f;
+ static final float piratio = (float) (Math.PI / 360.0);
+ // to reduce garbage
+ private static final float[] vr = {0, 0, 0};
+ private static final float[] vup = {0, 0, 0};
+ private static final float[] vf = {0, 0, 0};
+ private static final float[][] PLANE_XYZ = {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}};
+ private static final float[][] m = new float[3][3];
+ private static final float[][] im = new float[3][3];
+ private static final float[][] tmpmat = new float[3][3];
+ private static final float[][] zrot = new float[3][3];
+
+ public static void set(float v1[], float v2[]) {
+ v1[0] = v2[0];
+ v1[1] = v2[1];
+ v1[2] = v2[2];
+ }
+
+ public static void vectorSubtract(float[] a, float[] b, float[] c) {
+ c[0] = a[0] - b[0];
+ c[1] = a[1] - b[1];
+ c[2] = a[2] - b[2];
+ }
+
+ public static void vectorSubtract(short[] a, short[] b, int[] c) {
+ c[0] = a[0] - b[0];
+ c[1] = a[1] - b[1];
+ c[2] = a[2] - b[2];
+ }
+
+ public static void vectorAdd(float[] a, float[] b, float[] to) {
+ to[0] = a[0] + b[0];
+ to[1] = a[1] + b[1];
+ to[2] = a[2] + b[2];
+ }
+
+ public static void vectorCopy(float[] from, float[] to) {
+ to[0] = from[0];
+ to[1] = from[1];
+ to[2] = from[2];
+ }
+
+ public static void vectorCopy(short[] from, short[] to) {
+ to[0] = from[0];
+ to[1] = from[1];
+ to[2] = from[2];
+ }
+
+ public static void vectorCopy(short[] from, float[] to) {
+ to[0] = from[0];
+ to[1] = from[1];
+ to[2] = from[2];
+ }
+
+ public static void vectorCopy(float[] from, short[] to) {
+ to[0] = (short) from[0];
+ to[1] = (short) from[1];
+ to[2] = (short) from[2];
+ }
+
+ public static void vectorClear(float[] a) {
+ a[0] = a[1] = a[2] = 0;
+ }
+
+ public static boolean vectorEquals(float[] v1, float[] v2) {
+ return !(v1[0] != v2[0] || v1[1] != v2[1] || v1[2] != v2[2]);
+
+ }
+
+ public static void vectorNegate(float[] from, float[] to) {
+ to[0] = -from[0];
+ to[1] = -from[1];
+ to[2] = -from[2];
+ }
+
+ public static void vectorSet(float[] v, float x, float y, float z) {
+ v[0] = (x);
+ v[1] = (y);
+ v[2] = (z);
+ }
+
+ public static void vectorMA(float[] veca, float scale, float[] vecb, float[] to) {
+ to[0] = veca[0] + scale * vecb[0];
+ to[1] = veca[1] + scale * vecb[1];
+ to[2] = veca[2] + scale * vecb[2];
+ }
+
+ public static float vectorNormalize(float[] v) {
+
+ float length = vectorLength(v);
+ if (length != 0.0f) {
+ float ilength = 1.0f / length;
+ v[0] *= ilength;
+ v[1] *= ilength;
+ v[2] *= ilength;
+ }
+ return length;
+ }
+
+ public static float vectorLength(float v[]) {
+ return (float) Math.sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
+ }
+
+ public static void vectorScale(float[] in, float scale, float[] out) {
+ out[0] = in[0] * scale;
+ out[1] = in[1] * scale;
+ out[2] = in[2] * scale;
+ }
+
+ public static float vectoyaw(float[] vec) {
+ float yaw;
+
+ if (/*vec[YAW] == 0 &&*/
+ vec[Defines.PITCH] == 0) {
+ yaw = 0;
+ if (vec[Defines.YAW] > 0)
+ yaw = 90;
+ else if (vec[Defines.YAW] < 0)
+ yaw = -90;
+ } else {
+
+ yaw = (int) (Math.atan2(vec[Defines.YAW], vec[Defines.PITCH]) * 180 / Math.PI);
+ if (yaw < 0)
+ yaw += 360;
+ }
+
+ return yaw;
+ }
+
+ public static void vecToAngles(float[] value1, float[] angles) {
+
+ float yaw, pitch;
+
+ if (value1[1] == 0 && value1[0] == 0) {
+ yaw = 0;
+ if (value1[2] > 0)
+ pitch = 90;
+ else
+ pitch = 270;
+ } else {
+ if (value1[0] != 0)
+ yaw = (int) (Math.atan2(value1[1], value1[0]) * 180 / Math.PI);
+ else if (value1[1] > 0)
+ yaw = 90;
+ else
+ yaw = -90;
+ if (yaw < 0)
+ yaw += 360;
+
+ float forward = (float) Math.sqrt(value1[0] * value1[0] + value1[1] * value1[1]);
+ pitch = (int) (Math.atan2(value1[2], forward) * 180 / Math.PI);
+ if (pitch < 0)
+ pitch += 360;
+ }
+
+ angles[Defines.PITCH] = -pitch;
+ angles[Defines.YAW] = yaw;
+ angles[Defines.ROLL] = 0;
+ }
+
+ public static void rotatePointAroundVector(float[] dst, float[] dir, float[] point, float degrees) {
+ vf[0] = dir[0];
+ vf[1] = dir[1];
+ vf[2] = dir[2];
+
+ perpendicularVector(vr, dir);
+ crossProduct(vr, vf, vup);
+
+ m[0][0] = vr[0];
+ m[1][0] = vr[1];
+ m[2][0] = vr[2];
+
+ m[0][1] = vup[0];
+ m[1][1] = vup[1];
+ m[2][1] = vup[2];
+
+ m[0][2] = vf[0];
+ m[1][2] = vf[1];
+ m[2][2] = vf[2];
+
+ im[0][0] = m[0][0];
+ im[0][1] = m[1][0];
+ im[0][2] = m[2][0];
+ im[1][0] = m[0][1];
+ im[1][1] = m[1][1];
+ im[1][2] = m[2][1];
+ im[2][0] = m[0][2];
+ im[2][1] = m[1][2];
+ im[2][2] = m[2][2];
+
+ zrot[0][2] = zrot[1][2] = zrot[2][0] = zrot[2][1] = 0.0f;
+
+ zrot[2][2] = 1.0F;
+
+ zrot[0][0] = zrot[1][1] = (float) Math.cos(DEG2RAD(degrees));
+ zrot[0][1] = (float) Math.sin(DEG2RAD(degrees));
+ zrot[1][0] = -zrot[0][1];
+
+ R_ConcatRotations(m, zrot, tmpmat);
+ R_ConcatRotations(tmpmat, im, zrot);
+
+ for (int i = 0; i < 3; i++) {
+ dst[i] = zrot[i][0] * point[0] + zrot[i][1] * point[1] + zrot[i][2] * point[2];
+ }
+ }
+
+ public static void makeNormalVectors(float[] forward, float[] right, float[] up) {
+ // this rotate and negat guarantees a vector
+ // not colinear with the original
+ right[1] = -forward[0];
+ right[2] = forward[1];
+ right[0] = forward[2];
+
+ float d = dotProduct(right, forward);
+ vectorMA(right, -d, forward, right);
+ vectorNormalize(right);
+ crossProduct(right, forward, up);
+ }
+
+ public static float short2Angle(int x) {
+ return (x * shortratio);
+ }
+
+ /**
+ * concatenates 2 matrices each [3][3].
+ */
+ public static void R_ConcatRotations(float in1[][], float in2[][], float out[][]) {
+ out[0][0] = in1[0][0] * in2[0][0] + in1[0][1] * in2[1][0] + in1[0][2] * in2[2][0];
+ out[0][1] = in1[0][0] * in2[0][1] + in1[0][1] * in2[1][1] + in1[0][2] * in2[2][1];
+ out[0][2] = in1[0][0] * in2[0][2] + in1[0][1] * in2[1][2] + in1[0][2] * in2[2][2];
+ out[1][0] = in1[1][0] * in2[0][0] + in1[1][1] * in2[1][0] + in1[1][2] * in2[2][0];
+ out[1][1] = in1[1][0] * in2[0][1] + in1[1][1] * in2[1][1] + in1[1][2] * in2[2][1];
+ out[1][2] = in1[1][0] * in2[0][2] + in1[1][1] * in2[1][2] + in1[1][2] * in2[2][2];
+ out[2][0] = in1[2][0] * in2[0][0] + in1[2][1] * in2[1][0] + in1[2][2] * in2[2][0];
+ out[2][1] = in1[2][0] * in2[0][1] + in1[2][1] * in2[1][1] + in1[2][2] * in2[2][1];
+ out[2][2] = in1[2][0] * in2[0][2] + in1[2][1] * in2[1][2] + in1[2][2] * in2[2][2];
+ }
+
+ public static void projectPointOnPlane(float[] dst, float[] p, float[] normal) {
+
+ float inv_denom = 1.0F / dotProduct(normal, normal);
+
+ float d = dotProduct(normal, p) * inv_denom;
+
+ dst[0] = normal[0] * inv_denom;
+ dst[1] = normal[1] * inv_denom;
+ dst[2] = normal[2] * inv_denom;
+
+ dst[0] = p[0] - d * dst[0];
+ dst[1] = p[1] - d * dst[1];
+ dst[2] = p[2] - d * dst[2];
+ }
+ //=====================================================================
+
+ /**
+ * assumes "src" is normalized
+ */
+ public static void perpendicularVector(float[] dst, float[] src) {
+ int pos;
+ int i;
+ float minelem = 1.0F;
+
+ // find the smallest magnitude axially aligned vector
+ for (pos = 0, i = 0; i < 3; i++) {
+ if (Math.abs(src[i]) < minelem) {
+ pos = i;
+ minelem = Math.abs(src[i]);
+ }
+ }
+ // project the point onto the plane defined by src
+ projectPointOnPlane(dst, PLANE_XYZ[pos], src);
+
+ //normalize the result
+ vectorNormalize(dst);
+ }
+
+ /**
+ * stellt fest, auf welcher Seite sich die Kiste befindet, wenn die Ebene
+ * durch Entfernung und Senkrechten-Normale gegeben ist.
+ * erste Version mit vec3_t...
+ */
+ public static int boxOnPlaneSide(float emins[], float emaxs[], cplane_t p) {
+
+ assert (emins.length == 3 && emaxs.length == 3) : "vec3_t bug";
+
+ float dist1, dist2;
+ int sides;
+
+ // fast axial cases
+ if (p.type < 3) {
+ if (p.dist <= emins[p.type])
+ return 1;
+ if (p.dist >= emaxs[p.type])
+ return 2;
+ return 3;
+ }
+
+ // general case
+ switch (p.signbits) {
+ case 0:
+ dist1 = p.normal[0] * emaxs[0] + p.normal[1] * emaxs[1] + p.normal[2] * emaxs[2];
+ dist2 = p.normal[0] * emins[0] + p.normal[1] * emins[1] + p.normal[2] * emins[2];
+ break;
+ case 1:
+ dist1 = p.normal[0] * emins[0] + p.normal[1] * emaxs[1] + p.normal[2] * emaxs[2];
+ dist2 = p.normal[0] * emaxs[0] + p.normal[1] * emins[1] + p.normal[2] * emins[2];
+ break;
+ case 2:
+ dist1 = p.normal[0] * emaxs[0] + p.normal[1] * emins[1] + p.normal[2] * emaxs[2];
+ dist2 = p.normal[0] * emins[0] + p.normal[1] * emaxs[1] + p.normal[2] * emins[2];
+ break;
+ case 3:
+ dist1 = p.normal[0] * emins[0] + p.normal[1] * emins[1] + p.normal[2] * emaxs[2];
+ dist2 = p.normal[0] * emaxs[0] + p.normal[1] * emaxs[1] + p.normal[2] * emins[2];
+ break;
+ case 4:
+ dist1 = p.normal[0] * emaxs[0] + p.normal[1] * emaxs[1] + p.normal[2] * emins[2];
+ dist2 = p.normal[0] * emins[0] + p.normal[1] * emins[1] + p.normal[2] * emaxs[2];
+ break;
+ case 5:
+ dist1 = p.normal[0] * emins[0] + p.normal[1] * emaxs[1] + p.normal[2] * emins[2];
+ dist2 = p.normal[0] * emaxs[0] + p.normal[1] * emins[1] + p.normal[2] * emaxs[2];
+ break;
+ case 6:
+ dist1 = p.normal[0] * emaxs[0] + p.normal[1] * emins[1] + p.normal[2] * emins[2];
+ dist2 = p.normal[0] * emins[0] + p.normal[1] * emaxs[1] + p.normal[2] * emaxs[2];
+ break;
+ case 7:
+ dist1 = p.normal[0] * emins[0] + p.normal[1] * emins[1] + p.normal[2] * emins[2];
+ dist2 = p.normal[0] * emaxs[0] + p.normal[1] * emaxs[1] + p.normal[2] * emaxs[2];
+ break;
+ default:
+ dist1 = dist2 = 0;
+ assert (false) : "BoxOnPlaneSide bug";
+ break;
+ }
+
+ sides = 0;
+ if (dist1 >= p.dist)
+ sides = 1;
+ if (dist2 < p.dist)
+ sides |= 2;
+
+ assert (sides != 0) : "BoxOnPlaneSide(): sides == 0 bug";
+
+ return sides;
+ }
+
+ public static void angleVectors(float[] angles, float[] forward, float[] right, float[] up) {
+
+ float cr = 2.0f * piratio;
+ float angle = angles[Defines.YAW] * (cr);
+ float sy = (float) Math.sin(angle);
+ float cy = (float) Math.cos(angle);
+ angle = angles[Defines.PITCH] * (cr);
+ float sp = (float) Math.sin(angle);
+ float cp = (float) Math.cos(angle);
+
+ if (forward != null) {
+ forward[0] = cp * cy;
+ forward[1] = cp * sy;
+ forward[2] = -sp;
+ }
+
+ if (right != null || up != null) {
+ angle = angles[Defines.ROLL] * (cr);
+ float sr = (float) Math.sin(angle);
+ cr = (float) Math.cos(angle);
+
+ if (right != null) {
+ right[0] = (-sr * sp * cy + cr * sy);
+ right[1] = (-sr * sp * sy + -cr * cy);
+ right[2] = -sr * cp;
+ }
+ if (up != null) {
+ up[0] = (cr * sp * cy + sr * sy);
+ up[1] = (cr * sp * sy + -sr * cy);
+ up[2] = cr * cp;
+ }
+ }
+ }
+
+ public static float dotProduct(float[] x, float[] y) {
+ return x[0] * y[0] + x[1] * y[1] + x[2] * y[2];
+ }
+
+ public static void crossProduct(float[] v1, float[] v2, float[] cross) {
+ cross[0] = v1[1] * v2[2] - v1[2] * v2[1];
+ cross[1] = v1[2] * v2[0] - v1[0] * v2[2];
+ cross[2] = v1[0] * v2[1] - v1[1] * v2[0];
+ }
+
+ public static float DEG2RAD(float in) {
+ return (in * (float) Math.PI) / 180.0f;
+ }
+
+ public static float angleMod(float a) {
+ return shortratio * ((int) (a / (shortratio)) & 65535);
+ }
+
+ public static int angle2Short(float x) {
+ return ((int) ((x) / shortratio) & 65535);
+ }
+
+ public static float lerpAngle(float a2, float a1, float frac) {
+ if (a1 - a2 > 180)
+ a1 -= 360;
+ if (a1 - a2 < -180)
+ a1 += 360;
+ return a2 + frac * (a1 - a2);
+ }
+
+ public static float calcFov(float fov_x, float width, float height) {
+ double a = 0.0f;
+ double x;
+
+ if (fov_x < 1.0f || fov_x > 179.0f)
+ Com.Error(Defines.ERR_DROP, "Bad fov: " + fov_x);
+
+ x = width / Math.tan(fov_x * piratio);
+
+ a = Math.atan(height / x);
+
+ a = a / piratio;
+
+ return (float) a;
+ }
+}
--- /dev/null
+// (c) 2000 Sun Microsystems, Inc.
+// ALL RIGHTS RESERVED
+//
+// License Grant-
+//
+// Permission to use, copy, modify, and distribute this Software and its
+// documentation for NON-COMMERCIAL or COMMERCIAL purposes and without fee is
+// hereby granted.
+//
+// This Software is provided "AS IS". All express warranties, including any
+// implied warranty of merchantability, satisfactory quality, fitness for a
+// particular purpose, or non-infringement, are disclaimed, except to the extent
+// that such disclaimers are held to be legally invalid.
+//
+// You acknowledge that Software is not designed, licensed or intended for use in
+// the design, construction, operation or maintenance of any nuclear facility
+// ("High Risk Activities"). Sun disclaims any express or implied warranty of
+// fitness for such uses.
+//
+// Please refer to the file http://www.sun.com/policies/trademarks/ for further
+// important trademark information and to
+// http://java.sun.com/nav/business/index.html for further important licensing
+// information for the Java Technology.
+
+package lwjake2.util;
+
+import java.text.DecimalFormatSymbols;
+import java.util.Enumeration;
+import java.util.Locale;
+import java.util.Vector;
+
+/**
+ * PrintfFormat allows the formatting of an array of
+ * objects embedded within a string. Primitive types
+ * must be passed using wrapper types. The formatting
+ * is controlled by a control string.
+ * <p>
+ * A control string is a Java string that contains a
+ * control specification. The control specification
+ * starts at the first percent sign (%) in the string,
+ * provided that this percent sign
+ * <ol>
+ * <li>is not escaped protected by a matching % or is
+ * not an escape % character,
+ * <li>is not at the end of the format string, and
+ * <li>precedes a sequence of characters that parses as
+ * a valid control specification.
+ * </ol>
+ * </p><p>
+ * A control specification usually takes the form:
+ * <pre> % ['-+ #0]* [0..9]* { . [0..9]* }+
+ * { [hlL] }+ [idfgGoxXeEcs]
+ * </pre>
+ * There are variants of this basic form that are
+ * discussed below.</p>
+ * <p>
+ * The format is composed of zero or more directives
+ * defined as follows:
+ * <ul>
+ * <li>ordinary characters, which are simply copied to
+ * the output stream;
+ * <li>escape sequences, which represent non-graphic
+ * characters; and
+ * <li>conversion specifications, each of which
+ * results in the fetching of zero or more arguments.
+ * </ul></p>
+ * <p>
+ * The results are undefined if there are insufficient
+ * arguments for the format. Usually an unchecked
+ * exception will be thrown. If the format is
+ * exhausted while arguments remain, the excess
+ * arguments are evaluated but are otherwise ignored.
+ * In format strings containing the % form of
+ * conversion specifications, each argument in the
+ * argument list is used exactly once.</p>
+ * <p>
+ * Conversions can be applied to the <code>n</code>th
+ * argument after the format in the argument list,
+ * rather than to the next unused argument. In this
+ * case, the conversion characer % is replaced by the
+ * sequence %<code>n</code>$, where <code>n</code> is
+ * a decimal integer giving the position of the
+ * argument in the argument list.</p>
+ * <p>
+ * In format strings containing the %<code>n</code>$
+ * form of conversion specifications, each argument
+ * in the argument list is used exactly once.</p>
+ * <p>
+ * <h4>Escape Sequences</h4>
+ * <p>
+ * The following table lists escape sequences and
+ * associated actions on display devices capable of
+ * the action.
+ * <table>
+ * <tr><th align=left>Sequence</th>
+ * <th align=left>Name</th>
+ * <th align=left>Description</th></tr>
+ * <tr><td>\\</td><td>backlash</td><td>None.
+ * </td></tr>
+ * <tr><td>\a</td><td>alert</td><td>Attempts to alert
+ * the user through audible or visible
+ * notification.
+ * </td></tr>
+ * <tr><td>\b</td><td>backspace</td><td>Moves the
+ * printing position to one column before
+ * the current position, unless the
+ * current position is the start of a line.
+ * </td></tr>
+ * <tr><td>\f</td><td>form-feed</td><td>Moves the
+ * printing position to the initial
+ * printing position of the next logical
+ * page.
+ * </td></tr>
+ * <tr><td>\n</td><td>newline</td><td>Moves the
+ * printing position to the start of the
+ * next line.
+ * </td></tr>
+ * <tr><td>\r</td><td>carriage-return</td><td>Moves
+ * the printing position to the start of
+ * the current line.
+ * </td></tr>
+ * <tr><td>\t</td><td>tab</td><td>Moves the printing
+ * position to the next implementation-
+ * defined horizontal tab position.
+ * </td></tr>
+ * <tr><td>\v</td><td>vertical-tab</td><td>Moves the
+ * printing position to the start of the
+ * next implementation-defined vertical
+ * tab position.
+ * </td></tr>
+ * </table></p>
+ * <h4>Conversion Specifications</h4>
+ * <p>
+ * Each conversion specification is introduced by
+ * the percent sign character (%). After the character
+ * %, the following appear in sequence:</p>
+ * <p>
+ * Zero or more flags (in any order), which modify the
+ * meaning of the conversion specification.</p>
+ * <p>
+ * An optional minimum field width. If the converted
+ * value has fewer characters than the field width, it
+ * will be padded with spaces by default on the left;
+ * t will be padded on the right, if the left-
+ * adjustment flag (-), described below, is given to
+ * the field width. The field width takes the form
+ * of a decimal integer. If the conversion character
+ * is s, the field width is the the minimum number of
+ * characters to be printed.</p>
+ * <p>
+ * An optional precision that gives the minumum number
+ * of digits to appear for the d, i, o, x or X
+ * conversions (the field is padded with leading
+ * zeros); the number of digits to appear after the
+ * radix character for the e, E, and f conversions,
+ * the maximum number of significant digits for the g
+ * and G conversions; or the maximum number of
+ * characters to be written from a string is s and S
+ * conversions. The precision takes the form of an
+ * optional decimal digit string, where a null digit
+ * string is treated as 0. If a precision appears
+ * with a c conversion character the precision is
+ * ignored.
+ * </p>
+ * <p>
+ * An optional h specifies that a following d, i, o,
+ * x, or X conversion character applies to a type
+ * short argument (the argument will be promoted
+ * according to the integral promotions and its value
+ * converted to type short before printing).</p>
+ * <p>
+ * An optional l (ell) specifies that a following
+ * d, i, o, x, or X conversion character applies to a
+ * type long argument.</p>
+ * <p>
+ * A field width or precision may be indicated by an
+ * asterisk (*) instead of a digit string. In this
+ * case, an integer argument supplised the field width
+ * precision. The argument that is actually converted
+ * is not fetched until the conversion letter is seen,
+ * so the the arguments specifying field width or
+ * precision must appear before the argument (if any)
+ * to be converted. If the precision argument is
+ * negative, it will be changed to zero. A negative
+ * field width argument is taken as a - flag, followed
+ * by a positive field width.</p>
+ * <p>
+ * In format strings containing the %<code>n</code>$
+ * form of a conversion specification, a field width
+ * or precision may be indicated by the sequence
+ * *<code>m</code>$, where m is a decimal integer
+ * giving the position in the argument list (after the
+ * format argument) of an integer argument containing
+ * the field width or precision.</p>
+ * <p>
+ * The format can contain either numbered argument
+ * specifications (that is, %<code>n</code>$ and
+ * *<code>m</code>$), or unnumbered argument
+ * specifications (that is % and *), but normally not
+ * both. The only exception to this is that %% can
+ * be mixed with the %<code>n</code>$ form. The
+ * results of mixing numbered and unnumbered argument
+ * specifications in a format string are undefined.</p>
+ * <p>
+ * <h4>Flag Characters</h4>
+ * <p>
+ * The flags and their meanings are:</p>
+ * <dl>
+ * <dt>'<dd> integer portion of the result of a
+ * decimal conversion (%i, %d, %f, %g, or %G) will
+ * be formatted with thousands' grouping
+ * characters. For other conversions the flag
+ * is ignored. The non-monetary grouping
+ * character is used.
+ * <dt>-<dd> result of the conversion is left-justified
+ * within the field. (It will be right-justified
+ * if this flag is not specified).</td></tr>
+ * <dt>+<dd> result of a signed conversion always
+ * begins with a sign (+ or -). (It will begin
+ * with a sign only when a negative value is
+ * converted if this flag is not specified.)
+ * <dt><space><dd> If the first character of a
+ * signed conversion is not a sign, a space
+ * character will be placed before the result.
+ * This means that if the space character and +
+ * flags both appear, the space flag will be
+ * ignored.
+ * <dt>#<dd> value is to be converted to an alternative
+ * form. For c, d, i, and s conversions, the flag
+ * has no effect. For o conversion, it increases
+ * the precision to force the first digit of the
+ * result to be a zero. For x or X conversion, a
+ * non-zero result has 0x or 0X prefixed to it,
+ * respectively. For e, E, f, g, and G
+ * conversions, the result always contains a radix
+ * character, even if no digits follow the radix
+ * character (normally, a decimal point appears in
+ * the result of these conversions only if a digit
+ * follows it). For g and G conversions, trailing
+ * zeros will not be removed from the result as
+ * they normally are.
+ * <dt>0<dd> d, i, o, x, X, e, E, f, g, and G
+ * conversions, leading zeros (following any
+ * indication of sign or base) are used to pad to
+ * the field width; no space padding is
+ * performed. If the 0 and - flags both appear,
+ * the 0 flag is ignored. For d, i, o, x, and X
+ * conversions, if a precision is specified, the
+ * 0 flag will be ignored. For c conversions,
+ * the flag is ignored.
+ * </dl>
+ * <p>
+ * <h4>Conversion Characters</h4>
+ * <p>
+ * Each conversion character results in fetching zero
+ * or more arguments. The results are undefined if
+ * there are insufficient arguments for the format.
+ * Usually, an unchecked exception will be thrown.
+ * If the format is exhausted while arguments remain,
+ * the excess arguments are ignored.</p>
+ * <p>
+ * <p>
+ * The conversion characters and their meanings are:
+ * </p>
+ * <dl>
+ * <dt>d,i<dd>The int argument is converted to a
+ * signed decimal in the style [-]dddd. The
+ * precision specifies the minimum number of
+ * digits to appear; if the value being
+ * converted can be represented in fewer
+ * digits, it will be expanded with leading
+ * zeros. The default precision is 1. The
+ * result of converting 0 with an explicit
+ * precision of 0 is no characters.
+ * <dt>o<dd> The int argument is converted to unsigned
+ * octal format in the style ddddd. The
+ * precision specifies the minimum number of
+ * digits to appear; if the value being
+ * converted can be represented in fewer
+ * digits, it will be expanded with leading
+ * zeros. The default precision is 1. The
+ * result of converting 0 with an explicit
+ * precision of 0 is no characters.
+ * <dt>x<dd> The int argument is converted to unsigned
+ * hexadecimal format in the style dddd; the
+ * letters abcdef are used. The precision
+ * specifies the minimum numberof digits to
+ * appear; if the value being converted can be
+ * represented in fewer digits, it will be
+ * expanded with leading zeros. The default
+ * precision is 1. The result of converting 0
+ * with an explicit precision of 0 is no
+ * characters.
+ * <dt>X<dd> Behaves the same as the x conversion
+ * character except that letters ABCDEF are
+ * used instead of abcdef.
+ * <dt>f<dd> The floating point number argument is
+ * written in decimal notation in the style
+ * [-]ddd.ddd, where the number of digits after
+ * the radix character (shown here as a decimal
+ * point) is equal to the precision
+ * specification. A Locale is used to determine
+ * the radix character to use in this format.
+ * If the precision is omitted from the
+ * argument, six digits are written after the
+ * radix character; if the precision is
+ * explicitly 0 and the # flag is not specified,
+ * no radix character appears. If a radix
+ * character appears, at least 1 digit appears
+ * before it. The value is rounded to the
+ * appropriate number of digits.
+ * <dt>e,E<dd>The floating point number argument is
+ * written in the style [-]d.ddde{+-}dd
+ * (the symbols {+-} indicate either a plus or
+ * minus sign), where there is one digit before
+ * the radix character (shown here as a decimal
+ * point) and the number of digits after it is
+ * equal to the precision. A Locale is used to
+ * determine the radix character to use in this
+ * format. When the precision is missing, six
+ * digits are written after the radix character;
+ * if the precision is 0 and the # flag is not
+ * specified, no radix character appears. The
+ * E conversion will produce a number with E
+ * instead of e introducing the exponent. The
+ * exponent always contains at least two digits.
+ * However, if the value to be written requires
+ * an exponent greater than two digits,
+ * additional exponent digits are written as
+ * necessary. The value is rounded to the
+ * appropriate number of digits.
+ * <dt>g,G<dd>The floating point number argument is
+ * written in style f or e (or in sytle E in the
+ * case of a G conversion character), with the
+ * precision specifying the number of
+ * significant digits. If the precision is
+ * zero, it is taken as one. The style used
+ * depends on the value converted: style e
+ * (or E) will be used only if the exponent
+ * resulting from the conversion is less than
+ * -4 or greater than or equal to the precision.
+ * Trailing zeros are removed from the result.
+ * A radix character appears only if it is
+ * followed by a digit.
+ * <dt>c,C<dd>The integer argument is converted to a
+ * char and the result is written.
+ * <p>
+ * <dt>s,S<dd>The argument is taken to be a string and
+ * bytes from the string are written until the
+ * end of the string or the number of bytes
+ * indicated by the precision specification of
+ * the argument is reached. If the precision
+ * is omitted from the argument, it is taken to
+ * be infinite, so all characters up to the end
+ * of the string are written.
+ * <dt>%<dd>Write a % character; no argument is
+ * converted.
+ * </dl>
+ * <p>
+ * If a conversion specification does not match one of
+ * the above forms, an IllegalArgumentException is
+ * thrown and the instance of PrintfFormat is not
+ * created.</p>
+ * <p>
+ * If a floating point value is the internal
+ * representation for infinity, the output is
+ * [+]Infinity, where Infinity is either Infinity or
+ * Inf, depending on the desired output string length.
+ * Printing of the sign follows the rules described
+ * above.</p>
+ * <p>
+ * If a floating point value is the internal
+ * representation for "not-a-number," the output is
+ * [+]NaN. Printing of the sign follows the rules
+ * described above.</p>
+ * <p>
+ * In no case does a non-existent or small field width
+ * cause truncation of a field; if the result of a
+ * conversion is wider than the field width, the field
+ * is simply expanded to contain the conversion result.
+ * </p>
+ * <p>
+ * The behavior is like printf. One exception is that
+ * the minimum number of exponent digits is 3 instead
+ * of 2 for e and E formats when the optional L is used
+ * before the e, E, g, or G conversion character. The
+ * optional L does not imply conversion to a long long
+ * double. </p>
+ * <p>
+ * The biggest divergence from the C printf
+ * specification is in the use of 16 bit characters.
+ * This allows the handling of characters beyond the
+ * small ASCII character set and allows the utility to
+ * interoperate correctly with the rest of the Java
+ * runtime environment.</p>
+ * <p>
+ * Omissions from the C printf specification are
+ * numerous. All the known omissions are present
+ * because Java never uses bytes to represent
+ * characters and does not have pointers:</p>
+ * <ul>
+ * <li>%c is the same as %C.
+ * <li>%s is the same as %S.
+ * <li>u, p, and n conversion characters.
+ * <li>%ws format.
+ * <li>h modifier applied to an n conversion character.
+ * <li>l (ell) modifier applied to the c, n, or s
+ * conversion characters.
+ * <li>ll (ell ell) modifier to d, i, o, u, x, or X
+ * conversion characters.
+ * <li>ll (ell ell) modifier to an n conversion
+ * character.
+ * <li>c, C, d,i,o,u,x, and X conversion characters
+ * apply to Byte, Character, Short, Integer, Long
+ * types.
+ * <li>f, e, E, g, and G conversion characters apply
+ * to Float and Double types.
+ * <li>s and S conversion characters apply to String
+ * types.
+ * <li>All other reference types can be formatted
+ * using the s or S conversion characters only.
+ * </ul>
+ * <p>
+ * Most of this specification is quoted from the Unix
+ * man page for the sprintf utility.</p>
+ *
+ * @author Allan Jacobs
+ * @version 1
+ * Release 1: Initial release.
+ * Release 2: Asterisk field widths and precisions
+ * %n$ and *m$
+ * Bug fixes
+ * g format fix (2 digits in e form corrupt)
+ * rounding in f format implemented
+ * round up when digit not printed is 5
+ * formatting of -0.0f
+ * round up/down when last digits are 50000...
+ */
+public class PrintfFormat {
+ /**
+ * Vector of control strings and format literals.
+ */
+ private final Vector<Object> vFmt = new Vector<>();
+ /**
+ * Character position. Used by the constructor.
+ */
+ private int cPos = 0;
+ /**
+ * Character position. Used by the constructor.
+ */
+ private DecimalFormatSymbols dfs = null;
+
+ /**
+ * Constructs an array of control specifications
+ * possibly preceded, separated, or followed by
+ * ordinary strings. Control strings begin with
+ * unpaired percent signs. A pair of successive
+ * percent signs designates a single percent sign in
+ * the format.
+ *
+ * @param fmtArg Control string.
+ * @throws IllegalArgumentException if the control
+ * string is null, zero length, or otherwise
+ * malformed.
+ */
+ public PrintfFormat(String fmtArg) throws IllegalArgumentException {
+ this(Locale.getDefault(), fmtArg);
+ }
+
+ /**
+ * Constructs an array of control specifications
+ * possibly preceded, separated, or followed by
+ * ordinary strings. Control strings begin with
+ * unpaired percent signs. A pair of successive
+ * percent signs designates a single percent sign in
+ * the format.
+ *
+ * @param fmtArg Control string.
+ * @throws IllegalArgumentException if the control
+ * string is null, zero length, or otherwise
+ * malformed.
+ */
+ public PrintfFormat(Locale locale, String fmtArg) throws IllegalArgumentException {
+ dfs = new DecimalFormatSymbols(locale);
+ int ePos = 0;
+ ConversionSpecification sFmt = null;
+ String unCS = this.nonControl(fmtArg, 0);
+ if (unCS != null) {
+ sFmt = new ConversionSpecification();
+ sFmt.setLiteral(unCS);
+ vFmt.addElement(sFmt);
+ }
+ while (cPos != -1 && cPos < fmtArg.length()) {
+ for (ePos = cPos + 1; ePos < fmtArg.length(); ePos++) {
+ char c = 0;
+ c = fmtArg.charAt(ePos);
+ if (c == 'i')
+ break;
+ if (c == 'd')
+ break;
+ if (c == 'f')
+ break;
+ if (c == 'g')
+ break;
+ if (c == 'G')
+ break;
+ if (c == 'o')
+ break;
+ if (c == 'x')
+ break;
+ if (c == 'X')
+ break;
+ if (c == 'e')
+ break;
+ if (c == 'E')
+ break;
+ if (c == 'c')
+ break;
+ if (c == 's')
+ break;
+ if (c == '%')
+ break;
+ }
+ ePos = Math.min(ePos + 1, fmtArg.length());
+ sFmt = new ConversionSpecification(fmtArg.substring(cPos, ePos));
+ vFmt.addElement(sFmt);
+ unCS = this.nonControl(fmtArg, ePos);
+ if (unCS != null) {
+ sFmt = new ConversionSpecification();
+ sFmt.setLiteral(unCS);
+ vFmt.addElement(sFmt);
+ }
+ }
+ }
+
+ /**
+ * Return a substring starting at
+ * <code>start</code> and ending at either the end
+ * of the String <code>s</code>, the next unpaired
+ * percent sign, or at the end of the String if the
+ * last character is a percent sign.
+ *
+ * @param s Control string.
+ * @param start Position in the string
+ * <code>s</code> to begin looking for the start
+ * of a control string.
+ * @return the substring from the start position
+ * to the beginning of the control string.
+ */
+ private String nonControl(String s, int start) {
+ // String ret = "";
+ cPos = s.indexOf("%", start);
+ if (cPos == -1)
+ cPos = s.length();
+ return s.substring(start, cPos);
+ }
+
+ /**
+ * Format an array of objects. Byte, Short,
+ * Integer, Long, Float, Double, and Character
+ * arguments are treated as wrappers for primitive
+ * types.
+ *
+ * @param o The array of objects to format.
+ * @return The formatted String.
+ */
+ public String sprintf(Object[] o) {
+ Enumeration<Object> e = vFmt.elements();
+ ConversionSpecification cs = null;
+ char c = 0;
+ int i = 0;
+ StringBuilder sb = new StringBuilder();
+ while (e.hasMoreElements()) {
+ cs = (ConversionSpecification) e.nextElement();
+ c = cs.getConversionCharacter();
+ if (c == '\0')
+ sb.append(cs.getLiteral());
+ else if (c == '%')
+ sb.append("%");
+ else {
+ if (cs.isPositionalSpecification()) {
+ i = cs.getArgumentPosition() - 1;
+ if (cs.isPositionalFieldWidth()) {
+ int ifw = cs.getArgumentPositionForFieldWidth() - 1;
+ cs.setFieldWidthWithArg((Integer) o[ifw]);
+ }
+ if (cs.isPositionalPrecision()) {
+ int ipr = cs.getArgumentPositionForPrecision() - 1;
+ cs.setPrecisionWithArg((Integer) o[ipr]);
+ }
+ } else {
+ if (cs.isVariableFieldWidth()) {
+ cs.setFieldWidthWithArg((Integer) o[i]);
+ i++;
+ }
+ if (cs.isVariablePrecision()) {
+ cs.setPrecisionWithArg((Integer) o[i]);
+ i++;
+ }
+ }
+ if (o[i] instanceof Byte)
+ sb.append(cs.internalsprintf(((Byte) o[i]).byteValue()));
+ else if (o[i] instanceof Short)
+ sb.append(cs.internalsprintf(((Short) o[i]).shortValue()));
+ else if (o[i] instanceof Integer)
+ sb.append(cs.internalsprintf(((Integer) o[i]).intValue()));
+ else if (o[i] instanceof Long)
+ sb.append(cs.internalsprintf(((Long) o[i]).longValue()));
+ else if (o[i] instanceof Float)
+ sb.append(cs.internalsprintf(((Float) o[i]).floatValue()));
+ else if (o[i] instanceof Double)
+ sb.append(cs.internalsprintf(((Double) o[i]).doubleValue()));
+ else if (o[i] instanceof Character)
+ sb.append(cs.internalsprintf(((Character) o[i]).charValue()));
+ else if (o[i] instanceof String)
+ sb.append(cs.internalsprintf((String) o[i]));
+ else
+ sb.append(cs.internalsprintf(o[i]));
+ if (!cs.isPositionalSpecification())
+ i++;
+ }
+ }
+ return sb.toString();
+ }
+
+ /**
+ * <p>
+ * ConversionSpecification allows the formatting of
+ * a single primitive or object embedded within a
+ * string. The formatting is controlled by a
+ * format string. Only one Java primitive or
+ * object can be formatted at a time.
+ * <p>
+ * A format string is a Java string that contains
+ * a control string. The control string starts at
+ * the first percent sign (%) in the string,
+ * provided that this percent sign
+ * <ol>
+ * <li>is not escaped protected by a matching % or
+ * is not an escape % character,
+ * <li>is not at the end of the format string, and
+ * <li>precedes a sequence of characters that parses
+ * as a valid control string.
+ * </ol>
+ * <p>
+ * A control string takes the form:
+ * <pre> % ['-+ #0]* [0..9]* { . [0..9]* }+
+ * { [hlL] }+ [idfgGoxXeEcs]
+ * </pre>
+ * <p>
+ * The behavior is like printf. One (hopefully the
+ * only) exception is that the minimum number of
+ * exponent digits is 3 instead of 2 for e and E
+ * formats when the optional L is used before the
+ * e, E, g, or G conversion character. The
+ * optional L does not imply conversion to a long
+ * long double.
+ */
+ private class ConversionSpecification {
+ /**
+ * Default precision.
+ */
+ private final static int defaultDigits = 6;
+ /**
+ * The integer portion of the result of a decimal
+ * conversion (i, d, u, f, g, or G) will be
+ * formatted with thousands' grouping characters.
+ * For other conversions the flag is ignored.
+ */
+ private boolean thousands = false;
+ /**
+ * The result of the conversion will be
+ * left-justified within the field.
+ */
+ private boolean leftJustify = false;
+ /**
+ * The result of a signed conversion will always
+ * begin with a sign (+ or -).
+ */
+ private boolean leadingSign = false;
+ /**
+ * Flag indicating that left padding with spaces is
+ * specified.
+ */
+ private boolean leadingSpace = false;
+ /**
+ * For an o conversion, increase the precision to
+ * force the first digit of the result to be a
+ * zero. For x (or X) conversions, a non-zero
+ * result will have 0x (or 0X) prepended to it.
+ * For e, E, f, g, or G conversions, the result
+ * will always contain a radix character, even if
+ * no digits follow the point. For g and G
+ * conversions, trailing zeros will not be removed
+ * from the result.
+ */
+ private boolean alternateForm = false;
+ /**
+ * Flag indicating that left padding with zeroes is
+ * specified.
+ */
+ private boolean leadingZeros = false;
+ /**
+ * Flag indicating that the field width is *.
+ */
+ private boolean variableFieldWidth = false;
+ /**
+ * If the converted value has fewer bytes than the
+ * field width, it will be padded with spaces or
+ * zeroes.
+ */
+ private int fieldWidth = 0;
+ /**
+ * Flag indicating whether or not the field width
+ * has been set.
+ */
+ private boolean fieldWidthSet = false;
+ /**
+ * The minimum number of digits to appear for the
+ * d, i, o, u, x, or X conversions. The number of
+ * digits to appear after the radix character for
+ * the e, E, and f conversions. The maximum number
+ * of significant digits for the g and G
+ * conversions. The maximum number of bytes to be
+ * printed from a string in s and S conversions.
+ */
+ private int precision = 0;
+ /**
+ * Flag indicating that the precision is *.
+ */
+ private boolean variablePrecision = false;
+ /**
+ * Flag indicating whether or not the precision has
+ * been set.
+ */
+ private boolean precisionSet = false;
+ /*
+ */
+ private boolean positionalSpecification = false;
+ private int argumentPosition = 0;
+ private boolean positionalFieldWidth = false;
+ private int argumentPositionForFieldWidth = 0;
+ private boolean positionalPrecision = false;
+ private int argumentPositionForPrecision = 0;
+ /**
+ * Flag specifying that a following d, i, o, u, x,
+ * or X conversion character applies to a type
+ * short int.
+ */
+ private boolean optionalh = false;
+ /**
+ * Flag specifying that a following d, i, o, u, x,
+ * or X conversion character applies to a type lont
+ * int argument.
+ */
+ private boolean optionall = false;
+ /**
+ * Flag specifying that a following e, E, f, g, or
+ * G conversion character applies to a type double
+ * argument. This is a noop in Java.
+ */
+ private boolean optionalL = false;
+ /**
+ * Control string type.
+ */
+ private char conversionCharacter = '\0';
+ /**
+ * Position within the control string. Used by
+ * the constructor.
+ */
+ private int pos = 0;
+ /**
+ * Literal or control format string.
+ */
+ private String fmt;
+
+ /**
+ * Constructor. Used to prepare an instance
+ * to hold a literal, not a control string.
+ */
+ ConversionSpecification() {
+ }
+
+ /**
+ * Constructor for a conversion specification.
+ * The argument must begin with a % and end
+ * with the conversion character for the
+ * conversion specification.
+ *
+ * @param fmtArg String specifying the
+ * conversion specification.
+ * @throws IllegalArgumentException if the
+ * input string is null, zero length, or
+ * otherwise malformed.
+ */
+ ConversionSpecification(String fmtArg) throws IllegalArgumentException {
+ if (fmtArg == null)
+ throw new NullPointerException();
+ if (fmtArg.length() == 0)
+ throw new IllegalArgumentException("Control strings must have positive" + " lengths.");
+ if (fmtArg.charAt(0) == '%') {
+ fmt = fmtArg;
+ pos = 1;
+ setArgPosition();
+ setFlagCharacters();
+ setFieldWidth();
+ setPrecision();
+ setOptionalHL();
+ if (setConversionCharacter()) {
+ if (pos == fmtArg.length()) {
+ if (leadingZeros && leftJustify)
+ leadingZeros = false;
+ if (precisionSet && leadingZeros) {
+ if (conversionCharacter == 'd'
+ || conversionCharacter == 'i'
+ || conversionCharacter == 'o'
+ || conversionCharacter == 'x') {
+ leadingZeros = false;
+ }
+ }
+ } else
+ throw new IllegalArgumentException("Malformed conversion specification=" + fmtArg);
+ } else
+ throw new IllegalArgumentException("Malformed conversion specification=" + fmtArg);
+ } else
+ throw new IllegalArgumentException("Control strings must begin with %.");
+ }
+
+ /**
+ * Get the String for this instance. Translate
+ * any escape sequences.
+ *
+ * @return s the stored String.
+ */
+ String getLiteral() {
+ StringBuilder sb = new StringBuilder();
+ int i = 0;
+ while (i < fmt.length()) {
+ if (fmt.charAt(i) == '\\') {
+ i++;
+ if (i < fmt.length()) {
+ char c = fmt.charAt(i);
+ switch (c) {
+ case 'a':
+ sb.append((char) 0x07);
+ break;
+ case 'b':
+ sb.append('\b');
+ break;
+ case 'f':
+ sb.append('\f');
+ break;
+ case 'n':
+ sb.append(System.getProperty("line.separator"));
+ break;
+ case 'r':
+ sb.append('\r');
+ break;
+ case 't':
+ sb.append('\t');
+ break;
+ case 'v':
+ sb.append((char) 0x0b);
+ break;
+ case '\\':
+ sb.append('\\');
+ break;
+ }
+ i++;
+ } else
+ sb.append('\\');
+ } else
+ i++;
+ }
+ return fmt;
+ }
+
+ /**
+ * Set the String for this instance.
+ *
+ * @param s the String to store.
+ */
+ void setLiteral(String s) {
+ fmt = s;
+ }
+
+ /**
+ * Get the conversion character that tells what
+ * type of control character this instance has.
+ *
+ * @return the conversion character.
+ */
+ char getConversionCharacter() {
+ return conversionCharacter;
+ }
+
+ /**
+ * Check whether the specifier has a variable
+ * field width that is going to be set by an
+ * argument.
+ *
+ * @return <code>true</code> if the conversion
+ * uses an * field width; otherwise
+ * <code>false</code>.
+ */
+ boolean isVariableFieldWidth() {
+ return variableFieldWidth;
+ }
+
+ /**
+ * Set the field width with an argument. A
+ * negative field width is taken as a - flag
+ * followed by a positive field width.
+ *
+ * @param fw the field width.
+ */
+ void setFieldWidthWithArg(int fw) {
+ if (fw < 0)
+ leftJustify = true;
+ fieldWidthSet = true;
+ fieldWidth = Math.abs(fw);
+ }
+
+ /**
+ * Check whether the specifier has a variable
+ * precision that is going to be set by an
+ * argument.
+ *
+ * @return <code>true</code> if the conversion
+ * uses an * precision; otherwise
+ * <code>false</code>.
+ */
+ boolean isVariablePrecision() {
+ return variablePrecision;
+ }
+
+ /**
+ * Set the precision with an argument. A
+ * negative precision will be changed to zero.
+ *
+ * @param pr the precision.
+ */
+ void setPrecisionWithArg(int pr) {
+ precisionSet = true;
+ precision = Math.max(pr, 0);
+ }
+
+ /**
+ * Format an int argument using this conversion
+ * specification.
+ *
+ * @param s the int to format.
+ * @return the formatted String.
+ * @throws IllegalArgumentException if the
+ * conversion character is f, e, E, g, or G.
+ */
+ String internalsprintf(int s) throws IllegalArgumentException {
+ String s2 = "";
+ switch (conversionCharacter) {
+ case 'd':
+ case 'i':
+ if (optionalh)
+ s2 = printDFormat((short) s);
+ else if (optionall)
+ s2 = printDFormat((long) s);
+ else
+ s2 = printDFormat(s);
+ break;
+ case 'x':
+ case 'X':
+ if (optionalh)
+ s2 = printXFormat((short) s);
+ else if (optionall)
+ s2 = printXFormat((long) s);
+ else
+ s2 = printXFormat(s);
+ break;
+ case 'o':
+ if (optionalh)
+ s2 = printOFormat((short) s);
+ else if (optionall)
+ s2 = printOFormat((long) s);
+ else
+ s2 = printOFormat(s);
+ break;
+ case 'c':
+ case 'C':
+ s2 = printCFormat((char) s);
+ break;
+ default:
+ throw new IllegalArgumentException(
+ "Cannot format a int with a format using a " + conversionCharacter + " conversion character.");
+ }
+ return s2;
+ }
+
+ /**
+ * Format a long argument using this conversion
+ * specification.
+ *
+ * @param s the long to format.
+ * @return the formatted String.
+ * @throws IllegalArgumentException if the
+ * conversion character is f, e, E, g, or G.
+ */
+ String internalsprintf(long s) throws IllegalArgumentException {
+ String s2 = "";
+ switch (conversionCharacter) {
+ case 'd':
+ case 'i':
+ if (optionalh)
+ s2 = printDFormat((short) s);
+ else if (optionall)
+ s2 = printDFormat(s);
+ else
+ s2 = printDFormat((int) s);
+ break;
+ case 'x':
+ case 'X':
+ if (optionalh)
+ s2 = printXFormat((short) s);
+ else if (optionall)
+ s2 = printXFormat(s);
+ else
+ s2 = printXFormat((int) s);
+ break;
+ case 'o':
+ if (optionalh)
+ s2 = printOFormat((short) s);
+ else if (optionall)
+ s2 = printOFormat(s);
+ else
+ s2 = printOFormat((int) s);
+ break;
+ case 'c':
+ case 'C':
+ s2 = printCFormat((char) s);
+ break;
+ default:
+ throw new IllegalArgumentException(
+ "Cannot format a long with a format using a " + conversionCharacter + " conversion character.");
+ }
+ return s2;
+ }
+
+ /**
+ * Format a double argument using this conversion
+ * specification.
+ *
+ * @param s the double to format.
+ * @return the formatted String.
+ * @throws IllegalArgumentException if the
+ * conversion character is c, C, s, S, i, d,
+ * x, X, or o.
+ */
+ String internalsprintf(double s) throws IllegalArgumentException {
+ String s2 = "";
+ switch (conversionCharacter) {
+ case 'f':
+ s2 = printFFormat(s);
+ break;
+ case 'E':
+ case 'e':
+ s2 = printEFormat(s);
+ break;
+ case 'G':
+ case 'g':
+ s2 = printGFormat(s);
+ break;
+ default:
+ throw new IllegalArgumentException(
+ "Cannot " + "format a double with a format using a " + conversionCharacter + " conversion character.");
+ }
+ return s2;
+ }
+
+ /**
+ * Format a String argument using this conversion
+ * specification.
+ *
+ * @param s the String to format.
+ * @return the formatted String.
+ * @throws IllegalArgumentException if the
+ * conversion character is neither s nor S.
+ */
+ String internalsprintf(String s) throws IllegalArgumentException {
+ String s2 = "";
+ if (conversionCharacter == 's' || conversionCharacter == 'S')
+ s2 = printSFormat(s);
+ else
+ throw new IllegalArgumentException(
+ "Cannot " + "format a String with a format using a " + conversionCharacter + " conversion character.");
+ return s2;
+ }
+
+ /**
+ * Format an Object argument using this conversion
+ * specification.
+ *
+ * @param s the Object to format.
+ * @return the formatted String.
+ * @throws IllegalArgumentException if the
+ * conversion character is neither s nor S.
+ */
+ String internalsprintf(Object s) {
+ String s2 = "";
+ if (conversionCharacter == 's' || conversionCharacter == 'S')
+ s2 = printSFormat(s.toString());
+ else
+ throw new IllegalArgumentException(
+ "Cannot format a String with a format using" + " a " + conversionCharacter + " conversion character.");
+ return s2;
+ }
+
+ /**
+ * For f format, the flag character '-', means that
+ * the output should be left justified within the
+ * field. The default is to pad with blanks on the
+ * left. '+' character means that the conversion
+ * will always begin with a sign (+ or -). The
+ * blank flag character means that a non-negative
+ * input will be preceded with a blank. If both
+ * a '+' and a ' ' are specified, the blank flag
+ * is ignored. The '0' flag character implies that
+ * padding to the field width will be done with
+ * zeros instead of blanks.
+ * <p>
+ * The field width is treated as the minimum number
+ * of characters to be printed. The default is to
+ * add no padding. Padding is with blanks by
+ * default.
+ * <p>
+ * The precision, if set, is the number of digits
+ * to appear after the radix character. Padding is
+ * with trailing 0s.
+ */
+ private char[] fFormatDigits(double x) {
+ // int defaultDigits=6;
+ String sx; // sxOut;
+ int i, j, k;
+ int n1In, n2In;
+ int expon = 0;
+ boolean minusSign = false;
+ if (x > 0.0)
+ sx = Double.toString(x);
+ else if (x < 0.0) {
+ sx = Double.toString(-x);
+ minusSign = true;
+ } else {
+ sx = Double.toString(x);
+ if (sx.charAt(0) == '-') {
+ minusSign = true;
+ sx = sx.substring(1);
+ }
+ }
+ int ePos = sx.indexOf('E');
+ int rPos = sx.indexOf('.');
+ if (rPos != -1)
+ n1In = rPos;
+ else if (ePos != -1)
+ n1In = ePos;
+ else
+ n1In = sx.length();
+ if (rPos != -1) {
+ if (ePos != -1)
+ n2In = ePos - rPos - 1;
+ else
+ n2In = sx.length() - rPos - 1;
+ } else
+ n2In = 0;
+ if (ePos != -1) {
+ int ie = ePos + 1;
+ expon = 0;
+ if (sx.charAt(ie) == '-') {
+ for (++ie; ie < sx.length(); ie++)
+ if (sx.charAt(ie) != '0')
+ break;
+ if (ie < sx.length())
+ expon = -Integer.parseInt(sx.substring(ie));
+ } else {
+ if (sx.charAt(ie) == '+')
+ ++ie;
+ for (; ie < sx.length(); ie++)
+ if (sx.charAt(ie) != '0')
+ break;
+ if (ie < sx.length())
+ expon = Integer.parseInt(sx.substring(ie));
+ }
+ }
+ int p;
+ if (precisionSet)
+ p = precision;
+ else
+ p = defaultDigits - 1;
+ char[] ca1 = sx.toCharArray();
+ char[] ca2 = new char[n1In + n2In];
+ char[] ca3, ca4, ca5;
+ for (j = 0; j < n1In; j++)
+ ca2[j] = ca1[j];
+ i = j + 1;
+ for (k = 0; k < n2In; j++, i++, k++)
+ ca2[j] = ca1[i];
+ if (n1In + expon <= 0) {
+ ca3 = new char[-expon + n2In];
+ for (j = 0, k = 0; k < (-n1In - expon); k++, j++)
+ ca3[j] = '0';
+ for (i = 0; i < (n1In + n2In); i++, j++)
+ ca3[j] = ca2[i];
+ } else
+ ca3 = ca2;
+ boolean carry = false;
+ if (p < -expon + n2In) {
+ if (expon < 0)
+ i = p;
+ else
+ i = p + n1In;
+ carry = checkForCarry(ca3, i);
+ if (carry)
+ carry = startSymbolicCarry(ca3, i - 1, 0);
+ }
+ if (n1In + expon <= 0) {
+ ca4 = new char[2 + p];
+ if (!carry)
+ ca4[0] = '0';
+ else
+ ca4[0] = '1';
+ if (alternateForm || !precisionSet || precision != 0) {
+ ca4[1] = '.';
+ for (i = 0, j = 2; i < Math.min(p, ca3.length); i++, j++)
+ ca4[j] = ca3[i];
+ for (; j < ca4.length; j++)
+ ca4[j] = '0';
+ }
+ } else {
+ if (!carry) {
+ if (alternateForm || !precisionSet || precision != 0)
+ ca4 = new char[n1In + expon + p + 1];
+ else
+ ca4 = new char[n1In + expon];
+ j = 0;
+ } else {
+ if (alternateForm || !precisionSet || precision != 0)
+ ca4 = new char[n1In + expon + p + 2];
+ else
+ ca4 = new char[n1In + expon + 1];
+ ca4[0] = '1';
+ j = 1;
+ }
+ for (i = 0; i < Math.min(n1In + expon, ca3.length); i++, j++)
+ ca4[j] = ca3[i];
+ for (; i < n1In + expon; i++, j++)
+ ca4[j] = '0';
+ if (alternateForm || !precisionSet || precision != 0) {
+ ca4[j] = '.';
+ j++;
+ for (k = 0; i < ca3.length && k < p; i++, j++, k++)
+ ca4[j] = ca3[i];
+ for (; j < ca4.length; j++)
+ ca4[j] = '0';
+ }
+ }
+ int nZeros = 0;
+ if (!leftJustify && leadingZeros) {
+ int xThousands = 0;
+ if (thousands) {
+ int xlead = 0;
+ if (ca4[0] == '+' || ca4[0] == '-' || ca4[0] == ' ')
+ xlead = 1;
+ int xdp = xlead;
+ for (; xdp < ca4.length; xdp++)
+ if (ca4[xdp] == '.')
+ break;
+ xThousands = (xdp - xlead) / 3;
+ }
+ if (fieldWidthSet)
+ nZeros = fieldWidth - ca4.length;
+ if ((!minusSign && (leadingSign || leadingSpace)) || minusSign)
+ nZeros--;
+ nZeros -= xThousands;
+ if (nZeros < 0)
+ nZeros = 0;
+ }
+ j = 0;
+ if ((!minusSign && (leadingSign || leadingSpace)) || minusSign) {
+ ca5 = new char[ca4.length + nZeros + 1];
+ j++;
+ } else
+ ca5 = new char[ca4.length + nZeros];
+ if (!minusSign) {
+ if (leadingSign)
+ ca5[0] = '+';
+ if (leadingSpace)
+ ca5[0] = ' ';
+ } else
+ ca5[0] = '-';
+ for (i = 0; i < nZeros; i++, j++)
+ ca5[j] = '0';
+ for (i = 0; i < ca4.length; i++, j++)
+ ca5[j] = ca4[i];
+
+ int lead = 0;
+ if (ca5[0] == '+' || ca5[0] == '-' || ca5[0] == ' ')
+ lead = 1;
+ int dp = lead;
+ for (; dp < ca5.length; dp++)
+ if (ca5[dp] == '.')
+ break;
+ int nThousands = (dp - lead) / 3;
+ // Localize the decimal point.
+ if (dp < ca5.length)
+ ca5[dp] = dfs.getDecimalSeparator();
+ char[] ca6 = ca5;
+ if (thousands && nThousands > 0) {
+ ca6 = new char[ca5.length + nThousands + lead];
+ ca6[0] = ca5[0];
+ for (i = lead, k = lead; i < dp; i++) {
+ if (i > 0 && (dp - i) % 3 == 0) {
+ // ca6[k]=',';
+ ca6[k] = dfs.getGroupingSeparator();
+ ca6[k + 1] = ca5[i];
+ k += 2;
+ } else {
+ ca6[k] = ca5[i];
+ k++;
+ }
+ }
+ for (; i < ca5.length; i++, k++) {
+ ca6[k] = ca5[i];
+ }
+ }
+ return ca6;
+ }
+
+ /**
+ * An intermediate routine on the way to creating
+ * an f format String. The method decides whether
+ * the input double value is an infinity,
+ * not-a-number, or a finite double and formats
+ * each type of input appropriately.
+ *
+ * @param x the double value to be formatted.
+ * @return the converted double value.
+ */
+ private String fFormatString(double x) {
+ // boolean noDigits = false;
+ char[] ca6, ca7;
+ if (Double.isInfinite(x)) {
+ if (x == Double.POSITIVE_INFINITY) {
+ if (leadingSign)
+ ca6 = "+Inf".toCharArray();
+ else if (leadingSpace)
+ ca6 = " Inf".toCharArray();
+ else
+ ca6 = "Inf".toCharArray();
+ } else
+ ca6 = "-Inf".toCharArray();
+ // noDigits = true;
+ } else if (Double.isNaN(x)) {
+ if (leadingSign)
+ ca6 = "+NaN".toCharArray();
+ else if (leadingSpace)
+ ca6 = " NaN".toCharArray();
+ else
+ ca6 = "NaN".toCharArray();
+ // noDigits = true;
+ } else
+ ca6 = fFormatDigits(x);
+ ca7 = applyFloatPadding(ca6);
+ return new String(ca7);
+ }
+
+ /**
+ * For e format, the flag character '-', means that
+ * the output should be left justified within the
+ * field. The default is to pad with blanks on the
+ * left. '+' character means that the conversion
+ * will always begin with a sign (+ or -). The
+ * blank flag character means that a non-negative
+ * input will be preceded with a blank. If both a
+ * '+' and a ' ' are specified, the blank flag is
+ * ignored. The '0' flag character implies that
+ * padding to the field width will be done with
+ * zeros instead of blanks.
+ * <p>
+ * The field width is treated as the minimum number
+ * of characters to be printed. The default is to
+ * add no padding. Padding is with blanks by
+ * default.
+ * <p>
+ * The precision, if set, is the minimum number of
+ * digits to appear after the radix character.
+ * Padding is with trailing 0s.
+ * <p>
+ * The behavior is like printf. One (hopefully the
+ * only) exception is that the minimum number of
+ * exponent digits is 3 instead of 2 for e and E
+ * formats when the optional L is used before the
+ * e, E, g, or G conversion character. The optional
+ * L does not imply conversion to a long long
+ * double.
+ */
+ private char[] eFormatDigits(double x, char eChar) {
+ char[] ca1, ca2, ca3;
+ // int defaultDigits=6;
+ String sx; //, sxOut;
+ int i, j, k, p;
+ // int n1In, n2In;
+ int expon = 0;
+ int ePos, rPos, eSize;
+ boolean minusSign = false;
+ if (x > 0.0)
+ sx = Double.toString(x);
+ else if (x < 0.0) {
+ sx = Double.toString(-x);
+ minusSign = true;
+ } else {
+ sx = Double.toString(x);
+ if (sx.charAt(0) == '-') {
+ minusSign = true;
+ sx = sx.substring(1);
+ }
+ }
+ ePos = sx.indexOf('E');
+ if (ePos == -1)
+ ePos = sx.indexOf('e');
+ rPos = sx.indexOf('.');
+ /* Not exactly sure what the point is here - flibit
+ if (rPos != -1)
+ n1In = rPos;
+ else if (ePos != -1)
+ n1In = ePos;
+ else
+ n1In = sx.length();
+ if (rPos != -1) {
+ if (ePos != -1)
+ n2In = ePos - rPos - 1;
+ else
+ n2In = sx.length() - rPos - 1;
+ } else
+ n2In = 0;
+ */
+ if (ePos != -1) {
+ int ie = ePos + 1;
+ expon = 0;
+ if (sx.charAt(ie) == '-') {
+ for (++ie; ie < sx.length(); ie++)
+ if (sx.charAt(ie) != '0')
+ break;
+ if (ie < sx.length())
+ expon = -Integer.parseInt(sx.substring(ie));
+ } else {
+ if (sx.charAt(ie) == '+')
+ ++ie;
+ for (; ie < sx.length(); ie++)
+ if (sx.charAt(ie) != '0')
+ break;
+ if (ie < sx.length())
+ expon = Integer.parseInt(sx.substring(ie));
+ }
+ }
+ if (rPos != -1)
+ expon += rPos - 1;
+ if (precisionSet)
+ p = precision;
+ else
+ p = defaultDigits - 1;
+ if (rPos != -1 && ePos != -1)
+ ca1 = (sx.substring(0, rPos) + sx.substring(rPos + 1, ePos)).toCharArray();
+ else if (rPos != -1)
+ ca1 = (sx.substring(0, rPos) + sx.substring(rPos + 1)).toCharArray();
+ else if (ePos != -1)
+ ca1 = sx.substring(0, ePos).toCharArray();
+ else
+ ca1 = sx.toCharArray();
+ boolean carry = false;
+ int i0 = 0;
+ if (ca1[0] != '0')
+ i0 = 0;
+ else
+ for (i0 = 0; i0 < ca1.length; i0++)
+ if (ca1[i0] != '0')
+ break;
+ if (i0 + p < ca1.length - 1) {
+ carry = checkForCarry(ca1, i0 + p + 1);
+ if (carry)
+ carry = startSymbolicCarry(ca1, i0 + p, i0);
+ if (carry) {
+ ca2 = new char[i0 + p + 1];
+ ca2[i0] = '1';
+ for (j = 0; j < i0; j++)
+ ca2[j] = '0';
+ for (i = i0, j = i0 + 1; j < p + 1; i++, j++)
+ ca2[j] = ca1[i];
+ expon++;
+ ca1 = ca2;
+ }
+ }
+ if (Math.abs(expon) < 100 && !optionalL)
+ eSize = 4;
+ else
+ eSize = 5;
+ if (alternateForm || !precisionSet || precision != 0)
+ ca2 = new char[2 + p + eSize];
+ else
+ ca2 = new char[1 + eSize];
+ if (ca1[0] != '0') {
+ ca2[0] = ca1[0];
+ j = 1;
+ } else {
+ for (j = 1; j < (ePos == -1 ? ca1.length : ePos); j++)
+ if (ca1[j] != '0')
+ break;
+ if ((ePos != -1 && j < ePos) || (ePos == -1 && j < ca1.length)) {
+ ca2[0] = ca1[j];
+ expon -= j;
+ j++;
+ } else {
+ ca2[0] = '0';
+ j = 2;
+ }
+ }
+ if (alternateForm || !precisionSet || precision != 0) {
+ ca2[1] = '.';
+ i = 2;
+ } else
+ i = 1;
+ for (k = 0; k < p && j < ca1.length; j++, i++, k++)
+ ca2[i] = ca1[j];
+ for (; i < ca2.length - eSize; i++)
+ ca2[i] = '0';
+ ca2[i++] = eChar;
+ if (expon < 0)
+ ca2[i++] = '-';
+ else
+ ca2[i++] = '+';
+ expon = Math.abs(expon);
+ if (expon >= 100) {
+ switch (expon / 100) {
+ case 1:
+ ca2[i] = '1';
+ break;
+ case 2:
+ ca2[i] = '2';
+ break;
+ case 3:
+ ca2[i] = '3';
+ break;
+ case 4:
+ ca2[i] = '4';
+ break;
+ case 5:
+ ca2[i] = '5';
+ break;
+ case 6:
+ ca2[i] = '6';
+ break;
+ case 7:
+ ca2[i] = '7';
+ break;
+ case 8:
+ ca2[i] = '8';
+ break;
+ case 9:
+ ca2[i] = '9';
+ break;
+ }
+ i++;
+ }
+ switch ((expon % 100) / 10) {
+ case 0:
+ ca2[i] = '0';
+ break;
+ case 1:
+ ca2[i] = '1';
+ break;
+ case 2:
+ ca2[i] = '2';
+ break;
+ case 3:
+ ca2[i] = '3';
+ break;
+ case 4:
+ ca2[i] = '4';
+ break;
+ case 5:
+ ca2[i] = '5';
+ break;
+ case 6:
+ ca2[i] = '6';
+ break;
+ case 7:
+ ca2[i] = '7';
+ break;
+ case 8:
+ ca2[i] = '8';
+ break;
+ case 9:
+ ca2[i] = '9';
+ break;
+ }
+ i++;
+ switch (expon % 10) {
+ case 0:
+ ca2[i] = '0';
+ break;
+ case 1:
+ ca2[i] = '1';
+ break;
+ case 2:
+ ca2[i] = '2';
+ break;
+ case 3:
+ ca2[i] = '3';
+ break;
+ case 4:
+ ca2[i] = '4';
+ break;
+ case 5:
+ ca2[i] = '5';
+ break;
+ case 6:
+ ca2[i] = '6';
+ break;
+ case 7:
+ ca2[i] = '7';
+ break;
+ case 8:
+ ca2[i] = '8';
+ break;
+ case 9:
+ ca2[i] = '9';
+ break;
+ }
+ int nZeros = 0;
+ if (!leftJustify && leadingZeros) {
+ int xThousands = 0;
+ if (thousands) {
+ int xlead = 0;
+ if (ca2[0] == '+' || ca2[0] == '-' || ca2[0] == ' ')
+ xlead = 1;
+ int xdp = xlead;
+ for (; xdp < ca2.length; xdp++)
+ if (ca2[xdp] == '.')
+ break;
+ xThousands = (xdp - xlead) / 3;
+ }
+ if (fieldWidthSet)
+ nZeros = fieldWidth - ca2.length;
+ if ((!minusSign && (leadingSign || leadingSpace)) || minusSign)
+ nZeros--;
+ nZeros -= xThousands;
+ if (nZeros < 0)
+ nZeros = 0;
+ }
+ j = 0;
+ if ((!minusSign && (leadingSign || leadingSpace)) || minusSign) {
+ ca3 = new char[ca2.length + nZeros + 1];
+ j++;
+ } else
+ ca3 = new char[ca2.length + nZeros];
+ if (!minusSign) {
+ if (leadingSign)
+ ca3[0] = '+';
+ if (leadingSpace)
+ ca3[0] = ' ';
+ } else
+ ca3[0] = '-';
+ for (k = 0; k < nZeros; j++, k++)
+ ca3[j] = '0';
+ for (i = 0; i < ca2.length && j < ca3.length; i++, j++)
+ ca3[j] = ca2[i];
+
+ int lead = 0;
+ if (ca3[0] == '+' || ca3[0] == '-' || ca3[0] == ' ')
+ lead = 1;
+ int dp = lead;
+ for (; dp < ca3.length; dp++)
+ if (ca3[dp] == '.')
+ break;
+ int nThousands = dp / 3;
+ // Localize the decimal point.
+ if (dp < ca3.length)
+ ca3[dp] = dfs.getDecimalSeparator();
+ char[] ca4 = ca3;
+ if (thousands && nThousands > 0) {
+ ca4 = new char[ca3.length + nThousands + lead];
+ ca4[0] = ca3[0];
+ for (i = lead, k = lead; i < dp; i++) {
+ if (i > 0 && (dp - i) % 3 == 0) {
+ // ca4[k]=',';
+ ca4[k] = dfs.getGroupingSeparator();
+ ca4[k + 1] = ca3[i];
+ k += 2;
+ } else {
+ ca4[k] = ca3[i];
+ k++;
+ }
+ }
+ for (; i < ca3.length; i++, k++)
+ ca4[k] = ca3[i];
+ }
+ return ca4;
+ }
+
+ /**
+ * Check to see if the digits that are going to
+ * be truncated because of the precision should
+ * force a round in the preceding digits.
+ *
+ * @param ca1 the array of digits
+ * @param icarry the index of the first digit that
+ * is to be truncated from the print
+ * @return <code>true</code> if the truncation forces
+ * a round that will change the print
+ */
+ private boolean checkForCarry(char[] ca1, int icarry) {
+ boolean carry = false;
+ if (icarry < ca1.length) {
+ if (ca1[icarry] == '6' || ca1[icarry] == '7' || ca1[icarry] == '8' || ca1[icarry] == '9')
+ carry = true;
+ else if (ca1[icarry] == '5') {
+ int ii = icarry + 1;
+ for (; ii < ca1.length; ii++)
+ if (ca1[ii] != '0')
+ break;
+ carry = ii < ca1.length;
+ if (!carry && icarry > 0) {
+ carry =
+ (ca1[icarry - 1] == '1'
+ || ca1[icarry - 1] == '3'
+ || ca1[icarry - 1] == '5'
+ || ca1[icarry - 1] == '7'
+ || ca1[icarry - 1] == '9');
+ }
+ }
+ }
+ return carry;
+ }
+
+ /**
+ * Start the symbolic carry process. The process
+ * is not quite finished because the symbolic
+ * carry may change the length of the string and
+ * change the exponent (in e format).
+ *
+ * @param cLast index of the last digit changed
+ * by the round
+ * @param cFirst index of the first digit allowed
+ * to be changed by this phase of the round
+ * @return <code>true</code> if the carry forces
+ * a round that will change the print still
+ * more
+ */
+ private boolean startSymbolicCarry(char[] ca, int cLast, int cFirst) {
+ boolean carry = true;
+ for (int i = cLast; carry && i >= cFirst; i--) {
+ carry = false;
+ switch (ca[i]) {
+ case '0':
+ ca[i] = '1';
+ break;
+ case '1':
+ ca[i] = '2';
+ break;
+ case '2':
+ ca[i] = '3';
+ break;
+ case '3':
+ ca[i] = '4';
+ break;
+ case '4':
+ ca[i] = '5';
+ break;
+ case '5':
+ ca[i] = '6';
+ break;
+ case '6':
+ ca[i] = '7';
+ break;
+ case '7':
+ ca[i] = '8';
+ break;
+ case '8':
+ ca[i] = '9';
+ break;
+ case '9':
+ ca[i] = '0';
+ carry = true;
+ break;
+ }
+ }
+ return carry;
+ }
+
+ /**
+ * An intermediate routine on the way to creating
+ * an e format String. The method decides whether
+ * the input double value is an infinity,
+ * not-a-number, or a finite double and formats
+ * each type of input appropriately.
+ *
+ * @param x the double value to be formatted.
+ * @param eChar an 'e' or 'E' to use in the
+ * converted double value.
+ * @return the converted double value.
+ */
+ private String eFormatString(double x, char eChar) {
+ // boolean noDigits = false;
+ char[] ca4, ca5;
+ if (Double.isInfinite(x)) {
+ if (x == Double.POSITIVE_INFINITY) {
+ if (leadingSign)
+ ca4 = "+Inf".toCharArray();
+ else if (leadingSpace)
+ ca4 = " Inf".toCharArray();
+ else
+ ca4 = "Inf".toCharArray();
+ } else
+ ca4 = "-Inf".toCharArray();
+ // noDigits = true;
+ } else if (Double.isNaN(x)) {
+ if (leadingSign)
+ ca4 = "+NaN".toCharArray();
+ else if (leadingSpace)
+ ca4 = " NaN".toCharArray();
+ else
+ ca4 = "NaN".toCharArray();
+ // noDigits = true;
+ } else
+ ca4 = eFormatDigits(x, eChar);
+ ca5 = applyFloatPadding(ca4);
+ return new String(ca5);
+ }
+
+ /**
+ * Apply zero or blank, left or right padding.
+ *
+ * @param ca4 array of characters before padding is
+ * finished
+ * @return a padded array of characters
+ */
+ private char[] applyFloatPadding(char[] ca4) {
+ char[] ca5 = ca4;
+ if (fieldWidthSet) {
+ int i, j, nBlanks;
+ if (leftJustify) {
+ nBlanks = fieldWidth - ca4.length;
+ if (nBlanks > 0) {
+ ca5 = new char[ca4.length + nBlanks];
+ for (i = 0; i < ca4.length; i++)
+ ca5[i] = ca4[i];
+ for (j = 0; j < nBlanks; j++, i++)
+ ca5[i] = ' ';
+ }
+ } else if (!leadingZeros) {
+ nBlanks = fieldWidth - ca4.length;
+ if (nBlanks > 0) {
+ ca5 = new char[ca4.length + nBlanks];
+ for (i = 0; i < nBlanks; i++)
+ ca5[i] = ' ';
+ for (j = 0; j < ca4.length; i++, j++)
+ ca5[i] = ca4[j];
+ }
+ } else if (leadingZeros) {
+ nBlanks = fieldWidth - ca4.length;
+ if (nBlanks > 0) {
+ ca5 = new char[ca4.length + nBlanks];
+ i = 0;
+ j = 0;
+ if (ca4[0] == '-') {
+ ca5[0] = '-';
+ i++;
+ j++;
+ }
+ for (int k = 0; k < nBlanks; i++, k++)
+ ca5[i] = '0';
+ for (; j < ca4.length; i++, j++)
+ ca5[i] = ca4[j];
+ }
+ }
+ }
+ return ca5;
+ }
+
+ /**
+ * Format method for the f conversion character.
+ *
+ * @param x the double to format.
+ * @return the formatted String.
+ */
+ private String printFFormat(double x) {
+ return fFormatString(x);
+ }
+
+ /**
+ * Format method for the e or E conversion
+ * character.
+ *
+ * @param x the double to format.
+ * @return the formatted String.
+ */
+ private String printEFormat(double x) {
+ if (conversionCharacter == 'e')
+ return eFormatString(x, 'e');
+ else
+ return eFormatString(x, 'E');
+ }
+
+ /**
+ * Format method for the g conversion character.
+ * <p>
+ * For g format, the flag character '-', means that
+ * the output should be left justified within the
+ * field. The default is to pad with blanks on the
+ * left. '+' character means that the conversion
+ * will always begin with a sign (+ or -). The
+ * blank flag character means that a non-negative
+ * input will be preceded with a blank. If both a
+ * '+' and a ' ' are specified, the blank flag is
+ * ignored. The '0' flag character implies that
+ * padding to the field width will be done with
+ * zeros instead of blanks.
+ * <p>
+ * The field width is treated as the minimum number
+ * of characters to be printed. The default is to
+ * add no padding. Padding is with blanks by
+ * default.
+ * <p>
+ * The precision, if set, is the minimum number of
+ * digits to appear after the radix character.
+ * Padding is with trailing 0s.
+ *
+ * @param x the double to format.
+ * @return the formatted String.
+ */
+ private String printGFormat(double x) {
+ String sx, sy, sz, ret;
+ int savePrecision = precision;
+ int i;
+ char[] ca4, ca5;
+ // boolean noDigits = false;
+ if (Double.isInfinite(x)) {
+ if (x == Double.POSITIVE_INFINITY) {
+ if (leadingSign)
+ ca4 = "+Inf".toCharArray();
+ else if (leadingSpace)
+ ca4 = " Inf".toCharArray();
+ else
+ ca4 = "Inf".toCharArray();
+ } else
+ ca4 = "-Inf".toCharArray();
+ // noDigits = true;
+ } else if (Double.isNaN(x)) {
+ if (leadingSign)
+ ca4 = "+NaN".toCharArray();
+ else if (leadingSpace)
+ ca4 = " NaN".toCharArray();
+ else
+ ca4 = "NaN".toCharArray();
+ // noDigits = true;
+ } else {
+ if (!precisionSet)
+ precision = defaultDigits;
+ if (precision == 0)
+ precision = 1;
+ int ePos = -1;
+ if (conversionCharacter == 'g') {
+ sx = eFormatString(x, 'e').trim();
+ ePos = sx.indexOf('e');
+ } else {
+ sx = eFormatString(x, 'E').trim();
+ ePos = sx.indexOf('E');
+ }
+ i = ePos + 1;
+ int expon = 0;
+ if (sx.charAt(i) == '-') {
+ for (++i; i < sx.length(); i++)
+ if (sx.charAt(i) != '0')
+ break;
+ if (i < sx.length())
+ expon = -Integer.parseInt(sx.substring(i));
+ } else {
+ if (sx.charAt(i) == '+')
+ ++i;
+ for (; i < sx.length(); i++)
+ if (sx.charAt(i) != '0')
+ break;
+ if (i < sx.length())
+ expon = Integer.parseInt(sx.substring(i));
+ }
+ // Trim trailing zeros.
+ // If the radix character is not followed by
+ // a digit, trim it, too.
+ if (!alternateForm) {
+ if (expon >= -4 && expon < precision)
+ sy = fFormatString(x).trim();
+ else
+ sy = sx.substring(0, ePos);
+ i = sy.length() - 1;
+ for (; i >= 0; i--)
+ if (sy.charAt(i) != '0')
+ break;
+ if (i >= 0 && sy.charAt(i) == '.')
+ i--;
+ if (i == -1)
+ sz = "0";
+ else if (!Character.isDigit(sy.charAt(i)))
+ sz = sy.substring(0, i + 1) + "0";
+ else
+ sz = sy.substring(0, i + 1);
+ if (expon >= -4 && expon < precision)
+ ret = sz;
+ else
+ ret = sz + sx.substring(ePos);
+ } else {
+ if (expon >= -4 && expon < precision)
+ ret = fFormatString(x).trim();
+ else
+ ret = sx;
+ }
+ // leading space was trimmed off during
+ // construction
+ if (leadingSpace)
+ if (x >= 0)
+ ret = " " + ret;
+ ca4 = ret.toCharArray();
+ }
+ // Pad with blanks or zeros.
+ ca5 = applyFloatPadding(ca4);
+ precision = savePrecision;
+ return new String(ca5);
+ }
+
+ /**
+ * Format method for the d conversion specifer and
+ * short argument.
+ * <p>
+ * For d format, the flag character '-', means that
+ * the output should be left justified within the
+ * field. The default is to pad with blanks on the
+ * left. A '+' character means that the conversion
+ * will always begin with a sign (+ or -). The
+ * blank flag character means that a non-negative
+ * input will be preceded with a blank. If both a
+ * '+' and a ' ' are specified, the blank flag is
+ * ignored. The '0' flag character implies that
+ * padding to the field width will be done with
+ * zeros instead of blanks.
+ * <p>
+ * The field width is treated as the minimum number
+ * of characters to be printed. The default is to
+ * add no padding. Padding is with blanks by
+ * default.
+ * <p>
+ * The precision, if set, is the minimum number of
+ * digits to appear. Padding is with leading 0s.
+ *
+ * @param x the short to format.
+ * @return the formatted String.
+ */
+ private String printDFormat(short x) {
+ return printDFormat(Short.toString(x));
+ }
+
+ /**
+ * Format method for the d conversion character and
+ * long argument.
+ * <p>
+ * For d format, the flag character '-', means that
+ * the output should be left justified within the
+ * field. The default is to pad with blanks on the
+ * left. A '+' character means that the conversion
+ * will always begin with a sign (+ or -). The
+ * blank flag character means that a non-negative
+ * input will be preceded with a blank. If both a
+ * '+' and a ' ' are specified, the blank flag is
+ * ignored. The '0' flag character implies that
+ * padding to the field width will be done with
+ * zeros instead of blanks.
+ * <p>
+ * The field width is treated as the minimum number
+ * of characters to be printed. The default is to
+ * add no padding. Padding is with blanks by
+ * default.
+ * <p>
+ * The precision, if set, is the minimum number of
+ * digits to appear. Padding is with leading 0s.
+ *
+ * @param x the long to format.
+ * @return the formatted String.
+ */
+ private String printDFormat(long x) {
+ return printDFormat(Long.toString(x));
+ }
+
+ /**
+ * Format method for the d conversion character and
+ * int argument.
+ * <p>
+ * For d format, the flag character '-', means that
+ * the output should be left justified within the
+ * field. The default is to pad with blanks on the
+ * left. A '+' character means that the conversion
+ * will always begin with a sign (+ or -). The
+ * blank flag character means that a non-negative
+ * input will be preceded with a blank. If both a
+ * '+' and a ' ' are specified, the blank flag is
+ * ignored. The '0' flag character implies that
+ * padding to the field width will be done with
+ * zeros instead of blanks.
+ * <p>
+ * The field width is treated as the minimum number
+ * of characters to be printed. The default is to
+ * add no padding. Padding is with blanks by
+ * default.
+ * <p>
+ * The precision, if set, is the minimum number of
+ * digits to appear. Padding is with leading 0s.
+ *
+ * @param x the int to format.
+ * @return the formatted String.
+ */
+ private String printDFormat(int x) {
+ return printDFormat(Integer.toString(x));
+ }
+
+ /**
+ * Utility method for formatting using the d
+ * conversion character.
+ *
+ * @param sx the String to format, the result of
+ * converting a short, int, or long to a
+ * String.
+ * @return the formatted String.
+ */
+ private String printDFormat(String sx) {
+ int nLeadingZeros = 0;
+ int nBlanks = 0, n = 0;
+ int i = 0, jFirst = 0;
+ boolean neg = sx.charAt(0) == '-';
+ if (sx.equals("0") && precisionSet && precision == 0)
+ sx = "";
+ if (!neg) {
+ if (precisionSet && sx.length() < precision)
+ nLeadingZeros = precision - sx.length();
+ } else {
+ if (precisionSet && (sx.length() - 1) < precision)
+ nLeadingZeros = precision - sx.length() + 1;
+ }
+ if (nLeadingZeros < 0)
+ nLeadingZeros = 0;
+ if (fieldWidthSet) {
+ nBlanks = fieldWidth - nLeadingZeros - sx.length();
+ if (!neg && (leadingSign || leadingSpace))
+ nBlanks--;
+ }
+ if (nBlanks < 0)
+ nBlanks = 0;
+ if (leadingSign)
+ n++;
+ else if (leadingSpace)
+ n++;
+ n += nBlanks;
+ n += nLeadingZeros;
+ n += sx.length();
+ char[] ca = new char[n];
+ if (leftJustify) {
+ if (neg)
+ ca[i++] = '-';
+ else if (leadingSign)
+ ca[i++] = '+';
+ else if (leadingSpace)
+ ca[i++] = ' ';
+ char[] csx = sx.toCharArray();
+ jFirst = neg ? 1 : 0;
+ for (int j = 0; j < nLeadingZeros; i++, j++)
+ ca[i] = '0';
+ for (int j = jFirst; j < csx.length; j++, i++)
+ ca[i] = csx[j];
+ for (int j = 0; j < nBlanks; i++, j++)
+ ca[i] = ' ';
+ } else {
+ if (!leadingZeros) {
+ for (i = 0; i < nBlanks; i++)
+ ca[i] = ' ';
+ if (neg)
+ ca[i++] = '-';
+ else if (leadingSign)
+ ca[i++] = '+';
+ else if (leadingSpace)
+ ca[i++] = ' ';
+ } else {
+ if (neg)
+ ca[i++] = '-';
+ else if (leadingSign)
+ ca[i++] = '+';
+ else if (leadingSpace)
+ ca[i++] = ' ';
+ for (int j = 0; j < nBlanks; j++, i++)
+ ca[i] = '0';
+ }
+ for (int j = 0; j < nLeadingZeros; j++, i++)
+ ca[i] = '0';
+ char[] csx = sx.toCharArray();
+ jFirst = neg ? 1 : 0;
+ for (int j = jFirst; j < csx.length; j++, i++)
+ ca[i] = csx[j];
+ }
+ return new String(ca);
+ }
+
+ /**
+ * Format method for the x conversion character and
+ * short argument.
+ * <p>
+ * For x format, the flag character '-', means that
+ * the output should be left justified within the
+ * field. The default is to pad with blanks on the
+ * left. The '#' flag character means to lead with
+ * '0x'.
+ * <p>
+ * The field width is treated as the minimum number
+ * of characters to be printed. The default is to
+ * add no padding. Padding is with blanks by
+ * default.
+ * <p>
+ * The precision, if set, is the minimum number of
+ * digits to appear. Padding is with leading 0s.
+ *
+ * @param x the short to format.
+ * @return the formatted String.
+ */
+ private String printXFormat(short x) {
+ String sx = null;
+ if (x == Short.MIN_VALUE)
+ sx = "8000";
+ else if (x < 0) {
+ String t;
+ if (x == Short.MIN_VALUE)
+ t = "0";
+ else {
+ t = Integer.toString((~(-x - 1)) ^ Short.MIN_VALUE, 16);
+ if (t.charAt(0) == 'F' || t.charAt(0) == 'f')
+ t = t.substring(16, 32);
+ }
+ switch (t.length()) {
+ case 1:
+ sx = "800" + t;
+ break;
+ case 2:
+ sx = "80" + t;
+ break;
+ case 3:
+ sx = "8" + t;
+ break;
+ case 4:
+ switch (t.charAt(0)) {
+ case '1':
+ sx = "9" + t.substring(1, 4);
+ break;
+ case '2':
+ sx = "a" + t.substring(1, 4);
+ break;
+ case '3':
+ sx = "b" + t.substring(1, 4);
+ break;
+ case '4':
+ sx = "c" + t.substring(1, 4);
+ break;
+ case '5':
+ sx = "d" + t.substring(1, 4);
+ break;
+ case '6':
+ sx = "e" + t.substring(1, 4);
+ break;
+ case '7':
+ sx = "f" + t.substring(1, 4);
+ break;
+ }
+ break;
+ }
+ } else
+ sx = Integer.toString((int) x, 16);
+ return printXFormat(sx);
+ }
+
+ /**
+ * Format method for the x conversion character and
+ * long argument.
+ * <p>
+ * For x format, the flag character '-', means that
+ * the output should be left justified within the
+ * field. The default is to pad with blanks on the
+ * left. The '#' flag character means to lead with
+ * '0x'.
+ * <p>
+ * The field width is treated as the minimum number
+ * of characters to be printed. The default is to
+ * add no padding. Padding is with blanks by
+ * default.
+ * <p>
+ * The precision, if set, is the minimum number of
+ * digits to appear. Padding is with leading 0s.
+ *
+ * @param x the long to format.
+ * @return the formatted String.
+ */
+ private String printXFormat(long x) {
+ String sx = null;
+ if (x == Long.MIN_VALUE)
+ sx = "8000000000000000";
+ else if (x < 0) {
+ String t = Long.toString((~(-x - 1)) ^ Long.MIN_VALUE, 16);
+ switch (t.length()) {
+ case 1:
+ sx = "800000000000000" + t;
+ break;
+ case 2:
+ sx = "80000000000000" + t;
+ break;
+ case 3:
+ sx = "8000000000000" + t;
+ break;
+ case 4:
+ sx = "800000000000" + t;
+ break;
+ case 5:
+ sx = "80000000000" + t;
+ break;
+ case 6:
+ sx = "8000000000" + t;
+ break;
+ case 7:
+ sx = "800000000" + t;
+ break;
+ case 8:
+ sx = "80000000" + t;
+ break;
+ case 9:
+ sx = "8000000" + t;
+ break;
+ case 10:
+ sx = "800000" + t;
+ break;
+ case 11:
+ sx = "80000" + t;
+ break;
+ case 12:
+ sx = "8000" + t;
+ break;
+ case 13:
+ sx = "800" + t;
+ break;
+ case 14:
+ sx = "80" + t;
+ break;
+ case 15:
+ sx = "8" + t;
+ break;
+ case 16:
+ switch (t.charAt(0)) {
+ case '1':
+ sx = "9" + t.substring(1, 16);
+ break;
+ case '2':
+ sx = "a" + t.substring(1, 16);
+ break;
+ case '3':
+ sx = "b" + t.substring(1, 16);
+ break;
+ case '4':
+ sx = "c" + t.substring(1, 16);
+ break;
+ case '5':
+ sx = "d" + t.substring(1, 16);
+ break;
+ case '6':
+ sx = "e" + t.substring(1, 16);
+ break;
+ case '7':
+ sx = "f" + t.substring(1, 16);
+ break;
+ }
+ break;
+ }
+ } else
+ sx = Long.toString(x, 16);
+ return printXFormat(sx);
+ }
+
+ /**
+ * Format method for the x conversion character and
+ * int argument.
+ * <p>
+ * For x format, the flag character '-', means that
+ * the output should be left justified within the
+ * field. The default is to pad with blanks on the
+ * left. The '#' flag character means to lead with
+ * '0x'.
+ * <p>
+ * The field width is treated as the minimum number
+ * of characters to be printed. The default is to
+ * add no padding. Padding is with blanks by
+ * default.
+ * <p>
+ * The precision, if set, is the minimum number of
+ * digits to appear. Padding is with leading 0s.
+ *
+ * @param x the int to format.
+ * @return the formatted String.
+ */
+ private String printXFormat(int x) {
+ String sx = null;
+ if (x == Integer.MIN_VALUE)
+ sx = "80000000";
+ else if (x < 0) {
+ String t = Integer.toString((~(-x - 1)) ^ Integer.MIN_VALUE, 16);
+ switch (t.length()) {
+ case 1:
+ sx = "8000000" + t;
+ break;
+ case 2:
+ sx = "800000" + t;
+ break;
+ case 3:
+ sx = "80000" + t;
+ break;
+ case 4:
+ sx = "8000" + t;
+ break;
+ case 5:
+ sx = "800" + t;
+ break;
+ case 6:
+ sx = "80" + t;
+ break;
+ case 7:
+ sx = "8" + t;
+ break;
+ case 8:
+ switch (t.charAt(0)) {
+ case '1':
+ sx = "9" + t.substring(1, 8);
+ break;
+ case '2':
+ sx = "a" + t.substring(1, 8);
+ break;
+ case '3':
+ sx = "b" + t.substring(1, 8);
+ break;
+ case '4':
+ sx = "c" + t.substring(1, 8);
+ break;
+ case '5':
+ sx = "d" + t.substring(1, 8);
+ break;
+ case '6':
+ sx = "e" + t.substring(1, 8);
+ break;
+ case '7':
+ sx = "f" + t.substring(1, 8);
+ break;
+ }
+ break;
+ }
+ } else
+ sx = Integer.toString(x, 16);
+ return printXFormat(sx);
+ }
+
+ /**
+ * Utility method for formatting using the x
+ * conversion character.
+ *
+ * @param sx the String to format, the result of
+ * converting a short, int, or long to a
+ * String.
+ * @return the formatted String.
+ */
+ private String printXFormat(String sx) {
+ int nLeadingZeros = 0;
+ int nBlanks = 0;
+ if (sx.equals("0") && precisionSet && precision == 0)
+ sx = "";
+ if (precisionSet)
+ nLeadingZeros = precision - sx.length();
+ if (nLeadingZeros < 0)
+ nLeadingZeros = 0;
+ if (fieldWidthSet) {
+ nBlanks = fieldWidth - nLeadingZeros - sx.length();
+ if (alternateForm)
+ nBlanks = nBlanks - 2;
+ }
+ if (nBlanks < 0)
+ nBlanks = 0;
+ int n = 0;
+ if (alternateForm)
+ n += 2;
+ n += nLeadingZeros;
+ n += sx.length();
+ n += nBlanks;
+ char[] ca = new char[n];
+ int i = 0;
+ if (leftJustify) {
+ if (alternateForm) {
+ ca[i++] = '0';
+ ca[i++] = 'x';
+ }
+ for (int j = 0; j < nLeadingZeros; j++, i++)
+ ca[i] = '0';
+ char[] csx = sx.toCharArray();
+ for (int j = 0; j < csx.length; j++, i++)
+ ca[i] = csx[j];
+ for (int j = 0; j < nBlanks; j++, i++)
+ ca[i] = ' ';
+ } else {
+ if (!leadingZeros)
+ for (int j = 0; j < nBlanks; j++, i++)
+ ca[i] = ' ';
+ if (alternateForm) {
+ ca[i++] = '0';
+ ca[i++] = 'x';
+ }
+ if (leadingZeros)
+ for (int j = 0; j < nBlanks; j++, i++)
+ ca[i] = '0';
+ for (int j = 0; j < nLeadingZeros; j++, i++)
+ ca[i] = '0';
+ char[] csx = sx.toCharArray();
+ for (int j = 0; j < csx.length; j++, i++)
+ ca[i] = csx[j];
+ }
+ String caReturn = new String(ca);
+ if (conversionCharacter == 'X')
+ caReturn = caReturn.toUpperCase();
+ return caReturn;
+ }
+
+ /**
+ * Format method for the o conversion character and
+ * short argument.
+ * <p>
+ * For o format, the flag character '-', means that
+ * the output should be left justified within the
+ * field. The default is to pad with blanks on the
+ * left. The '#' flag character means that the
+ * output begins with a leading 0 and the precision
+ * is increased by 1.
+ * <p>
+ * The field width is treated as the minimum number
+ * of characters to be printed. The default is to
+ * add no padding. Padding is with blanks by
+ * default.
+ * <p>
+ * The precision, if set, is the minimum number of
+ * digits to appear. Padding is with leading 0s.
+ *
+ * @param x the short to format.
+ * @return the formatted String.
+ */
+ private String printOFormat(short x) {
+ String sx = null;
+ if (x == Short.MIN_VALUE)
+ sx = "100000";
+ else if (x < 0) {
+ String t = Integer.toString((~(-x - 1)) ^ Short.MIN_VALUE, 8);
+ switch (t.length()) {
+ case 1:
+ sx = "10000" + t;
+ break;
+ case 2:
+ sx = "1000" + t;
+ break;
+ case 3:
+ sx = "100" + t;
+ break;
+ case 4:
+ sx = "10" + t;
+ break;
+ case 5:
+ sx = "1" + t;
+ break;
+ }
+ } else
+ sx = Integer.toString((int) x, 8);
+ return printOFormat(sx);
+ }
+
+ /**
+ * Format method for the o conversion character and
+ * long argument.
+ * <p>
+ * For o format, the flag character '-', means that
+ * the output should be left justified within the
+ * field. The default is to pad with blanks on the
+ * left. The '#' flag character means that the
+ * output begins with a leading 0 and the precision
+ * is increased by 1.
+ * <p>
+ * The field width is treated as the minimum number
+ * of characters to be printed. The default is to
+ * add no padding. Padding is with blanks by
+ * default.
+ * <p>
+ * The precision, if set, is the minimum number of
+ * digits to appear. Padding is with leading 0s.
+ *
+ * @param x the long to format.
+ * @return the formatted String.
+ */
+ private String printOFormat(long x) {
+ String sx = null;
+ if (x == Long.MIN_VALUE)
+ sx = "1000000000000000000000";
+ else if (x < 0) {
+ String t = Long.toString((~(-x - 1)) ^ Long.MIN_VALUE, 8);
+ switch (t.length()) {
+ case 1:
+ sx = "100000000000000000000" + t;
+ break;
+ case 2:
+ sx = "10000000000000000000" + t;
+ break;
+ case 3:
+ sx = "1000000000000000000" + t;
+ break;
+ case 4:
+ sx = "100000000000000000" + t;
+ break;
+ case 5:
+ sx = "10000000000000000" + t;
+ break;
+ case 6:
+ sx = "1000000000000000" + t;
+ break;
+ case 7:
+ sx = "100000000000000" + t;
+ break;
+ case 8:
+ sx = "10000000000000" + t;
+ break;
+ case 9:
+ sx = "1000000000000" + t;
+ break;
+ case 10:
+ sx = "100000000000" + t;
+ break;
+ case 11:
+ sx = "10000000000" + t;
+ break;
+ case 12:
+ sx = "1000000000" + t;
+ break;
+ case 13:
+ sx = "100000000" + t;
+ break;
+ case 14:
+ sx = "10000000" + t;
+ break;
+ case 15:
+ sx = "1000000" + t;
+ break;
+ case 16:
+ sx = "100000" + t;
+ break;
+ case 17:
+ sx = "10000" + t;
+ break;
+ case 18:
+ sx = "1000" + t;
+ break;
+ case 19:
+ sx = "100" + t;
+ break;
+ case 20:
+ sx = "10" + t;
+ break;
+ case 21:
+ sx = "1" + t;
+ break;
+ }
+ } else
+ sx = Long.toString(x, 8);
+ return printOFormat(sx);
+ }
+
+ /**
+ * Format method for the o conversion character and
+ * int argument.
+ * <p>
+ * For o format, the flag character '-', means that
+ * the output should be left justified within the
+ * field. The default is to pad with blanks on the
+ * left. The '#' flag character means that the
+ * output begins with a leading 0 and the precision
+ * is increased by 1.
+ * <p>
+ * The field width is treated as the minimum number
+ * of characters to be printed. The default is to
+ * add no padding. Padding is with blanks by
+ * default.
+ * <p>
+ * The precision, if set, is the minimum number of
+ * digits to appear. Padding is with leading 0s.
+ *
+ * @param x the int to format.
+ * @return the formatted String.
+ */
+ private String printOFormat(int x) {
+ String sx = null;
+ if (x == Integer.MIN_VALUE)
+ sx = "20000000000";
+ else if (x < 0) {
+ String t = Integer.toString((~(-x - 1)) ^ Integer.MIN_VALUE, 8);
+ switch (t.length()) {
+ case 1:
+ sx = "2000000000" + t;
+ break;
+ case 2:
+ sx = "200000000" + t;
+ break;
+ case 3:
+ sx = "20000000" + t;
+ break;
+ case 4:
+ sx = "2000000" + t;
+ break;
+ case 5:
+ sx = "200000" + t;
+ break;
+ case 6:
+ sx = "20000" + t;
+ break;
+ case 7:
+ sx = "2000" + t;
+ break;
+ case 8:
+ sx = "200" + t;
+ break;
+ case 9:
+ sx = "20" + t;
+ break;
+ case 10:
+ sx = "2" + t;
+ break;
+ case 11:
+ sx = "3" + t.substring(1);
+ break;
+ }
+ } else
+ sx = Integer.toString(x, 8);
+ return printOFormat(sx);
+ }
+
+ /**
+ * Utility method for formatting using the o
+ * conversion character.
+ *
+ * @param sx the String to format, the result of
+ * converting a short, int, or long to a
+ * String.
+ * @return the formatted String.
+ */
+ private String printOFormat(String sx) {
+ int nLeadingZeros = 0;
+ int nBlanks = 0;
+ if (sx.equals("0") && precisionSet && precision == 0)
+ sx = "";
+ if (precisionSet)
+ nLeadingZeros = precision - sx.length();
+ if (alternateForm)
+ nLeadingZeros++;
+ if (nLeadingZeros < 0)
+ nLeadingZeros = 0;
+ if (fieldWidthSet)
+ nBlanks = fieldWidth - nLeadingZeros - sx.length();
+ if (nBlanks < 0)
+ nBlanks = 0;
+ int n = nLeadingZeros + sx.length() + nBlanks;
+ char[] ca = new char[n];
+ int i;
+ if (leftJustify) {
+ for (i = 0; i < nLeadingZeros; i++)
+ ca[i] = '0';
+ char[] csx = sx.toCharArray();
+ for (int j = 0; j < csx.length; j++, i++)
+ ca[i] = csx[j];
+ for (int j = 0; j < nBlanks; j++, i++)
+ ca[i] = ' ';
+ } else {
+ if (leadingZeros)
+ for (i = 0; i < nBlanks; i++)
+ ca[i] = '0';
+ else
+ for (i = 0; i < nBlanks; i++)
+ ca[i] = ' ';
+ for (int j = 0; j < nLeadingZeros; j++, i++)
+ ca[i] = '0';
+ char[] csx = sx.toCharArray();
+ for (int j = 0; j < csx.length; j++, i++)
+ ca[i] = csx[j];
+ }
+ return new String(ca);
+ }
+
+ /**
+ * Format method for the c conversion character and
+ * char argument.
+ * <p>
+ * The only flag character that affects c format is
+ * the '-', meaning that the output should be left
+ * justified within the field. The default is to
+ * pad with blanks on the left.
+ * <p>
+ * The field width is treated as the minimum number
+ * of characters to be printed. Padding is with
+ * blanks by default. The default width is 1.
+ * <p>
+ * The precision, if set, is ignored.
+ *
+ * @param x the char to format.
+ * @return the formatted String.
+ */
+ private String printCFormat(char x) {
+ int nPrint = 1;
+ int width = fieldWidth;
+ if (!fieldWidthSet)
+ width = nPrint;
+ char[] ca = new char[width];
+ int i = 0;
+ if (leftJustify) {
+ ca[0] = x;
+ for (i = 1; i <= width - nPrint; i++)
+ ca[i] = ' ';
+ } else {
+ for (i = 0; i < width - nPrint; i++)
+ ca[i] = ' ';
+ ca[i] = x;
+ }
+ return new String(ca);
+ }
+
+ /**
+ * Format method for the s conversion character and
+ * String argument.
+ * <p>
+ * The only flag character that affects s format is
+ * the '-', meaning that the output should be left
+ * justified within the field. The default is to
+ * pad with blanks on the left.
+ * <p>
+ * The field width is treated as the minimum number
+ * of characters to be printed. The default is the
+ * smaller of the number of characters in the the
+ * input and the precision. Padding is with blanks
+ * by default.
+ * <p>
+ * The precision, if set, specifies the maximum
+ * number of characters to be printed from the
+ * string. A null digit string is treated
+ * as a 0. The default is not to set a maximum
+ * number of characters to be printed.
+ *
+ * @param x the String to format.
+ * @return the formatted String.
+ */
+ private String printSFormat(String x) {
+ int nPrint = x.length();
+ int width = fieldWidth;
+ if (precisionSet && nPrint > precision)
+ nPrint = precision;
+ if (!fieldWidthSet)
+ width = nPrint;
+ int n = 0;
+ if (width > nPrint)
+ n += width - nPrint;
+ if (nPrint >= x.length())
+ n += x.length();
+ else
+ n += nPrint;
+ char[] ca = new char[n];
+ int i = 0;
+ if (leftJustify) {
+ if (nPrint >= x.length()) {
+ char[] csx = x.toCharArray();
+ for (i = 0; i < x.length(); i++)
+ ca[i] = csx[i];
+ } else {
+ char[] csx = x.substring(0, nPrint).toCharArray();
+ for (i = 0; i < nPrint; i++)
+ ca[i] = csx[i];
+ }
+ for (int j = 0; j < width - nPrint; j++, i++)
+ ca[i] = ' ';
+ } else {
+ for (i = 0; i < width - nPrint; i++)
+ ca[i] = ' ';
+ if (nPrint >= x.length()) {
+ char[] csx = x.toCharArray();
+ for (int j = 0; j < x.length(); i++, j++)
+ ca[i] = csx[j];
+ } else {
+ char[] csx = x.substring(0, nPrint).toCharArray();
+ for (int j = 0; j < nPrint; i++, j++)
+ ca[i] = csx[j];
+ }
+ }
+ return new String(ca);
+ }
+
+ /**
+ * Check for a conversion character. If it is
+ * there, store it.
+ *
+ * @param x the String to format.
+ * @return <code>true</code> if the conversion
+ * character is there, and
+ * <code>false</code> otherwise.
+ */
+ private boolean setConversionCharacter() {
+ /* idfgGoxXeEcs */
+ boolean ret = false;
+ conversionCharacter = '\0';
+ if (pos < fmt.length()) {
+ char c = fmt.charAt(pos);
+ if (c == 'i'
+ || c == 'd'
+ || c == 'f'
+ || c == 'g'
+ || c == 'G'
+ || c == 'o'
+ || c == 'x'
+ || c == 'X'
+ || c == 'e'
+ || c == 'E'
+ || c == 'c'
+ || c == 's'
+ || c == '%') {
+ conversionCharacter = c;
+ pos++;
+ ret = true;
+ }
+ }
+ return ret;
+ }
+
+ /**
+ * Check for an h, l, or L in a format. An L is
+ * used to control the minimum number of digits
+ * in an exponent when using floating point
+ * formats. An l or h is used to control
+ * conversion of the input to a long or short,
+ * respectively, before formatting. If any of
+ * these is present, store them.
+ */
+ private void setOptionalHL() {
+ optionalh = false;
+ optionall = false;
+ optionalL = false;
+ if (pos < fmt.length()) {
+ char c = fmt.charAt(pos);
+ if (c == 'h') {
+ optionalh = true;
+ pos++;
+ } else if (c == 'l') {
+ optionall = true;
+ pos++;
+ } else if (c == 'L') {
+ optionalL = true;
+ pos++;
+ }
+ }
+ }
+
+ /**
+ * Set the precision.
+ */
+ private void setPrecision() {
+ int firstPos = pos;
+ precisionSet = false;
+ if (pos < fmt.length() && fmt.charAt(pos) == '.') {
+ pos++;
+ if ((pos < fmt.length()) && (fmt.charAt(pos) == '*')) {
+ pos++;
+ if (!setPrecisionArgPosition()) {
+ variablePrecision = true;
+ precisionSet = true;
+ }
+ } else {
+ while (pos < fmt.length()) {
+ char c = fmt.charAt(pos);
+ if (Character.isDigit(c))
+ pos++;
+ else
+ break;
+ }
+ if (pos > firstPos + 1) {
+ String sz = fmt.substring(firstPos + 1, pos);
+ precision = Integer.parseInt(sz);
+ precisionSet = true;
+ }
+ }
+ }
+ }
+
+ /**
+ * Set the field width.
+ */
+ private void setFieldWidth() {
+ int firstPos = pos;
+ fieldWidth = 0;
+ fieldWidthSet = false;
+ if ((pos < fmt.length()) && (fmt.charAt(pos) == '*')) {
+ pos++;
+ if (!setFieldWidthArgPosition()) {
+ variableFieldWidth = true;
+ fieldWidthSet = true;
+ }
+ } else {
+ while (pos < fmt.length()) {
+ char c = fmt.charAt(pos);
+ if (Character.isDigit(c))
+ pos++;
+ else
+ break;
+ }
+ if (firstPos < pos && firstPos < fmt.length()) {
+ String sz = fmt.substring(firstPos, pos);
+ fieldWidth = Integer.parseInt(sz);
+ fieldWidthSet = true;
+ }
+ }
+ }
+
+ /**
+ * Store the digits <code>n</code> in %n$ forms.
+ */
+ private void setArgPosition() {
+ int xPos;
+ for (xPos = pos; xPos < fmt.length(); xPos++) {
+ if (!Character.isDigit(fmt.charAt(xPos)))
+ break;
+ }
+ if (xPos > pos && xPos < fmt.length()) {
+ if (fmt.charAt(xPos) == '$') {
+ positionalSpecification = true;
+ argumentPosition = Integer.parseInt(fmt.substring(pos, xPos));
+ pos = xPos + 1;
+ }
+ }
+ }
+
+ /**
+ * Store the digits <code>n</code> in *n$ forms.
+ */
+ private boolean setFieldWidthArgPosition() {
+ boolean ret = false;
+ int xPos;
+ for (xPos = pos; xPos < fmt.length(); xPos++) {
+ if (!Character.isDigit(fmt.charAt(xPos)))
+ break;
+ }
+ if (xPos > pos && xPos < fmt.length()) {
+ if (fmt.charAt(xPos) == '$') {
+ positionalFieldWidth = true;
+ argumentPositionForFieldWidth = Integer.parseInt(fmt.substring(pos, xPos));
+ pos = xPos + 1;
+ ret = true;
+ }
+ }
+ return ret;
+ }
+
+ /**
+ * Store the digits <code>n</code> in *n$ forms.
+ */
+ private boolean setPrecisionArgPosition() {
+ boolean ret = false;
+ int xPos;
+ for (xPos = pos; xPos < fmt.length(); xPos++) {
+ if (!Character.isDigit(fmt.charAt(xPos)))
+ break;
+ }
+ if (xPos > pos && xPos < fmt.length()) {
+ if (fmt.charAt(xPos) == '$') {
+ positionalPrecision = true;
+ argumentPositionForPrecision = Integer.parseInt(fmt.substring(pos, xPos));
+ pos = xPos + 1;
+ ret = true;
+ }
+ }
+ return ret;
+ }
+
+ boolean isPositionalSpecification() {
+ return positionalSpecification;
+ }
+
+ int getArgumentPosition() {
+ return argumentPosition;
+ }
+
+ boolean isPositionalFieldWidth() {
+ return positionalFieldWidth;
+ }
+
+ int getArgumentPositionForFieldWidth() {
+ return argumentPositionForFieldWidth;
+ }
+
+ boolean isPositionalPrecision() {
+ return positionalPrecision;
+ }
+
+ int getArgumentPositionForPrecision() {
+ return argumentPositionForPrecision;
+ }
+
+ /**
+ * Set flag characters, one of '-+#0 or a space.
+ */
+ private void setFlagCharacters() {
+ /* '-+ #0 */
+ thousands = false;
+ leftJustify = false;
+ leadingSign = false;
+ leadingSpace = false;
+ alternateForm = false;
+ leadingZeros = false;
+ for (; pos < fmt.length(); pos++) {
+ char c = fmt.charAt(pos);
+ if (c == '\'')
+ thousands = true;
+ else if (c == '-') {
+ leftJustify = true;
+ leadingZeros = false;
+ } else if (c == '+') {
+ leadingSign = true;
+ leadingSpace = false;
+ } else if (c == ' ') {
+ if (!leadingSign)
+ leadingSpace = true;
+ } else if (c == '#')
+ alternateForm = true;
+ else if (c == '0') {
+ if (!leftJustify)
+ leadingZeros = true;
+ } else
+ break;
+ }
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.util;
+
+import lwjake2.game.EDict;
+import lwjake2.game.GameBase;
+import lwjake2.game.SuperAdapter;
+import lwjake2.game.gitem_t;
+import lwjake2.qcommon.Com;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+
+/**
+ * RandomAccessFile, but handles readString/WriteString specially and offers
+ * other helper functions
+ */
+public class QuakeFile extends RandomAccessFile {
+
+ /**
+ * Standard Constructor.
+ */
+ public QuakeFile(String filename, String mode) throws FileNotFoundException {
+ super(filename, mode);
+ }
+
+ /**
+ * Writes a Vector to a RandomAccessFile.
+ */
+ public void writeVector(float v[]) throws IOException {
+ for (int n = 0; n < 3; n++)
+ writeFloat(v[n]);
+ }
+
+ /**
+ * Writes a Vector to a RandomAccessFile.
+ */
+ public float[] readVector() throws IOException {
+ float res[] = {0, 0, 0};
+ for (int n = 0; n < 3; n++)
+ res[n] = readFloat();
+
+ return res;
+ }
+
+ /**
+ * Reads a length specified string from a file.
+ */
+ public String readString() throws IOException {
+ int len = readInt();
+
+ if (len == -1)
+ return null;
+
+ if (len == 0)
+ return "";
+
+ byte bb[] = new byte[len];
+
+ super.read(bb, 0, len);
+
+ return new String(bb, 0, len);
+ }
+
+ /**
+ * Writes a length specified string to a file.
+ */
+ public void writeString(String s) throws IOException {
+ if (s == null) {
+ writeInt(-1);
+ return;
+ }
+
+ writeInt(s.length());
+ if (s.length() != 0)
+ writeBytes(s);
+ }
+
+ /**
+ * Writes the edict reference.
+ */
+ public void writeEdictRef(EDict ent) throws IOException {
+ if (ent == null)
+ writeInt(-1);
+ else {
+ writeInt(ent.s.number);
+ }
+ }
+
+ /**
+ * Reads an edict index from a file and returns the edict.
+ */
+
+ public EDict readEdictRef() throws IOException {
+ int i = readInt();
+
+ // handle -1
+ if (i < 0)
+ return null;
+
+ if (i > GameBase.g_edicts.length) {
+ Com.DPrintf("jake2: illegal edict num:" + i + "\n");
+ return null;
+ }
+
+ // valid edict.
+ return GameBase.g_edicts[i];
+ }
+
+ /**
+ * Writes the Adapter-ID to the file.
+ */
+ public void writeAdapter(SuperAdapter a) throws IOException {
+ writeInt(3988);
+ if (a == null)
+ writeString(null);
+ else {
+ String str = a.getID();
+ if (str == null) {
+ Com.DPrintf("writeAdapter: invalid Adapter id for " + a + "\n");
+ }
+ writeString(str);
+ }
+ }
+
+ /**
+ * Reads the adapter id and returns the adapter.
+ */
+ public SuperAdapter readAdapter() throws IOException {
+ if (readInt() != 3988)
+ Com.DPrintf("wrong read position: readadapter 3988 \n");
+
+ String id = readString();
+
+ if (id == null) {
+ // null adapter. :-)
+ return null;
+ }
+
+ return SuperAdapter.getFromID(id);
+ }
+
+ /**
+ * Writes an item reference.
+ */
+ public void writeItem(gitem_t item) throws IOException {
+ if (item == null)
+ writeInt(-1);
+ else
+ writeInt(item.index);
+ }
+
+
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (C) 1997-2001 Id Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.util;
+
+import java.util.Vector;
+
+/**
+ * Vargs is a helper class to encapsulate printf arguments.
+ *
+ * @author cwei
+ */
+public class Vargs {
+
+ // initial capacity
+ static final int SIZE = 5;
+
+ Vector<Object> v;
+
+ public Vargs() {
+ this(SIZE);
+ }
+
+ public Vargs(int initialSize) {
+ if (v != null)
+ v.clear(); // clear previous list for GC
+ v = new Vector<>(initialSize);
+ }
+
+ public Vargs add(boolean value) {
+ v.add(value);
+ return this;
+ }
+
+ public Vargs add(byte value) {
+ v.add(value);
+ return this;
+ }
+
+ public Vargs add(char value) {
+ v.add(value);
+ return this;
+ }
+
+ public Vargs add(short value) {
+ v.add(value);
+ return this;
+ }
+
+ public Vargs add(int value) {
+ v.add(value);
+ return this;
+ }
+
+ public Vargs add(long value) {
+ v.add(value);
+ return this;
+ }
+
+ public Vargs add(float value) {
+ v.add(value);
+ return this;
+ }
+
+ public Vargs add(double value) {
+ v.add(value);
+ return this;
+ }
+
+ public Vargs add(String value) {
+ v.add(value);
+ return this;
+ }
+
+ public Vargs add(Object value) {
+ v.add(value);
+ return this;
+ }
+
+ public Vargs clear() {
+ v.clear();
+ return this;
+ }
+
+ /* This apparently isn't even used? - flibit
+ public Vector toVector() {
+ // Vector tmp = v;
+ // v = null;
+ // return tmp;
+ return (Vector) v.clone();
+ }
+ */
+
+ public Object[] toArray() {
+ return v.toArray();
+ }
+
+ public int size() {
+ return v.size();
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 2003 Carsten "cwei" Weisse
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package lwjake2.util;
+
+/**
+ * Vec3Cache contains float[3] for temporary usage.
+ * The usage can reduce the garbage at runtime.
+ *
+ * @author cwei
+ */
+public final class Vec3Cache {
+
+ //private static Stack cache = new Stack();
+ private static final float[][] cache = new float[64][3];
+ private static int index = 0;
+
+ public static float[] get() {
+ //max = Math.max(index, max);
+ return cache[index++];
+ }
+
+ public static void release() {
+ index--;
+ }
+
+ public static void release(int count) {
+ index -= count;
+ }
+
+ public static void debug() {
+ int max = 0;
+ System.err.println("Vec3Cache: max. " + (max + 1) + " vectors used.");
+ }
+}
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<application xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" generated-by="intellij"
+ xmlns="http://www.zeroturnaround.com"
+ xsi:schemaLocation="http://www.zeroturnaround.com http://update.zeroturnaround.com/jrebel/rebel-2_1.xsd">
+
+ <classpath>
+ <dir name="${rebel.project.path}/target/classes">
+ </dir>
+ </classpath>
+
+</application>
--- /dev/null
+#!/bin/bash
+cd "${0%/*}"; if [ "$1" != "T" ]; then gnome-terminal -e "'$0' T"; exit; fi;
+
+cd ..
+
+cola
+git push
+
+echo ""
+echo "Press ENTER to close this window."
+read
--- /dev/null
+#!/bin/bash
+
+if [ "$1" != "T" ]; then gnome-terminal -e "'$0' T"; exit; fi
+
+#
+# This is a helper bash script that starts current Java project in debug mode
+# with JRebel attached. It also opens its own terminal window, so you can run
+# this script by simply clicking on it in file navigator.
+#
+#
+# Script assumes:
+#
+# + GNU OS
+# + Gnome workspace
+# + JRebel is installed in /opt/jrebel
+#
+
+
+cd "${0%/*}"
+cd ..
+
+
+while true; do
+
+ # clear screen
+ printf "\033c"
+
+ # enable debugging
+ export DEBUG_OPTIONS="-Xdebug -Xrunjdwp:transport=dt_socket,address=127.0.0.1:8000,server=y,suspend=n"
+
+ # enable JRebel
+ export REBEL_BASE="$HOME/.jrebel"
+ export JREBEL_OPTS="-agentpath:/opt/jrebel/libjrebel64.so -Drebel.project.path=`pwd`"
+
+ # enable LWJGL native libraries
+ export LWJGL_OPTS="-Djava.library.path=target/natives"
+
+ # define Maven options
+ export MAVEN_OPTS="-Xmx4000m $DEBUG_OPTIONS $JREBEL_OPTS $LWJGL_OPTS"
+
+ mvn compile exec:java -Dexec.mainClass="lwjake2.LWJake2"
+
+ echo "press ENTER to reload application"
+ read
+
+done
--- /dev/null
+#!/bin/bash
+
+#
+# This is a helper bash script that starts IntelliJ with the current project.
+# Script is written is such a way that you can simply click on it in file
+# navigator to run it.
+#
+#
+# Script assumes:
+#
+# + GNU operating system
+# + IntelliJ is installed and commandline launcher "idea" is enabled.
+#
+
+cd "${0%/*}"
+cd ..
+
+setsid idea . &>/dev/null
--- /dev/null
+#!/bin/bash
+cd "${0%/*}"; if [ "$1" != "T" ]; then gnome-terminal -e "'$0' T"; exit; fi;
+
+cd ..
+
+rsync -avz --delete -e 'ssh -p 10006' doc/ n0@www3.svjatoslav.eu:/mnt/big/projects/quake2/
+
+echo ""
+echo "Press ENTER to close this window."
+read