0

приводим исходники в нормальное состояние

This commit is contained in:
2018-12-18 11:09:07 +03:00
parent 82b38a8f5a
commit 4eed67ff69
235 changed files with 1 additions and 14 deletions

View File

@@ -0,0 +1,71 @@
/*
* 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 com.flibitijibibo.flibitEFX;
import org.lwjgl.openal.EFX10;
/**
* @author Ethan "flibitijibibo" Lee
*/
public abstract class EFXEffect {
private int slotIndex;
private 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 passedProperty The EFX10 effect property
* @param passedValue The EFX10 effect value
*/
protected void addEffecti(int passedProperty, int passedValue) {
EFX10.alEffecti(effectIndex, passedProperty, passedValue);
}
}

View File

@@ -0,0 +1,93 @@
/*
* 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 com.flibitijibibo.flibitEFX;
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);
}
}

View File

@@ -0,0 +1,35 @@
/*
* 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 com.flibitijibibo.flibitEFX;
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.
}

View File

@@ -0,0 +1,70 @@
/*
* 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 com.flibitijibibo.flibitEFX;
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(EFX10.AL_RING_MODULATOR_WAVEFORM, waveform);
}
}

View File

@@ -0,0 +1,59 @@
/*
* 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 com.flibitijibibo.flibitEFX;
import org.lwjgl.openal.EFX10;
/**
* @author Ethan "flibitijibibo" Lee
*/
public abstract class EFXFilter {
private 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);
}
}

View File

@@ -0,0 +1,47 @@
/*
* 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 com.flibitijibibo.flibitEFX;
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);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,389 @@
/*
* 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.centity_t;
import lwjake2.client.client_state_t;
import lwjake2.client.client_static_t;
import lwjake2.client.console_t;
import lwjake2.client.refexport_t;
import lwjake2.client.viddef_t;
import lwjake2.client.vrect_t;
import lwjake2.game.cmdalias_t;
import lwjake2.game.cvar_t;
import lwjake2.game.entity_state_t;
import lwjake2.qcommon.netadr_t;
import lwjake2.qcommon.sizebuf_t;
import lwjake2.render.DummyRenderer;
import lwjake2.render.model_t;
import java.io.FileWriter;
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.
*
* As consequence you dont 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";
/*
* 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 cvar_t cl_add_blend;
public static cvar_t cl_add_entities;
public static cvar_t cl_add_lights;
public static cvar_t cl_add_particles;
public static cvar_t cl_anglespeedkey;
public static cvar_t cl_autoskins;
public static cvar_t cl_footsteps;
public static cvar_t cl_forwardspeed;
public static cvar_t cl_gun;
public static cvar_t cl_maxfps;
public static cvar_t cl_noskins;
public static cvar_t cl_pitchspeed;
public static cvar_t cl_predict;
public static cvar_t cl_run;
public static cvar_t cl_sidespeed;
public static cvar_t cl_stereo;
public static cvar_t cl_stereo_separation;
public static cvar_t cl_timedemo = new cvar_t();
public static cvar_t cl_timeout;
public static cvar_t cl_upspeed;
public static cvar_t cl_yawspeed;
public static cvar_t dedicated;
public static cvar_t developer;
public static cvar_t fixedtime;
public static cvar_t freelook;
public static cvar_t host_speeds;
public static cvar_t log_stats;
public static cvar_t logfile_active;
public static cvar_t lookspring;
public static cvar_t lookstrafe;
public static cvar_t nostdout;
public static cvar_t sensitivity;
public static cvar_t showtrace;
public static cvar_t timescale;
public static cvar_t in_mouse;
public static cvar_t in_joystick;
public static sizebuf_t net_message = new sizebuf_t();
/*
=============================================================================
COMMAND BUFFER
=============================================================================
*/
public static sizebuf_t cmd_text = new sizebuf_t();
public static byte defer_text_buf[] = new byte[8192];
public static byte cmd_text_buf[] = new byte[8192];
public static cmdalias_t cmd_alias;
//=============================================================================
public static byte[] net_message_buffer = new byte[MAX_MSGLEN];
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 FileWriter log_stats_file = null;
public static cvar_t m_pitch;
public static cvar_t m_yaw;
public static cvar_t m_forward;
public static cvar_t m_side;
public static cvar_t cl_lightlevel;
//
// userinfo
//
public static cvar_t info_password;
public static cvar_t info_spectator;
public static cvar_t name;
public static cvar_t skin;
public static cvar_t rate;
public static cvar_t fov;
public static cvar_t msg;
public static cvar_t hand;
public static cvar_t gender;
public static cvar_t gender_auto;
public static cvar_t cl_vwep;
public static client_static_t cls = new client_static_t();
public static client_state_t cl = new client_state_t();
public static centity_t cl_entities[] = new centity_t[Defines.MAX_EDICTS];
static {
for (int i = 0; i < cl_entities.length; i++) {
cl_entities[i] = new centity_t();
}
}
public static entity_state_t cl_parse_entities[] = new entity_state_t[Defines.MAX_PARSE_ENTITIES];
static {
for (int i = 0; i < cl_parse_entities.length; i++)
{
cl_parse_entities[i] = new entity_state_t(null);
}
}
public static cvar_t rcon_client_password;
public static cvar_t rcon_address;
public static cvar_t cl_shownet;
public static cvar_t cl_showmiss;
public static cvar_t cl_showclamp;
public static cvar_t cl_paused;
// 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 boolean userinfo_modified = false;
public static cvar_t cvar_vars;
public static final console_t con = new console_t();
public static cvar_t con_notifytime;
public static viddef_t viddef = new viddef_t();
// Renderer interface used by VID, SCR, ...
public static refexport_t re = new DummyRenderer();
public static String[] keybindings = new String[256];
public static boolean[] keydown = new boolean[256];
public static boolean chat_team = false;
public static String chat_buffer = "";
public static byte[][] key_lines = new byte[32][];
public static int key_linepos;
static {
for (int i = 0; i < key_lines.length; i++)
key_lines[i] = new byte[Defines.MAXCMDLINE];
};
public static int edit_line;
public static cvar_t crosshair;
public static vrect_t scr_vrect = new vrect_t();
public static int sys_frame_time;
public static int chat_bufferlen = 0;
public static int gun_frame;
public static model_t gun_model;
public static netadr_t net_from = new netadr_t();
// logfile
public static RandomAccessFile logfile = null;
public static float vec3_origin[] = { 0.0f, 0.0f, 0.0f };
public static cvar_t m_filter;
public static int vidref_val = VIDREF_GL;
public static Random rnd = new Random();
}

View File

@@ -0,0 +1,95 @@
/*
* 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 lombok.NonNull;
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(@NonNull 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;
}
}
}
// 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;
// in C the first arg is the filename
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);
}
Qcommon.Init(c_args);
Globals.nostdout = Cvar.Get("nostdout", "0", 0);
int oldtime = Timer.Milliseconds();
int newtime;
int time;
while (true) {
// find time spending rendering last frame
newtime = Timer.Milliseconds();
time = newtime - oldtime;
if (time > 0)
Qcommon.Frame(time);
oldtime = newtime;
}
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,622 @@
/*
* 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.cvar_t;
import lwjake2.game.usercmd_t;
import lwjake2.qcommon.Com;
import lwjake2.qcommon.Cvar;
import lwjake2.qcommon.MSG;
import lwjake2.qcommon.Netchan;
import lwjake2.qcommon.SZ;
import lwjake2.qcommon.sizebuf_t;
import lwjake2.sys.IN;
import lwjake2.util.Lib;
import lwjake2.util.Math3D;
/**
* CL_input
*/
public class CL_input {
static long frame_msec;
static long old_sys_frame_time;
static cvar_t cl_nodelta;
/*
* ===============================================================================
*
* 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
*
* ===============================================================================
*/
static kbutton_t in_klook = new kbutton_t();
static kbutton_t in_left = new kbutton_t();
static kbutton_t in_right = new kbutton_t();
static kbutton_t in_forward = new kbutton_t();
static kbutton_t in_back = new kbutton_t();
static kbutton_t in_lookup = new kbutton_t();
static kbutton_t in_lookdown = new kbutton_t();
static kbutton_t in_moveleft = new kbutton_t();
static kbutton_t in_moveright = new kbutton_t();
public static kbutton_t in_strafe = new kbutton_t();
static kbutton_t in_speed = new kbutton_t();
static kbutton_t in_use = new kbutton_t();
static kbutton_t in_attack = new kbutton_t();
static kbutton_t in_up = new kbutton_t();
static kbutton_t in_down = new kbutton_t();
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.cls.frametime * Globals.cl_anglespeedkey.value;
else
speed = Globals.cls.frametime;
if ((in_strafe.state & 1) == 0) {
Globals.cl.viewangles[Defines.YAW] -= speed * Globals.cl_yawspeed.value * KeyState(in_right);
Globals.cl.viewangles[Defines.YAW] += speed * Globals.cl_yawspeed.value * KeyState(in_left);
}
if ((in_klook.state & 1) != 0) {
Globals.cl.viewangles[Defines.PITCH] -= speed * Globals.cl_pitchspeed.value * KeyState(in_forward);
Globals.cl.viewangles[Defines.PITCH] += speed * Globals.cl_pitchspeed.value * KeyState(in_back);
}
up = KeyState(in_lookup);
down = KeyState(in_lookdown);
Globals.cl.viewangles[Defines.PITCH] -= speed * Globals.cl_pitchspeed.value * up;
Globals.cl.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.cl.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.cl.frame.playerstate.pmove.delta_angles[Defines.PITCH]);
if (pitch > 180)
pitch -= 360;
if (Globals.cl.viewangles[Defines.PITCH] + pitch < -360)
Globals.cl.viewangles[Defines.PITCH] += 360; // wrapped
if (Globals.cl.viewangles[Defines.PITCH] + pitch > 360)
Globals.cl.viewangles[Defines.PITCH] -= 360; // wrapped
if (Globals.cl.viewangles[Defines.PITCH] + pitch > 89)
Globals.cl.viewangles[Defines.PITCH] = 89 - pitch;
if (Globals.cl.viewangles[Defines.PITCH] + pitch < -89)
Globals.cl.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.cls.key_dest == Defines.key_game)
cmd.buttons |= Defines.BUTTON_ANY;
// send milliseconds of time to apply the move
ms = (int) (Globals.cls.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.cl.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
IN.Move(cmd);
FinishMove(cmd);
old_sys_frame_time = Globals.sys_frame_time;
//return cmd;
}
/*
* ============ CL_InitInput ============
*/
static void InitInput() {
Cmd.AddCommand("centerview", IN::CenterView);
Cmd.AddCommand("+moveup", CL_input::IN_UpDown);
Cmd.AddCommand("-moveup", CL_input::IN_UpUp);
Cmd.AddCommand("+movedown", CL_input::IN_DownDown);
Cmd.AddCommand("-movedown", CL_input::IN_DownUp);
Cmd.AddCommand("+left", CL_input::IN_LeftDown);
Cmd.AddCommand("-left", CL_input::IN_LeftUp);
Cmd.AddCommand("+right", CL_input::IN_RightDown);
Cmd.AddCommand("-right", CL_input::IN_RightUp);
Cmd.AddCommand("+forward", CL_input::IN_ForwardDown);
Cmd.AddCommand("-forward", CL_input::IN_ForwardUp);
Cmd.AddCommand("+back", CL_input::IN_BackDown);
Cmd.AddCommand("-back", CL_input::IN_BackUp);
Cmd.AddCommand("+lookup", CL_input::IN_LookupDown);
Cmd.AddCommand("-lookup", CL_input::IN_LookupUp);
Cmd.AddCommand("+lookdown", CL_input::IN_LookdownDown);
Cmd.AddCommand("-lookdown", CL_input::IN_LookdownUp);
Cmd.AddCommand("+strafe", CL_input::IN_StrafeDown);
Cmd.AddCommand("-strafe", CL_input::IN_StrafeUp);
Cmd.AddCommand("+moveleft", CL_input::IN_MoveleftDown);
Cmd.AddCommand("-moveleft", CL_input::IN_MoveleftUp);
Cmd.AddCommand("+moveright", CL_input::IN_MoverightDown);
Cmd.AddCommand("-moveright", CL_input::IN_MoverightUp);
Cmd.AddCommand("+speed", CL_input::IN_SpeedDown);
Cmd.AddCommand("-speed", CL_input::IN_SpeedUp);
Cmd.AddCommand("+attack", CL_input::IN_AttackDown);
Cmd.AddCommand("-attack", CL_input::IN_AttackUp);
Cmd.AddCommand("+use", CL_input::IN_UseDown);
Cmd.AddCommand("-use", CL_input::IN_UseUp);
Cmd.AddCommand("impulse", CL_input::IN_Impulse);
Cmd.AddCommand("+klook", CL_input::IN_KLookDown);
Cmd.AddCommand("-klook", CL_input::IN_KLookUp);
cl_nodelta = Cvar.Get("cl_nodelta", "0", 0);
}
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();
/*
* ================= 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.cls.netchan.outgoing_sequence & (Defines.CMD_BACKUP - 1);
cmd = Globals.cl.cmds[i];
Globals.cl.cmd_time[i] = (int) Globals.cls.realtime; // for netgraph
// ping calculation
// fill the cmd
CreateCmd(cmd);
Globals.cl.cmd.set(cmd);
if (Globals.cls.state == Defines.ca_disconnected || Globals.cls.state == Defines.ca_connecting)
return;
if (Globals.cls.state == Defines.ca_connected) {
if (Globals.cls.netchan.message.cursize != 0 || Globals.curtime - Globals.cls.netchan.last_sent > 1000)
Netchan.Transmit(Globals.cls.netchan, 0, new byte[0]);
return;
}
// send a userinfo update if needed
if (Globals.userinfo_modified) {
CL.FixUpGender();
Globals.userinfo_modified = false;
MSG.WriteByte(Globals.cls.netchan.message, Defines.clc_userinfo);
MSG.WriteString(Globals.cls.netchan.message, Cvar.Userinfo());
}
SZ.Init(buf, data, data.length);
if (cmd.buttons != 0 && Globals.cl.cinematictime > 0 && !Globals.cl.attractloop
&& Globals.cls.realtime - Globals.cl.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.cl.frame.valid || Globals.cls.demowaiting)
MSG.WriteLong(buf, -1); // no compression
else
MSG.WriteLong(buf, Globals.cl.frame.serverframe);
// send this and the previous cmds in the message, so
// if the last packet was dropped, it can be recovered
i = (Globals.cls.netchan.outgoing_sequence - 2) & (Defines.CMD_BACKUP - 1);
cmd = Globals.cl.cmds[i];
//memset (nullcmd, 0, sizeof(nullcmd));
nullcmd.clear();
MSG.WriteDeltaUsercmd(buf, nullcmd, cmd);
oldcmd = cmd;
i = (Globals.cls.netchan.outgoing_sequence - 1) & (Defines.CMD_BACKUP - 1);
cmd = Globals.cl.cmds[i];
MSG.WriteDeltaUsercmd(buf, oldcmd, cmd);
oldcmd = cmd;
i = (Globals.cls.netchan.outgoing_sequence) & (Defines.CMD_BACKUP - 1);
cmd = Globals.cl.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.cls.netchan.outgoing_sequence);
//
// deliver the message
//
Netchan.Transmit(Globals.cls.netchan, buf.cursize, buf.data);
}
}

View File

@@ -0,0 +1,137 @@
/*
* 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_ParseInventory ================
*/
static void ParseInventory() {
int i;
for (i = 0; i < Defines.MAX_ITEMS; i++)
Globals.cl.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);
}
/*
* ================ CL_DrawInventory ================
*/
static final int DISPLAY_ITEMS = 17;
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.cl.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.cl.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.cl.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.cl.inventory[item]).add(
Globals.cl.configstrings[Defines.CS_ITEMS + item]));
if (item != selected)
string = getHighBitString(string);
else // draw a blinky cursor by the selected item
{
if (((int) (Globals.cls.realtime * 10) & 1) != 0)
Globals.re.DrawChar(x - 8, y, 15);
}
Inv_DrawString(x, y, string);
y += 8;
}
}
}

View File

@@ -0,0 +1,829 @@
/*
* 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 {
static void Flashlight(int ent, float[] pos) {
CL_fx.cdlight_t dl;
dl = CL_fx.AllocDlight(ent);
Math3D.VectorCopy(pos, dl.origin);
dl.radius = 400;
dl.minlight = 250;
dl.die = Globals.cl.time + 100;
dl.color[0] = 1;
dl.color[1] = 1;
dl.color[2] = 1;
}
/*
* ====== CL_ColorFlash - flash of light ======
*/
static void ColorFlash(float[] pos, int ent, int intensity, float r,
float g, float b) {
CL_fx.cdlight_t dl;
if ((Globals.vidref_val == Defines.VIDREF_SOFT)
&& ((r < 0) || (g < 0) || (b < 0))) {
intensity = -intensity;
r = -r;
g = -g;
b = -b;
}
dl = CL_fx.AllocDlight(ent);
Math3D.VectorCopy(pos, dl.origin);
dl.radius = intensity;
dl.minlight = 250;
dl.die = Globals.cl.time + 100;
dl.color[0] = r;
dl.color[1] = g;
dl.color[2] = b;
}
// 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};
/*
* ====== CL_DebugTrail ======
*/
static void DebugTrail(float[] start, float[] end) {
float len;
// int j;
cparticle_t p;
float dec;
// int i;
// float d, c, s;
// float[] dir;
Math3D.VectorCopy(start, move);
Math3D.VectorSubtract(end, start, 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(start, 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.cl.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(float[] start, float[] end, int color) {
float len;
int j;
cparticle_t p;
Math3D.VectorCopy(start, move);
Math3D.VectorSubtract(end, start, 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.cl.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(float[] start, float[] end, int dist) {
float len;
int i, j;
cparticle_t p;
float dec;
Math3D.VectorCopy(start, move);
Math3D.VectorSubtract(end, start, 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.cl.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);
}
}
// stack variable
// move, vec, right, up
private static final float[] dir = {0, 0, 0};
private static final float[] end = {0, 0, 0};
static void Heatbeam(float[] start, float[] forward) {
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(start, 4096, forward, end);
Math3D.VectorCopy(start, move);
Math3D.VectorSubtract(end, start, vec);
len = Math3D.VectorNormalize(vec);
// FIXME - pmm - these might end up using old values?
// MakeNormalVectors (vec, right, up);
Math3D.VectorCopy(Globals.cl.v_right, right);
Math3D.VectorCopy(Globals.cl.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.cl.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.cl.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);
}
}
// stack variable
private static final float[] r = {0, 0, 0};
private static final float[] u = {0, 0, 0};
/*
* =============== CL_ParticleSteamEffect
*
* Puffs with velocity along direction, with some randomness thrown in
* ===============
*/
static void ParticleSteamEffect(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.MakeNormalVectors(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.cl.time;
p.color = color + (Lib.rand() & 7);
for (j = 0; j < 3; j++) {
p.org[j] = org[j] + magnitude * 0.1f * Lib.crand();
// p.vel[j] = dir[j]*magnitude;
}
Math3D.VectorScale(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.cl.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;
}
// stack variable
// move, vec, right, up
private static final float[] forward = {0, 0, 0};
private static final float[] angle_dir = {0, 0, 0};
/*
* =============== CL_TrackerTrail ===============
*/
static void TrackerTrail(float[] start, float[] end, int particleColor) {
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.cl.time;
p.alpha = 1.0f;
p.alphavel = -2.0f;
p.color = particleColor;
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.cl.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.cl.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);
}
}
private static int[] wb_colortable = { 2 * 8, 13 * 8, 21 * 8, 18 * 8 };
// 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.cl.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.cl.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);
}
}
private static int[] nb_colortable = { 110, 112, 114, 116 };
// 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.cl.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.cl.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);
}
}
private static int[] ws_colortable = { 2 * 8, 13 * 8, 21 * 8, 18 * 8 };
// stack variable
// dir
static void WidowSplash(float[] org) {
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.cl.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(org, 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 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 (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.cl.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);
}
}
/*
* =============== CL_ColorExplosionParticles ===============
*/
static void ColorExplosionParticles(float[] org, int color, int run) {
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.cl.time;
p.color = color + (Lib.rand() % run);
for (j = 0; j < 3; j++) {
p.org[j] = org[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(float[] org, float[] dir, int color,
int count, int magnitude) {
int i, j;
cparticle_t p;
float d;
Math3D.MakeNormalVectors(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.cl.time;
p.color = color + (Lib.rand() & 7);
for (j = 0; j < 3; j++) {
p.org[j] = org[j] + magnitude * 0.1f * Lib.crand();
// p.vel[j] = dir[j]*magnitude;
}
Math3D.VectorScale(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] = 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(float[] org, float[] dir, 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.cl.time;
p.color = color + (Lib.rand() & 7);
d = Lib.rand() & 15;
for (j = 0; j < 3; j++) {
p.org[j] = org[j] + ((Lib.rand() & 7) - 4) + d * dir[j];
p.vel[j] = 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.cl.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);
}
}
}

View File

@@ -0,0 +1,804 @@
/*
* 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 lombok.extern.slf4j.Slf4j;
import lwjake2.Defines;
import lwjake2.Globals;
import lwjake2.game.Cmd;
import lwjake2.game.entity_state_t;
import lwjake2.qcommon.CM;
import lwjake2.qcommon.Cbuf;
import lwjake2.qcommon.Com;
import lwjake2.qcommon.Cvar;
import lwjake2.qcommon.FileSystem;
import lwjake2.qcommon.BaseQ2FileSystem;
import lwjake2.qcommon.MSG;
import lwjake2.qcommon.SZ;
import lwjake2.render.model_t;
import lwjake2.sound.S;
import lwjake2.sys.Sys;
import lwjake2.util.Lib;
import java.io.IOException;
import java.io.RandomAccessFile;
/**
* CL_parse
*/
@Slf4j
public class CL_parse {
private static final FileSystem fileSystem = BaseQ2FileSystem.getInstance();
//// cl_parse.c -- parse a message received from the server
public static 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" };
// =============================================================================
public static String DownloadFileName(String fn) {
return fileSystem.getGamedir() + "/" + 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.indexOf("..") != -1) {
Com.Printf("Refusing to download a path with ..\n");
return true;
}
if (fileSystem.fileLength(filename) > 0) { // it exists, no need to download
return true;
}
Globals.cls.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.cls.downloadtempname = Com
.StripExtension(Globals.cls.downloadname);
Globals.cls.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.cls.downloadtempname);
fp = Lib.fopen(name, "r+b");
if (fp != null) {
// it exists
long len = 0;
try {
len = fp.length();
}
catch (IOException e) {
}
Globals.cls.download = fp;
// give the server an offset to start the download
Com.Printf("Resuming " + Globals.cls.downloadname + "\n");
MSG.WriteByte(Globals.cls.netchan.message, Defines.clc_stringcmd);
MSG.WriteString(Globals.cls.netchan.message, "download "
+ Globals.cls.downloadname + " " + len);
} else {
Com.Printf("Downloading " + Globals.cls.downloadname + "\n");
MSG.WriteByte(Globals.cls.netchan.message, Defines.clc_stringcmd);
MSG.WriteString(Globals.cls.netchan.message, "download "
+ Globals.cls.downloadname);
}
Globals.cls.downloadnumber++;
return false;
}
/*
* =============== CL_Download_f
*
* Request a download from the server ===============
*/
public static Runnable Download_f = () -> {
String filename;
if (Cmd.Argc() != 2) {
Com.Printf("Usage: download <filename>\n");
return;
}
filename = Cmd.Argv(1);
if (filename.indexOf("..") != -1) {
Com.Printf("Refusing to download a path with ..\n");
return;
}
if (fileSystem.loadFile(filename) != null) { // it exists, no need to
// download
Com.Printf("File already exists.\n");
return;
}
Globals.cls.downloadname = filename;
Com.Printf("Downloading " + Globals.cls.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.cls.downloadtempname = Com
.StripExtension(Globals.cls.downloadname);
Globals.cls.downloadtempname += ".tmp";
MSG.WriteByte(Globals.cls.netchan.message, Defines.clc_stringcmd);
MSG.WriteString(Globals.cls.netchan.message, "download "
+ Globals.cls.downloadname);
Globals.cls.downloadnumber++;
};
/*
* ====================== CL_RegisterSounds ======================
*/
public static void RegisterSounds() {
S.BeginRegistration();
CL_tent.RegisterTEntSounds();
for (int i = 1; i < Defines.MAX_SOUNDS; i++) {
if (Globals.cl.configstrings[Defines.CS_SOUNDS + i] == null
|| Globals.cl.configstrings[Defines.CS_SOUNDS + i]
.equals("")
|| Globals.cl.configstrings[Defines.CS_SOUNDS + i]
.equals("\0"))
break;
Globals.cl.sound_precache[i] = S
.RegisterSound(Globals.cl.configstrings[Defines.CS_SOUNDS
+ i]);
Sys.SendKeyEvents(); // pump message loop
}
S.EndRegistration();
}
/*
* ===================== 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.cls.download != null) {
// if here, we tried to resume a file but the server said no
try {
Globals.cls.download.close();
} catch (IOException e) {
}
Globals.cls.download = null;
}
CL.RequestNextDownload();
return;
}
// open the file if not opened yet
if (Globals.cls.download == null) {
String name = DownloadFileName(Globals.cls.downloadtempname).toLowerCase();
fileSystem.createPath(name);
Globals.cls.download = Lib.fopen(name, "rw");
if (Globals.cls.download == null) {
Globals.net_message.readcount += size;
Com.Printf("Failed to open " + Globals.cls.downloadtempname
+ "\n");
CL.RequestNextDownload();
return;
}
}
//fwrite(net_message.data[net_message.readcount], 1, size,
// cls.download);
try {
Globals.cls.download.write(Globals.net_message.data,
Globals.net_message.readcount, size);
} catch (Exception e) {
}
Globals.net_message.readcount += size;
if (percent != 100) {
// request next block
// change display routines by zoid
Globals.cls.downloadpercent = percent;
MSG.WriteByte(Globals.cls.netchan.message, Defines.clc_stringcmd);
SZ.Print(Globals.cls.netchan.message, "nextdl");
} else {
String oldn, newn;
//char oldn[MAX_OSPATH];
//char newn[MAX_OSPATH];
// Com.Printf ("100%%\n");
try {
Globals.cls.download.close();
} catch (IOException e) {
}
// rename the temp file to it's final name
oldn = DownloadFileName(Globals.cls.downloadtempname);
newn = DownloadFileName(Globals.cls.downloadname);
int r = Lib.rename(oldn, newn);
if (r != 0)
Com.Printf("failed to rename.\n");
Globals.cls.download = null;
Globals.cls.downloadpercent = 0;
// get another file if needed
CL.RequestNextDownload();
}
}
/*
* =====================================================================
*
* SERVER CONNECTING MESSAGES
*
* =====================================================================
*/
/*
* ================== CL_ParseServerData ==================
*/
//checked once, was ok.
public static void ParseServerData() {
String str;
int i;
Com.DPrintf("ParseServerData():Serverdata packet received.\n");
//
// wipe the client_state_t struct
//
CL.ClearState();
Globals.cls.state = Defines.ca_connected;
// parse protocol version number
i = MSG.ReadLong(Globals.net_message);
Globals.cls.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.cl.servercount = MSG.ReadLong(Globals.net_message);
Globals.cl.attractloop = MSG.ReadByte(Globals.net_message) != 0;
// game directory
str = MSG.ReadString(Globals.net_message);
Globals.cl.gamedir = str;
Com.dprintln("gamedir=" + str);
// set gamedir
if (str.length() > 0
&& (fileSystem.getGamedirVar().string == null
|| fileSystem.getGamedirVar().string.length() == 0 || fileSystem.getGamedirVar().string
.equals(str))
|| (str.length() == 0 && (fileSystem.getGamedirVar().string != null || fileSystem.getGamedirVar().string
.length() == 0)))
Cvar.Set("game", str);
// parse player entity number
Globals.cl.playernum = MSG.ReadShort(Globals.net_message);
Com.dprintln("numplayers=" + Globals.cl.playernum);
// get the full level name
str = MSG.ReadString(Globals.net_message);
Com.dprintln("levelname=" + str);
if (Globals.cl.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.cl.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_t ci, String s) {
int i;
int t;
//char model_name[MAX_QPATH];
//char skin_name[MAX_QPATH];
//char model_filename[MAX_QPATH];
//char skin_filename[MAX_QPATH];
//char weapon_filename[MAX_QPATH];
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.weaponmodel = new model_t[Defines.MAX_CLIENTWEAPONMODELS];
ci.weaponmodel[0] = Globals.re.RegisterModel(weapon_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);
}
// weapon file
for (i = 0; i < CL_view.num_cl_weaponmodels; i++) {
weapon_filename = "players/" + model_name + "/"
+ CL_view.cl_weaponmodels[i];
ci.weaponmodel[i] = Globals.re.RegisterModel(weapon_filename);
if (null == ci.weaponmodel[i] && model_name.equals("cyborg")) {
// try male
weapon_filename = "players/male/"
+ CL_view.cl_weaponmodels[i];
ci.weaponmodel[i] = Globals.re
.RegisterModel(weapon_filename);
}
if (0 == Globals.cl_vwep.value)
break; // only one when vwep is off
}
// 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.weaponmodel[0] == null) {
ci.skin = null;
ci.icon = null;
ci.model = null;
ci.weaponmodel[0] = null;
return;
}
}
/*
* ================ CL_ParseClientinfo
*
* Load the skin, icon, and model for a client ================
*/
public static void ParseClientinfo(int player) {
String s;
clientinfo_t ci;
s = Globals.cl.configstrings[player + Defines.CS_PLAYERSKINS];
ci = Globals.cl.clientinfo[player];
LoadClientinfo(ci, s);
}
/*
* ================ 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.cl.configstrings[i];
Globals.cl.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.cl.refresh_prepped) {
Globals.cl.model_draw[i - Defines.CS_MODELS] = Globals.re
.RegisterModel(Globals.cl.configstrings[i]);
if (Globals.cl.configstrings[i].startsWith("*"))
Globals.cl.model_clip[i - Defines.CS_MODELS] = CM
.InlineModel(Globals.cl.configstrings[i]);
else
Globals.cl.model_clip[i - Defines.CS_MODELS] = null;
}
} else if (i >= Defines.CS_SOUNDS
&& i < Defines.CS_SOUNDS + Defines.MAX_MODELS) {
if (Globals.cl.refresh_prepped)
Globals.cl.sound_precache[i - Defines.CS_SOUNDS] = S
.RegisterSound(Globals.cl.configstrings[i]);
} else if (i >= Defines.CS_IMAGES
&& i < Defines.CS_IMAGES + Defines.MAX_MODELS) {
if (Globals.cl.refresh_prepped)
Globals.cl.image_precache[i - Defines.CS_IMAGES] = Globals.re
.RegisterPic(Globals.cl.configstrings[i]);
} else if (i >= Defines.CS_PLAYERSKINS
&& i < Defines.CS_PLAYERSKINS + Defines.MAX_CLIENTS) {
if (Globals.cl.refresh_prepped && !olds.equals(s))
ParseClientinfo(i - Defines.CS_PLAYERSKINS);
}
}
/*
* =====================================================================
*
* ACTION MESSAGES
*
* =====================================================================
*/
private static final float[] pos_v = { 0, 0, 0 };
/*
* ================== 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.cl.sound_precache[sound_num])
return;
S.StartSound(pos, ent, channel, Globals.cl.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.cls.download != null) {
//ZOID, close download
try {
Globals.cls.download.close();
} catch (IOException e) {
}
Globals.cls.download = null;
}
Globals.cls.state = Defines.ca_connecting;
Globals.cls.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;
}
//TODO потом уброать
String msg = MSG.ReadString(Globals.net_message);
while (msg.startsWith("\n")) { msg = msg.substring(1); }
while (msg.endsWith("\n")) { msg = msg.substring(0, msg.lastIndexOf("\n")); }
while (msg.endsWith("\r")) { msg = msg.substring(0, msg.lastIndexOf("\r")); }
msg = msg.trim();
if (!msg.isEmpty()) {
log.info(msg);
}
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");
Cbuf.AddText(s);
break;
case Defines.svc_serverdata:
Cbuf.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_muzzleflash:
CL_fx.ParseMuzzleFlash();
break;
case Defines.svc_muzzleflash2:
CL_fx.ParseMuzzleFlash2();
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.cl.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.cls.demorecording && !Globals.cls.demowaiting)
CL.WriteDemoMessage();
}
}

View File

@@ -0,0 +1,297 @@
/*
* 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.cmodel_t;
import lwjake2.game.edict_t;
import lwjake2.game.entity_state_t;
import lwjake2.game.pmove_t;
import lwjake2.game.trace_t;
import lwjake2.game.usercmd_t;
import lwjake2.qcommon.CM;
import lwjake2.qcommon.Com;
import lwjake2.qcommon.PMove;
import lwjake2.util.Math3D;
/**
* CL_pred
*/
public class CL_pred {
/*
* =================== CL_CheckPredictionError ===================
*/
static void CheckPredictionError() {
int frame;
int[] delta = new int[3];
int i;
int len;
if (Globals.cl_predict.value == 0.0f
|| (Globals.cl.frame.playerstate.pmove.pm_flags & pmove_t.PMF_NO_PREDICTION) != 0)
return;
// calculate the last usercmd_t we sent that the server has processed
frame = Globals.cls.netchan.incoming_acknowledged;
frame &= (Defines.CMD_BACKUP - 1);
// compare what the server returned with what we had predicted it to be
Math3D.VectorSubtract(Globals.cl.frame.playerstate.pmove.origin,
Globals.cl.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.cl.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.cl.frame.serverframe
+ ": " + (delta[0] + delta[1] + delta[2]) + "\n");
Math3D.VectorCopy(Globals.cl.frame.playerstate.pmove.origin,
Globals.cl.predicted_origins[frame]);
// save for error itnerpolation
for (i = 0; i < 3; i++)
Globals.cl.prediction_error[i] = delta[i] * 0.125f;
}
}
/*
* ==================== CL_ClipMoveToEntities
*
* ====================
*/
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.cl.frame.num_entities; i++) {
num = (Globals.cl.frame.parse_entities + i)
& (Defines.MAX_PARSE_ENTITIES - 1);
ent = Globals.cl_parse_entities[num];
if (ent.solid == 0)
continue;
if (ent.number == Globals.cl.playernum + 1)
continue;
if (ent.solid == 31) { // special value for bmodel
cmodel = Globals.cl.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
}
}
}
/*
* ================ CL_PMTrace ================
*/
public static edict_t DUMMY_ENT = new edict_t(-1);
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.cl.frame.num_entities; i++) {
num = (Globals.cl.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.cl.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.cls.state != Defines.ca_active)
return;
if (Globals.cl_paused.value != 0.0f)
return;
if (Globals.cl_predict.value == 0.0f
|| (Globals.cl.frame.playerstate.pmove.pm_flags & pmove_t.PMF_NO_PREDICTION) != 0) {
// just set angles
for (int i = 0; i < 3; i++) {
Globals.cl.predicted_angles[i] = Globals.cl.viewangles[i]
+ Math3D
.SHORT2ANGLE(Globals.cl.frame.playerstate.pmove.delta_angles[i]);
}
return;
}
int ack = Globals.cls.netchan.incoming_acknowledged;
int current = Globals.cls.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));
pmove_t pm = new pmove_t();
pm.trace = new pmove_t.TraceAdapter() {
public trace_t trace(float[] start, float[] mins, float[] maxs,
float[] end) {
return PMTrace(start, mins, maxs, end);
}
};
pm.pointcontents = new pmove_t.PointContentsAdapter() {
public int pointcontents(float[] point) {
return PMpointcontents(point);
}
};
try {
PMove.pm_airaccelerate = Float
.parseFloat(Globals.cl.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.cl.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.cl.cmds[frame];
pm.cmd.set(cmd);
PMove.Pmove(pm);
// save for debug checking
Math3D.VectorCopy(pm.s.origin, Globals.cl.predicted_origins[frame]);
}
int oldframe = (ack - 2) & (Defines.CMD_BACKUP - 1);
int oldz = Globals.cl.predicted_origins[oldframe][2];
int step = pm.s.origin[2] - oldz;
if (step > 63 && step < 160
&& (pm.s.pm_flags & pmove_t.PMF_ON_GROUND) != 0) {
Globals.cl.predicted_step = step * 0.125f;
Globals.cl.predicted_step_time = (int) (Globals.cls.realtime - Globals.cls.frametime * 500);
}
// copy results out for rendering
Globals.cl.predicted_origin[0] = pm.s.origin[0] * 0.125f;
Globals.cl.predicted_origin[1] = pm.s.origin[1] * 0.125f;
Globals.cl.predicted_origin[2] = pm.s.origin[2] * 0.125f;
Math3D.VectorCopy(pm.viewangles, Globals.cl.predicted_angles);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,184 @@
/*
* 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 {
static int num_cl_weaponmodels;
static String[] cl_weaponmodels = new String[Defines.MAX_CLIENTWEAPONMODELS];
/*
* =================
*
* 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.cl.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.cl.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();
num_cl_weaponmodels = 1;
cl_weaponmodels[0] = "weapon.md2";
for (i = 1; i < Defines.MAX_MODELS
&& Globals.cl.configstrings[Defines.CS_MODELS + i].length() != 0; i++) {
name = new String(Globals.cl.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
if (name.charAt(0) == '#') {
// special player weapon model
if (num_cl_weaponmodels < Defines.MAX_CLIENTWEAPONMODELS) {
cl_weaponmodels[num_cl_weaponmodels] = Globals.cl.configstrings[Defines.CS_MODELS
+ i].substring(1);
num_cl_weaponmodels++;
}
} else {
Globals.cl.model_draw[i] = Globals.re
.RegisterModel(Globals.cl.configstrings[Defines.CS_MODELS
+ i]);
if (name.charAt(0) == '*')
Globals.cl.model_clip[i] = CM
.InlineModel(Globals.cl.configstrings[Defines.CS_MODELS
+ i]);
else
Globals.cl.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.cl.configstrings[Defines.CS_IMAGES + i].length() > 0; i++) {
Globals.cl.image_precache[i] = Globals.re
.RegisterPic(Globals.cl.configstrings[Defines.CS_IMAGES + i]);
Sys.SendKeyEvents(); // pump message loop
}
Com.Printf(" \r");
for (i = 0; i < Defines.MAX_CLIENTS; i++) {
if (Globals.cl.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.cl.baseclientinfo,
"unnamed\\male/grunt");
// set sky textures and speed
Com.Printf("sky\r");
SCR.UpdateScreen();
rotate = Float
.parseFloat(Globals.cl.configstrings[Defines.CS_SKYROTATE]);
StringTokenizer st = new StringTokenizer(
Globals.cl.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.cl.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.cl.refresh_prepped = true;
Globals.cl.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.cls.netchan.dropped; i++)
SCR.DebugGraph(30, 0x40);
for (i = 0; i < Globals.cl.surpressCount; i++)
SCR.DebugGraph(30, 0xdf);
// see what the latency was on this packet
in = Globals.cls.netchan.incoming_acknowledged
& (Defines.CMD_BACKUP - 1);
ping = (int) (Globals.cls.realtime - Globals.cl.cmd_time[in]);
ping /= 30;
if (ping > 30)
ping = 30;
SCR.DebugGraph(ping, 0xd0);
}
}

View File

@@ -0,0 +1,595 @@
/*
* 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 lombok.extern.slf4j.Slf4j;
import lwjake2.Defines;
import lwjake2.Globals;
import lwjake2.game.Cmd;
import lwjake2.qcommon.Cbuf;
import lwjake2.qcommon.Com;
import lwjake2.qcommon.Cvar;
import lwjake2.qcommon.FileSystem;
import lwjake2.qcommon.BaseQ2FileSystem;
import lwjake2.util.Lib;
import lwjake2.util.Vargs;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.Arrays;
/**
* Console
*/
@Slf4j
public final class Console extends Globals {
private static final FileSystem fileSystem = BaseQ2FileSystem.getInstance();
public static Runnable ToggleConsole_f = () -> {
SCR.EndLoadingPlaque(); // get rid of loading plaque
if (Globals.cl.attractloop) {
Cbuf.AddText("killserver\n");
return;
}
if (Globals.cls.state == Defines.ca_disconnected) {
// start the demo loop again
Cbuf.AddText("d1\n");
return;
}
Key.ClearTyping();
Console.ClearNotify();
if (Globals.cls.key_dest == Defines.key_console) {
Menu.ForceMenuOff();
Cvar.Set("paused", "0");
} else {
Menu.ForceMenuOff();
Globals.cls.key_dest = Defines.key_console;
if (Cvar.VariableValue("maxclients") == 1
&& Globals.server_state != 0)
Cvar.Set("paused", "1");
}
};
public static Runnable Clear_f = () -> Arrays.fill(Globals.con.text, (byte) ' ');
public static Runnable Dump_f = () -> {
int l, x;
int line;
RandomAccessFile f;
byte[] buffer = new byte[1024];
String name;
if (Cmd.Argc() != 2) {
log.info("usage: condump <filename>");
return;
}
//Com_sprintf (name, sizeof(name), "%s/%s.txt", FS_Gamedir(),
// Cmd_Argv(1));
name = fileSystem.getGamedir() + "/" + Cmd.Argv(1) + ".txt";
log.info("Dumped console text to {}", name);
fileSystem.createPath(name);
f = Lib.fopen(name, "rw");
if (f == null) {
log.error("ERROR: couldn't open.");
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 e) {
}
}
Lib.fclose(f);
};
/**
*
*/
public static void Init() {
Globals.con.linewidth = -1;
CheckResize();
log.info("Console initialized.");
//
// 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++) {
for (int j = 0; j < numchars; j++) {
Globals.con.text[(Globals.con.totallines - 1 - i)
* Globals.con.linewidth + j] = tbuf[((Globals.con.current
- i + oldtotallines) % oldtotallines)
* oldwidth + j];
}
}
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_ToggleChat_f ================
*/
static Runnable ToggleChat_f = () -> {
Key.ClearTyping();
if (cls.key_dest == key_console) {
if (cls.state == ca_active) {
Menu.ForceMenuOff();
cls.key_dest = key_game;
}
} else
cls.key_dest = key_console;
ClearNotify();
};
/*
* ================ Con_MessageMode_f ================
*/
static Runnable MessageMode_f = () -> {
chat_team = false;
cls.key_dest = key_message;
};
/*
* ================ Con_MessageMode2_f ================
*/
static Runnable MessageMode2_f = () -> {
chat_team = true;
cls.key_dest = key_message;
};
/*
* =============== 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] = ' ';
}
/*
* ================ 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 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] = cls.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;
StringBuffer sb = new StringBuffer(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 (cls.key_dest == key_menu)
return;
if (cls.key_dest != key_console && cls.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 + ((int) (cls.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 = (int) (cls.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 (cls.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,
(int) (10 + ((cls.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 (cls.download != null) {
if ((text = cls.downloadname.lastIndexOf('/')) != 0)
text++;
else
text = 0;
x = con.linewidth - ((con.linewidth * 7) / 40);
y = x - (cls.downloadname.length() - text) - 8;
i = con.linewidth / 3;
StringBuffer dlbar = new StringBuffer(512);
if (cls.downloadname.length() - text > i) {
y = x - i - 11;
int end = text + i - 1;
;
dlbar.append(cls.downloadname.substring(text, end));
dlbar.append("...");
} else {
dlbar.append(cls.downloadname.substring(text));
}
dlbar.append(": ");
dlbar.append((char) 0x80);
// where's the dot go?
if (cls.downloadpercent == 0)
n = 0;
else
n = y * cls.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((cls.downloadpercent < 10) ? " 0" : " ");
dlbar.append(cls.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();
}
}

View File

@@ -0,0 +1,813 @@
/*
* 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 lombok.extern.slf4j.Slf4j;
import lwjake2.Defines;
import lwjake2.Globals;
import lwjake2.game.Cmd;
import lwjake2.qcommon.Cbuf;
import lwjake2.qcommon.Com;
import lwjake2.qcommon.Cvar;
import lwjake2.util.Lib;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.Vector;
/**
* Key
*/
@Slf4j
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 int anykeydown = 0;
static int key_waiting;
static int history_line = 0;
static boolean shift_down = false;
static int[] key_repeats = new int[256];
//static int[] keyshift = new int[256];
static boolean[] menubound = new boolean[256];
static boolean[] consolekeys = new boolean[256];
static String[] keynames = new String[256];
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.cls.key_dest == Defines.key_game
&& !(Globals.cls.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.run();
return;
}
// any key during the attract mode will bring up the menu
if (Globals.cl.attractloop && Globals.cls.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.cl.frame.playerstate.stats[Defines.STAT_LAYOUTS] != 0 && Globals.cls.key_dest == Defines.key_game) {
// put away help computer / inventory
Cbuf.AddText("cmd putaway\n");
return;
}
switch (Globals.cls.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";
Cbuf.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";
// Cbuf.AddText(cmd);
// }
// }
return;
}
//
// if not a consolekey, send to the interpreter no matter what mode is
//
if ((Globals.cls.key_dest == Defines.key_menu && menubound[key])
|| (Globals.cls.key_dest == Defines.key_console && !consolekeys[key])
|| (Globals.cls.key_dest == Defines.key_game && (Globals.cls.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";
Cbuf.AddText(cmd);
}
else {
Cbuf.AddText(kb + "\n");
}
}
return;
}
// if (shift_down)
// key = keyshift[key];
switch (Globals.cls.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)
Cbuf.AddText("say_team \"");
else
Cbuf.AddText("say \"");
Cbuf.AddText(Globals.chat_buffer);
Cbuf.AddText("\"\n");
Globals.cls.key_dest = Defines.key_game;
Globals.chat_buffer = "";
return;
}
if (key == K_ESCAPE) {
Globals.cls.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]) {
Cbuf.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] == '/')
Cbuf.AddText(
new String(Globals.key_lines[Globals.edit_line], 2, Lib.strlen(Globals.key_lines[Globals.edit_line]) - 2));
else
Cbuf.AddText(
new String(Globals.key_lines[Globals.edit_line], 1, Lib.strlen(Globals.key_lines[Globals.edit_line]) - 1));
Cbuf.AddText("\n");
log.info(new String(Globals.key_lines[Globals.edit_line], 0, Lib.strlen(Globals.key_lines[Globals.edit_line])));
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.cls.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) {
StringBuilder sb = new StringBuilder(compl.size());
for (int i = 0; i < compl.size(); i++) {
sb.append(compl.get(i)).append(' ');
}
//TODO потом убрать
while (type.startsWith("\n")) { type = type.substring(1); }
while (type.endsWith("\n")) { type = type.substring(0, type.lastIndexOf("\n")); }
while (type.endsWith("\r")) { type = type.substring(0, type.lastIndexOf("\r")); }
type = type.trim();
log.info("{} {}", type, sb.toString());
}
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 = (String)cmds.get(0);
} else if (v == 1) {
s = (String)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;
return;
}
public static Runnable Bind_f = Key::Key_Bind_f;
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
String cmd = ""; // start out with a null string
for (int i = 2; i < c; i++) {
cmd += Cmd.Argv(i);
if (i != (c - 1))
cmd += " ";
}
SetBinding(b, cmd);
}
static void SetBinding(int keynum, String binding) {
if (keynum == -1)
return;
// free old bindings
Globals.keybindings[keynum] = null;
Globals.keybindings[keynum] = binding;
}
static Runnable Unbind_f = Key::Key_Unbind_f;
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 Runnable Unbindall_f = Key::Key_Unbindall_f;
static void Key_Unbindall_f() {
for (int i = 0; i < 256; i++)
Key.SetBinding(i, null);
}
static Runnable Bindlist_f = Key::Key_Bindlist_f;
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 e) {}
}
}

View File

@@ -0,0 +1,495 @@
/*
* 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.EntThinkAdapter;
import lwjake2.game.GameBase;
import lwjake2.game.GameCombat;
import lwjake2.game.edict_t;
import lwjake2.game.mmove_t;
import lwjake2.game.trace_t;
import lwjake2.server.SV;
import lwjake2.util.Lib;
import lwjake2.util.Math3D;
/**
* M
*/
public final class M {
public static void M_CheckGround(edict_t 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_t 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_t 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);
}
/**
* M_MoveToGoal.
*/
public static void M_MoveToGoal(edict_t ent, float dist) {
edict_t goal = ent.goalentity;
if (ent.groundentity == null
&& (ent.flags & (Defines.FL_FLY | Defines.FL_SWIM)) == 0)
return;
// if the next step hits the enemy, return immediately
if (ent.enemy != null && SV.SV_CloseEnough(ent, ent.enemy, dist))
return;
// bump around...
if ((Lib.rand() & 3) == 1
|| !SV.SV_StepDirection(ent, ent.ideal_yaw, dist)) {
if (ent.inuse)
SV.SV_NewChaseDir(ent, goal, dist);
}
}
/**
* M_walkmove.
*/
public static boolean M_walkmove(edict_t ent, float yaw, float dist) {
float[] move = { 0, 0, 0 };
if ((ent.groundentity == null)
&& (ent.flags & (Defines.FL_FLY | Defines.FL_SWIM)) == 0)
return false;
yaw = (float) (yaw * Math.PI * 2 / 360);
move[0] = (float) Math.cos(yaw) * dist;
move[1] = (float) Math.sin(yaw) * dist;
move[2] = 0;
return SV.SV_movestep(ent, move, true);
}
public static void M_CatagorizePosition(edict_t 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;
}
public static void M_WorldEffects(edict_t ent) {
int dmg;
if (ent.health > 0) {
if (0 == (ent.flags & Defines.FL_SWIM)) {
if (ent.waterlevel < 3) {
ent.air_finished = GameBase.level.time + 12;
} else if (ent.air_finished < GameBase.level.time) {
// drown!
if (ent.pain_debounce_time < GameBase.level.time) {
dmg = (int) (2f + 2f * Math.floor(GameBase.level.time
- ent.air_finished));
if (dmg > 15)
dmg = 15;
GameCombat.T_Damage(ent, GameBase.g_edicts[0],
GameBase.g_edicts[0], Globals.vec3_origin,
ent.s.origin, Globals.vec3_origin, dmg, 0,
Defines.DAMAGE_NO_ARMOR, Defines.MOD_WATER);
ent.pain_debounce_time = GameBase.level.time + 1;
}
}
} else {
if (ent.waterlevel > 0) {
ent.air_finished = GameBase.level.time + 9;
} else if (ent.air_finished < GameBase.level.time) {
// suffocate!
if (ent.pain_debounce_time < GameBase.level.time) {
dmg = (int) (2 + 2 * Math.floor(GameBase.level.time
- ent.air_finished));
if (dmg > 15)
dmg = 15;
GameCombat.T_Damage(ent, GameBase.g_edicts[0],
GameBase.g_edicts[0], Globals.vec3_origin,
ent.s.origin, Globals.vec3_origin, dmg, 0,
Defines.DAMAGE_NO_ARMOR, Defines.MOD_WATER);
ent.pain_debounce_time = GameBase.level.time + 1;
}
}
}
}
if (ent.waterlevel == 0) {
if ((ent.flags & Defines.FL_INWATER) != 0) {
GameBase.gi.sound(ent, Defines.CHAN_BODY, GameBase.gi
.soundindex("player/watr_out.wav"), 1,
Defines.ATTN_NORM, 0);
ent.flags &= ~Defines.FL_INWATER;
}
return;
}
if ((ent.watertype & Defines.CONTENTS_LAVA) != 0
&& 0 == (ent.flags & Defines.FL_IMMUNE_LAVA)) {
if (ent.damage_debounce_time < GameBase.level.time) {
ent.damage_debounce_time = GameBase.level.time + 0.2f;
GameCombat.T_Damage(ent, GameBase.g_edicts[0],
GameBase.g_edicts[0], Globals.vec3_origin,
ent.s.origin, Globals.vec3_origin, 10 * ent.waterlevel,
0, 0, Defines.MOD_LAVA);
}
}
if ((ent.watertype & Defines.CONTENTS_SLIME) != 0
&& 0 == (ent.flags & Defines.FL_IMMUNE_SLIME)) {
if (ent.damage_debounce_time < GameBase.level.time) {
ent.damage_debounce_time = GameBase.level.time + 1;
GameCombat.T_Damage(ent, GameBase.g_edicts[0],
GameBase.g_edicts[0], Globals.vec3_origin,
ent.s.origin, Globals.vec3_origin, 4 * ent.waterlevel,
0, 0, Defines.MOD_SLIME);
}
}
if (0 == (ent.flags & Defines.FL_INWATER)) {
if (0 == (ent.svflags & Defines.SVF_DEADMONSTER)) {
if ((ent.watertype & Defines.CONTENTS_LAVA) != 0)
if (Globals.rnd.nextFloat() <= 0.5)
GameBase.gi.sound(ent, Defines.CHAN_BODY, GameBase.gi
.soundindex("player/lava1.wav"), 1,
Defines.ATTN_NORM, 0);
else
GameBase.gi.sound(ent, Defines.CHAN_BODY, GameBase.gi
.soundindex("player/lava2.wav"), 1,
Defines.ATTN_NORM, 0);
else if ((ent.watertype & Defines.CONTENTS_SLIME) != 0)
GameBase.gi.sound(ent, Defines.CHAN_BODY, GameBase.gi
.soundindex("player/watr_in.wav"), 1,
Defines.ATTN_NORM, 0);
else if ((ent.watertype & Defines.CONTENTS_WATER) != 0)
GameBase.gi.sound(ent, Defines.CHAN_BODY, GameBase.gi
.soundindex("player/watr_in.wav"), 1,
Defines.ATTN_NORM, 0);
}
ent.flags |= Defines.FL_INWATER;
ent.damage_debounce_time = 0;
}
}
public static EntThinkAdapter M_droptofloor = new EntThinkAdapter() {
public String getID() { return "m_drop_to_floor";}
public boolean think(edict_t 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 true;
Math3D.VectorCopy(trace.endpos, ent.s.origin);
GameBase.gi.linkentity(ent);
M.M_CheckGround(ent);
M_CatagorizePosition(ent);
return true;
}
};
public static void M_SetEffects(edict_t ent) {
ent.s.effects &= ~(Defines.EF_COLOR_SHELL | Defines.EF_POWERSCREEN);
ent.s.renderfx &= ~(Defines.RF_SHELL_RED | Defines.RF_SHELL_GREEN | Defines.RF_SHELL_BLUE);
if ((ent.monsterinfo.aiflags & Defines.AI_RESURRECTING) != 0) {
ent.s.effects |= Defines.EF_COLOR_SHELL;
ent.s.renderfx |= Defines.RF_SHELL_RED;
}
if (ent.health <= 0)
return;
if (ent.powerarmor_time > GameBase.level.time) {
if (ent.monsterinfo.power_armor_type == Defines.POWER_ARMOR_SCREEN) {
ent.s.effects |= Defines.EF_POWERSCREEN;
} else if (ent.monsterinfo.power_armor_type == Defines.POWER_ARMOR_SHIELD) {
ent.s.effects |= Defines.EF_COLOR_SHELL;
ent.s.renderfx |= Defines.RF_SHELL_GREEN;
}
}
};
//ok
public static void M_MoveFrame(edict_t self) {
mmove_t move; //ptr
int index;
move = self.monsterinfo.currentmove;
self.nextthink = GameBase.level.time + Defines.FRAMETIME;
if ((self.monsterinfo.nextframe != 0)
&& (self.monsterinfo.nextframe >= move.firstframe)
&& (self.monsterinfo.nextframe <= move.lastframe)) {
self.s.frame = self.monsterinfo.nextframe;
self.monsterinfo.nextframe = 0;
} else {
if (self.s.frame == move.lastframe) {
if (move.endfunc != null) {
move.endfunc.think(self);
// regrab move, endfunc is very likely to change it
move = self.monsterinfo.currentmove;
// check for death
if ((self.svflags & Defines.SVF_DEADMONSTER) != 0)
return;
}
}
if (self.s.frame < move.firstframe || self.s.frame > move.lastframe) {
self.monsterinfo.aiflags &= ~Defines.AI_HOLD_FRAME;
self.s.frame = move.firstframe;
} else {
if (0 == (self.monsterinfo.aiflags & Defines.AI_HOLD_FRAME)) {
self.s.frame++;
if (self.s.frame > move.lastframe)
self.s.frame = move.firstframe;
}
}
}
index = self.s.frame - move.firstframe;
if (move.frame[index].ai != null)
if (0 == (self.monsterinfo.aiflags & Defines.AI_HOLD_FRAME))
move.frame[index].ai.ai(self, move.frame[index].dist
* self.monsterinfo.scale);
else
move.frame[index].ai.ai(self, 0);
if (move.frame[index].think != null)
move.frame[index].think.think(self);
}
/** Stops the Flies. */
public static EntThinkAdapter M_FliesOff = new EntThinkAdapter() {
public String getID() { return "m_fliesoff";}
public boolean think(edict_t self) {
self.s.effects &= ~Defines.EF_FLIES;
self.s.sound = 0;
return true;
}
};
/** Starts the Flies as setting the animation flag in the entity. */
public static EntThinkAdapter M_FliesOn = new EntThinkAdapter() {
public String getID() { return "m_flies_on";}
public boolean think(edict_t self) {
if (self.waterlevel != 0)
return true;
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;
return true;
}
};
/** Adds some flies after a random time */
public static EntThinkAdapter M_FlyCheck = new EntThinkAdapter() {
public String getID() { return "m_fly_check";}
public boolean think(edict_t self) {
if (self.waterlevel != 0)
return true;
if (Globals.rnd.nextFloat() > 0.5)
return true;
self.think = M_FliesOn;
self.nextthink = GameBase.level.time + 5 + 10
* Globals.rnd.nextFloat();
return true;
}
};
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,397 @@
/*
* 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 lombok.extern.slf4j.Slf4j;
import lwjake2.Globals;
import lwjake2.game.Cmd;
import lwjake2.game.cvar_t;
import lwjake2.qcommon.Com;
import lwjake2.qcommon.Cvar;
import lwjake2.sys.Timer;
import lwjake2.util.Math3D;
import java.io.IOException;
import java.nio.FloatBuffer;
/**
* V
*/
@Slf4j
public final class V extends Globals {
static cvar_t cl_testblend;
static cvar_t cl_testparticles;
static cvar_t cl_testentities;
static cvar_t cl_testlights;
static cvar_t cl_stats;
static int r_numdlights;
static dlight_t[] r_dlights = new dlight_t[MAX_DLIGHTS];
static int r_numentities;
static entity_t[] r_entities = new entity_t[MAX_ENTITIES];
static int r_numparticles;
//static particle_t[] r_particles = new particle_t[MAX_PARTICLES];
static lightstyle_t[] r_lightstyles = new lightstyle_t[MAX_LIGHTSTYLES];
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;
}
// stack variable
private static final float[] origin = { 0, 0, 0 };
/*
* ================ 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] = cl.refdef.vieworg[j] + cl.v_forward[j] * d
+ cl.v_right[j] * r + cl.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] = cl.refdef.vieworg[j] + cl.v_forward[j] * f
+ cl.v_right[j] * r;
ent.model = cl.baseclientinfo.model;
ent.skin = cl.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] = cl.refdef.vieworg[j] + cl.v_forward[j] * f
+ cl.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;
}
}
static Runnable Gun_Next_f = () -> {
gun_frame++;
Com.Printf("frame " + gun_frame + "\n");
};
static Runnable Gun_Prev_f = () -> {
gun_frame--;
if (gun_frame < 0)
gun_frame = 0;
Com.Printf("frame " + gun_frame + "\n");
};
static Runnable Gun_Model_f = () -> {
if (Cmd.Argc() != 2) {
gun_model = null;
return;
}
String name = "models/" + Cmd.Argv(1) + "/tris.md2";
gun_model = re.RegisterModel(name);
};
/*
* ================== V_RenderView
*
* ==================
*/
static void RenderView(float stereo_separation) {
// extern int entitycmpfnc( const entity_t *, const entity_t * );
//
if (cls.state != ca_active)
return;
if (!cl.refresh_prepped)
return; // still loading
if (cl_timedemo.value != 0.0f) {
if (cl.timedemo_start == 0)
cl.timedemo_start = Timer.Milliseconds();
cl.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 (cl.frame.valid && (cl.force_refdef || cl_paused.value == 0.0f)) {
cl.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) {
cl.refdef.blend[0] = 1.0f;
cl.refdef.blend[1] = 0.5f;
cl.refdef.blend[2] = 0.25f;
cl.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(cl.v_right, stereo_separation, tmp);
Math3D.VectorAdd(cl.refdef.vieworg, tmp, cl.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
cl.refdef.vieworg[0] += 1.0 / 16;
cl.refdef.vieworg[1] += 1.0 / 16;
cl.refdef.vieworg[2] += 1.0 / 16;
cl.refdef.x = scr_vrect.x;
cl.refdef.y = scr_vrect.y;
cl.refdef.width = scr_vrect.width;
cl.refdef.height = scr_vrect.height;
cl.refdef.fov_y = Math3D.CalcFov(cl.refdef.fov_x, cl.refdef.width,
cl.refdef.height);
cl.refdef.time = cl.time * 0.001f;
cl.refdef.areabits = cl.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(cl.refdef.blend);
}
cl.refdef.num_entities = r_numentities;
cl.refdef.entities = r_entities;
cl.refdef.num_particles = r_numparticles;
cl.refdef.num_dlights = r_numdlights;
cl.refdef.dlights = r_dlights;
cl.refdef.lightstyles = r_lightstyles;
cl.refdef.rdflags = cl.frame.playerstate.rdflags;
// sort entities for better cache locality
// !!! useless in Java !!!
//Arrays.sort(cl.refdef.entities, entitycmpfnc);
}
re.RenderFrame(cl.refdef);
if (cl_stats.value != 0.0f)
log.info("ent:{} lt:{} part:{}", r_numentities, r_numdlights, r_numparticles);
if (log_stats.value != 0.0f && (log_stats_file != null))
try {
log_stats_file.write(r_numentities + "," + r_numdlights + ","
+ r_numparticles);
} catch (IOException e) {
}
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();
}
/*
* ============= V_Viewpos_f =============
*/
static Runnable Viewpos_f = () -> log.info("({} {} {}) : {}",
(int) cl.refdef.vieworg[0],
(int) cl.refdef.vieworg[1],
(int) cl.refdef.vieworg[2],
(int) cl.refdef.viewangles[YAW]
);
public static void Init() {
Cmd.AddCommand("gun_next", Gun_Next_f);
Cmd.AddCommand("gun_prev", Gun_Prev_f);
Cmd.AddCommand("gun_model", Gun_Model_f);
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);
}
}

View File

@@ -0,0 +1,746 @@
/*
* 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 lombok.extern.slf4j.Slf4j;
import lwjake2.Defines;
import lwjake2.Globals;
import lwjake2.game.Cmd;
import lwjake2.game.cvar_t;
import lwjake2.qcommon.Callback;
import lwjake2.qcommon.Com;
import lwjake2.qcommon.Cvar;
import lwjake2.render.Renderer;
import lwjake2.sound.S;
import lwjake2.sys.IN;
import lwjake2.util.Vargs;
import java.awt.Dimension;
import java.awt.DisplayMode;
/**
* VID is a video driver.
*
* source: client/vid.h linux/vid_so.c
*
* @author cwei
*/
@Slf4j
public class VID 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;
// Console variables that we need to access from this module
static cvar_t vid_gamma;
static cvar_t vid_ref; // Name of Refresh DLL loaded
static cvar_t vid_xpos; // X coordinate of window position
static cvar_t vid_ypos; // Y coordinate of window position
static cvar_t vid_width;
static cvar_t vid_height;
static cvar_t vid_fullscreen;
// Global variables used internally by this module
// void *reflib_library; // Handle to refresh DLL
static boolean reflib_active = false;
// const char so_file[] = "/etc/quake2.conf";
/*
==========================================================================
DLL GLUE
==========================================================================
*/
public static void Printf(int print_level, String fmt) {
while (fmt.startsWith("\n")) { fmt = fmt.substring(1); }
while (fmt.endsWith("\n")) { fmt = fmt.substring(0, fmt.lastIndexOf("\n")); }
while (fmt.endsWith("\r")) { fmt = fmt.substring(0, fmt.lastIndexOf("\r")); }
fmt = fmt.trim();
log.warn("{}", fmt);
}
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;
}
/*
** VID_GetModeInfo
*/
static 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 vidmode_t fs_modes[];
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();
IN.Shutdown();
}
Globals.re = null;
reflib_active = false;
}
/*
==============
VID_LoadRefresh
==============
*/
static boolean LoadRefresh( String name )
{
if ( reflib_active )
{
Globals.re.getKeyboardHandler().Close();
IN.Shutdown();
Globals.re.Shutdown();
FreeReflib();
}
log.info("------- Loading {} -------", name);
boolean found = false;
String[] driverNames = Renderer.getDriverNames();
for (int i = 0; i < driverNames.length; i++) {
if (driverNames[i].equals(name)) {
found = true;
break;
}
}
if (!found) {
log.warn("LoadLibrary(\"{}\") failed", name);
return false;
}
log.info("LoadLibrary(\"{}\")", name);
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");
}
IN.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();
log.info("------------------------------------");
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()
{
cvar_t gl_mode;
if ( vid_ref.modified )
{
S.StopAllSounds();
}
while (vid_ref.modified)
{
/*
** refresh has changed
*/
vid_ref.modified = false;
vid_fullscreen.modified = true;
Globals.cl.refresh_prepped = false;
Globals.cls.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;
log.info("Refresh failed");
gl_mode = Cvar.Get( "gl_mode", "0", 0 );
if (gl_mode.value != 0.0f) {
log.info("Trying mode 0");
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.cls.key_dest != Defines.key_console )
{
try {
Console.ToggleConsole_f.run();
} catch (Exception e) {
}
}
}
Globals.cls.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", VID::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();
IN.Shutdown();
Globals.re.Shutdown();
FreeReflib();
}
}
// ==========================================================================
//
// vid_menu.c
//
// ==========================================================================
static final int REF_OPENGL_JOGL = 0;
static final int REF_OPENGL_FASTJOGL =1;
static final int REF_OPENGL_LWJGL =2;
static cvar_t gl_mode;
static cvar_t gl_driver;
static cvar_t gl_picmip;
static cvar_t gl_ext_palettedtexture;
static cvar_t sw_mode;
static cvar_t sw_stipplealpha;
static cvar_t _windowed_mouse;
/*
====================================================================
MENU INTERACTION
====================================================================
*/
static Menu.menuframework_s s_opengl_menu = new Menu.menuframework_s();
static Menu.menuframework_s s_current_menu; // referenz
static Menu.menulist_s s_mode_list = new Menu.menulist_s();
static Menu.menulist_s s_ref_list = new Menu.menulist_s();
static Menu.menuslider_s s_tq_slider = new Menu.menuslider_s();
static Menu.menuslider_s s_screensize_slider = new Menu.menuslider_s();
static Menu.menuslider_s s_brightness_slider = new Menu.menuslider_s();
static Menu.menulist_s s_fs_box = new Menu.menulist_s();
static Menu.menulist_s s_stipple_box = new Menu.menulist_s();
static Menu.menulist_s s_paletted_texture_box = new Menu.menulist_s();
static Menu.menulist_s s_windowed_mouse = new Menu.menulist_s();
static Menu.menuaction_s s_apply_action = new Menu.menuaction_s();
static Menu.menuaction_s s_defaults_action= new Menu.menuaction_s();
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 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 String[] fs_resolutions;
static int mode_x;
static String[] refs;
static String[] drivers;
static final String[] yesno_names =
{
"no",
"yes",
};
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];
StringBuffer sb = new StringBuffer(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];
StringBuffer sb = new StringBuffer();
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 = VID::DriverCallback;
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 = VID::ScreenSizeCallback;
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 = VID::BrightnessCallback;
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 = 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 = VID::ResetDefaults;
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 = VID::ApplyChanges;
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;
}
}

View File

@@ -0,0 +1,34 @@
/*
* 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 {
entity_state_t baseline= new entity_state_t(null); // delta from this if not from a previous frame
public entity_state_t current= new entity_state_t(null);
entity_state_t prev= new entity_state_t(null); // will always be valid, but might just be a copy of current
int serverframe; // if not current, this ent isn't in the frame
int trailcount; // for diminishing grenade trails
float[] lerp_origin = { 0, 0, 0 }; // for trails (variable hz)
int fly_stoptime;
}

View File

@@ -0,0 +1,48 @@
/*
* 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 {
static abstract class ThinkAdapter {
abstract void think(cl_sustain_t self);
}
int id;
int type;
int endtime;
int nextthink;
int thinkinterval;
float[] org = new float[3];
float[] dir = new float[3];
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;
}
}

View File

@@ -0,0 +1,133 @@
/*
* 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_t;
import lwjake2.render.model_t;
import lwjake2.sound.sfx_t;
import java.nio.ByteBuffer;
public class client_state_t {
public client_state_t() {
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] = new String();
for (int n=0; n < Defines.MAX_CLIENTS; n++)
clientinfo[n] = new clientinfo_t();
}
//
// the client_state_t structure is wiped completely at every
// server map change
//
int timeoutcount;
int timedemo_frames;
int timedemo_start;
public boolean refresh_prepped; // false if on new level or new ref dll
public boolean sound_prepped; // ambient sounds can 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[]
usercmd_t cmd = new usercmd_t();
usercmd_t cmds[] = new usercmd_t[Defines.CMD_BACKUP]; // each mesage will send several old cmds
int cmd_time[] = new int[Defines.CMD_BACKUP]; // time sent, for calculating pings
short predicted_origins[][] = new short[Defines.CMD_BACKUP][3]; // for debug comparing against server
float predicted_step; // for stair up smoothing
int predicted_step_time;
float[] predicted_origin ={0,0,0}; // generated by CL_PredictMovement
float[] predicted_angles={0,0,0};
float[] prediction_error={0,0,0};
public frame_t frame = new frame_t(); // received from server
int surpressCount; // number of messages rate supressed
frame_t frames[] = new frame_t[Defines.UPDATE_BACKUP];
// 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 float[] viewangles = { 0, 0, 0 };
public int time; // this is the time value that the client
// is rendering at. always <= cls.realtime
float lerpfrac; // between oldframe and frame
refdef_t refdef = new refdef_t();
float[] v_forward = { 0, 0, 0 };
float[] v_right = { 0, 0, 0 };
float[] v_up = { 0, 0, 0 }; // set when refdef.angles is set
//
// transient data from server
//
String layout = ""; // general 2D overlay
int inventory[] = new int[Defines.MAX_ITEMS];
//
// 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;
byte cinematicpalette[] = new byte[768];
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 int playernum;
public String configstrings[] = new String[Defines.MAX_CONFIGSTRINGS];
//
// locally derived information from server state
//
model_t model_draw[] = new model_t[Defines.MAX_MODELS];
cmodel_t model_clip[] = new cmodel_t[Defines.MAX_MODELS];
public sfx_t sound_precache[] = new sfx_t[Defines.MAX_SOUNDS];
image_t image_precache[] = new image_t[Defines.MAX_IMAGES];
clientinfo_t clientinfo[] = new clientinfo_t[Defines.MAX_CLIENTS];
clientinfo_t baseclientinfo = new clientinfo_t();
}

View File

@@ -0,0 +1,67 @@
/*
* 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 client_static_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
// or changing rendering dlls
// if time gets > 30 seconds ahead, break it
public int disable_servercount; // when we receive a frame and cl.servercount
// > cls.disable_servercount, clear disable_screen
// connection information
public String servername = ""; // name of server from original connect
public float connect_time; // for connection retransmits
int quakePort; // a 16 bit value that allows quake servers
// to work around address translating routers
public netchan_t netchan = new netchan_t();
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;
}

View File

@@ -0,0 +1,49 @@
/*
* 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.render.image_t;
import lwjake2.render.model_t;
public class clientinfo_t {
String name ="";
String cinfo ="";
image_t skin; // ptr
image_t icon; // ptr
String iconname ="";
model_t model; // ptr
model_t weaponmodel[] = new model_t[Defines.MAX_CLIENTWEAPONMODELS]; // arary of references
// public void reset()
// {
// set(new clientinfo_t());
// }
public void set (clientinfo_t from)
{
name = from.name;
cinfo = from.cinfo;
skin = from.skin;
icon = from.icon;
iconname = from.iconname;
model = from.model;
System.arraycopy(from.weaponmodel,0, weaponmodel, 0 , Defines.MAX_CLIENTWEAPONMODELS);
}
}

View File

@@ -0,0 +1,44 @@
/*
* 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 {
boolean initialized;
byte[] text = new byte[Defines.CON_TEXTSIZE];
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;
float[] times = new float[Defines.NUM_CON_TIMES]; // cls.realtime time the line was generated
// for transparent notify lines
}

View File

@@ -0,0 +1,39 @@
/*
* 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 cparticle_t next;
public float time;
public float[] org = {0, 0, 0}; // vec3_t
public float[] vel = {0, 0, 0}; // vec3_t
public float[] accel = {0, 0, 0}; // vec3_t
public float color;
//public float colorvel;
public float alpha;
public float alphavel;
}

View File

@@ -0,0 +1,26 @@
/*
* 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 float origin[] = { 0, 0, 0 };
public float color[] = { 0, 0, 0 };
public float intensity;
}

View File

@@ -0,0 +1,86 @@
/*
* 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_t;
import lwjake2.render.model_t;
import lwjake2.util.Math3D;
public class entity_t implements Cloneable{
//ptr
public model_t model; // opaque type outside refresh
public float angles[] = { 0, 0, 0 };
/*
** most recent data
*/
public float origin[] = { 0, 0, 0 }; // also used as RF_BEAM's "from"
public int frame; // also used as RF_BEAM's diameter
/*
** previous data for lerping
*/
public float oldorigin[] = { 0, 0, 0 }; // also used as RF_BEAM's "to"
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_t 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;
}
}

View File

@@ -0,0 +1,56 @@
/*
* 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;
boolean valid; // cleared if delta parsing was invalid
int serverframe;
public int servertime; // server time the message is valid for (in msec)
int deltaframe;
byte areabits[] = new byte [MAX_MAP_AREAS/8]; // portalarea visibility bits
public player_state_t playerstate = new player_state_t(); // mem
public int num_entities;
public int parse_entities; // non-masked index into cl_parse_entities array
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;
}
}

View File

@@ -0,0 +1,29 @@
/*
* 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 {
int[] down = new int[2]; // key nums holding it down
long downtime; // msec timestamp
long msec; // msec down this frame
public int state;
}

View File

@@ -0,0 +1,25 @@
/*
* 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 float rgb[] = { 0, 0, 0 }; // 0.0 - 2.0
public float white; // highest of rgb
}

View File

@@ -0,0 +1,48 @@
/*
* 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 {
// lwjgl renderer needs a ByteBuffer
private static ByteBuffer colorByteArray = Lib.newByteBuffer(Defines.MAX_PARTICLES * Lib.SIZEOF_INT, ByteOrder.LITTLE_ENDIAN);
public static FloatBuffer vertexArray = Lib.newFloatBuffer(Defines.MAX_PARTICLES * 3);
public static int[] colorTable = new int[256];
public static 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;
}
}

View File

@@ -0,0 +1,42 @@
/*
* 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 int x, y, width, height;// in virtual screen coordinates
public float fov_x, fov_y;
public float vieworg[] ={0,0,0};
public float viewangles[]={0,0,0};
public float blend[]={0,0,0,0}; // rgba 0-1 full screen blend
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[];
}

View File

@@ -0,0 +1,104 @@
/*
* 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_t;
import lwjake2.render.model_t;
import lwjake2.sys.KBD;
import java.awt.Dimension;
import java.awt.DisplayMode;
/**
* 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_t RegisterModel(String name);
image_t RegisterSkin(String name);
image_t 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(Runnable callback);
int apiVersion();
DisplayMode[] getModeList();
KBD getKeyboardHandler();
}

View File

@@ -0,0 +1,23 @@
/*
* 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;
}

View File

@@ -0,0 +1,37 @@
/*
* 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 {
String description;
int width, height;
int mode;
vidmode_t (String description, int width, int height, int mode) {
this.description = description;
this.width = width;
this.height = height;
this.mode = mode;
}
}

View File

@@ -0,0 +1,32 @@
/*
* 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;
}

View File

@@ -0,0 +1,24 @@
/*
* 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 AIAdapter extends SuperAdapter
{
public abstract void ai(edict_t self, float dist);
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,29 @@
/*
* 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_t e, String s)
{
return false;
};
}

View File

@@ -0,0 +1,31 @@
/*
* 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
{
EdictIterator(int i)
{
this.i = i;
}
public edict_t o;
int i;
}

View File

@@ -0,0 +1,69 @@
/*
* 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;
abstract public float BigFloat(float f);
abstract public short BigShort(short s);
abstract public int BigLong(int i);
abstract public float LittleFloat(float f);
abstract public short LittleShort(short s);
abstract public int LittleLong(int i);
public static float swapFloat(float f)
{
int i = Float.floatToRawIntBits(f);
i = swapInt(i);
f = Float.intBitsToFloat(i);
return f;
}
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;
}
public static short swapShort(short s)
{
int a = s & mask;
a <<= 8;
int b = (s >>> 8) & mask;
return (short) (b | a);
}
}

View File

@@ -0,0 +1,25 @@
/*
* 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_t self, edict_t other);
}

View File

@@ -0,0 +1,24 @@
/*
* 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 EntDieAdapter extends SuperAdapter
{
public abstract void die(edict_t self, edict_t inflictor, edict_t attacker, int damage, float[] point);
}

View File

@@ -0,0 +1,24 @@
/*
* 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
{
public abstract void dodge(edict_t self, edict_t other, float eta);
}

View File

@@ -0,0 +1,24 @@
/*
* 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
{
public abstract boolean interact(edict_t self, edict_t other);
}

View File

@@ -0,0 +1,24 @@
/*
* 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 EntPainAdapter extends SuperAdapter
{
public abstract void pain(edict_t self, edict_t other, float kick, int damage);
}

View File

@@ -0,0 +1,24 @@
/*
* 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 boolean think(edict_t self);
}

View File

@@ -0,0 +1,24 @@
/*
* 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_t self, edict_t other, cplane_t plane, csurface_t surf);
}

View File

@@ -0,0 +1,24 @@
/*
* 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_t self, edict_t other, edict_t activator);
}

View File

@@ -0,0 +1,758 @@
/*
* 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.client.M;
import lwjake2.util.Lib;
import lwjake2.util.Math3D;
public class GameAI {
public static void AttackFinished(edict_t self, float time) {
self.monsterinfo.attack_finished = GameBase.level.time + time;
}
/** Don't move, but turn towards ideal_yaw Distance is for slight position
* adjustments needed by the animations.
*/
public static void ai_turn(edict_t self, float dist) {
if (dist != 0)
M.M_walkmove(self, self.s.angles[Defines.YAW], dist);
if (GameUtil.FindTarget(self))
return;
M.M_ChangeYaw(self);
}
/**
* Checks, if the monster should turn left/right.
*/
public static boolean FacingIdeal(edict_t self) {
float delta;
delta = Math3D.anglemod(self.s.angles[Defines.YAW] - self.ideal_yaw);
if (delta > 45 && delta < 315)
return false;
return true;
}
/**
* Turn and close until within an angle to launch a melee attack.
*/
public static void ai_run_melee(edict_t self) {
self.ideal_yaw = enemy_yaw;
M.M_ChangeYaw(self);
if (FacingIdeal(self)) {
self.monsterinfo.melee.think(self);
self.monsterinfo.attack_state = Defines.AS_STRAIGHT;
}
}
/**
* Turn in place until within an angle to launch a missile attack.
*/
public static void ai_run_missile(edict_t self) {
self.ideal_yaw = enemy_yaw;
M.M_ChangeYaw(self);
if (FacingIdeal(self)) {
self.monsterinfo.attack.think(self);
self.monsterinfo.attack_state = Defines.AS_STRAIGHT;
}
};
/**
* Strafe sideways, but stay at aproximately the same range.
*/
public static void ai_run_slide(edict_t self, float distance) {
float ofs;
self.ideal_yaw = enemy_yaw;
M.M_ChangeYaw(self);
if (self.monsterinfo.lefty != 0)
ofs = 90;
else
ofs = -90;
if (M.M_walkmove(self, self.ideal_yaw + ofs, distance))
return;
self.monsterinfo.lefty = 1 - self.monsterinfo.lefty;
M.M_walkmove(self, self.ideal_yaw - ofs, distance);
}
/**
* Decides if we're going to attack or do something else used by ai_run and
* ai_stand.
*
* .enemy Will be world if not currently angry at anyone.
*
* .movetarget The next path spot to walk toward. If .enemy, ignore
* .movetarget. When an enemy is killed, the monster will try to return to
* it's path.
*
* .hunt_time Set to time + something when the player is in sight, but
* movement straight for him is blocked. This causes the monster to use wall
* following code for movement direction instead of sighting on the player.
*
* .ideal_yaw A yaw angle of the intended direction, which will be turned
* towards at up to 45 deg / state. If the enemy is in view and hunt_time is
* not active, this will be the exact line towards the enemy.
*
* .pausetime A monster will leave it's stand state and head towards it's
* .movetarget when time > .pausetime.
*
* walkmove(angle, speed) primitive is all or nothing
*/
public static boolean ai_checkattack(edict_t self, float dist) {
float temp[] = { 0, 0, 0 };
boolean hesDeadJim;
// this causes monsters to run blindly to the combat point w/o firing
if (self.goalentity != null) {
if ((self.monsterinfo.aiflags & Defines.AI_COMBAT_POINT) != 0)
return false;
if ((self.monsterinfo.aiflags & Defines.AI_SOUND_TARGET) != 0) {
if ((GameBase.level.time - self.enemy.teleport_time) > 5.0) {
if (self.goalentity == self.enemy)
if (self.movetarget != null)
self.goalentity = self.movetarget;
else
self.goalentity = null;
self.monsterinfo.aiflags &= ~Defines.AI_SOUND_TARGET;
if ((self.monsterinfo.aiflags & Defines.AI_TEMP_STAND_GROUND) != 0)
self.monsterinfo.aiflags &= ~(Defines.AI_STAND_GROUND | Defines.AI_TEMP_STAND_GROUND);
} else {
self.show_hostile = (int) GameBase.level.time + 1;
return false;
}
}
}
enemy_vis = false;
// see if the enemy is dead
hesDeadJim = false;
if ((null == self.enemy) || (!self.enemy.inuse)) {
hesDeadJim = true;
} else if ((self.monsterinfo.aiflags & Defines.AI_MEDIC) != 0) {
if (self.enemy.health > 0) {
hesDeadJim = true;
self.monsterinfo.aiflags &= ~Defines.AI_MEDIC;
}
} else {
if ((self.monsterinfo.aiflags & Defines.AI_BRUTAL) != 0) {
if (self.enemy.health <= -80)
hesDeadJim = true;
} else {
if (self.enemy.health <= 0)
hesDeadJim = true;
}
}
if (hesDeadJim) {
self.enemy = null;
// FIXME: look all around for other targets
if (self.oldenemy != null && self.oldenemy.health > 0) {
self.enemy = self.oldenemy;
self.oldenemy = null;
HuntTarget(self);
} else {
if (self.movetarget != null) {
self.goalentity = self.movetarget;
self.monsterinfo.walk.think(self);
} else {
// we need the pausetime otherwise the stand code
// will just revert to walking with no target and
// the monsters will wonder around aimlessly trying
// to hunt the world entity
self.monsterinfo.pausetime = GameBase.level.time + 100000000;
self.monsterinfo.stand.think(self);
}
return true;
}
}
self.show_hostile = (int) GameBase.level.time + 1; // wake up other
// monsters check knowledge of enemy
enemy_vis = GameUtil.visible(self, self.enemy);
if (enemy_vis) {
self.monsterinfo.search_time = GameBase.level.time + 5;
Math3D.VectorCopy(self.enemy.s.origin,
self.monsterinfo.last_sighting);
}
enemy_infront = GameUtil.infront(self, self.enemy);
enemy_range = GameUtil.range(self, self.enemy);
Math3D.VectorSubtract(self.enemy.s.origin, self.s.origin, temp);
enemy_yaw = Math3D.vectoyaw(temp);
// JDC self.ideal_yaw = enemy_yaw;
if (self.monsterinfo.attack_state == Defines.AS_MISSILE) {
ai_run_missile(self);
return true;
}
if (self.monsterinfo.attack_state == Defines.AS_MELEE) {
ai_run_melee(self);
return true;
}
// if enemy is not currently visible, we will never attack
if (!enemy_vis)
return false;
return self.monsterinfo.checkattack.think(self);
}
/**
* The monster is walking it's beat.
*/
static void ai_walk(edict_t self, float dist) {
M.M_MoveToGoal(self, dist);
// check for noticing a player
if (GameUtil.FindTarget(self))
return;
if ((self.monsterinfo.search != null)
&& (GameBase.level.time > self.monsterinfo.idle_time)) {
if (self.monsterinfo.idle_time != 0) {
self.monsterinfo.search.think(self);
self.monsterinfo.idle_time = GameBase.level.time + 15
+ Lib.random() * 15;
} else {
self.monsterinfo.idle_time = GameBase.level.time + Lib.random()
* 15;
}
}
}
/**
* Called once each frame to set level.sight_client to the player to be
* checked for in findtarget.
*
* If all clients are either dead or in notarget, sight_client will be null.
*
* In coop games, sight_client will cycle between the clients.
*/
static void AI_SetSightClient() {
edict_t 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
}
}
}
/**
* Move the specified distance at current facing. This replaces the QC
* functions: ai_forward, ai_back, ai_pain, and ai_painforward
*/
static void ai_move(edict_t self, float dist) {
M.M_walkmove(self, self.s.angles[Defines.YAW], dist);
}
/**
* Decides running or standing according to flag AI_STAND_GROUND.
*/
static void HuntTarget(edict_t self) {
float[] vec = { 0, 0, 0 };
self.goalentity = self.enemy;
if ((self.monsterinfo.aiflags & Defines.AI_STAND_GROUND) != 0)
self.monsterinfo.stand.think(self);
else
self.monsterinfo.run.think(self);
Math3D.VectorSubtract(self.enemy.s.origin, self.s.origin, vec);
self.ideal_yaw = Math3D.vectoyaw(vec);
// wait a while before first attack
if (0 == (self.monsterinfo.aiflags & Defines.AI_STAND_GROUND))
GameUtil.AttackFinished(self, 1);
}
public static EntThinkAdapter walkmonster_start_go = new EntThinkAdapter() {
public String getID() { return "walkmonster_start_go"; }
public boolean think(edict_t self) {
if (0 == (self.spawnflags & 2) && GameBase.level.time < 1) {
M.M_droptofloor.think(self);
if (self.groundentity != null)
if (!M.M_walkmove(self, 0, 0))
GameBase.gi.dprintf(self.classname + " in solid at "
+ Lib.vtos(self.s.origin) + "\n");
}
if (0 == self.yaw_speed)
self.yaw_speed = 40;
self.viewheight = 25;
Monster.monster_start_go(self);
if ((self.spawnflags & 2) != 0)
Monster.monster_triggered_start.think(self);
return true;
}
};
public static EntThinkAdapter walkmonster_start = new EntThinkAdapter() {
public String getID() { return "walkmonster_start";}
public boolean think(edict_t self) {
self.think = walkmonster_start_go;
Monster.monster_start(self);
return true;
}
};
public static EntThinkAdapter flymonster_start_go = new EntThinkAdapter() {
public String getID() { return "flymonster_start_go";}
public boolean think(edict_t self) {
if (!M.M_walkmove(self, 0, 0))
GameBase.gi.dprintf(self.classname + " in solid at "
+ Lib.vtos(self.s.origin) + "\n");
if (0 == self.yaw_speed)
self.yaw_speed = 20;
self.viewheight = 25;
Monster.monster_start_go(self);
if ((self.spawnflags & 2) != 0)
Monster.monster_triggered_start.think(self);
return true;
}
};
public static EntThinkAdapter flymonster_start = new EntThinkAdapter() {
public String getID() { return "flymonster_start";}
public boolean think(edict_t self) {
self.flags |= Defines.FL_FLY;
self.think = flymonster_start_go;
Monster.monster_start(self);
return true;
}
};
public static EntThinkAdapter swimmonster_start_go = new EntThinkAdapter() {
public String getID() { return "swimmonster_start_go";}
public boolean think(edict_t self) {
if (0 == self.yaw_speed)
self.yaw_speed = 20;
self.viewheight = 10;
Monster.monster_start_go(self);
if ((self.spawnflags & 2) != 0)
Monster.monster_triggered_start.think(self);
return true;
}
};
public static EntThinkAdapter swimmonster_start = new EntThinkAdapter() {
public String getID() { return "swimmonster_start";}
public boolean think(edict_t self) {
self.flags |= Defines.FL_SWIM;
self.think = swimmonster_start_go;
Monster.monster_start(self);
return true;
}
};
/**
* Don't move, but turn towards ideal_yaw Distance is for slight position
* adjustments needed by the animations
*/
public static AIAdapter ai_turn = new AIAdapter() {
public String getID() { return "ai_turn";}
public void ai(edict_t self, float dist) {
if (dist != 0)
M.M_walkmove(self, self.s.angles[Defines.YAW], dist);
if (GameUtil.FindTarget(self))
return;
M.M_ChangeYaw(self);
}
};
/**
* Move the specified distance at current facing. This replaces the QC
* functions: ai_forward, ai_back, ai_pain, and ai_painforward
*/
public static AIAdapter ai_move = new AIAdapter() {
public String getID() { return "ai_move";}
public void ai(edict_t self, float dist) {
M.M_walkmove(self, self.s.angles[Defines.YAW], dist);
}
};
/**
* The monster is walking it's beat.
*/
public static AIAdapter ai_walk = new AIAdapter() {
public String getID() { return "ai_walk";}
public void ai(edict_t self, float dist) {
M.M_MoveToGoal(self, dist);
// check for noticing a player
if (GameUtil.FindTarget(self))
return;
if ((self.monsterinfo.search != null)
&& (GameBase.level.time > self.monsterinfo.idle_time)) {
if (self.monsterinfo.idle_time != 0) {
self.monsterinfo.search.think(self);
self.monsterinfo.idle_time = GameBase.level.time + 15 + Globals.rnd.nextFloat() * 15;
} else {
self.monsterinfo.idle_time = GameBase.level.time + Globals.rnd.nextFloat() * 15;
}
}
}
};
/**
* Used for standing around and looking for players Distance is for slight
* position adjustments needed by the animations.
*/
public static AIAdapter ai_stand = new AIAdapter() {
public String getID() { return "ai_stand";}
public void ai(edict_t self, float dist) {
float[] v = { 0, 0, 0 };
if (dist != 0)
M.M_walkmove(self, self.s.angles[Defines.YAW], dist);
if ((self.monsterinfo.aiflags & Defines.AI_STAND_GROUND) != 0) {
if (self.enemy != null) {
Math3D.VectorSubtract(self.enemy.s.origin, self.s.origin, v);
self.ideal_yaw = Math3D.vectoyaw(v);
if (self.s.angles[Defines.YAW] != self.ideal_yaw
&& 0 != (self.monsterinfo.aiflags & Defines.AI_TEMP_STAND_GROUND)) {
self.monsterinfo.aiflags &= ~(Defines.AI_STAND_GROUND | Defines.AI_TEMP_STAND_GROUND);
self.monsterinfo.run.think(self);
}
M.M_ChangeYaw(self);
ai_checkattack(self, 0);
} else
GameUtil.FindTarget(self);
return;
}
if (GameUtil.FindTarget(self))
return;
if (GameBase.level.time > self.monsterinfo.pausetime) {
self.monsterinfo.walk.think(self);
return;
}
if (0 == (self.spawnflags & 1) && (self.monsterinfo.idle != null)
&& (GameBase.level.time > self.monsterinfo.idle_time)) {
if (self.monsterinfo.idle_time != 0) {
self.monsterinfo.idle.think(self);
self.monsterinfo.idle_time = GameBase.level.time + 15 + Globals.rnd.nextFloat() * 15;
} else {
self.monsterinfo.idle_time = GameBase.level.time + Globals.rnd.nextFloat() * 15;
}
}
}
};
/**
* Turns towards target and advances Use this call with a distnace of 0 to
* replace ai_face.
*/
public static AIAdapter ai_charge = new AIAdapter() {
public String getID() { return "ai_charge";}
public void ai(edict_t self, float dist) {
float[] v = { 0, 0, 0 };
Math3D.VectorSubtract(self.enemy.s.origin, self.s.origin, v);
self.ideal_yaw = Math3D.vectoyaw(v);
M.M_ChangeYaw(self);
if (dist != 0)
M.M_walkmove(self, self.s.angles[Defines.YAW], dist);
}
};
/**
* The monster has an enemy it is trying to kill.
*/
public static AIAdapter ai_run = new AIAdapter() {
public String getID() { return "ai_run";}
public void ai(edict_t self, float dist) {
float[] v = { 0, 0, 0 };
edict_t tempgoal;
edict_t save;
boolean new1;
edict_t marker;
float d1, d2;
trace_t tr; // mem
float[] v_forward = { 0, 0, 0 }, v_right = { 0, 0, 0 };
float left, center, right;
float[] left_target = { 0, 0, 0 }, right_target = { 0, 0, 0 };
// if we're going to a combat point, just proceed
if ((self.monsterinfo.aiflags & Defines.AI_COMBAT_POINT) != 0) {
M.M_MoveToGoal(self, dist);
return;
}
if ((self.monsterinfo.aiflags & Defines.AI_SOUND_TARGET) != 0) {
Math3D.VectorSubtract(self.s.origin, self.enemy.s.origin, v);
// ...and reached it
if (Math3D.VectorLength(v) < 64) {
//don't move, just stand and listen.
//self.monsterinfo.aiflags |= (Defines.AI_STAND_GROUND | Defines.AI_TEMP_STAND_GROUND);
self.monsterinfo.stand.think(self);
// since now it is aware and does not to be triggered again.
self.spawnflags &= ~1;
self.enemy = null;
}
else
M.M_MoveToGoal(self, dist);
// look for new targets
if (!GameUtil.FindTarget(self))
return;
}
if (ai_checkattack(self, dist))
return;
if (self.monsterinfo.attack_state == Defines.AS_SLIDING) {
ai_run_slide(self, dist);
return;
}
if (enemy_vis) {
//if (self.aiflags & AI_LOST_SIGHT)
// dprint("regained sight\n");
M.M_MoveToGoal(self, dist);
self.monsterinfo.aiflags &= ~Defines.AI_LOST_SIGHT;
Math3D.VectorCopy(self.enemy.s.origin, self.monsterinfo.last_sighting);
self.monsterinfo.trail_time = GameBase.level.time;
return;
}
// coop will change to another enemy if visible
if (GameBase.coop.value != 0) {
// FIXME: insane guys get mad with this, which causes crashes!
if (GameUtil.FindTarget(self))
return;
}
if ((self.monsterinfo.search_time != 0)
&& (GameBase.level.time > (self.monsterinfo.search_time + 20))) {
M.M_MoveToGoal(self, dist);
self.monsterinfo.search_time = 0;
//dprint("search timeout\n");
return;
}
save = self.goalentity;
tempgoal = GameUtil.G_Spawn();
self.goalentity = tempgoal;
new1 = false;
if (0 == (self.monsterinfo.aiflags & Defines.AI_LOST_SIGHT)) {
// just lost sight of the player, decide where to go first
// dprint("lost sight of player, last seen at ");
// dprint(vtos(self.last_sighting));
// dprint("\n");
self.monsterinfo.aiflags |= (Defines.AI_LOST_SIGHT | Defines.AI_PURSUIT_LAST_SEEN);
self.monsterinfo.aiflags &= ~(Defines.AI_PURSUE_NEXT | Defines.AI_PURSUE_TEMP);
new1 = true;
}
if ((self.monsterinfo.aiflags & Defines.AI_PURSUE_NEXT) != 0) {
self.monsterinfo.aiflags &= ~Defines.AI_PURSUE_NEXT;
// dprint("reached current goal: ");
// dprint(vtos(self.origin));
// dprint(" ");
// dprint(vtos(self.last_sighting));
// dprint(" ");
// dprint(ftos(vlen(self.origin - self.last_sighting)));
// dprint("\n");
// give ourself more time since we got this far
self.monsterinfo.search_time = GameBase.level.time + 5;
if ((self.monsterinfo.aiflags & Defines.AI_PURSUE_TEMP) != 0) {
// dprint("was temp goal; retrying original\n");
self.monsterinfo.aiflags &= ~Defines.AI_PURSUE_TEMP;
marker = null;
Math3D.VectorCopy(self.monsterinfo.saved_goal, self.monsterinfo.last_sighting);
new1 = true;
} else if ((self.monsterinfo.aiflags & Defines.AI_PURSUIT_LAST_SEEN) != 0) {
self.monsterinfo.aiflags &= ~Defines.AI_PURSUIT_LAST_SEEN;
marker = PlayerTrail.PickFirst(self);
} else {
marker = PlayerTrail.PickNext(self);
}
if (marker != null) {
Math3D.VectorCopy(marker.s.origin, self.monsterinfo.last_sighting);
self.monsterinfo.trail_time = marker.timestamp;
self.s.angles[Defines.YAW] = self.ideal_yaw = marker.s.angles[Defines.YAW];
// dprint("heading is ");
// dprint(ftos(self.ideal_yaw));
// dprint("\n");
// debug_drawline(self.origin, self.last_sighting, 52);
new1 = true;
}
}
Math3D.VectorSubtract(self.s.origin, self.monsterinfo.last_sighting, v);
d1 = Math3D.VectorLength(v);
if (d1 <= dist) {
self.monsterinfo.aiflags |= Defines.AI_PURSUE_NEXT;
dist = d1;
}
Math3D.VectorCopy(self.monsterinfo.last_sighting, self.goalentity.s.origin);
if (new1) {
// gi.dprintf("checking for course correction\n");
tr = GameBase.gi.trace(self.s.origin, self.mins, self.maxs,
self.monsterinfo.last_sighting, self,
Defines.MASK_PLAYERSOLID);
if (tr.fraction < 1) {
Math3D.VectorSubtract(self.goalentity.s.origin, self.s.origin, v);
d1 = Math3D.VectorLength(v);
center = tr.fraction;
d2 = d1 * ((center + 1) / 2);
self.s.angles[Defines.YAW] = self.ideal_yaw = Math3D.vectoyaw(v);
Math3D.AngleVectors(self.s.angles, v_forward, v_right, null);
Math3D.VectorSet(v, d2, -16, 0);
Math3D.G_ProjectSource(self.s.origin, v, v_forward, v_right, left_target);
tr = GameBase.gi.trace(self.s.origin, self.mins, self.maxs,
left_target, self, Defines.MASK_PLAYERSOLID);
left = tr.fraction;
Math3D.VectorSet(v, d2, 16, 0);
Math3D.G_ProjectSource(self.s.origin, v, v_forward,
v_right, right_target);
tr = GameBase.gi.trace(self.s.origin, self.mins, self.maxs,
right_target, self, Defines.MASK_PLAYERSOLID);
right = tr.fraction;
center = (d1 * center) / d2;
if (left >= center && left > right) {
if (left < 1) {
Math3D.VectorSet(v, d2 * left * 0.5f, -16f, 0f);
Math3D.G_ProjectSource(self.s.origin, v, v_forward,
v_right, left_target);
// gi.dprintf("incomplete path, go part way and adjust again\n");
}
Math3D.VectorCopy(self.monsterinfo.last_sighting, self.monsterinfo.saved_goal);
self.monsterinfo.aiflags |= Defines.AI_PURSUE_TEMP;
Math3D.VectorCopy(left_target, self.goalentity.s.origin);
Math3D.VectorCopy(left_target, self.monsterinfo.last_sighting);
Math3D.VectorSubtract(self.goalentity.s.origin, self.s.origin, v);
self.s.angles[Defines.YAW] = self.ideal_yaw = Math3D.vectoyaw(v);
// gi.dprintf("adjusted left\n");
// debug_drawline(self.origin, self.last_sighting, 152);
} else if (right >= center && right > left) {
if (right < 1) {
Math3D.VectorSet(v, d2 * right * 0.5f, 16f, 0f);
Math3D.G_ProjectSource(self.s.origin, v, v_forward,
v_right, right_target);
// gi.dprintf("incomplete path, go part way and adjust again\n");
}
Math3D.VectorCopy(self.monsterinfo.last_sighting, self.monsterinfo.saved_goal);
self.monsterinfo.aiflags |= Defines.AI_PURSUE_TEMP;
Math3D.VectorCopy(right_target, self.goalentity.s.origin);
Math3D.VectorCopy(right_target, self.monsterinfo.last_sighting);
Math3D.VectorSubtract(self.goalentity.s.origin, self.s.origin, v);
self.s.angles[Defines.YAW] = self.ideal_yaw = Math3D.vectoyaw(v);
// gi.dprintf("adjusted right\n");
// debug_drawline(self.origin, self.last_sighting, 152);
}
}
// else gi.dprintf("course was fine\n");
}
M.M_MoveToGoal(self, dist);
GameUtil.G_FreeEdict(tempgoal);
if (self != null)
self.goalentity = save;
}
};
static boolean enemy_vis;
static boolean enemy_infront;
static int enemy_range;
static float enemy_yaw;
}

View File

@@ -0,0 +1,658 @@
/*
* 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 lombok.extern.slf4j.Slf4j;
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 javax.annotation.Nullable;
import java.util.StringTokenizer;
@Slf4j
public class GameBase {
public static cplane_t dummyplane = new cplane_t();
public static game_locals_t game = new game_locals_t();
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_t g_edicts[] = new edict_t[Defines.MAX_EDICTS];
static {
for (int n = 0; n < Defines.MAX_EDICTS; n++)
g_edicts[n] = new edict_t(n);
}
public static cvar_t deathmatch = new cvar_t();
public static cvar_t coop = new cvar_t();
public static cvar_t dmflags = new cvar_t();
public static cvar_t skill; // = new cvar_t();
public static cvar_t fraglimit = new cvar_t();
public static cvar_t timelimit = new cvar_t();
public static cvar_t password = new cvar_t();
public static cvar_t spectator_password = new cvar_t();
public static cvar_t needpass = new cvar_t();
public static cvar_t maxclients = new cvar_t();
public static cvar_t maxspectators = new cvar_t();
public static cvar_t maxentities = new cvar_t();
public static cvar_t g_select_empty = new cvar_t();
public static cvar_t filterban = new cvar_t();
public static cvar_t sv_maxvelocity = new cvar_t();
public static cvar_t sv_gravity = new cvar_t();
public static cvar_t sv_rollspeed = new cvar_t();
public static cvar_t sv_rollangle = new cvar_t();
public static cvar_t gun_x = new cvar_t();
public static cvar_t gun_y = new cvar_t();
public static cvar_t gun_z = new cvar_t();
public static cvar_t run_pitch = new cvar_t();
public static cvar_t run_roll = new cvar_t();
public static cvar_t bob_up = new cvar_t();
public static cvar_t bob_pitch = new cvar_t();
public static cvar_t bob_roll = new cvar_t();
public static cvar_t sv_cheats = new cvar_t();
public static cvar_t flood_msgs = new cvar_t();
public static cvar_t flood_persecond = new cvar_t();
public static cvar_t flood_waitdelay = new cvar_t();
public static cvar_t sv_maplist = new cvar_t();
public final static float STOP_EPSILON = 0.1f;
/**
* Slide off of the impacting object returns the blocked flags (1 = floor, 2 =
* step / wall).
*/
public static int 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;
}
return blocked;
}
/**
* Searches all active entities for the next one that holds the matching
* string at fieldofs (use the FOFS() macro) in the structure.
*
* 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(0);
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_t 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(0);
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;
}
/**
* Searches all active entities for the next one that holds the matching
* string at fieldofs (use the FOFS() macro) in the structure.
*
* 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 int MAXCHOICES = 8;
public static edict_t G_PickTarget(String targetname) {
int num_choices = 0;
edict_t choice[] = new edict_t[MAXCHOICES];
if (targetname == null) {
log.info("G_PickTarget called with null targetname");
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) {
log.info("G_PickTarget: target {} not found", targetname);
return null;
}
return choice[Lib.rand() % num_choices];
}
public static float[] VEC_UP = { 0, -1, 0 };
public static float[] MOVEDIR_UP = { 0, 0, 1 };
public static float[] VEC_DOWN = { 0, -2, 0 };
public static float[] MOVEDIR_DOWN = { 0, 0, -1 };
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 new String(in);
}
/**
* G_TouchTriggers
*/
static edict_t touch[] = new edict_t[Defines.MAX_EDICTS];
public static void G_TouchTriggers(edict_t ent) {
int i, num;
edict_t 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);
}
}
public static pushed_t pushed[] = new pushed_t[Defines.MAX_EDICTS];
static {
for (int n = 0; n < Defines.MAX_EDICTS; n++)
pushed[n] = new pushed_t();
}
public static int pushed_p;
public static edict_t obstacle;
public static int c_yes, c_no;
public static int STEPSIZE = 18;
/**
* G_RunEntity
*/
public static void G_RunEntity(edict_t ent) {
if (ent.prethink != null)
ent.prethink.think(ent);
switch ((int) 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 " + (int) 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 EdictFindFilter findByTarget = new EdictFindFilter() {
public boolean matches(edict_t e, String s) {
if (e.targetname == null)
return false;
return e.targetname.equalsIgnoreCase(s);
}
};
public static EdictFindFilter findByClass = new EdictFindFilter() {
public boolean matches(edict_t e, String s) {
return e.classname.equalsIgnoreCase(s);
}
};
public static void ShutdownGame() {
log.info("==== ShutdownGame ====");
}
/**
* ClientEndServerFrames.
*/
public static void ClientEndServerFrames() {
int i;
edict_t 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.
*/
public static edict_t CreateTargetChangeLevel(String map) {
edict_t 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.
*/
public static void EndDMLevel() {
edict_t 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.
*/
public 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.
*/
public static void CheckDMRules() {
int i;
gclient_t 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.
*/
public static void ExitLevel() {
int i;
edict_t 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
*
* Advances the world by Defines.FRAMETIME (0.1) seconds.
*/
public static void G_RunFrame() {
int i;
edict_t 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 pmove_t.PointContentsAdapter() {
public int pointcontents(float[] o) {
return SV_WORLD.SV_PointContents(o);
}
};
}
}

View File

@@ -0,0 +1,183 @@
/*
* 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.server.SV_WORLD;
import lwjake2.util.Math3D;
public class GameChase {
public static void UpdateChaseCam(edict_t ent) {
float[] o = { 0, 0, 0 }, ownerv = { 0, 0, 0 }, goal = { 0, 0, 0 };
edict_t targ;
float[] forward = { 0, 0, 0 }, right = { 0, 0, 0 };
trace_t trace;
int i;
float[] oldgoal = { 0, 0, 0 };
float[] angles = { 0, 0, 0 };
// is our chase target gone?
if (!ent.client.chase_target.inuse
|| ent.client.chase_target.client.resp.spectator) {
edict_t old = ent.client.chase_target;
ChaseNext(ent);
if (ent.client.chase_target == old) {
ent.client.chase_target = null;
ent.client.ps.pmove.pm_flags &= ~pmove_t.PMF_NO_PREDICTION;
return;
}
}
targ = ent.client.chase_target;
Math3D.VectorCopy(targ.s.origin, ownerv);
Math3D.VectorCopy(ent.s.origin, oldgoal);
ownerv[2] += targ.viewheight;
Math3D.VectorCopy(targ.client.v_angle, angles);
if (angles[Defines.PITCH] > 56)
angles[Defines.PITCH] = 56;
Math3D.AngleVectors(angles, forward, right, null);
Math3D.VectorNormalize(forward);
Math3D.VectorMA(ownerv, -30, forward, o);
if (o[2] < targ.s.origin[2] + 20)
o[2] = targ.s.origin[2] + 20;
// jump animation lifts
if (targ.groundentity == null)
o[2] += 16;
trace = GameBase.gi.trace(ownerv, Globals.vec3_origin,
Globals.vec3_origin, o, targ, Defines.MASK_SOLID);
Math3D.VectorCopy(trace.endpos, goal);
Math3D.VectorMA(goal, 2, forward, goal);
// pad for floors and ceilings
Math3D.VectorCopy(goal, o);
o[2] += 6;
trace = GameBase.gi.trace(goal, Globals.vec3_origin,
Globals.vec3_origin, o, targ, Defines.MASK_SOLID);
if (trace.fraction < 1) {
Math3D.VectorCopy(trace.endpos, goal);
goal[2] -= 6;
}
Math3D.VectorCopy(goal, o);
o[2] -= 6;
trace = GameBase.gi.trace(goal, Globals.vec3_origin,
Globals.vec3_origin, o, targ, Defines.MASK_SOLID);
if (trace.fraction < 1) {
Math3D.VectorCopy(trace.endpos, goal);
goal[2] += 6;
}
if (targ.deadflag != 0)
ent.client.ps.pmove.pm_type = Defines.PM_DEAD;
else
ent.client.ps.pmove.pm_type = Defines.PM_FREEZE;
Math3D.VectorCopy(goal, ent.s.origin);
for (i = 0; i < 3; i++)
ent.client.ps.pmove.delta_angles[i] = (short) Math3D
.ANGLE2SHORT(targ.client.v_angle[i]
- ent.client.resp.cmd_angles[i]);
if (targ.deadflag != 0) {
ent.client.ps.viewangles[Defines.ROLL] = 40;
ent.client.ps.viewangles[Defines.PITCH] = -15;
ent.client.ps.viewangles[Defines.YAW] = targ.client.killer_yaw;
} else {
Math3D.VectorCopy(targ.client.v_angle, ent.client.ps.viewangles);
Math3D.VectorCopy(targ.client.v_angle, ent.client.v_angle);
}
ent.viewheight = 0;
ent.client.ps.pmove.pm_flags |= pmove_t.PMF_NO_PREDICTION;
SV_WORLD.SV_LinkEdict(ent);
}
public static void ChaseNext(edict_t ent) {
int i;
edict_t e;
if (null == ent.client.chase_target)
return;
i = ent.client.chase_target.index;
do {
i++;
if (i > GameBase.maxclients.value)
i = 1;
e = GameBase.g_edicts[i];
if (!e.inuse)
continue;
if (!e.client.resp.spectator)
break;
} while (e != ent.client.chase_target);
ent.client.chase_target = e;
ent.client.update_chase = true;
}
public static void ChasePrev(edict_t ent) {
int i;
edict_t e;
if (ent.client.chase_target == null)
return;
i = ent.client.chase_target.index;
do {
i--;
if (i < 1)
i = (int) GameBase.maxclients.value;
e = GameBase.g_edicts[i];
if (!e.inuse)
continue;
if (!e.client.resp.spectator)
break;
} while (e != ent.client.chase_target);
ent.client.chase_target = e;
ent.client.update_chase = true;
}
public static void GetChaseTarget(edict_t ent) {
int i;
edict_t other;
for (i = 1; i <= GameBase.maxclients.value; i++) {
other = GameBase.g_edicts[i];
if (other.inuse && !other.client.resp.spectator) {
ent.client.chase_target = other;
ent.client.update_chase = true;
UpdateChaseCam(ent);
return;
}
}
GameBase.gi.centerprintf(ent, "No other players to chase.");
}
}

View File

@@ -0,0 +1,548 @@
/*
* 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.Math3D;
public class GameCombat {
/**
* CanDamage
*
* Returns true if the inflictor can directly damage the target. Used for
* explosions and melee attacks.
*/
static boolean CanDamage(edict_t targ, edict_t inflictor) {
float[] dest = { 0, 0, 0 };
trace_t trace;
// bmodels need special checking because their origin is 0,0,0
if (targ.movetype == Defines.MOVETYPE_PUSH) {
Math3D.VectorAdd(targ.absmin, targ.absmax, dest);
Math3D.VectorScale(dest, 0.5f, dest);
trace = GameBase.gi.trace(inflictor.s.origin, Globals.vec3_origin,
Globals.vec3_origin, dest, inflictor, Defines.MASK_SOLID);
if (trace.fraction == 1.0f)
return true;
if (trace.ent == targ)
return true;
return false;
}
trace = GameBase.gi.trace(inflictor.s.origin, Globals.vec3_origin,
Globals.vec3_origin, targ.s.origin, inflictor,
Defines.MASK_SOLID);
if (trace.fraction == 1.0)
return true;
Math3D.VectorCopy(targ.s.origin, dest);
dest[0] += 15.0;
dest[1] += 15.0;
trace = GameBase.gi.trace(inflictor.s.origin, Globals.vec3_origin,
Globals.vec3_origin, dest, inflictor, Defines.MASK_SOLID);
if (trace.fraction == 1.0)
return true;
Math3D.VectorCopy(targ.s.origin, dest);
dest[0] += 15.0;
dest[1] -= 15.0;
trace = GameBase.gi.trace(inflictor.s.origin, Globals.vec3_origin,
Globals.vec3_origin, dest, inflictor, Defines.MASK_SOLID);
if (trace.fraction == 1.0)
return true;
Math3D.VectorCopy(targ.s.origin, dest);
dest[0] -= 15.0;
dest[1] += 15.0;
trace = GameBase.gi.trace(inflictor.s.origin, Globals.vec3_origin,
Globals.vec3_origin, dest, inflictor, Defines.MASK_SOLID);
if (trace.fraction == 1.0)
return true;
Math3D.VectorCopy(targ.s.origin, dest);
dest[0] -= 15.0;
dest[1] -= 15.0;
trace = GameBase.gi.trace(inflictor.s.origin, Globals.vec3_origin,
Globals.vec3_origin, dest, inflictor, Defines.MASK_SOLID);
if (trace.fraction == 1.0)
return true;
return false;
}
/**
* Killed.
*/
public static void Killed(edict_t targ, edict_t inflictor,
edict_t attacker, int damage, float[] point) {
Com.DPrintf("Killing a " + targ.classname + "\n");
if (targ.health < -999)
targ.health = -999;
targ.enemy = attacker;
if ((targ.svflags & Defines.SVF_MONSTER) != 0
&& (targ.deadflag != Defines.DEAD_DEAD)) {
// targ.svflags |= SVF_DEADMONSTER; // now treat as a different
// content type
if (0 == (targ.monsterinfo.aiflags & Defines.AI_GOOD_GUY)) {
GameBase.level.killed_monsters++;
if (GameBase.coop.value != 0 && attacker.client != null)
attacker.client.resp.score++;
// medics won't heal monsters that they kill themselves
if (attacker.classname.equals("monster_medic"))
targ.owner = attacker;
}
}
if (targ.movetype == Defines.MOVETYPE_PUSH
|| targ.movetype == Defines.MOVETYPE_STOP
|| targ.movetype == Defines.MOVETYPE_NONE) { // doors, triggers,
// etc
targ.die.die(targ, inflictor, attacker, damage, point);
return;
}
if ((targ.svflags & Defines.SVF_MONSTER) != 0
&& (targ.deadflag != Defines.DEAD_DEAD)) {
targ.touch = null;
Monster.monster_death_use(targ);
}
targ.die.die(targ, inflictor, attacker, damage, point);
}
/**
* SpawnDamage.
*/
static void SpawnDamage(int type, float[] origin, float[] normal, int damage) {
if (damage > 255)
damage = 255;
GameBase.gi.WriteByte(Defines.svc_temp_entity);
GameBase.gi.WriteByte(type);
// gi.WriteByte (damage);
GameBase.gi.WritePosition(origin);
GameBase.gi.WriteDir(normal);
GameBase.gi.multicast(origin, Defines.MULTICAST_PVS);
}
static int CheckPowerArmor(edict_t ent, float[] point, float[] normal,
int damage, int dflags) {
gclient_t client;
int save;
int power_armor_type;
int index = 0;
int damagePerCell;
int pa_te_type;
int power = 0;
int power_used;
if (damage == 0)
return 0;
client = ent.client;
if ((dflags & Defines.DAMAGE_NO_ARMOR) != 0)
return 0;
if (client != null) {
power_armor_type = GameItems.PowerArmorType(ent);
if (power_armor_type != Defines.POWER_ARMOR_NONE) {
index = GameItems.ITEM_INDEX(GameItems.FindItem("Cells"));
power = client.pers.inventory[index];
}
} else if ((ent.svflags & Defines.SVF_MONSTER) != 0) {
power_armor_type = ent.monsterinfo.power_armor_type;
power = ent.monsterinfo.power_armor_power;
} else
return 0;
if (power_armor_type == Defines.POWER_ARMOR_NONE)
return 0;
if (power == 0)
return 0;
if (power_armor_type == Defines.POWER_ARMOR_SCREEN) {
float[] vec = { 0, 0, 0 };
float dot;
float[] forward = { 0, 0, 0 };
// only works if damage point is in front
Math3D.AngleVectors(ent.s.angles, forward, null, null);
Math3D.VectorSubtract(point, ent.s.origin, vec);
Math3D.VectorNormalize(vec);
dot = Math3D.DotProduct(vec, forward);
if (dot <= 0.3)
return 0;
damagePerCell = 1;
pa_te_type = Defines.TE_SCREEN_SPARKS;
damage = damage / 3;
} else {
damagePerCell = 2;
pa_te_type = Defines.TE_SHIELD_SPARKS;
damage = (2 * damage) / 3;
}
save = power * damagePerCell;
if (save == 0)
return 0;
if (save > damage)
save = damage;
SpawnDamage(pa_te_type, point, normal, save);
ent.powerarmor_time = GameBase.level.time + 0.2f;
power_used = save / damagePerCell;
if (client != null)
client.pers.inventory[index] -= power_used;
else
ent.monsterinfo.power_armor_power -= power_used;
return save;
}
static int CheckArmor(edict_t ent, float[] point, float[] normal,
int damage, int te_sparks, int dflags) {
gclient_t client;
int save;
int index;
gitem_t armor;
if (damage == 0)
return 0;
client = ent.client;
if (client == null)
return 0;
if ((dflags & Defines.DAMAGE_NO_ARMOR) != 0)
return 0;
index = GameItems.ArmorIndex(ent);
if (index == 0)
return 0;
armor = GameItems.GetItemByIndex(index);
gitem_armor_t garmor = (gitem_armor_t) armor.info;
if (0 != (dflags & Defines.DAMAGE_ENERGY))
save = (int) Math.ceil(garmor.energy_protection * damage);
else
save = (int) Math.ceil(garmor.normal_protection * damage);
if (save >= client.pers.inventory[index])
save = client.pers.inventory[index];
if (save == 0)
return 0;
client.pers.inventory[index] -= save;
SpawnDamage(te_sparks, point, normal, save);
return save;
}
public static void M_ReactToDamage(edict_t targ, edict_t attacker) {
if ((null != attacker.client)
&& 0 != (attacker.svflags & Defines.SVF_MONSTER))
return;
if (attacker == targ || attacker == targ.enemy)
return;
// if we are a good guy monster and our attacker is a player
// or another good guy, do not get mad at them
if (0 != (targ.monsterinfo.aiflags & Defines.AI_GOOD_GUY)) {
if (attacker.client != null
|| (attacker.monsterinfo.aiflags & Defines.AI_GOOD_GUY) != 0)
return;
}
// we now know that we are not both good guys
// if attacker is a client, get mad at them because he's good and we're
// not
if (attacker.client != null) {
targ.monsterinfo.aiflags &= ~Defines.AI_SOUND_TARGET;
// this can only happen in coop (both new and old enemies are
// clients)
// only switch if can't see the current enemy
if (targ.enemy != null && targ.enemy.client != null) {
if (GameUtil.visible(targ, targ.enemy)) {
targ.oldenemy = attacker;
return;
}
targ.oldenemy = targ.enemy;
}
targ.enemy = attacker;
if (0 == (targ.monsterinfo.aiflags & Defines.AI_DUCKED))
GameUtil.FoundTarget(targ);
return;
}
// it's the same base (walk/swim/fly) type and a different classname and
// it's not a tank
// (they spray too much), get mad at them
if (((targ.flags & (Defines.FL_FLY | Defines.FL_SWIM)) == (attacker.flags & (Defines.FL_FLY | Defines.FL_SWIM)))
&& (!(targ.classname.equals(attacker.classname)))
&& (!(attacker.classname.equals("monster_tank")))
&& (!(attacker.classname.equals("monster_supertank")))
&& (!(attacker.classname.equals("monster_makron")))
&& (!(attacker.classname.equals("monster_jorg")))) {
if (targ.enemy != null && targ.enemy.client != null)
targ.oldenemy = targ.enemy;
targ.enemy = attacker;
if (0 == (targ.monsterinfo.aiflags & Defines.AI_DUCKED))
GameUtil.FoundTarget(targ);
}
// if they *meant* to shoot us, then shoot back
else if (attacker.enemy == targ) {
if (targ.enemy != null && targ.enemy.client != null)
targ.oldenemy = targ.enemy;
targ.enemy = attacker;
if (0 == (targ.monsterinfo.aiflags & Defines.AI_DUCKED))
GameUtil.FoundTarget(targ);
}
// otherwise get mad at whoever they are mad at (help our buddy) unless
// it is us!
else if (attacker.enemy != null && attacker.enemy != targ) {
if (targ.enemy != null && targ.enemy.client != null)
targ.oldenemy = targ.enemy;
targ.enemy = attacker.enemy;
if (0 == (targ.monsterinfo.aiflags & Defines.AI_DUCKED))
GameUtil.FoundTarget(targ);
}
}
static boolean CheckTeamDamage(edict_t targ, edict_t attacker) {
//FIXME make the next line real and uncomment this block
// if ((ability to damage a teammate == OFF) && (targ's team ==
// attacker's team))
return false;
}
/**
* T_RadiusDamage.
*/
static void T_RadiusDamage(edict_t inflictor, edict_t attacker,
float damage, edict_t ignore, float radius, int mod) {
float points;
EdictIterator edictit = null;
float[] v = { 0, 0, 0 };
float[] dir = { 0, 0, 0 };
while ((edictit = GameBase.findradius(edictit, inflictor.s.origin,
radius)) != null) {
edict_t ent = edictit.o;
if (ent == ignore)
continue;
if (ent.takedamage == 0)
continue;
Math3D.VectorAdd(ent.mins, ent.maxs, v);
Math3D.VectorMA(ent.s.origin, 0.5f, v, v);
Math3D.VectorSubtract(inflictor.s.origin, v, v);
points = damage - 0.5f * Math3D.VectorLength(v);
if (ent == attacker)
points = points * 0.5f;
if (points > 0) {
if (CanDamage(ent, inflictor)) {
Math3D.VectorSubtract(ent.s.origin, inflictor.s.origin, dir);
T_Damage(ent, inflictor, attacker, dir, inflictor.s.origin,
Globals.vec3_origin, (int) points, (int) points,
Defines.DAMAGE_RADIUS, mod);
}
}
}
}
public static void T_Damage(edict_t targ, edict_t inflictor,
edict_t attacker, float[] dir, float[] point, float[] normal,
int damage, int knockback, int dflags, int mod) {
gclient_t client;
int take;
int save;
int asave;
int psave;
int te_sparks;
if (targ.takedamage == 0)
return;
// friendly fire avoidance
// if enabled you can't hurt teammates (but you can hurt yourself)
// knockback still occurs
if ((targ != attacker)
&& ((GameBase.deathmatch.value != 0 && 0 != ((int) (GameBase.dmflags.value) & (Defines.DF_MODELTEAMS | Defines.DF_SKINTEAMS))) || GameBase.coop.value != 0)) {
if (GameUtil.OnSameTeam(targ, attacker)) {
if (((int) (GameBase.dmflags.value) & Defines.DF_NO_FRIENDLY_FIRE) != 0)
damage = 0;
else
mod |= Defines.MOD_FRIENDLY_FIRE;
}
}
GameBase.meansOfDeath = mod;
// easy mode takes half damage
if (GameBase.skill.value == 0 && GameBase.deathmatch.value == 0
&& targ.client != null) {
damage *= 0.5;
if (damage == 0)
damage = 1;
}
client = targ.client;
if ((dflags & Defines.DAMAGE_BULLET) != 0)
te_sparks = Defines.TE_BULLET_SPARKS;
else
te_sparks = Defines.TE_SPARKS;
Math3D.VectorNormalize(dir);
// bonus damage for suprising a monster
if (0 == (dflags & Defines.DAMAGE_RADIUS)
&& (targ.svflags & Defines.SVF_MONSTER) != 0
&& (attacker.client != null) && (targ.enemy == null)
&& (targ.health > 0))
damage *= 2;
if ((targ.flags & Defines.FL_NO_KNOCKBACK) != 0)
knockback = 0;
// figure momentum add
if (0 == (dflags & Defines.DAMAGE_NO_KNOCKBACK)) {
if ((knockback != 0) && (targ.movetype != Defines.MOVETYPE_NONE)
&& (targ.movetype != Defines.MOVETYPE_BOUNCE)
&& (targ.movetype != Defines.MOVETYPE_PUSH)
&& (targ.movetype != Defines.MOVETYPE_STOP)) {
float[] kvel = { 0, 0, 0 };
float mass;
if (targ.mass < 50)
mass = 50;
else
mass = targ.mass;
if (targ.client != null && attacker == targ)
Math3D.VectorScale(dir, 1600.0f * (float) knockback / mass,
kvel);
// the rocket jump hack...
else
Math3D.VectorScale(dir, 500.0f * (float) knockback / mass,
kvel);
Math3D.VectorAdd(targ.velocity, kvel, targ.velocity);
}
}
take = damage;
save = 0;
// check for godmode
if ((targ.flags & Defines.FL_GODMODE) != 0
&& 0 == (dflags & Defines.DAMAGE_NO_PROTECTION)) {
take = 0;
save = damage;
SpawnDamage(te_sparks, point, normal, save);
}
// check for invincibility
if ((client != null && client.invincible_framenum > GameBase.level.framenum)
&& 0 == (dflags & Defines.DAMAGE_NO_PROTECTION)) {
if (targ.pain_debounce_time < GameBase.level.time) {
GameBase.gi.sound(targ, Defines.CHAN_ITEM, GameBase.gi
.soundindex("items/protect4.wav"), 1,
Defines.ATTN_NORM, 0);
targ.pain_debounce_time = GameBase.level.time + 2;
}
take = 0;
save = damage;
}
psave = CheckPowerArmor(targ, point, normal, take, dflags);
take -= psave;
asave = CheckArmor(targ, point, normal, take, te_sparks, dflags);
take -= asave;
// treat cheat/powerup savings the same as armor
asave += save;
// team damage avoidance
if (0 == (dflags & Defines.DAMAGE_NO_PROTECTION)
&& CheckTeamDamage(targ, attacker))
return;
// do the damage
if (take != 0) {
if (0 != (targ.svflags & Defines.SVF_MONSTER) || (client != null))
SpawnDamage(Defines.TE_BLOOD, point, normal, take);
else
SpawnDamage(te_sparks, point, normal, take);
targ.health = targ.health - take;
if (targ.health <= 0) {
if ((targ.svflags & Defines.SVF_MONSTER) != 0
|| (client != null))
targ.flags |= Defines.FL_NO_KNOCKBACK;
Killed(targ, inflictor, attacker, take, point);
return;
}
}
if ((targ.svflags & Defines.SVF_MONSTER) != 0) {
M_ReactToDamage(targ, attacker);
if (0 == (targ.monsterinfo.aiflags & Defines.AI_DUCKED)
&& (take != 0)) {
targ.pain.pain(targ, attacker, knockback, take);
// nightmare mode monsters don't go into pain frames often
if (GameBase.skill.value == 3)
targ.pain_debounce_time = GameBase.level.time + 5;
}
} else if (client != null) {
if (((targ.flags & Defines.FL_GODMODE) == 0) && (take != 0))
targ.pain.pain(targ, attacker, knockback, take);
} else if (take != 0) {
if (targ.pain != null)
targ.pain.pain(targ, attacker, knockback, take);
}
// add to the damage inflicted on a player this frame
// the total will be turned into screen blends and view angle kicks
// at the end of the frame
if (client != null) {
client.damage_parmor += psave;
client.damage_armor += asave;
client.damage_blood += take;
client.damage_knockback += knockback;
Math3D.VectorCopy(point, client.damage_from);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,753 @@
/*
* 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;
public class GameItemList {
// RST: this was separated in the java conversion from the g_item.c
// because all adapters have to be created in the other
// classes before this class can be loaded.
public static gitem_t itemlist[] = {
//leave index 0 alone
new gitem_t(null, null, null, null, null, null, null, 0, null,
null, null, 0, 0, null, 0, 0, null, 0, null),
//
// ARMOR
//
new gitem_t(
/**
* QUAKED item_armor_body (.3 .3 1) (-16 -16 -16) (16 16 16)
*/
"item_armor_body", GameItems.Pickup_Armor, null, null, null,
"misc/ar1_pkup.wav", "models/items/armor/body/tris.md2",
Defines.EF_ROTATE, null,
/* icon */
"i_bodyarmor",
/* pickup */
"Body Armor",
/* width */
3, 0, null, Defines.IT_ARMOR, 0, GameItems.bodyarmor_info,
Defines.ARMOR_BODY,
/* precache */
""),
/*
* QUAKED item_armor_combat (.3 .3 1) (-16 -16 -16) (16 16 16)
*/
new gitem_t("item_armor_combat", GameItems.Pickup_Armor, null, null, null,
"misc/ar1_pkup.wav", "models/items/armor/combat/tris.md2",
Defines.EF_ROTATE, null,
/* icon */
"i_combatarmor",
/* pickup */
"Combat Armor",
/* width */
3, 0, null, Defines.IT_ARMOR, 0, GameItems.combatarmor_info,
Defines.ARMOR_COMBAT,
/* precache */
""),
/*
* QUAKED item_armor_jacket (.3 .3 1) (-16 -16 -16) (16 16 16)
*/
new gitem_t("item_armor_jacket", GameItems.Pickup_Armor, null, null, null,
"misc/ar1_pkup.wav", "models/items/armor/jacket/tris.md2",
Defines.EF_ROTATE, null,
/* icon */
"i_jacketarmor",
/* pickup */
"Jacket Armor",
/* width */
3, 0, null, Defines.IT_ARMOR, 0, GameItems.jacketarmor_info,
Defines.ARMOR_JACKET,
/* precache */
""),
/*
* QUAKED item_armor_shard (.3 .3 1) (-16 -16 -16) (16 16 16)
*/
new gitem_t("item_armor_shard", GameItems.Pickup_Armor, null, null, null,
"misc/ar2_pkup.wav", "models/items/armor/shard/tris.md2",
Defines.EF_ROTATE, null,
/* icon */
"i_jacketarmor",
/* pickup */
"Armor Shard",
/* width */
3, 0, null, Defines.IT_ARMOR, 0, null, Defines.ARMOR_SHARD,
/* precache */
""),
/*
* QUAKED item_power_screen (.3 .3 1) (-16 -16 -16) (16 16 16)
*/
new gitem_t("item_power_screen", GameItems.Pickup_PowerArmor, GameItems.Use_PowerArmor,
GameItems.Drop_PowerArmor, null, "misc/ar3_pkup.wav",
"models/items/armor/screen/tris.md2", Defines.EF_ROTATE,
null,
/* icon */
"i_powerscreen",
/* pickup */
"Power Screen",
/* width */
0, 60, null, Defines.IT_ARMOR, 0, null, 0,
/* precache */
""),
/*
* QUAKED item_power_shield (.3 .3 1) (-16 -16 -16) (16 16 16)
*/
new gitem_t("item_power_shield", GameItems.Pickup_PowerArmor, GameItems.Use_PowerArmor,
GameItems.Drop_PowerArmor, null, "misc/ar3_pkup.wav",
"models/items/armor/shield/tris.md2", Defines.EF_ROTATE,
null,
/* icon */
"i_powershield",
/* pickup */
"Power Shield",
/* width */
0, 60, null, Defines.IT_ARMOR, 0, null, 0,
/* precache */
"misc/power2.wav misc/power1.wav"),
//
// WEAPONS
//
/*
* weapon_blaster (.3 .3 1) (-16 -16 -16) (16 16 16) always owned,
* never in the world
*/
new gitem_t("weapon_blaster", null, PlayerWeapon.Use_Weapon, null,
PlayerWeapon.Weapon_Blaster, "misc/w_pkup.wav", null, 0,
"models/weapons/v_blast/tris.md2",
/* icon */
"w_blaster",
/* pickup */
"Blaster", 0, 0, null, Defines.IT_WEAPON
| Defines.IT_STAY_COOP, Defines.WEAP_BLASTER, null,
0,
/* precache */
"weapons/blastf1a.wav misc/lasfly.wav"),
/*
* QUAKED weapon_shotgun (.3 .3 1) (-16 -16 -16) (16 16 16)
*/
new gitem_t("weapon_shotgun", PlayerWeapon.Pickup_Weapon,
PlayerWeapon.Use_Weapon, PlayerWeapon.Drop_Weapon,
PlayerWeapon.Weapon_Shotgun, "misc/w_pkup.wav",
"models/weapons/g_shotg/tris.md2", Defines.EF_ROTATE,
"models/weapons/v_shotg/tris.md2",
/* icon */
"w_shotgun",
/* pickup */
"Shotgun", 0, 1, "Shells", Defines.IT_WEAPON
| Defines.IT_STAY_COOP, Defines.WEAP_SHOTGUN, null,
0,
/* precache */
"weapons/shotgf1b.wav weapons/shotgr1b.wav"),
/*
* QUAKED weapon_supershotgun (.3 .3 1) (-16 -16 -16) (16 16 16)
*/
new gitem_t("weapon_supershotgun", PlayerWeapon.Pickup_Weapon,
PlayerWeapon.Use_Weapon, PlayerWeapon.Drop_Weapon,
PlayerWeapon.Weapon_SuperShotgun, "misc/w_pkup.wav",
"models/weapons/g_shotg2/tris.md2", Defines.EF_ROTATE,
"models/weapons/v_shotg2/tris.md2",
/* icon */
"w_sshotgun",
/* pickup */
"Super Shotgun", 0, 2, "Shells", Defines.IT_WEAPON
| Defines.IT_STAY_COOP, Defines.WEAP_SUPERSHOTGUN,
null, 0,
/* precache */
"weapons/sshotf1b.wav"),
/*
* QUAKED weapon_machinegun (.3 .3 1) (-16 -16 -16) (16 16 16)
*/
new gitem_t(
"weapon_machinegun",
PlayerWeapon.Pickup_Weapon,
PlayerWeapon.Use_Weapon,
PlayerWeapon.Drop_Weapon,
PlayerWeapon.Weapon_Machinegun,
"misc/w_pkup.wav",
"models/weapons/g_machn/tris.md2",
Defines.EF_ROTATE,
"models/weapons/v_machn/tris.md2",
/* icon */
"w_machinegun",
/* pickup */
"Machinegun",
0,
1,
"Bullets",
Defines.IT_WEAPON | Defines.IT_STAY_COOP,
Defines.WEAP_MACHINEGUN,
null,
0,
/* precache */
"weapons/machgf1b.wav weapons/machgf2b.wav weapons/machgf3b.wav weapons/machgf4b.wav weapons/machgf5b.wav"),
/*
* QUAKED weapon_chaingun (.3 .3 1) (-16 -16 -16) (16 16 16)
*/
new gitem_t(
"weapon_chaingun",
PlayerWeapon.Pickup_Weapon,
PlayerWeapon.Use_Weapon,
PlayerWeapon.Drop_Weapon,
PlayerWeapon.Weapon_Chaingun,
"misc/w_pkup.wav",
"models/weapons/g_chain/tris.md2",
Defines.EF_ROTATE,
"models/weapons/v_chain/tris.md2",
/* icon */
"w_chaingun",
/* pickup */
"Chaingun",
0,
1,
"Bullets",
Defines.IT_WEAPON | Defines.IT_STAY_COOP,
Defines.WEAP_CHAINGUN,
null,
0,
/* precache */
"weapons/chngnu1a.wav weapons/chngnl1a.wav weapons/machgf3b.wav` weapons/chngnd1a.wav"),
/*
* QUAKED ammo_grenades (.3 .3 1) (-16 -16 -16) (16 16 16)
*/
new gitem_t(
"ammo_grenades",
GameItems.Pickup_Ammo,
PlayerWeapon.Use_Weapon,
GameItems.Drop_Ammo,
PlayerWeapon.Weapon_Grenade,
"misc/am_pkup.wav",
"models/items/ammo/grenades/medium/tris.md2",
0,
"models/weapons/v_handgr/tris.md2",
/* icon */
"a_grenades",
/* pickup */
"Grenades",
/* width */
3,
5,
"grenades",
Defines.IT_AMMO | Defines.IT_WEAPON,
Defines.WEAP_GRENADES,
null,
Defines.AMMO_GRENADES,
/* precache */
"weapons/hgrent1a.wav weapons/hgrena1b.wav weapons/hgrenc1b.wav weapons/hgrenb1a.wav weapons/hgrenb2a.wav "),
/*
* QUAKED weapon_grenadelauncher (.3 .3 1) (-16 -16 -16) (16 16 16)
*/
new gitem_t(
"weapon_grenadelauncher",
PlayerWeapon.Pickup_Weapon,
PlayerWeapon.Use_Weapon,
PlayerWeapon.Drop_Weapon,
PlayerWeapon.Weapon_GrenadeLauncher,
"misc/w_pkup.wav",
"models/weapons/g_launch/tris.md2",
Defines.EF_ROTATE,
"models/weapons/v_launch/tris.md2",
/* icon */
"w_glauncher",
/* pickup */
"Grenade Launcher",
0,
1,
"Grenades",
Defines.IT_WEAPON | Defines.IT_STAY_COOP,
Defines.WEAP_GRENADELAUNCHER,
null,
0,
/* precache */
"models/objects/grenade/tris.md2 weapons/grenlf1a.wav weapons/grenlr1b.wav weapons/grenlb1b.wav"),
/*
* QUAKED weapon_rocketlauncher (.3 .3 1) (-16 -16 -16) (16 16 16)
*/
new gitem_t(
"weapon_rocketlauncher",
PlayerWeapon.Pickup_Weapon,
PlayerWeapon.Use_Weapon,
PlayerWeapon.Drop_Weapon,
PlayerWeapon.Weapon_RocketLauncher,
"misc/w_pkup.wav",
"models/weapons/g_rocket/tris.md2",
Defines.EF_ROTATE,
"models/weapons/v_rocket/tris.md2",
/* icon */
"w_rlauncher",
/* pickup */
"Rocket Launcher",
0,
1,
"Rockets",
Defines.IT_WEAPON | Defines.IT_STAY_COOP,
Defines.WEAP_ROCKETLAUNCHER,
null,
0,
/* precache */
"models/objects/rocket/tris.md2 weapons/rockfly.wav weapons/rocklf1a.wav weapons/rocklr1b.wav models/objects/debris2/tris.md2"),
/*
* QUAKED weapon_hyperblaster (.3 .3 1) (-16 -16 -16) (16 16 16)
*/
new gitem_t(
"weapon_hyperblaster",
PlayerWeapon.Pickup_Weapon,
PlayerWeapon.Use_Weapon,
PlayerWeapon.Drop_Weapon,
PlayerWeapon.Weapon_HyperBlaster,
"misc/w_pkup.wav",
"models/weapons/g_hyperb/tris.md2",
Defines.EF_ROTATE,
"models/weapons/v_hyperb/tris.md2",
/* icon */
"w_hyperblaster",
/* pickup */
"HyperBlaster",
0,
1,
"Cells",
Defines.IT_WEAPON | Defines.IT_STAY_COOP,
Defines.WEAP_HYPERBLASTER,
null,
0,
/* precache */
"weapons/hyprbu1a.wav weapons/hyprbl1a.wav weapons/hyprbf1a.wav weapons/hyprbd1a.wav misc/lasfly.wav"),
/*
* QUAKED weapon_railgun (.3 .3 1) (-16 -16 -16) (16 16 16)
*/
new gitem_t("weapon_railgun", PlayerWeapon.Pickup_Weapon,
PlayerWeapon.Use_Weapon, PlayerWeapon.Drop_Weapon,
PlayerWeapon.Weapon_Railgun, "misc/w_pkup.wav",
"models/weapons/g_rail/tris.md2", Defines.EF_ROTATE,
"models/weapons/v_rail/tris.md2",
/* icon */
"w_railgun",
/* pickup */
"Railgun", 0, 1, "Slugs", Defines.IT_WEAPON
| Defines.IT_STAY_COOP, Defines.WEAP_RAILGUN, null,
0,
/* precache */
"weapons/rg_hum.wav"),
/*
* QUAKED weapon_bfg (.3 .3 1) (-16 -16 -16) (16 16 16)
*/
new gitem_t(
"weapon_bfg",
PlayerWeapon.Pickup_Weapon,
PlayerWeapon.Use_Weapon,
PlayerWeapon.Drop_Weapon,
PlayerWeapon.Weapon_BFG,
"misc/w_pkup.wav",
"models/weapons/g_bfg/tris.md2",
Defines.EF_ROTATE,
"models/weapons/v_bfg/tris.md2",
/* icon */
"w_bfg",
/* pickup */
"BFG10K",
0,
50,
"Cells",
Defines.IT_WEAPON | Defines.IT_STAY_COOP,
Defines.WEAP_BFG,
null,
0,
/* precache */
"sprites/s_bfg1.sp2 sprites/s_bfg2.sp2 sprites/s_bfg3.sp2 weapons/bfg__f1y.wav weapons/bfg__l1a.wav weapons/bfg__x1b.wav weapons/bfg_hum.wav"),
//
// AMMO ITEMS
//
/*
* QUAKED ammo_shells (.3 .3 1) (-16 -16 -16) (16 16 16)
*/
new gitem_t("ammo_shells", GameItems.Pickup_Ammo, null, GameItems.Drop_Ammo, null,
"misc/am_pkup.wav",
"models/items/ammo/shells/medium/tris.md2", 0, null,
/* icon */
"a_shells",
/* pickup */
"Shells",
/* width */
3, 10, null, Defines.IT_AMMO, 0, null, Defines.AMMO_SHELLS,
/* precache */
""),
/*
* QUAKED ammo_bullets (.3 .3 1) (-16 -16 -16) (16 16 16)
*/
new gitem_t("ammo_bullets", GameItems.Pickup_Ammo, null, GameItems.Drop_Ammo, null,
"misc/am_pkup.wav",
"models/items/ammo/bullets/medium/tris.md2", 0, null,
/* icon */
"a_bullets",
/* pickup */
"Bullets",
/* width */
3, 50, null, Defines.IT_AMMO, 0, null,
Defines.AMMO_BULLETS,
/* precache */
""),
/*
* QUAKED ammo_cells (.3 .3 1) (-16 -16 -16) (16 16 16)
*/
new gitem_t("ammo_cells", GameItems.Pickup_Ammo, null, GameItems.Drop_Ammo, null,
"misc/am_pkup.wav",
"models/items/ammo/cells/medium/tris.md2", 0, null,
/* icon */
"a_cells",
/* pickup */
"Cells",
/* width */
3, 50, null, Defines.IT_AMMO, 0, null, Defines.AMMO_CELLS,
/* precache */
""),
/*
* QUAKED ammo_rockets (.3 .3 1) (-16 -16 -16) (16 16 16)
*/
new gitem_t("ammo_rockets", GameItems.Pickup_Ammo, null, GameItems.Drop_Ammo, null,
"misc/am_pkup.wav",
"models/items/ammo/rockets/medium/tris.md2", 0, null,
/* icon */
"a_rockets",
/* pickup */
"Rockets",
/* width */
3, 5, null, Defines.IT_AMMO, 0, null, Defines.AMMO_ROCKETS,
/* precache */
""),
/*
* QUAKED ammo_slugs (.3 .3 1) (-16 -16 -16) (16 16 16)
*/
new gitem_t("ammo_slugs", GameItems.Pickup_Ammo, null, GameItems.Drop_Ammo, null,
"misc/am_pkup.wav",
"models/items/ammo/slugs/medium/tris.md2", 0, null,
/* icon */
"a_slugs",
/* pickup */
"Slugs",
/* width */
3, 10, null, Defines.IT_AMMO, 0, null, Defines.AMMO_SLUGS,
/* precache */
""),
//
// POWERUP ITEMS
//
/*
* QUAKED item_quad (.3 .3 1) (-16 -16 -16) (16 16 16)
*/
new gitem_t("item_quad", GameItems.Pickup_Powerup, GameItems.Use_Quad,
GameItems.Drop_General, null, "items/pkup.wav",
"models/items/quaddama/tris.md2", Defines.EF_ROTATE, null,
/* icon */
"p_quad",
/* pickup */
"Quad Damage",
/* width */
2, 60, null, Defines.IT_POWERUP, 0, null, 0,
/* precache */
"items/damage.wav items/damage2.wav items/damage3.wav"),
/*
* QUAKED item_invulnerability (.3 .3 1) (-16 -16 -16) (16 16 16)
*/
new gitem_t("item_invulnerability", GameItems.Pickup_Powerup,
GameItems.Use_Invulnerability, GameItems.Drop_General, null,
"items/pkup.wav", "models/items/invulner/tris.md2",
Defines.EF_ROTATE, null,
/* icon */
"p_invulnerability",
/* pickup */
"Invulnerability",
/* width */
2, 300, null, Defines.IT_POWERUP, 0, null, 0,
/* precache */
"items/protect.wav items/protect2.wav items/protect4.wav"),
/*
* QUAKED item_silencer (.3 .3 1) (-16 -16 -16) (16 16 16)
*/
new gitem_t("item_silencer", GameItems.Pickup_Powerup, GameItems.Use_Silencer,
GameItems.Drop_General, null, "items/pkup.wav",
"models/items/silencer/tris.md2", Defines.EF_ROTATE, null,
/* icon */
"p_silencer",
/* pickup */
"Silencer",
/* width */
2, 60, null, Defines.IT_POWERUP, 0, null, 0,
/* precache */
""),
/*
* QUAKED item_breather (.3 .3 1) (-16 -16 -16) (16 16 16)
*/
new gitem_t("item_breather", GameItems.Pickup_Powerup, GameItems.Use_Breather,
GameItems.Drop_General, null, "items/pkup.wav",
"models/items/breather/tris.md2", Defines.EF_ROTATE, null,
/* icon */
"p_rebreather",
/* pickup */
"Rebreather",
/* width */
2, 60, null, Defines.IT_STAY_COOP | Defines.IT_POWERUP, 0,
null, 0,
/* precache */
"items/airout.wav"),
/*
* QUAKED item_enviro (.3 .3 1) (-16 -16 -16) (16 16 16)
*/
new gitem_t("item_enviro", GameItems.Pickup_Powerup, GameItems.Use_Envirosuit,
GameItems.Drop_General, null, "items/pkup.wav",
"models/items/enviro/tris.md2", Defines.EF_ROTATE, null,
/* icon */
"p_envirosuit",
/* pickup */
"Environment Suit",
/* width */
2, 60, null, Defines.IT_STAY_COOP | Defines.IT_POWERUP, 0,
null, 0,
/* precache */
"items/airout.wav"),
/*
* QUAKED item_ancient_head (.3 .3 1) (-16 -16 -16) (16 16 16)
* Special item that gives +2 to maximum health
*/
new gitem_t("item_ancient_head", GameItems.Pickup_AncientHead, null, null,
null, "items/pkup.wav", "models/items/c_head/tris.md2",
Defines.EF_ROTATE, null,
/* icon */
"i_fixme",
/* pickup */
"Ancient Head",
/* width */
2, 60, null, 0, 0, null, 0,
/* precache */
""),
/*
* QUAKED item_adrenaline (.3 .3 1) (-16 -16 -16) (16 16 16) gives
* +1 to maximum health
*/
new gitem_t("item_adrenaline", GameItems.Pickup_Adrenaline, null, null, null,
"items/pkup.wav", "models/items/adrenal/tris.md2",
Defines.EF_ROTATE, null,
/* icon */
"p_adrenaline",
/* pickup */
"Adrenaline",
/* width */
2, 60, null, 0, 0, null, 0,
/* precache */
""),
/*
* QUAKED item_bandolier (.3 .3 1) (-16 -16 -16) (16 16 16)
*/
new gitem_t("item_bandolier", GameItems.Pickup_Bandolier, null, null, null,
"items/pkup.wav", "models/items/band/tris.md2",
Defines.EF_ROTATE, null,
/* icon */
"p_bandolier",
/* pickup */
"Bandolier",
/* width */
2, 60, null, 0, 0, null, 0,
/* precache */
""),
/*
* QUAKED item_pack (.3 .3 1) (-16 -16 -16) (16 16 16)
*/
new gitem_t("item_pack", GameItems.Pickup_Pack, null, null, null,
"items/pkup.wav", "models/items/pack/tris.md2",
Defines.EF_ROTATE, null,
/* icon */
"i_pack",
/* pickup */
"Ammo Pack",
/* width */
2, 180, null, 0, 0, null, 0,
/* precache */
""),
//
// KEYS
//
/*
* QUAKED key_data_cd (0 .5 .8) (-16 -16 -16) (16 16 16) key for
* computer centers
*/
new gitem_t("key_data_cd", GameItems.Pickup_Key, null, GameItems.Drop_General,
null, "items/pkup.wav",
"models/items/keys/data_cd/tris.md2", Defines.EF_ROTATE,
null, "k_datacd", "Data CD", 2, 0, null,
Defines.IT_STAY_COOP | Defines.IT_KEY, 0, null, 0,
/* precache */
""),
/*
* QUAKED key_power_cube (0 .5 .8) (-16 -16 -16) (16 16 16)
* TRIGGER_SPAWN NO_TOUCH warehouse circuits
*/
new gitem_t("key_power_cube", GameItems.Pickup_Key, null,
GameItems.Drop_General, null, "items/pkup.wav",
"models/items/keys/power/tris.md2", Defines.EF_ROTATE,
null, "k_powercube", "Power Cube", 2, 0, null,
Defines.IT_STAY_COOP | Defines.IT_KEY, 0, null, 0,
/* precache */
""),
/*
* QUAKED key_pyramid (0 .5 .8) (-16 -16 -16) (16 16 16) key for the
* entrance of jail3
*/
new gitem_t("key_pyramid", GameItems.Pickup_Key, null, GameItems.Drop_General,
null, "items/pkup.wav",
"models/items/keys/pyramid/tris.md2", Defines.EF_ROTATE,
null, "k_pyramid", "Pyramid Key", 2, 0, null,
Defines.IT_STAY_COOP | Defines.IT_KEY, 0, null, 0,
/* precache */
""),
/*
* QUAKED key_data_spinner (0 .5 .8) (-16 -16 -16) (16 16 16) key
* for the city computer
*/
new gitem_t("key_data_spinner", GameItems.Pickup_Key, null,
GameItems.Drop_General, null, "items/pkup.wav",
"models/items/keys/spinner/tris.md2", Defines.EF_ROTATE,
null, "k_dataspin", "Data Spinner", 2, 0, null,
Defines.IT_STAY_COOP | Defines.IT_KEY, 0, null, 0,
/* precache */
""),
/*
* QUAKED key_pass (0 .5 .8) (-16 -16 -16) (16 16 16) security pass
* for the security level
*/
new gitem_t("key_pass", GameItems.Pickup_Key, null, GameItems.Drop_General,
null, "items/pkup.wav", "models/items/keys/pass/tris.md2",
Defines.EF_ROTATE, null, "k_security", "Security Pass", 2,
0, null, Defines.IT_STAY_COOP | Defines.IT_KEY, 0, null, 0,
/* precache */
""),
/*
* QUAKED key_blue_key (0 .5 .8) (-16 -16 -16) (16 16 16) normal
* door key - blue
*/
new gitem_t("key_blue_key", GameItems.Pickup_Key, null,
GameItems.Drop_General, null, "items/pkup.wav",
"models/items/keys/key/tris.md2", Defines.EF_ROTATE, null,
"k_bluekey", "Blue Key", 2, 0, null, Defines.IT_STAY_COOP
| Defines.IT_KEY, 0, null, 0,
/* precache */
""),
/*
* QUAKED key_red_key (0 .5 .8) (-16 -16 -16) (16 16 16) normal door
* key - red
*/
new gitem_t("key_red_key", GameItems.Pickup_Key, null, GameItems.Drop_General,
null, "items/pkup.wav",
"models/items/keys/red_key/tris.md2", Defines.EF_ROTATE,
null, "k_redkey", "Red Key", 2, 0, null,
Defines.IT_STAY_COOP | Defines.IT_KEY, 0, null, 0,
/* precache */
""),
/*
* QUAKED key_commander_head (0 .5 .8) (-16 -16 -16) (16 16 16) tank
* commander's head
*/
new gitem_t("key_commander_head", GameItems.Pickup_Key, null,
GameItems.Drop_General, null, "items/pkup.wav",
"models/monsters/commandr/head/tris.md2", Defines.EF_GIB,
null,
/* icon */
"k_comhead",
/* pickup */
"Commander's Head",
/* width */
2, 0, null, Defines.IT_STAY_COOP | Defines.IT_KEY, 0, null,
0,
/* precache */
""),
/*
* QUAKED key_airstrike_target (0 .5 .8) (-16 -16 -16) (16 16 16)
* tank commander's head
*/
new gitem_t("key_airstrike_target", GameItems.Pickup_Key, null,
GameItems.Drop_General, null, "items/pkup.wav",
"models/items/keys/target/tris.md2", Defines.EF_ROTATE,
null,
/* icon */
"i_airstrike",
/* pickup */
"Airstrike Marker",
/* width */
2, 0, null, Defines.IT_STAY_COOP | Defines.IT_KEY, 0, null,
0,
/* precache */
""),
new gitem_t(null, GameItems.Pickup_Health, null, null, null,
"items/pkup.wav", null, 0, null,
/* icon */
"i_health",
/* pickup */
"Health",
/* width */
3, 0, null, 0, 0, null, 0,
/* precache */
"items/s_health.wav items/n_health.wav items/l_health.wav items/m_health.wav"),
// end of list marker
null };
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,294 @@
/*
* 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 {
/**
*
* PACKET FILTERING
*
*
* You can add or remove addresses from the filter list with:
*
* addip <ip> removeip <ip>
*
* 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".
*
* Removeip will only remove an address specified exactly the same way. You
* cannot addip a subnet, then removeip a single host.
*
* listip Prints the current list of filters.
*
* 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.
*
* filterban <0 or 1>
*
* If 1 (the default), then ip addresses matching the current list will be
* prohibited from entering the game. This is the default setting.
*
* 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.
*
*/
public static class ipfilter_t {
int mask;
int compare;
};
public static void Svcmd_Test_f() {
GameBase.gi.cprintf(null, Defines.PRINT_HIGH, "Svcmd_Test_f()\n");
}
public static final int MAX_IPFILTERS = 1024;
static GameSVCmds.ipfilter_t ipfilters[] = new GameSVCmds.ipfilter_t[MAX_IPFILTERS];
static int numipfilters;
static {
for (int n = 0; n < GameSVCmds.MAX_IPFILTERS; n++)
GameSVCmds.ipfilters[n] = new ipfilter_t();
}
/**
* StringToFilter.
*/
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 ((int) 1 - GameBase.filterban.value) != 0;
}
/**
* SV_AddIP_f.
*/
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.
*/
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.
*/
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.
*/
static void SVCmd_WriteIP_f() {
RandomAccessFile f;
//char name[MAX_OSPATH];
String name;
byte b[];
int i;
cvar_t 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
*
* 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");
}
}

View File

@@ -0,0 +1,377 @@
/*
* 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 lombok.extern.slf4j.Slf4j;
import lwjake2.Defines;
import lwjake2.Globals;
import lwjake2.qcommon.Com;
import lwjake2.util.Lib;
import lwjake2.util.QuakeFile;
@Slf4j
public class GameSave {
public static void CreateEdicts() {
GameBase.g_edicts = new edict_t[GameBase.game.maxentities];
for (int i = 0; i < GameBase.game.maxentities; i++)
GameBase.g_edicts[i] = new edict_t(i);
}
public static void CreateClients() {
GameBase.game.clients = new gclient_t[GameBase.game.maxclients];
for (int i = 0; i < GameBase.game.maxclients; i++)
GameBase.game.clients[i] = new gclient_t(i);
}
private static String preloadclasslist [] =
{
"jake2.game.PlayerWeapon",
"jake2.game.AIAdapter",
"jake2.game.Cmd",
"jake2.game.EdictFindFilter",
"jake2.game.EdictIterator",
"jake2.game.EndianHandler",
"jake2.game.EntBlockedAdapter",
"jake2.game.EntDieAdapter",
"jake2.game.EntDodgeAdapter",
"jake2.game.EntInteractAdapter",
"jake2.game.EntPainAdapter",
"jake2.game.EntThinkAdapter",
"jake2.game.EntTouchAdapter",
"jake2.game.EntUseAdapter",
"jake2.game.GameAI",
"jake2.game.GameBase",
"jake2.game.GameChase",
"jake2.game.GameCombat",
"jake2.game.GameFunc",
"jake2.game.GameMisc",
"jake2.game.GameSVCmds",
"jake2.game.GameSave",
"jake2.game.GameSpawn",
"jake2.game.GameTarget",
"jake2.game.GameTrigger",
"jake2.game.GameTurret",
"jake2.game.GameUtil",
"jake2.game.GameWeapon",
"jake2.game.Info",
"jake2.game.ItemDropAdapter",
"jake2.game.ItemUseAdapter",
"jake2.game.Monster",
"jake2.game.PlayerClient",
"jake2.game.PlayerHud",
"jake2.game.PlayerTrail",
"jake2.game.PlayerView",
"jake2.game.SuperAdapter",
"jake2.game.monsters.M_Actor",
"jake2.game.monsters.M_Berserk",
"jake2.game.monsters.M_Boss2",
"jake2.game.monsters.M_Boss3",
"jake2.game.monsters.M_Boss31",
"jake2.game.monsters.M_Boss32",
"jake2.game.monsters.M_Brain",
"jake2.game.monsters.M_Chick",
"jake2.game.monsters.M_Flash",
"jake2.game.monsters.M_Flipper",
"jake2.game.monsters.M_Float",
"jake2.game.monsters.M_Flyer",
"jake2.game.monsters.M_Gladiator",
"jake2.game.monsters.M_Gunner",
"jake2.game.monsters.M_Hover",
"jake2.game.monsters.M_Infantry",
"jake2.game.monsters.M_Insane",
"jake2.game.monsters.M_Medic",
"jake2.game.monsters.M_Mutant",
"jake2.game.monsters.M_Parasite",
"jake2.game.monsters.M_Player",
"jake2.game.monsters.M_Soldier",
"jake2.game.monsters.M_Supertank",
"jake2.game.monsters.M_Tank",
"jake2.game.GameItems",
// DANGER! init as last, when all adatpers are != null
"jake2.game.GameItemList"
};
/**
* InitGame
*
* 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() {
log.info("==== InitGame ====");
// preload all classes to register the adapters
for ( int n=0; n < preloadclasslist.length; n++)
{
try
{
Class.forName(preloadclasslist[n]);
}
catch(Exception e)
{
Com.DPrintf("error loading class: " + e.getMessage());
}
}
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.skill = GameBase.gi.cvar("skill", "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);
// items
GameItems.InitItems();
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
*
* This will be called whenever the game goes to a new level, and when the
* user explicitly saves the game.
*
* Game information include cross level data, like multi level triggers,
* help computer info, and all client states.
*
* 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 gclient_t(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_t 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
*
* SpawnEntities will allready have been called on the level the same way it
* was when the level was saved.
*
* That is necessary to get the baselines set up identically.
*
* The server will have cleared all of the world links before calling
* ReadLevel.
*
* No clients are connected yet.
*/
public static void ReadLevel(String filename) {
try {
edict_t 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);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,848 @@
/*
* 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 GameTarget {
public static void SP_target_temp_entity(edict_t ent) {
ent.use = Use_Target_Tent;
}
public static void SP_target_speaker(edict_t 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.indexOf(".wav") < 0)
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_t 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_t 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_t 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_t ent) {
ent.use = use_target_explosion;
ent.svflags = Defines.SVF_NOCLIENT;
}
public static void SP_target_changelevel(edict_t 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_t 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_spawner(edict_t self) {
self.use = use_target_spawner;
self.svflags = Defines.SVF_NOCLIENT;
if (self.speed != 0) {
GameBase.G_SetMovedir(self.s.angles, self.movedir);
Math3D.VectorScale(self.movedir, self.speed, self.movedir);
}
}
public static void SP_target_blaster(edict_t 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_t self) {
self.svflags = Defines.SVF_NOCLIENT;
self.use = trigger_crosslevel_trigger_use;
}
public static void SP_target_crosslevel_target(edict_t 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;
}
public static void target_laser_on(edict_t self) {
if (null == self.activator)
self.activator = self;
self.spawnflags |= 0x80000001;
self.svflags &= ~Defines.SVF_NOCLIENT;
target_laser_think.think(self);
}
public static void target_laser_off(edict_t self) {
self.spawnflags &= ~1;
self.svflags |= Defines.SVF_NOCLIENT;
self.nextthink = 0;
}
public static void SP_target_laser(edict_t 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_t 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_t 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");
}
/**
* 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
*/
public static EntUseAdapter Use_Target_Tent = new EntUseAdapter() {
public String getID() { return "Use_Target_Tent"; }
public void use(edict_t ent, edict_t other, edict_t 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
*
* Normal sounds play each time the target is used. The reliable flag can be
* set for crucial voiceovers.
*
* 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.
*/
public static EntUseAdapter Use_Target_Speaker = new EntUseAdapter() {
public String getID() { return "Use_Target_Speaker"; }
public void use(edict_t ent, edict_t other, edict_t 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);
}
}
};
public static EntUseAdapter Use_Target_Help = new EntUseAdapter() {
public String getID() { return "Use_Target_Help"; }
public void use(edict_t ent, edict_t other, edict_t activator) {
if ((ent.spawnflags & 1) != 0)
GameBase.game.helpmessage1 = ent.message;
else
GameBase.game.helpmessage2 = ent.message;
GameBase.game.helpchanged++;
}
};
/**
* QUAKED target_secret (1 0 1) (-8 -8 -8) (8 8 8) Counts a secret found.
* These are single use targets.
*/
static EntUseAdapter use_target_secret = new EntUseAdapter() {
public String getID() { return "use_target_secret"; }
public void use(edict_t ent, edict_t other, edict_t 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.
*/
static EntUseAdapter use_target_goal = new EntUseAdapter() {
public String getID() { return "use_target_goal"; }
public void use(edict_t ent, edict_t other, edict_t 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.
*
* "delay" wait this long before going off "dmg" how much radius damage
* should be done, defaults to 0
*/
static EntThinkAdapter target_explosion_explode = new EntThinkAdapter() {
public String getID() { return "target_explosion_explode"; }
public boolean think(edict_t 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);
GameCombat.T_RadiusDamage(self, self.activator, self.dmg, null,
self.dmg + 40, Defines.MOD_EXPLOSIVE);
save = self.delay;
self.delay = 0;
GameUtil.G_UseTargets(self, self.activator);
self.delay = save;
return true;
}
};
static EntUseAdapter use_target_explosion = new EntUseAdapter() {
public String getID() { return "use_target_explosion"; }
public void use(edict_t self, edict_t other, edict_t 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
*/
static EntUseAdapter use_target_changelevel = new EntUseAdapter() {
public String getID() { return "use_target_changelevel"; }
public void use(edict_t self, edict_t other, edict_t 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 */
) {
GameCombat.T_Damage(other, self, self, Globals.vec3_origin,
other.s.origin, Globals.vec3_origin,
10 * other.max_health, 1000, 0, Defines.MOD_EXIT);
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.
*
* Set "sounds" to one of the following: 1) sparks 2) blue water 3) brown
* water 4) slime 5) lava 6) blood
*
* "count" how many pixels in the splash "dmg" if set, does a radius damage
* at this location when it splashes useful for lava/sparks
*/
static EntUseAdapter use_target_splash = new EntUseAdapter() {
public String getID() { return "use_target_splash"; }
public void use(edict_t self, edict_t other, edict_t 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);
if (self.dmg != 0)
GameCombat.T_RadiusDamage(self, activator, self.dmg, null,
self.dmg + 40, Defines.MOD_SPLASH);
}
};
/**
* 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.
*
* For monsters: Set direction to the facing you want it to have.
*
* For gibs: Set direction if you want it moving and speed how fast it
* should be moving otherwise it will just be dropped
*/
static EntUseAdapter use_target_spawner = new EntUseAdapter() {
public String getID() { return "use_target_spawner"; }
public void use(edict_t self, edict_t other, edict_t activator) {
edict_t ent;
ent = GameUtil.G_Spawn();
ent.classname = self.target;
Math3D.VectorCopy(self.s.origin, ent.s.origin);
Math3D.VectorCopy(self.s.angles, ent.s.angles);
GameSpawn.ED_CallSpawn(ent);
GameBase.gi.unlinkentity(ent);
GameUtil.KillBox(ent);
GameBase.gi.linkentity(ent);
if (self.speed != 0)
Math3D.VectorCopy(self.movedir, ent.velocity);
}
};
/**
* QUAKED target_blaster (1 0 0) (-8 -8 -8) (8 8 8) NOTRAIL NOEFFECTS Fires
* a blaster bolt in the set direction when triggered.
*
* dmg default is 15 speed default is 1000
*/
public static EntUseAdapter use_target_blaster = new EntUseAdapter() {
public String getID() { return "use_target_blaster"; }
public void use(edict_t self, edict_t other, edict_t 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;
*/
GameWeapon.fire_blaster(self, self.s.origin, self.movedir, self.dmg,
(int) self.speed, Defines.EF_BLASTER,
Defines.MOD_TARGET_BLASTER != 0
/* true */
);
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.
*/
public static EntUseAdapter trigger_crosslevel_trigger_use = new EntUseAdapter() {
public String getID() { return "trigger_crosslevel_trigger_use"; }
public void use(edict_t self, edict_t other, edict_t activator) {
GameBase.game.serverflags |= self.spawnflags;
GameUtil.G_FreeEdict(self);
}
};
/**
* 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.
*
* "delay" delay before using targets if the trigger has been activated
* (default 1)
*/
static EntThinkAdapter target_crosslevel_target_think = new EntThinkAdapter() {
public String getID() { return "target_crosslevel_target_think"; }
public boolean think(edict_t self) {
if (self.spawnflags == (GameBase.game.serverflags
& Defines.SFL_CROSS_TRIGGER_MASK & self.spawnflags)) {
GameUtil.G_UseTargets(self, self);
GameUtil.G_FreeEdict(self);
}
return true;
}
};
/**
* 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.
*/
public static EntThinkAdapter target_laser_think = new EntThinkAdapter() {
public String getID() { return "target_laser_think"; }
public boolean think(edict_t self) {
edict_t 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;
// hurt it if we can
if ((tr.ent.takedamage != 0)
&& 0 == (tr.ent.flags & Defines.FL_IMMUNE_LASER))
GameCombat.T_Damage(tr.ent, self, self.activator,
self.movedir, tr.endpos, Globals.vec3_origin,
self.dmg, 1, Defines.DAMAGE_ENERGY,
Defines.MOD_TARGET_LASER);
// 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;
return true;
}
};
public static EntUseAdapter target_laser_use = new EntUseAdapter() {
public String getID() { return "target_laser_use"; }
public void use(edict_t self, edict_t other, edict_t activator) {
self.activator = activator;
if ((self.spawnflags & 1) != 0)
target_laser_off(self);
else
target_laser_on(self);
}
};
static EntThinkAdapter target_laser_start = new EntThinkAdapter() {
public String getID() { return "target_laser_start"; }
public boolean think(edict_t self) {
self.movetype = Defines.MOVETYPE_NONE;
self.solid = Defines.SOLID_NOT;
self.s.renderfx |= Defines.RF_BEAM | Defines.RF_TRANSLUCENT;
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 != null ? edit.o : null);
} 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);
return true;
}
};
/**
* 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
*/
static EntThinkAdapter target_lightramp_think = new EntThinkAdapter() {
public String getID() { return "target_lightramp_think"; }
public boolean think(edict_t 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;
}
return true;
}
};
static EntUseAdapter target_lightramp_use = new EntUseAdapter() {
public String getID() { return "target_lightramp_use"; }
public void use(edict_t self, edict_t other, edict_t activator) {
if (self.enemy == null) {
edict_t 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)
*/
static EntThinkAdapter target_earthquake_think = new EntThinkAdapter() {
public String getID() { return "target_earthquake_think"; }
public boolean think(edict_t self) {
int i;
edict_t 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;
return true;
}
};
static EntUseAdapter target_earthquake_use = new EntUseAdapter() {
public String getID() { return "target_earthquake_use"; }
public void use(edict_t self, edict_t other, edict_t activator) {
self.timestamp = GameBase.level.time + self.count;
self.nextthink = GameBase.level.time + Defines.FRAMETIME;
self.activator = activator;
self.last_move_time = 0;
}
};
}

View File

@@ -0,0 +1,583 @@
/*
* 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 void InitTrigger(edict_t 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_t 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_t 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);
}
/**
* 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".
*
* If TRIGGERED, this trigger must be triggered before it is live.
*
* sounds 1) secret 2) beep beep 3) large switch 4)
*
* "message" string to be displayed when triggered
*/
public static void SP_trigger_once(edict_t 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_t self) {
self.use = trigger_relay_use;
}
public static void SP_trigger_key(edict_t self) {
if (GameBase.st.item == null) {
GameBase.gi.dprintf("no key item for trigger_key at "
+ Lib.vtos(self.s.origin) + "\n");
return;
}
self.item = GameItems.FindItemByClassname(GameBase.st.item);
if (null == self.item) {
GameBase.gi.dprintf("item " + GameBase.st.item
+ " not found for trigger_key at "
+ Lib.vtos(self.s.origin) + "\n");
return;
}
if (self.target == null) {
GameBase.gi.dprintf(self.classname + " at "
+ Lib.vtos(self.s.origin) + " has no target\n");
return;
}
GameBase.gi.soundindex("misc/keytry.wav");
GameBase.gi.soundindex("misc/keyuse.wav");
self.use = trigger_key_use;
}
public static void SP_trigger_counter(edict_t self) {
self.wait = -1;
if (0 == self.count)
self.count = 2;
self.use = trigger_counter_use;
}
/*
* ==============================================================================
*
* trigger_always
*
* ==============================================================================
*/
/*
* 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_t 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_t 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);
}
public static void SP_trigger_hurt(edict_t self) {
InitTrigger(self);
self.noise_index = GameBase.gi.soundindex("world/electro.wav");
self.touch = hurt_touch;
if (0 == self.dmg)
self.dmg = 5;
if ((self.spawnflags & 1) != 0)
self.solid = Defines.SOLID_NOT;
else
self.solid = Defines.SOLID_TRIGGER;
if ((self.spawnflags & 2) != 0)
self.use = hurt_use;
GameBase.gi.linkentity(self);
}
public static void SP_trigger_gravity(edict_t 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;
}
public static void SP_trigger_monsterjump(edict_t 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;
}
// the wait time has passed, so set back up for another activation
public static EntThinkAdapter multi_wait = new EntThinkAdapter() {
public String getID(){ return "multi_wait"; }
public boolean think(edict_t ent) {
ent.nextthink = 0;
return true;
}
};
static EntUseAdapter Use_Multi = new EntUseAdapter() {
public String getID(){ return "Use_Multi"; }
public void use(edict_t ent, edict_t other, edict_t activator) {
ent.activator = activator;
multi_trigger(ent);
}
};
static EntTouchAdapter Touch_Multi = new EntTouchAdapter() {
public String getID(){ return "Touch_Multi"; }
public void touch(edict_t self, edict_t 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 EntUseAdapter trigger_enable = new EntUseAdapter() {
public String getID(){ return "trigger_enable"; }
public void use(edict_t self, edict_t other, edict_t activator) {
self.solid = Defines.SOLID_TRIGGER;
self.use = Use_Multi;
GameBase.gi.linkentity(self);
}
};
/**
* 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 EntUseAdapter trigger_relay_use = new EntUseAdapter() {
public String getID(){ return "trigger_relay_use"; }
public void use(edict_t self, edict_t other, edict_t activator) {
GameUtil.G_UseTargets(self, activator);
}
};
/*
* ==============================================================================
*
* trigger_key
*
* ==============================================================================
*/
/**
* QUAKED trigger_key (.5 .5 .5) (-8 -8 -8) (8 8 8) A relay trigger that
* only fires it's targets if player has the proper key. Use "item" to
* specify the required key, for example "key_data_cd"
*/
static EntUseAdapter trigger_key_use = new EntUseAdapter() {
public String getID(){ return "trigger_key_use"; }
public void use(edict_t self, edict_t other, edict_t activator) {
int index;
if (self.item == null)
return;
if (activator.client == null)
return;
index = GameItems.ITEM_INDEX(self.item);
if (activator.client.pers.inventory[index] == 0) {
if (GameBase.level.time < self.touch_debounce_time)
return;
self.touch_debounce_time = GameBase.level.time + 5.0f;
GameBase.gi.centerprintf(activator, "You need the "
+ self.item.pickup_name);
GameBase.gi.sound(activator, Defines.CHAN_AUTO,
GameBase.gi.soundindex("misc/keytry.wav"), 1,
Defines.ATTN_NORM, 0);
return;
}
GameBase.gi.sound(activator, Defines.CHAN_AUTO, GameBase.gi
.soundindex("misc/keyuse.wav"), 1, Defines.ATTN_NORM, 0);
if (GameBase.coop.value != 0) {
int player;
edict_t ent;
if (Lib.strcmp(self.item.classname, "key_power_cube") == 0) {
int cube;
for (cube = 0; cube < 8; cube++)
if ((activator.client.pers.power_cubes & (1 << cube)) != 0)
break;
for (player = 1; player <= GameBase.game.maxclients; player++) {
ent = GameBase.g_edicts[player];
if (!ent.inuse)
continue;
if (null == ent.client)
continue;
if ((ent.client.pers.power_cubes & (1 << cube)) != 0) {
ent.client.pers.inventory[index]--;
ent.client.pers.power_cubes &= ~(1 << cube);
}
}
} else {
for (player = 1; player <= GameBase.game.maxclients; player++) {
ent = GameBase.g_edicts[player];
if (!ent.inuse)
continue;
if (ent.client == null)
continue;
ent.client.pers.inventory[index] = 0;
}
}
} else {
activator.client.pers.inventory[index]--;
}
GameUtil.G_UseTargets(self, activator);
self.use = null;
}
};
/**
* QUAKED trigger_counter (.5 .5 .5) ? nomessage Acts as an intermediary for
* an action that takes multiple inputs.
*
* If nomessage is not set, t will print "1 more.. " etc when triggered and
* "sequence complete" when finished.
*
* After the counter has been triggered "count" times (default 2), it will
* fire all of it's targets and remove itself.
*/
static EntUseAdapter trigger_counter_use = new EntUseAdapter() {
public String getID(){ return "trigger_counter_use"; }
public void use(edict_t self, edict_t other, edict_t 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_push
*
* ==============================================================================
*/
public static final int PUSH_ONCE = 1;
public static int windsound;
static EntTouchAdapter trigger_push_touch = new EntTouchAdapter() {
public String getID(){ return "trigger_push_touch"; }
public void touch(edict_t self, edict_t 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.
*
* It does dmg points of damage each server frame
*
* SILENT supresses playing the sound SLOW changes the damage rate to once
* per second NO_PROTECTION *nothing* stops the damage
*
* "dmg" default 5 (whole numbers only)
*
*/
static EntUseAdapter hurt_use = new EntUseAdapter() {
public String getID(){ return "hurt_use"; }
public void use(edict_t self, edict_t other, edict_t 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;
}
};
static EntTouchAdapter hurt_touch = new EntTouchAdapter() {
public String getID(){ return "hurt_touch"; }
public void touch(edict_t self, edict_t other, cplane_t plane,
csurface_t surf) {
int dflags;
if (other.takedamage == 0)
return;
if (self.timestamp > GameBase.level.time)
return;
if ((self.spawnflags & 16) != 0)
self.timestamp = GameBase.level.time + 1;
else
self.timestamp = GameBase.level.time + Defines.FRAMETIME;
if (0 == (self.spawnflags & 4)) {
if ((GameBase.level.framenum % 10) == 0)
GameBase.gi.sound(other, Defines.CHAN_AUTO,
self.noise_index, 1, Defines.ATTN_NORM, 0);
}
if ((self.spawnflags & 8) != 0)
dflags = Defines.DAMAGE_NO_PROTECTION;
else
dflags = 0;
GameCombat.T_Damage(other, self, self, Globals.vec3_origin,
other.s.origin, Globals.vec3_origin, self.dmg, self.dmg,
dflags, Defines.MOD_TRIGGER_HURT);
}
};
/*
* ==============================================================================
*
* trigger_gravity
*
* ==============================================================================
*/
/**
* 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 EntTouchAdapter trigger_gravity_touch = new EntTouchAdapter() {
public String getID(){ return "trigger_gravity_touch"; }
public void touch(edict_t self, edict_t other, cplane_t plane,
csurface_t surf) {
other.gravity = self.gravity;
}
};
/*
* ==============================================================================
*
* trigger_monsterjump
*
* ==============================================================================
*/
/**
* 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 EntTouchAdapter trigger_monsterjump_touch = new EntTouchAdapter() {
public String getID(){ return "trigger_monsterjump_touch"; }
public void touch(edict_t self, edict_t 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];
}
};
}

View File

@@ -0,0 +1,443 @@
/*
* 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.game.monsters.M_Infantry;
import lwjake2.util.Lib;
import lwjake2.util.Math3D;
public class GameTurret {
public static void AnglesNormalize(float[] vec) {
while (vec[0] > 360)
vec[0] -= 360;
while (vec[0] < 0)
vec[0] += 360;
while (vec[1] > 360)
vec[1] -= 360;
while (vec[1] < 0)
vec[1] += 360;
}
public static float SnapToEights(float x) {
x *= 8.0;
if (x > 0.0)
x += 0.5;
else
x -= 0.5;
return 0.125f * (int) x;
}
/**
* QUAKED turret_breach (0 0 0) ? This portion of the turret can change both
* pitch and yaw. The model should be made with a flat pitch. It (and the
* associated base) need to be oriented towards 0. Use "angle" to set the
* starting angle.
*
* "speed" default 50 "dmg" default 10 "angle" point this forward "target"
* point this at an info_notnull at the muzzle tip "minpitch" min acceptable
* pitch angle : default -30 "maxpitch" max acceptable pitch angle : default
* 30 "minyaw" min acceptable yaw angle : default 0 "maxyaw" max acceptable
* yaw angle : default 360
*/
public static void turret_breach_fire(edict_t self) {
float[] f = { 0, 0, 0 }, r = { 0, 0, 0 }, u = { 0, 0, 0 };
float[] start = { 0, 0, 0 };
int damage;
int speed;
Math3D.AngleVectors(self.s.angles, f, r, u);
Math3D.VectorMA(self.s.origin, self.move_origin[0], f, start);
Math3D.VectorMA(start, self.move_origin[1], r, start);
Math3D.VectorMA(start, self.move_origin[2], u, start);
damage = (int) (100 + Lib.random() * 50);
speed = (int) (550 + 50 * GameBase.skill.value);
GameWeapon.fire_rocket(self.teammaster.owner, start, f, damage, speed, 150,
damage);
GameBase.gi.positioned_sound(start, self, Defines.CHAN_WEAPON,
GameBase.gi.soundindex("weapons/rocklf1a.wav"), 1,
Defines.ATTN_NORM, 0);
}
public static void SP_turret_breach(edict_t self) {
self.solid = Defines.SOLID_BSP;
self.movetype = Defines.MOVETYPE_PUSH;
GameBase.gi.setmodel(self, self.model);
if (self.speed == 0)
self.speed = 50;
if (self.dmg == 0)
self.dmg = 10;
if (GameBase.st.minpitch == 0)
GameBase.st.minpitch = -30;
if (GameBase.st.maxpitch == 0)
GameBase.st.maxpitch = 30;
if (GameBase.st.maxyaw == 0)
GameBase.st.maxyaw = 360;
self.pos1[Defines.PITCH] = -1 * GameBase.st.minpitch;
self.pos1[Defines.YAW] = GameBase.st.minyaw;
self.pos2[Defines.PITCH] = -1 * GameBase.st.maxpitch;
self.pos2[Defines.YAW] = GameBase.st.maxyaw;
self.ideal_yaw = self.s.angles[Defines.YAW];
self.move_angles[Defines.YAW] = self.ideal_yaw;
self.blocked = turret_blocked;
self.think = turret_breach_finish_init;
self.nextthink = GameBase.level.time + Defines.FRAMETIME;
GameBase.gi.linkentity(self);
}
/**
* QUAKED turret_base (0 0 0) ? This portion of the turret changes yaw only.
* MUST be teamed with a turret_breach.
*/
public static void SP_turret_base(edict_t self) {
self.solid = Defines.SOLID_BSP;
self.movetype = Defines.MOVETYPE_PUSH;
GameBase.gi.setmodel(self, self.model);
self.blocked = turret_blocked;
GameBase.gi.linkentity(self);
}
public static void SP_turret_driver(edict_t self) {
if (GameBase.deathmatch.value != 0) {
GameUtil.G_FreeEdict(self);
return;
}
self.movetype = Defines.MOVETYPE_PUSH;
self.solid = Defines.SOLID_BBOX;
self.s.modelindex = GameBase.gi
.modelindex("models/monsters/infantry/tris.md2");
Math3D.VectorSet(self.mins, -16, -16, -24);
Math3D.VectorSet(self.maxs, 16, 16, 32);
self.health = 100;
self.gib_health = 0;
self.mass = 200;
self.viewheight = 24;
self.die = turret_driver_die;
self.monsterinfo.stand = M_Infantry.infantry_stand;
self.flags |= Defines.FL_NO_KNOCKBACK;
GameBase.level.total_monsters++;
self.svflags |= Defines.SVF_MONSTER;
self.s.renderfx |= Defines.RF_FRAMELERP;
self.takedamage = Defines.DAMAGE_AIM;
self.use = GameUtil.monster_use;
self.clipmask = Defines.MASK_MONSTERSOLID;
Math3D.VectorCopy(self.s.origin, self.s.old_origin);
self.monsterinfo.aiflags |= Defines.AI_STAND_GROUND | Defines.AI_DUCKED;
if (GameBase.st.item != null) {
self.item = GameItems.FindItemByClassname(GameBase.st.item);
if (self.item == null)
GameBase.gi.dprintf(self.classname + " at "
+ Lib.vtos(self.s.origin) + " has bad item: "
+ GameBase.st.item + "\n");
}
self.think = turret_driver_link;
self.nextthink = GameBase.level.time + Defines.FRAMETIME;
GameBase.gi.linkentity(self);
}
static EntBlockedAdapter turret_blocked = new EntBlockedAdapter() {
public String getID() { return "turret_blocked"; }
public void blocked(edict_t self, edict_t other) {
edict_t attacker;
if (other.takedamage != 0) {
if (self.teammaster.owner != null)
attacker = self.teammaster.owner;
else
attacker = self.teammaster;
GameCombat.T_Damage(other, self, attacker, Globals.vec3_origin,
other.s.origin, Globals.vec3_origin,
self.teammaster.dmg, 10, 0, Defines.MOD_CRUSH);
}
}
};
static EntThinkAdapter turret_breach_think = new EntThinkAdapter() {
public String getID() { return "turret_breach_think"; }
public boolean think(edict_t self) {
edict_t ent;
float[] current_angles = { 0, 0, 0 };
float[] delta = { 0, 0, 0 };
Math3D.VectorCopy(self.s.angles, current_angles);
AnglesNormalize(current_angles);
AnglesNormalize(self.move_angles);
if (self.move_angles[Defines.PITCH] > 180)
self.move_angles[Defines.PITCH] -= 360;
// clamp angles to mins & maxs
if (self.move_angles[Defines.PITCH] > self.pos1[Defines.PITCH])
self.move_angles[Defines.PITCH] = self.pos1[Defines.PITCH];
else if (self.move_angles[Defines.PITCH] < self.pos2[Defines.PITCH])
self.move_angles[Defines.PITCH] = self.pos2[Defines.PITCH];
if ((self.move_angles[Defines.YAW] < self.pos1[Defines.YAW])
|| (self.move_angles[Defines.YAW] > self.pos2[Defines.YAW])) {
float dmin, dmax;
dmin = Math.abs(self.pos1[Defines.YAW]
- self.move_angles[Defines.YAW]);
if (dmin < -180)
dmin += 360;
else if (dmin > 180)
dmin -= 360;
dmax = Math.abs(self.pos2[Defines.YAW]
- self.move_angles[Defines.YAW]);
if (dmax < -180)
dmax += 360;
else if (dmax > 180)
dmax -= 360;
if (Math.abs(dmin) < Math.abs(dmax))
self.move_angles[Defines.YAW] = self.pos1[Defines.YAW];
else
self.move_angles[Defines.YAW] = self.pos2[Defines.YAW];
}
Math3D.VectorSubtract(self.move_angles, current_angles, delta);
if (delta[0] < -180)
delta[0] += 360;
else if (delta[0] > 180)
delta[0] -= 360;
if (delta[1] < -180)
delta[1] += 360;
else if (delta[1] > 180)
delta[1] -= 360;
delta[2] = 0;
if (delta[0] > self.speed * Defines.FRAMETIME)
delta[0] = self.speed * Defines.FRAMETIME;
if (delta[0] < -1 * self.speed * Defines.FRAMETIME)
delta[0] = -1 * self.speed * Defines.FRAMETIME;
if (delta[1] > self.speed * Defines.FRAMETIME)
delta[1] = self.speed * Defines.FRAMETIME;
if (delta[1] < -1 * self.speed * Defines.FRAMETIME)
delta[1] = -1 * self.speed * Defines.FRAMETIME;
Math3D.VectorScale(delta, 1.0f / Defines.FRAMETIME, self.avelocity);
self.nextthink = GameBase.level.time + Defines.FRAMETIME;
for (ent = self.teammaster; ent != null; ent = ent.teamchain)
ent.avelocity[1] = self.avelocity[1];
// if we have adriver, adjust his velocities
if (self.owner != null) {
float angle;
float target_z;
float diff;
float[] target = { 0, 0, 0 };
float[] dir = { 0, 0, 0 };
// angular is easy, just copy ours
self.owner.avelocity[0] = self.avelocity[0];
self.owner.avelocity[1] = self.avelocity[1];
// x & y
angle = self.s.angles[1] + self.owner.move_origin[1];
angle *= (Math.PI * 2 / 360);
target[0] = GameTurret.SnapToEights((float) (self.s.origin[0] +
Math.cos(angle) * self.owner.move_origin[0]));
target[1] = GameTurret.SnapToEights((float) (self.s.origin[1] +
Math.sin(angle) * self.owner.move_origin[0]));
target[2] = self.owner.s.origin[2];
Math3D.VectorSubtract(target, self.owner.s.origin, dir);
self.owner.velocity[0] = dir[0] * 1.0f / Defines.FRAMETIME;
self.owner.velocity[1] = dir[1] * 1.0f / Defines.FRAMETIME;
// z
angle = self.s.angles[Defines.PITCH] * (float) (Math.PI * 2f / 360f);
target_z = GameTurret.SnapToEights((float) (self.s.origin[2]
+ self.owner.move_origin[0] * Math.tan(angle) + self.owner.move_origin[2]));
diff = target_z - self.owner.s.origin[2];
self.owner.velocity[2] = diff * 1.0f / Defines.FRAMETIME;
if ((self.spawnflags & 65536) != 0) {
turret_breach_fire(self);
self.spawnflags &= ~65536;
}
}
return true;
}
};
static EntThinkAdapter turret_breach_finish_init = new EntThinkAdapter() {
public String getID() { return "turret_breach_finish_init"; }
public boolean think(edict_t self) {
// get and save info for muzzle location
if (self.target == null) {
GameBase.gi.dprintf(self.classname + " at "
+ Lib.vtos(self.s.origin) + " needs a target\n");
} else {
self.target_ent = GameBase.G_PickTarget(self.target);
Math3D.VectorSubtract(self.target_ent.s.origin, self.s.origin,
self.move_origin);
GameUtil.G_FreeEdict(self.target_ent);
}
self.teammaster.dmg = self.dmg;
self.think = turret_breach_think;
self.think.think(self);
return true;
}
};
/*
* QUAKED turret_driver (1 .5 0) (-16 -16 -24) (16 16 32) Must NOT be on the
* team with the rest of the turret parts. Instead it must target the
* turret_breach.
*/
static EntDieAdapter turret_driver_die = new EntDieAdapter() {
public String getID() { return "turret_driver_die"; }
public void die(edict_t self, edict_t inflictor, edict_t attacker,
int damage, float[] point) {
edict_t ent;
// level the gun
self.target_ent.move_angles[0] = 0;
// remove the driver from the end of them team chain
for (ent = self.target_ent.teammaster; ent.teamchain != self; ent = ent.teamchain)
;
ent.teamchain = null;
self.teammaster = null;
self.flags &= ~Defines.FL_TEAMSLAVE;
self.target_ent.owner = null;
self.target_ent.teammaster.owner = null;
M_Infantry.infantry_die.die(self, inflictor, attacker, damage, null);
}
};
static EntThinkAdapter turret_driver_think = new EntThinkAdapter() {
public String getID() { return "turret_driver_think"; }
public boolean think(edict_t self) {
float[] target = { 0, 0, 0 };
float[] dir = { 0, 0, 0 };
float reaction_time;
self.nextthink = GameBase.level.time + Defines.FRAMETIME;
if (self.enemy != null
&& (!self.enemy.inuse || self.enemy.health <= 0))
self.enemy = null;
if (null == self.enemy) {
if (!GameUtil.FindTarget(self))
return true;
self.monsterinfo.trail_time = GameBase.level.time;
self.monsterinfo.aiflags &= ~Defines.AI_LOST_SIGHT;
} else {
if (GameUtil.visible(self, self.enemy)) {
if ((self.monsterinfo.aiflags & Defines.AI_LOST_SIGHT) != 0) {
self.monsterinfo.trail_time = GameBase.level.time;
self.monsterinfo.aiflags &= ~Defines.AI_LOST_SIGHT;
}
} else {
self.monsterinfo.aiflags |= Defines.AI_LOST_SIGHT;
return true;
}
}
// let the turret know where we want it to aim
Math3D.VectorCopy(self.enemy.s.origin, target);
target[2] += self.enemy.viewheight;
Math3D.VectorSubtract(target, self.target_ent.s.origin, dir);
Math3D.vectoangles(dir, self.target_ent.move_angles);
// decide if we should shoot
if (GameBase.level.time < self.monsterinfo.attack_finished)
return true;
reaction_time = (3 - GameBase.skill.value) * 1.0f;
if ((GameBase.level.time - self.monsterinfo.trail_time) < reaction_time)
return true;
self.monsterinfo.attack_finished = GameBase.level.time
+ reaction_time + 1.0f;
//FIXME how do we really want to pass this along?
self.target_ent.spawnflags |= 65536;
return true;
}
};
public static EntThinkAdapter turret_driver_link = new EntThinkAdapter() {
public String getID() { return "turret_driver_link"; }
public boolean think(edict_t self) {
float[] vec = { 0, 0, 0 };
edict_t ent;
self.think = turret_driver_think;
self.nextthink = GameBase.level.time + Defines.FRAMETIME;
self.target_ent = GameBase.G_PickTarget(self.target);
self.target_ent.owner = self;
self.target_ent.teammaster.owner = self;
Math3D.VectorCopy(self.target_ent.s.angles, self.s.angles);
vec[0] = self.target_ent.s.origin[0] - self.s.origin[0];
vec[1] = self.target_ent.s.origin[1] - self.s.origin[1];
vec[2] = 0;
self.move_origin[0] = Math3D.VectorLength(vec);
Math3D.VectorSubtract(self.s.origin, self.target_ent.s.origin, vec);
Math3D.vectoangles(vec, vec);
AnglesNormalize(vec);
self.move_origin[1] = vec[1];
self.move_origin[2] = self.s.origin[2] - self.target_ent.s.origin[2];
// add the driver to the end of them team chain
for (ent = self.target_ent.teammaster; ent.teamchain != null; ent = ent.teamchain)
;
ent.teamchain = self;
self.teammaster = self.target_ent.teammaster;
self.flags |= Defines.FL_TEAMSLAVE;
return true;
}
};
}

View File

@@ -0,0 +1,686 @@
/*
* 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.client.M;
import lwjake2.qcommon.Com;
import lwjake2.util.Lib;
import lwjake2.util.Math3D;
public class GameUtil {
public static void checkClassname(edict_t ent) {
if (ent.classname == null) {
Com.Printf("edict with classname = null: " + ent.index);
}
}
/**
* Use the targets.
*
* The global "activator" should be set to the entity that initiated the
* firing.
*
* If self.delay is set, a DelayedUse entity will be created that will
* actually do the SUB_UseTargets after that many seconds have passed.
*
* Centerprints any self.message to the activator.
*
* Search for (string)targetname in all entities that match
* (string)self.target and call their .use function
*/
public static void G_UseTargets(edict_t ent, edict_t activator) {
edict_t 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;
t.killtarget = ent.killtarget;
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;
if (ent.killtarget != null) {
while ((edit = GameBase.G_Find(edit, GameBase.findByTarget,
ent.killtarget)) != null) {
t = edit.o;
G_FreeEdict(t);
if (!ent.inuse) {
GameBase.gi
.dprintf("entity was removed while using killtargets\n");
return;
}
}
}
// 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_t 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_t G_Spawn() {
int i;
edict_t 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_t(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_t(i);
GameBase.num_edicts++;
G_InitEdict(e, i);
return e;
}
/**
* Marks the edict as free
*/
public static void G_FreeEdict(edict_t 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_t(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_t ent) {
int i = ent.index;
GameBase.g_edicts[i] = new edict_t(i);
}
/**
* Kills all entities that would touch the proposed new positioning of ent.
* Ent should be unlinked before calling this!
*/
public static boolean KillBox(edict_t ent) {
trace_t tr;
while (true) {
tr = GameBase.gi.trace(ent.s.origin, ent.mins, ent.maxs,
ent.s.origin, null, Defines.MASK_PLAYERSOLID);
if (tr.ent == null || tr.ent == GameBase.g_edicts[0])
break;
// nail it
GameCombat.T_Damage(tr.ent, ent, ent, Globals.vec3_origin, ent.s.origin,
Globals.vec3_origin, 100000, 0,
Defines.DAMAGE_NO_PROTECTION, Defines.MOD_TELEFRAG);
// if we didn't kill it, fail
if (tr.ent.solid != 0)
return false;
}
return true; // all clear
}
/**
* Returns true, if two edicts are on the same team.
*/
public static boolean OnSameTeam(edict_t ent1, edict_t ent2) {
if (0 == ((int) (GameBase.dmflags.value) & (Defines.DF_MODELTEAMS | Defines.DF_SKINTEAMS)))
return false;
if (ClientTeam(ent1).equals(ClientTeam(ent2)))
return true;
return false;
}
/**
* Returns the team string of an entity
* with respect to rteam_by_model and team_by_skin.
*/
static String ClientTeam(edict_t 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());
}
static void ValidateSelectedItem(edict_t ent) {
gclient_t cl;
cl = ent.client;
if (cl.pers.inventory[cl.pers.selected_item] != 0)
return; // valid
GameItems.SelectNextItem(ent, -1);
}
/**
* Returns the range catagorization of an entity reletive to self 0 melee
* range, will become hostile even if back is turned 1 visibility and
* infront, or visibility and show hostile 2 infront and show hostile 3 only
* triggered by damage.
*/
public static int range(edict_t self, edict_t other) {
float[] v = { 0, 0, 0 };
float len;
Math3D.VectorSubtract(self.s.origin, other.s.origin, v);
len = Math3D.VectorLength(v);
if (len < Defines.MELEE_DISTANCE)
return Defines.RANGE_MELEE;
if (len < 500)
return Defines.RANGE_NEAR;
if (len < 1000)
return Defines.RANGE_MID;
return Defines.RANGE_FAR;
}
static void AttackFinished(edict_t self, float time) {
self.monsterinfo.attack_finished = GameBase.level.time + time;
}
/**
* Returns true if the entity is in front (in sight) of self
*/
public static boolean infront(edict_t self, edict_t other) {
float[] vec = { 0, 0, 0 };
float dot;
float[] forward = { 0, 0, 0 };
Math3D.AngleVectors(self.s.angles, forward, null, null);
Math3D.VectorSubtract(other.s.origin, self.s.origin, vec);
Math3D.VectorNormalize(vec);
dot = Math3D.DotProduct(vec, forward);
if (dot > 0.3)
return true;
return false;
}
/**
* Returns 1 if the entity is visible to self, even if not infront().
*/
public static boolean visible(edict_t self, edict_t 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);
if (trace.fraction == 1.0)
return true;
return false;
}
/**
* Finds a target.
*
* Self is currently not attacking anything, so try to find a target
*
* Returns TRUE if an enemy was sighted
*
* When a player fires a missile, the point of impact becomes a fakeplayer
* so that monsters that see the impact will respond as if they had seen the
* player.
*
* To avoid spending too much time, only a single client (or fakeclient) is
* checked each frame. This means multi player games will have slightly
* slower noticing monsters.
*/
static boolean FindTarget(edict_t self) {
edict_t client;
boolean heardit;
int r;
if ((self.monsterinfo.aiflags & Defines.AI_GOOD_GUY) != 0) {
if (self.goalentity != null && self.goalentity.inuse
&& self.goalentity.classname != null) {
if (self.goalentity.classname.equals("target_actor"))
return false;
}
//FIXME look for monsters?
return false;
}
// if we're going to a combat point, just proceed
if ((self.monsterinfo.aiflags & Defines.AI_COMBAT_POINT) != 0)
return false;
// if the first spawnflag bit is set, the monster will only wake up on
// really seeing the player, not another monster getting angry or
// hearing something
// revised behavior so they will wake up if they "see" a player make a
// noise but not weapon impact/explosion noises
heardit = false;
if ((GameBase.level.sight_entity_framenum >= (GameBase.level.framenum - 1))
&& 0 == (self.spawnflags & 1)) {
client = GameBase.level.sight_entity;
if (client.enemy == self.enemy)
return false;
} else if (GameBase.level.sound_entity_framenum >= (GameBase.level.framenum - 1)) {
client = GameBase.level.sound_entity;
heardit = true;
} else if (null != (self.enemy)
&& (GameBase.level.sound2_entity_framenum >= (GameBase.level.framenum - 1))
&& 0 != (self.spawnflags & 1)) {
client = GameBase.level.sound2_entity;
heardit = true;
} else {
client = GameBase.level.sight_client;
if (client == null)
return false; // no clients to get mad at
}
// if the entity went away, forget it
if (!client.inuse)
return false;
if (client.client != null) {
if ((client.flags & Defines.FL_NOTARGET) != 0)
return false;
} else if ((client.svflags & Defines.SVF_MONSTER) != 0) {
if (client.enemy == null)
return false;
if ((client.enemy.flags & Defines.FL_NOTARGET) != 0)
return false;
} else if (heardit) {
if ((client.owner.flags & Defines.FL_NOTARGET) != 0)
return false;
} else
return false;
if (!heardit) {
r = range(self, client);
if (r == Defines.RANGE_FAR)
return false;
// this is where we would check invisibility
// is client in an spot too dark to be seen?
if (client.light_level <= 5)
return false;
if (!visible(self, client))
return false;
if (r == Defines.RANGE_NEAR) {
if (client.show_hostile < GameBase.level.time
&& !infront(self, client))
return false;
} else if (r == Defines.RANGE_MID) {
if (!infront(self, client))
return false;
}
if (client == self.enemy)
return true; // JDC false;
self.enemy = client;
if (!self.enemy.classname.equals("player_noise")) {
self.monsterinfo.aiflags &= ~Defines.AI_SOUND_TARGET;
if (self.enemy.client == null) {
self.enemy = self.enemy.enemy;
if (self.enemy.client == null) {
self.enemy = null;
return false;
}
}
}
} else {
// heard it
float[] temp = { 0, 0, 0 };
if ((self.spawnflags & 1) != 0) {
if (!visible(self, client))
return false;
} else {
if (!GameBase.gi.inPHS(self.s.origin, client.s.origin))
return false;
}
Math3D.VectorSubtract(client.s.origin, self.s.origin, temp);
if (Math3D.VectorLength(temp) > 1000) // too far to hear
return false;
// check area portals - if they are different and not connected then
// we can't hear it
if (client.areanum != self.areanum)
if (!GameBase.gi.AreasConnected(self.areanum, client.areanum))
return false;
self.ideal_yaw = Math3D.vectoyaw(temp);
M.M_ChangeYaw(self);
// hunt the sound for a bit; hopefully find the real player
self.monsterinfo.aiflags |= Defines.AI_SOUND_TARGET;
if (client == self.enemy)
return true; // JDC false;
self.enemy = client;
}
// got one
FoundTarget(self);
if (0 == (self.monsterinfo.aiflags & Defines.AI_SOUND_TARGET)
&& (self.monsterinfo.sight != null))
self.monsterinfo.sight.interact(self, self.enemy);
return true;
}
public static void FoundTarget(edict_t self) {
// let other monsters see this monster for a while
if (self.enemy.client != null) {
GameBase.level.sight_entity = self;
GameBase.level.sight_entity_framenum = GameBase.level.framenum;
GameBase.level.sight_entity.light_level = 128;
}
self.show_hostile = (int) GameBase.level.time + 1; // wake up other
// monsters
Math3D.VectorCopy(self.enemy.s.origin, self.monsterinfo.last_sighting);
self.monsterinfo.trail_time = GameBase.level.time;
if (self.combattarget == null) {
GameAI.HuntTarget(self);
return;
}
self.goalentity = self.movetarget = GameBase
.G_PickTarget(self.combattarget);
if (self.movetarget == null) {
self.goalentity = self.movetarget = self.enemy;
GameAI.HuntTarget(self);
GameBase.gi.dprintf("" + self.classname + "at "
+ Lib.vtos(self.s.origin) + ", combattarget "
+ self.combattarget + " not found\n");
return;
}
// clear out our combattarget, these are a one shot deal
self.combattarget = null;
self.monsterinfo.aiflags |= Defines.AI_COMBAT_POINT;
// clear the targetname, that point is ours!
self.movetarget.targetname = null;
self.monsterinfo.pausetime = 0;
// run for it
self.monsterinfo.run.think(self);
}
public static EntThinkAdapter Think_Delay = new EntThinkAdapter() {
public String getID() { return "Think_Delay"; }
public boolean think(edict_t ent) {
G_UseTargets(ent, ent.activator);
G_FreeEdict(ent);
return true;
}
};
public static EntThinkAdapter G_FreeEdictA = new EntThinkAdapter() {
public String getID() { return "G_FreeEdictA"; }
public boolean think(edict_t ent) {
G_FreeEdict(ent);
return false;
}
};
static EntThinkAdapter MegaHealth_think = new EntThinkAdapter() {
public String getID() { return "MegaHealth_think"; }
public boolean think(edict_t self) {
if (self.owner.health > self.owner.max_health) {
self.nextthink = GameBase.level.time + 1;
self.owner.health -= 1;
return false;
}
if (!((self.spawnflags & Defines.DROPPED_ITEM) != 0)
&& (GameBase.deathmatch.value != 0))
GameItems.SetRespawn(self, 20);
else
G_FreeEdict(self);
return false;
}
};
public static EntThinkAdapter M_CheckAttack = new EntThinkAdapter() {
public String getID() { return "M_CheckAttack"; }
public boolean think(edict_t self) {
float[] spot1 = { 0, 0, 0 };
float[] spot2 = { 0, 0, 0 };
float chance;
trace_t tr;
if (self.enemy.health > 0) {
// see if any entities are in the way of the shot
Math3D.VectorCopy(self.s.origin, spot1);
spot1[2] += self.viewheight;
Math3D.VectorCopy(self.enemy.s.origin, spot2);
spot2[2] += self.enemy.viewheight;
tr = GameBase.gi.trace(spot1, null, null, spot2, self,
Defines.CONTENTS_SOLID | Defines.CONTENTS_MONSTER
| Defines.CONTENTS_SLIME
| Defines.CONTENTS_LAVA
| Defines.CONTENTS_WINDOW);
// do we have a clear shot?
if (tr.ent != self.enemy)
return false;
}
// melee attack
if (GameAI.enemy_range == Defines.RANGE_MELEE) {
// don't always melee in easy mode
if (GameBase.skill.value == 0 && (Lib.rand() & 3) != 0)
return false;
if (self.monsterinfo.melee != null)
self.monsterinfo.attack_state = Defines.AS_MELEE;
else
self.monsterinfo.attack_state = Defines.AS_MISSILE;
return true;
}
// missile attack
if (self.monsterinfo.attack == null)
return false;
if (GameBase.level.time < self.monsterinfo.attack_finished)
return false;
if (GameAI.enemy_range == Defines.RANGE_FAR)
return false;
if ((self.monsterinfo.aiflags & Defines.AI_STAND_GROUND) != 0) {
chance = 0.4f;
} else if (GameAI.enemy_range == Defines.RANGE_MELEE) {
chance = 0.2f;
} else if (GameAI.enemy_range == Defines.RANGE_NEAR) {
chance = 0.1f;
} else if (GameAI.enemy_range == Defines.RANGE_MID) {
chance = 0.02f;
} else {
return false;
}
if (GameBase.skill.value == 0)
chance *= 0.5;
else if (GameBase.skill.value >= 2)
chance *= 2;
if (Lib.random() < chance) {
self.monsterinfo.attack_state = Defines.AS_MISSILE;
self.monsterinfo.attack_finished = GameBase.level.time + 2
* Lib.random();
return true;
}
if ((self.flags & Defines.FL_FLY) != 0) {
if (Lib.random() < 0.3f)
self.monsterinfo.attack_state = Defines.AS_SLIDING;
else
self.monsterinfo.attack_state = Defines.AS_STRAIGHT;
}
return false;
}
};
static EntUseAdapter monster_use = new EntUseAdapter() {
public String getID() { return "monster_use"; }
public void use(edict_t self, edict_t other, edict_t activator) {
if (self.enemy != null)
return;
if (self.health <= 0)
return;
if ((activator.flags & Defines.FL_NOTARGET) != 0)
return;
if ((null == activator.client)
&& 0 == (activator.monsterinfo.aiflags & Defines.AI_GOOD_GUY))
return;
// delay reaction so if the monster is teleported, its sound is
// still heard
self.enemy = activator;
FoundTarget(self);
}
};
}

View File

@@ -0,0 +1,972 @@
/*
* 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 GameWeapon {
static EntTouchAdapter blaster_touch = new EntTouchAdapter() {
public String getID() { return "blaster_touch"; }
public void touch(edict_t self, edict_t other, cplane_t plane,
csurface_t surf) {
int mod;
if (other == self.owner)
return;
if (surf != null && (surf.flags & Defines.SURF_SKY) != 0) {
GameUtil.G_FreeEdict(self);
return;
}
if (self.owner.client != null)
PlayerWeapon.PlayerNoise(self.owner, self.s.origin,
Defines.PNOISE_IMPACT);
if (other.takedamage != 0) {
if ((self.spawnflags & 1) != 0)
mod = Defines.MOD_HYPERBLASTER;
else
mod = Defines.MOD_BLASTER;
// bugfix null plane rst
float[] normal;
if (plane == null)
normal = new float[3];
else
normal = plane.normal;
GameCombat.T_Damage(other, self, self.owner, self.velocity,
self.s.origin, normal, self.dmg, 1,
Defines.DAMAGE_ENERGY, mod);
} else {
GameBase.gi.WriteByte(Defines.svc_temp_entity);
GameBase.gi.WriteByte(Defines.TE_BLASTER);
GameBase.gi.WritePosition(self.s.origin);
if (plane == null)
GameBase.gi.WriteDir(Globals.vec3_origin);
else
GameBase.gi.WriteDir(plane.normal);
GameBase.gi.multicast(self.s.origin, Defines.MULTICAST_PVS);
}
GameUtil.G_FreeEdict(self);
}
};
static EntThinkAdapter Grenade_Explode = new EntThinkAdapter() {
public String getID() { return "Grenade_Explode"; }
public boolean think(edict_t ent) {
float[] origin = { 0, 0, 0 };
int mod;
if (ent.owner.client != null)
PlayerWeapon.PlayerNoise(ent.owner, ent.s.origin,
Defines.PNOISE_IMPACT);
//FIXME: if we are onground then raise our Z just a bit since we
// are a point?
if (ent.enemy != null) {
float points = 0;
float[] v = { 0, 0, 0 };
float[] dir = { 0, 0, 0 };
Math3D.VectorAdd(ent.enemy.mins, ent.enemy.maxs, v);
Math3D.VectorMA(ent.enemy.s.origin, 0.5f, v, v);
Math3D.VectorSubtract(ent.s.origin, v, v);
points = ent.dmg - 0.5f * Math3D.VectorLength(v);
Math3D.VectorSubtract(ent.enemy.s.origin, ent.s.origin, dir);
if ((ent.spawnflags & 1) != 0)
mod = Defines.MOD_HANDGRENADE;
else
mod = Defines.MOD_GRENADE;
GameCombat.T_Damage(ent.enemy, ent, ent.owner, dir, ent.s.origin,
Globals.vec3_origin, (int) points, (int) points,
Defines.DAMAGE_RADIUS, mod);
}
if ((ent.spawnflags & 2) != 0)
mod = Defines.MOD_HELD_GRENADE;
else if ((ent.spawnflags & 1) != 0)
mod = Defines.MOD_HG_SPLASH;
else
mod = Defines.MOD_G_SPLASH;
GameCombat.T_RadiusDamage(ent, ent.owner, ent.dmg, ent.enemy,
ent.dmg_radius, mod);
Math3D.VectorMA(ent.s.origin, -0.02f, ent.velocity, origin);
GameBase.gi.WriteByte(Defines.svc_temp_entity);
if (ent.waterlevel != 0) {
if (ent.groundentity != null)
GameBase.gi.WriteByte(Defines.TE_GRENADE_EXPLOSION_WATER);
else
GameBase.gi.WriteByte(Defines.TE_ROCKET_EXPLOSION_WATER);
} else {
if (ent.groundentity != null)
GameBase.gi.WriteByte(Defines.TE_GRENADE_EXPLOSION);
else
GameBase.gi.WriteByte(Defines.TE_ROCKET_EXPLOSION);
}
GameBase.gi.WritePosition(origin);
GameBase.gi.multicast(ent.s.origin, Defines.MULTICAST_PHS);
GameUtil.G_FreeEdict(ent);
return true;
}
};
static EntTouchAdapter Grenade_Touch = new EntTouchAdapter() {
public String getID() { return "Grenade_Touch"; }
public void touch(edict_t ent, edict_t other, cplane_t plane,
csurface_t surf) {
if (other == ent.owner)
return;
if (surf != null && 0 != (surf.flags & Defines.SURF_SKY)) {
GameUtil.G_FreeEdict(ent);
return;
}
if (other.takedamage == 0) {
if ((ent.spawnflags & 1) != 0) {
if (Lib.random() > 0.5f)
GameBase.gi.sound(ent, Defines.CHAN_VOICE, GameBase.gi
.soundindex("weapons/hgrenb1a.wav"), 1,
Defines.ATTN_NORM, 0);
else
GameBase.gi.sound(ent, Defines.CHAN_VOICE, GameBase.gi
.soundindex("weapons/hgrenb2a.wav"), 1,
Defines.ATTN_NORM, 0);
} else {
GameBase.gi.sound(ent, Defines.CHAN_VOICE, GameBase.gi
.soundindex("weapons/grenlb1b.wav"), 1,
Defines.ATTN_NORM, 0);
}
return;
}
ent.enemy = other;
Grenade_Explode.think(ent);
}
};
/*
* =================
* fire_rocket
* =================
*/
static EntTouchAdapter rocket_touch = new EntTouchAdapter() {
public String getID() { return "rocket_touch"; }
public void touch(edict_t ent, edict_t other, cplane_t plane,
csurface_t surf) {
float[] origin = { 0, 0, 0 };
int n;
if (other == ent.owner)
return;
if (surf != null && (surf.flags & Defines.SURF_SKY) != 0) {
GameUtil.G_FreeEdict(ent);
return;
}
if (ent.owner.client != null)
PlayerWeapon.PlayerNoise(ent.owner, ent.s.origin,
Defines.PNOISE_IMPACT);
// calculate position for the explosion entity
Math3D.VectorMA(ent.s.origin, -0.02f, ent.velocity, origin);
if (other.takedamage != 0) {
GameCombat.T_Damage(other, ent, ent.owner, ent.velocity,
ent.s.origin, plane.normal, ent.dmg, 0, 0,
Defines.MOD_ROCKET);
} else {
// don't throw any debris in net games
if (GameBase.deathmatch.value == 0 && 0 == GameBase.coop.value) {
if ((surf != null)
&& 0 == (surf.flags & (Defines.SURF_WARP
| Defines.SURF_TRANS33
| Defines.SURF_TRANS66 | Defines.SURF_FLOWING))) {
n = Lib.rand() % 5;
while (n-- > 0)
GameMisc.ThrowDebris(ent,
"models/objects/debris2/tris.md2", 2,
ent.s.origin);
}
}
}
GameCombat.T_RadiusDamage(ent, ent.owner, ent.radius_dmg, other,
ent.dmg_radius, Defines.MOD_R_SPLASH);
GameBase.gi.WriteByte(Defines.svc_temp_entity);
if (ent.waterlevel != 0)
GameBase.gi.WriteByte(Defines.TE_ROCKET_EXPLOSION_WATER);
else
GameBase.gi.WriteByte(Defines.TE_ROCKET_EXPLOSION);
GameBase.gi.WritePosition(origin);
GameBase.gi.multicast(ent.s.origin, Defines.MULTICAST_PHS);
GameUtil.G_FreeEdict(ent);
}
};
/*
* =================
* fire_bfg
* =================
*/
static EntThinkAdapter bfg_explode = new EntThinkAdapter() {
public String getID() { return "bfg_explode"; }
public boolean think(edict_t self) {
edict_t ent;
float points;
float[] v = { 0, 0, 0 };
float dist;
EdictIterator edit = null;
if (self.s.frame == 0) {
// the BFG effect
ent = null;
while ((edit = GameBase.findradius(edit, self.s.origin,
self.dmg_radius)) != null) {
ent = edit.o;
if (ent.takedamage == 0)
continue;
if (ent == self.owner)
continue;
if (!GameCombat.CanDamage(ent, self))
continue;
if (!GameCombat.CanDamage(ent, self.owner))
continue;
Math3D.VectorAdd(ent.mins, ent.maxs, v);
Math3D.VectorMA(ent.s.origin, 0.5f, v, v);
Math3D.VectorSubtract(self.s.origin, v, v);
dist = Math3D.VectorLength(v);
points = (float) (self.radius_dmg * (1.0 - Math.sqrt(dist
/ self.dmg_radius)));
if (ent == self.owner)
points = points * 0.5f;
GameBase.gi.WriteByte(Defines.svc_temp_entity);
GameBase.gi.WriteByte(Defines.TE_BFG_EXPLOSION);
GameBase.gi.WritePosition(ent.s.origin);
GameBase.gi.multicast(ent.s.origin, Defines.MULTICAST_PHS);
GameCombat.T_Damage(ent, self, self.owner, self.velocity,
ent.s.origin, Globals.vec3_origin, (int) points, 0,
Defines.DAMAGE_ENERGY, Defines.MOD_BFG_EFFECT);
}
}
self.nextthink = GameBase.level.time + Defines.FRAMETIME;
self.s.frame++;
if (self.s.frame == 5)
self.think = GameUtil.G_FreeEdictA;
return true;
}
};
static EntTouchAdapter bfg_touch = new EntTouchAdapter() {
public String getID() { return "bfg_touch"; }
public void touch(edict_t self, edict_t other, cplane_t plane,
csurface_t surf) {
if (other == self.owner)
return;
if (surf != null && (surf.flags & Defines.SURF_SKY) != 0) {
GameUtil.G_FreeEdict(self);
return;
}
if (self.owner.client != null)
PlayerWeapon.PlayerNoise(self.owner, self.s.origin,
Defines.PNOISE_IMPACT);
// core explosion - prevents firing it into the wall/floor
if (other.takedamage != 0)
GameCombat.T_Damage(other, self, self.owner, self.velocity,
self.s.origin, plane.normal, 200, 0, 0,
Defines.MOD_BFG_BLAST);
GameCombat.T_RadiusDamage(self, self.owner, 200, other, 100,
Defines.MOD_BFG_BLAST);
GameBase.gi.sound(self, Defines.CHAN_VOICE, GameBase.gi
.soundindex("weapons/bfg__x1b.wav"), 1, Defines.ATTN_NORM,
0);
self.solid = Defines.SOLID_NOT;
self.touch = null;
Math3D.VectorMA(self.s.origin, -1 * Defines.FRAMETIME,
self.velocity, self.s.origin);
Math3D.VectorClear(self.velocity);
self.s.modelindex = GameBase.gi.modelindex("sprites/s_bfg3.sp2");
self.s.frame = 0;
self.s.sound = 0;
self.s.effects &= ~Defines.EF_ANIM_ALLFAST;
self.think = bfg_explode;
self.nextthink = GameBase.level.time + Defines.FRAMETIME;
self.enemy = other;
GameBase.gi.WriteByte(Defines.svc_temp_entity);
GameBase.gi.WriteByte(Defines.TE_BFG_BIGEXPLOSION);
GameBase.gi.WritePosition(self.s.origin);
GameBase.gi.multicast(self.s.origin, Defines.MULTICAST_PVS);
}
};
static EntThinkAdapter bfg_think = new EntThinkAdapter() {
public String getID() { return "bfg_think"; }
public boolean think(edict_t self) {
edict_t ent;
edict_t ignore;
float[] point = { 0, 0, 0 };
float[] dir = { 0, 0, 0 };
float[] start = { 0, 0, 0 };
float[] end = { 0, 0, 0 };
int dmg;
trace_t tr;
if (GameBase.deathmatch.value != 0)
dmg = 5;
else
dmg = 10;
EdictIterator edit = null;
while ((edit = GameBase.findradius(edit, self.s.origin, 256)) != null) {
ent = edit.o;
if (ent == self)
continue;
if (ent == self.owner)
continue;
if (ent.takedamage == 0)
continue;
if (0 == (ent.svflags & Defines.SVF_MONSTER)
&& (null == ent.client)
&& (Lib.strcmp(ent.classname, "misc_explobox") != 0))
continue;
Math3D.VectorMA(ent.absmin, 0.5f, ent.size, point);
Math3D.VectorSubtract(point, self.s.origin, dir);
Math3D.VectorNormalize(dir);
ignore = self;
Math3D.VectorCopy(self.s.origin, start);
Math3D.VectorMA(start, 2048, dir, end);
while (true) {
tr = GameBase.gi.trace(start, null, null, end, ignore,
Defines.CONTENTS_SOLID | Defines.CONTENTS_MONSTER
| Defines.CONTENTS_DEADMONSTER);
if (null == tr.ent)
break;
// hurt it if we can
if ((tr.ent.takedamage != 0)
&& 0 == (tr.ent.flags & Defines.FL_IMMUNE_LASER)
&& (tr.ent != self.owner))
GameCombat.T_Damage(tr.ent, self, self.owner, dir,
tr.endpos, Globals.vec3_origin, dmg, 1,
Defines.DAMAGE_ENERGY, Defines.MOD_BFG_LASER);
// if we hit something that's not a monster or player we're
// done
if (0 == (tr.ent.svflags & Defines.SVF_MONSTER)
&& (null == tr.ent.client)) {
GameBase.gi.WriteByte(Defines.svc_temp_entity);
GameBase.gi.WriteByte(Defines.TE_LASER_SPARKS);
GameBase.gi.WriteByte(4);
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);
}
GameBase.gi.WriteByte(Defines.svc_temp_entity);
GameBase.gi.WriteByte(Defines.TE_BFG_LASER);
GameBase.gi.WritePosition(self.s.origin);
GameBase.gi.WritePosition(tr.endpos);
GameBase.gi.multicast(self.s.origin, Defines.MULTICAST_PHS);
}
self.nextthink = GameBase.level.time + Defines.FRAMETIME;
return true;
}
};
/*
* =================
* check_dodge
*
* This is a support routine used when a client is firing a non-instant
* attack weapon. It checks to see if a monster's dodge function should be
* called.
* =================
*/
static void check_dodge(edict_t self, float[] start, float[] dir, int speed) {
float[] end = { 0, 0, 0 };
float[] v = { 0, 0, 0 };
trace_t tr;
float eta;
// easy mode only ducks one quarter the time
if (GameBase.skill.value == 0) {
if (Lib.random() > 0.25)
return;
}
Math3D.VectorMA(start, 8192, dir, end);
tr = GameBase.gi.trace(start, null, null, end, self, Defines.MASK_SHOT);
if ((tr.ent != null) && (tr.ent.svflags & Defines.SVF_MONSTER) != 0
&& (tr.ent.health > 0) && (null != tr.ent.monsterinfo.dodge)
&& GameUtil.infront(tr.ent, self)) {
Math3D.VectorSubtract(tr.endpos, start, v);
eta = (Math3D.VectorLength(v) - tr.ent.maxs[0]) / speed;
tr.ent.monsterinfo.dodge.dodge(tr.ent, self, eta);
}
}
/*
* =================
* fire_hit
*
* Used for all impact (hit/punch/slash) attacks
* =================
*/
public static boolean fire_hit(edict_t self, float[] aim, int damage,
int kick) {
trace_t tr;
float[] forward = { 0, 0, 0 }, right = { 0, 0, 0 }, up = { 0, 0, 0 };
float[] v = { 0, 0, 0 };
float[] point = { 0, 0, 0 };
float range;
float[] dir = { 0, 0, 0 };
//see if enemy is in range
Math3D.VectorSubtract(self.enemy.s.origin, self.s.origin, dir);
range = Math3D.VectorLength(dir);
if (range > aim[0])
return false;
if (aim[1] > self.mins[0] && aim[1] < self.maxs[0]) {
// the hit is straight on so back the range up to the edge of their
// bbox
range -= self.enemy.maxs[0];
} else {
// this is a side hit so adjust the "right" value out to the edge of
// their bbox
if (aim[1] < 0)
aim[1] = self.enemy.mins[0];
else
aim[1] = self.enemy.maxs[0];
}
Math3D.VectorMA(self.s.origin, range, dir, point);
tr = GameBase.gi.trace(self.s.origin, null, null, point, self,
Defines.MASK_SHOT);
if (tr.fraction < 1) {
if (0 == tr.ent.takedamage)
return false;
// if it will hit any client/monster then hit the one we wanted to
// hit
if ((tr.ent.svflags & Defines.SVF_MONSTER) != 0
|| (tr.ent.client != null))
tr.ent = self.enemy;
}
Math3D.AngleVectors(self.s.angles, forward, right, up);
Math3D.VectorMA(self.s.origin, range, forward, point);
Math3D.VectorMA(point, aim[1], right, point);
Math3D.VectorMA(point, aim[2], up, point);
Math3D.VectorSubtract(point, self.enemy.s.origin, dir);
// do the damage
GameCombat.T_Damage(tr.ent, self, self, dir, point, Globals.vec3_origin,
damage, kick / 2, Defines.DAMAGE_NO_KNOCKBACK, Defines.MOD_HIT);
if (0 == (tr.ent.svflags & Defines.SVF_MONSTER)
&& (null == tr.ent.client))
return false;
// do our special form of knockback here
Math3D.VectorMA(self.enemy.absmin, 0.5f, self.enemy.size, v);
Math3D.VectorSubtract(v, point, v);
Math3D.VectorNormalize(v);
Math3D.VectorMA(self.enemy.velocity, kick, v, self.enemy.velocity);
if (self.enemy.velocity[2] > 0)
self.enemy.groundentity = null;
return true;
}
/*
* =================
* fire_lead
*
* This is an internal support routine used for bullet/pellet based weapons.
* =================
*/
public static void fire_lead(edict_t self, float[] start, float[] aimdir,
int damage, int kick, int te_impact, int hspread, int vspread,
int mod) {
trace_t tr;
float[] dir = { 0, 0, 0 };
float[] forward = { 0, 0, 0 }, right = { 0, 0, 0 }, up = { 0, 0, 0 };
float[] end = { 0, 0, 0 };
float r;
float u;
float[] water_start = { 0, 0, 0 };
boolean water = false;
int content_mask = Defines.MASK_SHOT | Defines.MASK_WATER;
tr = GameBase.gi.trace(self.s.origin, null, null, start, self,
Defines.MASK_SHOT);
if (!(tr.fraction < 1.0)) {
Math3D.vectoangles(aimdir, dir);
Math3D.AngleVectors(dir, forward, right, up);
r = Lib.crandom() * hspread;
u = Lib.crandom() * vspread;
Math3D.VectorMA(start, 8192, forward, end);
Math3D.VectorMA(end, r, right, end);
Math3D.VectorMA(end, u, up, end);
if ((GameBase.gi.pointcontents.pointcontents(start) & Defines.MASK_WATER) != 0) {
water = true;
Math3D.VectorCopy(start, water_start);
content_mask &= ~Defines.MASK_WATER;
}
tr = GameBase.gi.trace(start, null, null, end, self, content_mask);
// see if we hit water
if ((tr.contents & Defines.MASK_WATER) != 0) {
int color;
water = true;
Math3D.VectorCopy(tr.endpos, water_start);
if (!Math3D.VectorEquals(start, tr.endpos)) {
if ((tr.contents & Defines.CONTENTS_WATER) != 0) {
if (Lib.strcmp(tr.surface.name, "*brwater") == 0)
color = Defines.SPLASH_BROWN_WATER;
else
color = Defines.SPLASH_BLUE_WATER;
} else if ((tr.contents & Defines.CONTENTS_SLIME) != 0)
color = Defines.SPLASH_SLIME;
else if ((tr.contents & Defines.CONTENTS_LAVA) != 0)
color = Defines.SPLASH_LAVA;
else
color = Defines.SPLASH_UNKNOWN;
if (color != Defines.SPLASH_UNKNOWN) {
GameBase.gi.WriteByte(Defines.svc_temp_entity);
GameBase.gi.WriteByte(Defines.TE_SPLASH);
GameBase.gi.WriteByte(8);
GameBase.gi.WritePosition(tr.endpos);
GameBase.gi.WriteDir(tr.plane.normal);
GameBase.gi.WriteByte(color);
GameBase.gi.multicast(tr.endpos, Defines.MULTICAST_PVS);
}
// change bullet's course when it enters water
Math3D.VectorSubtract(end, start, dir);
Math3D.vectoangles(dir, dir);
Math3D.AngleVectors(dir, forward, right, up);
r = Lib.crandom() * hspread * 2;
u = Lib.crandom() * vspread * 2;
Math3D.VectorMA(water_start, 8192, forward, end);
Math3D.VectorMA(end, r, right, end);
Math3D.VectorMA(end, u, up, end);
}
// re-trace ignoring water this time
tr = GameBase.gi.trace(water_start, null, null, end, self,
Defines.MASK_SHOT);
}
}
// send gun puff / flash
if (!((tr.surface != null) && 0 != (tr.surface.flags & Defines.SURF_SKY))) {
if (tr.fraction < 1.0) {
if (tr.ent.takedamage != 0) {
GameCombat.T_Damage(tr.ent, self, self, aimdir, tr.endpos,
tr.plane.normal, damage, kick,
Defines.DAMAGE_BULLET, mod);
} else {
if (!"sky".equals(tr.surface.name)) {
GameBase.gi.WriteByte(Defines.svc_temp_entity);
GameBase.gi.WriteByte(te_impact);
GameBase.gi.WritePosition(tr.endpos);
GameBase.gi.WriteDir(tr.plane.normal);
GameBase.gi.multicast(tr.endpos, Defines.MULTICAST_PVS);
if (self.client != null)
PlayerWeapon.PlayerNoise(self, tr.endpos,
Defines.PNOISE_IMPACT);
}
}
}
}
// if went through water, determine where the end and make a bubble
// trail
if (water) {
float[] pos = { 0, 0, 0 };
Math3D.VectorSubtract(tr.endpos, water_start, dir);
Math3D.VectorNormalize(dir);
Math3D.VectorMA(tr.endpos, -2, dir, pos);
if ((GameBase.gi.pointcontents.pointcontents(pos) & Defines.MASK_WATER) != 0)
Math3D.VectorCopy(pos, tr.endpos);
else
tr = GameBase.gi.trace(pos, null, null, water_start, tr.ent,
Defines.MASK_WATER);
Math3D.VectorAdd(water_start, tr.endpos, pos);
Math3D.VectorScale(pos, 0.5f, pos);
GameBase.gi.WriteByte(Defines.svc_temp_entity);
GameBase.gi.WriteByte(Defines.TE_BUBBLETRAIL);
GameBase.gi.WritePosition(water_start);
GameBase.gi.WritePosition(tr.endpos);
GameBase.gi.multicast(pos, Defines.MULTICAST_PVS);
}
}
/*
* ================= fire_bullet
*
* Fires a single round. Used for machinegun and chaingun. Would be fine for
* pistols, rifles, etc.... =================
*/
public static void fire_bullet(edict_t self, float[] start, float[] aimdir,
int damage, int kick, int hspread, int vspread, int mod) {
fire_lead(self, start, aimdir, damage, kick, Defines.TE_GUNSHOT,
hspread, vspread, mod);
}
/*
* =================
* fire_shotgun
*
* Shoots shotgun pellets. Used by shotgun and super shotgun.
* =================
*/
public static void fire_shotgun(edict_t self, float[] start,
float[] aimdir, int damage, int kick, int hspread, int vspread,
int count, int mod) {
int i;
for (i = 0; i < count; i++)
fire_lead(self, start, aimdir, damage, kick, Defines.TE_SHOTGUN,
hspread, vspread, mod);
}
/*
* =================
* fire_blaster
*
* Fires a single blaster bolt. Used by the blaster and hyper blaster.
* =================
*/
public static void fire_blaster(edict_t self, float[] start, float[] dir,
int damage, int speed, int effect, boolean hyper) {
edict_t bolt;
trace_t tr;
Math3D.VectorNormalize(dir);
bolt = GameUtil.G_Spawn();
bolt.svflags = Defines.SVF_DEADMONSTER;
// yes, I know it looks weird that projectiles are deadmonsters
// what this means is that when prediction is used against the object
// (blaster/hyperblaster shots), the player won't be solid clipped
// against
// the object. Right now trying to run into a firing hyperblaster
// is very jerky since you are predicted 'against' the shots.
Math3D.VectorCopy(start, bolt.s.origin);
Math3D.VectorCopy(start, bolt.s.old_origin);
Math3D.vectoangles(dir, bolt.s.angles);
Math3D.VectorScale(dir, speed, bolt.velocity);
bolt.movetype = Defines.MOVETYPE_FLYMISSILE;
bolt.clipmask = Defines.MASK_SHOT;
bolt.solid = Defines.SOLID_BBOX;
bolt.s.effects |= effect;
Math3D.VectorClear(bolt.mins);
Math3D.VectorClear(bolt.maxs);
bolt.s.modelindex = GameBase.gi
.modelindex("models/objects/laser/tris.md2");
bolt.s.sound = GameBase.gi.soundindex("misc/lasfly.wav");
bolt.owner = self;
bolt.touch = blaster_touch;
bolt.nextthink = GameBase.level.time + 2;
bolt.think = GameUtil.G_FreeEdictA;
bolt.dmg = damage;
bolt.classname = "bolt";
if (hyper)
bolt.spawnflags = 1;
GameBase.gi.linkentity(bolt);
if (self.client != null)
check_dodge(self, bolt.s.origin, dir, speed);
tr = GameBase.gi.trace(self.s.origin, null, null, bolt.s.origin, bolt,
Defines.MASK_SHOT);
if (tr.fraction < 1.0) {
Math3D.VectorMA(bolt.s.origin, -10, dir, bolt.s.origin);
bolt.touch.touch(bolt, tr.ent, GameBase.dummyplane, null);
}
}
public static void fire_grenade(edict_t self, float[] start,
float[] aimdir, int damage, int speed, float timer,
float damage_radius) {
edict_t grenade;
float[] dir = { 0, 0, 0 };
float[] forward = { 0, 0, 0 }, right = { 0, 0, 0 }, up = { 0, 0, 0 };
Math3D.vectoangles(aimdir, dir);
Math3D.AngleVectors(dir, forward, right, up);
grenade = GameUtil.G_Spawn();
Math3D.VectorCopy(start, grenade.s.origin);
Math3D.VectorScale(aimdir, speed, grenade.velocity);
Math3D.VectorMA(grenade.velocity, 200f + Lib.crandom() * 10.0f, up,
grenade.velocity);
Math3D.VectorMA(grenade.velocity, Lib.crandom() * 10.0f, right,
grenade.velocity);
Math3D.VectorSet(grenade.avelocity, 300, 300, 300);
grenade.movetype = Defines.MOVETYPE_BOUNCE;
grenade.clipmask = Defines.MASK_SHOT;
grenade.solid = Defines.SOLID_BBOX;
grenade.s.effects |= Defines.EF_GRENADE;
Math3D.VectorClear(grenade.mins);
Math3D.VectorClear(grenade.maxs);
grenade.s.modelindex = GameBase.gi
.modelindex("models/objects/grenade/tris.md2");
grenade.owner = self;
grenade.touch = Grenade_Touch;
grenade.nextthink = GameBase.level.time + timer;
grenade.think = Grenade_Explode;
grenade.dmg = damage;
grenade.dmg_radius = damage_radius;
grenade.classname = "grenade";
GameBase.gi.linkentity(grenade);
}
public static void fire_grenade2(edict_t self, float[] start,
float[] aimdir, int damage, int speed, float timer,
float damage_radius, boolean held) {
edict_t grenade;
float[] dir = { 0, 0, 0 };
float[] forward = { 0, 0, 0 }, right = { 0, 0, 0 }, up = { 0, 0, 0 };
Math3D.vectoangles(aimdir, dir);
Math3D.AngleVectors(dir, forward, right, up);
grenade = GameUtil.G_Spawn();
Math3D.VectorCopy(start, grenade.s.origin);
Math3D.VectorScale(aimdir, speed, grenade.velocity);
Math3D.VectorMA(grenade.velocity, 200f + Lib.crandom() * 10.0f, up,
grenade.velocity);
Math3D.VectorMA(grenade.velocity, Lib.crandom() * 10.0f, right,
grenade.velocity);
Math3D.VectorSet(grenade.avelocity, 300f, 300f, 300f);
grenade.movetype = Defines.MOVETYPE_BOUNCE;
grenade.clipmask = Defines.MASK_SHOT;
grenade.solid = Defines.SOLID_BBOX;
grenade.s.effects |= Defines.EF_GRENADE;
Math3D.VectorClear(grenade.mins);
Math3D.VectorClear(grenade.maxs);
grenade.s.modelindex = GameBase.gi
.modelindex("models/objects/grenade2/tris.md2");
grenade.owner = self;
grenade.touch = Grenade_Touch;
grenade.nextthink = GameBase.level.time + timer;
grenade.think = Grenade_Explode;
grenade.dmg = damage;
grenade.dmg_radius = damage_radius;
grenade.classname = "hgrenade";
if (held)
grenade.spawnflags = 3;
else
grenade.spawnflags = 1;
grenade.s.sound = GameBase.gi.soundindex("weapons/hgrenc1b.wav");
if (timer <= 0.0)
Grenade_Explode.think(grenade);
else {
GameBase.gi.sound(self, Defines.CHAN_WEAPON, GameBase.gi
.soundindex("weapons/hgrent1a.wav"), 1, Defines.ATTN_NORM,
0);
GameBase.gi.linkentity(grenade);
}
}
public static void fire_rocket(edict_t self, float[] start, float[] dir,
int damage, int speed, float damage_radius, int radius_damage) {
edict_t rocket;
rocket = GameUtil.G_Spawn();
Math3D.VectorCopy(start, rocket.s.origin);
Math3D.VectorCopy(dir, rocket.movedir);
Math3D.vectoangles(dir, rocket.s.angles);
Math3D.VectorScale(dir, speed, rocket.velocity);
rocket.movetype = Defines.MOVETYPE_FLYMISSILE;
rocket.clipmask = Defines.MASK_SHOT;
rocket.solid = Defines.SOLID_BBOX;
rocket.s.effects |= Defines.EF_ROCKET;
Math3D.VectorClear(rocket.mins);
Math3D.VectorClear(rocket.maxs);
rocket.s.modelindex = GameBase.gi
.modelindex("models/objects/rocket/tris.md2");
rocket.owner = self;
rocket.touch = rocket_touch;
rocket.nextthink = GameBase.level.time + 8000 / speed;
rocket.think = GameUtil.G_FreeEdictA;
rocket.dmg = damage;
rocket.radius_dmg = radius_damage;
rocket.dmg_radius = damage_radius;
rocket.s.sound = GameBase.gi.soundindex("weapons/rockfly.wav");
rocket.classname = "rocket";
if (self.client != null)
check_dodge(self, rocket.s.origin, dir, speed);
GameBase.gi.linkentity(rocket);
}
/*
* =================
* fire_rail
* =================
*/
public static void fire_rail(edict_t self, float[] start, float[] aimdir,
int damage, int kick) {
float[] from = { 0, 0, 0 };
float[] end = { 0, 0, 0 };
trace_t tr = null;
edict_t ignore;
int mask;
boolean water;
Math3D.VectorMA(start, 8192f, aimdir, end);
Math3D.VectorCopy(start, from);
ignore = self;
water = false;
mask = Defines.MASK_SHOT | Defines.CONTENTS_SLIME
| Defines.CONTENTS_LAVA;
while (ignore != null) {
tr = GameBase.gi.trace(from, null, null, end, ignore, mask);
if ((tr.contents & (Defines.CONTENTS_SLIME | Defines.CONTENTS_LAVA)) != 0) {
mask &= ~(Defines.CONTENTS_SLIME | Defines.CONTENTS_LAVA);
water = true;
} else {
//ZOID--added so rail goes through SOLID_BBOX entities (gibs,
// etc)
if ((tr.ent.svflags & Defines.SVF_MONSTER) != 0
|| (tr.ent.client != null)
|| (tr.ent.solid == Defines.SOLID_BBOX))
ignore = tr.ent;
else
ignore = null;
if ((tr.ent != self) && (tr.ent.takedamage != 0))
GameCombat.T_Damage(tr.ent, self, self, aimdir, tr.endpos,
tr.plane.normal, damage, kick, 0,
Defines.MOD_RAILGUN);
}
Math3D.VectorCopy(tr.endpos, from);
}
// send gun puff / flash
GameBase.gi.WriteByte(Defines.svc_temp_entity);
GameBase.gi.WriteByte(Defines.TE_RAILTRAIL);
GameBase.gi.WritePosition(start);
GameBase.gi.WritePosition(tr.endpos);
GameBase.gi.multicast(self.s.origin, Defines.MULTICAST_PHS);
// gi.multicast (start, MULTICAST_PHS);
if (water) {
GameBase.gi.WriteByte(Defines.svc_temp_entity);
GameBase.gi.WriteByte(Defines.TE_RAILTRAIL);
GameBase.gi.WritePosition(start);
GameBase.gi.WritePosition(tr.endpos);
GameBase.gi.multicast(tr.endpos, Defines.MULTICAST_PHS);
}
if (self.client != null)
PlayerWeapon.PlayerNoise(self, tr.endpos, Defines.PNOISE_IMPACT);
}
public static void fire_bfg(edict_t self, float[] start, float[] dir,
int damage, int speed, float damage_radius) {
edict_t bfg;
bfg = GameUtil.G_Spawn();
Math3D.VectorCopy(start, bfg.s.origin);
Math3D.VectorCopy(dir, bfg.movedir);
Math3D.vectoangles(dir, bfg.s.angles);
Math3D.VectorScale(dir, speed, bfg.velocity);
bfg.movetype = Defines.MOVETYPE_FLYMISSILE;
bfg.clipmask = Defines.MASK_SHOT;
bfg.solid = Defines.SOLID_BBOX;
bfg.s.effects |= Defines.EF_BFG | Defines.EF_ANIM_ALLFAST;
Math3D.VectorClear(bfg.mins);
Math3D.VectorClear(bfg.maxs);
bfg.s.modelindex = GameBase.gi.modelindex("sprites/s_bfg1.sp2");
bfg.owner = self;
bfg.touch = bfg_touch;
bfg.nextthink = GameBase.level.time + 8000 / speed;
bfg.think = GameUtil.G_FreeEdictA;
bfg.radius_dmg = damage;
bfg.dmg_radius = damage_radius;
bfg.classname = "bfg blast";
bfg.s.sound = GameBase.gi.soundindex("weapons/bfg__l1a.wav");
bfg.think = bfg_think;
bfg.nextthink = GameBase.level.time + Defines.FRAMETIME;
bfg.teammaster = bfg;
bfg.teamchain = null;
if (self.client != null)
check_dodge(self, bfg.s.origin, dir, speed);
GameBase.gi.linkentity(bfg);
}
}

View File

@@ -0,0 +1,161 @@
/*
* 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;
}
StringBuffer sb = new StringBuffer(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.
*/
public static String Info_RemoveKey(String s, String key) {
StringBuffer sb = new StringBuffer(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));
}
private static String fillspaces = " ";
public static void Print(String s) {
StringBuffer sb = new StringBuffer(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) {
sb.append(fillspaces.substring(len));
}
sb.append('=').append(value1).append('\n');
}
Com.Printf(sb.toString());
}
}

View File

@@ -0,0 +1,24 @@
/*
* 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 ItemDropAdapter extends SuperAdapter {
public void drop(edict_t ent, gitem_t item) {
}
}

View File

@@ -0,0 +1,24 @@
/*
* 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 ItemUseAdapter extends SuperAdapter {
public void use(edict_t ent, gitem_t item) {
}
}

View File

@@ -0,0 +1,357 @@
/*
* 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.client.M;
import lwjake2.qcommon.Com;
import lwjake2.util.Lib;
import lwjake2.util.Math3D;
public class Monster {
// FIXME monsters should call these with a totally accurate direction
// and we can mess it up based on skill. Spread should be for normal
// and we can tighten or loosen based on skill. We could muck with
// the damages too, but I'm not sure that's such a good idea.
public static void monster_fire_bullet(edict_t self, float[] start,
float[] dir, int damage, int kick, int hspread, int vspread,
int flashtype) {
GameWeapon.fire_bullet(self, start, dir, damage, kick, hspread, vspread,
Defines.MOD_UNKNOWN);
GameBase.gi.WriteByte(Defines.svc_muzzleflash2);
GameBase.gi.WriteShort(self.index);
GameBase.gi.WriteByte(flashtype);
GameBase.gi.multicast(start, Defines.MULTICAST_PVS);
}
/** The Moster fires the shotgun. */
public static void monster_fire_shotgun(edict_t self, float[] start,
float[] aimdir, int damage, int kick, int hspread, int vspread,
int count, int flashtype) {
GameWeapon.fire_shotgun(self, start, aimdir, damage, kick, hspread, vspread,
count, Defines.MOD_UNKNOWN);
GameBase.gi.WriteByte(Defines.svc_muzzleflash2);
GameBase.gi.WriteShort(self.index);
GameBase.gi.WriteByte(flashtype);
GameBase.gi.multicast(start, Defines.MULTICAST_PVS);
}
/** The Moster fires the blaster. */
public static void monster_fire_blaster(edict_t self, float[] start,
float[] dir, int damage, int speed, int flashtype, int effect) {
GameWeapon.fire_blaster(self, start, dir, damage, speed, effect, false);
GameBase.gi.WriteByte(Defines.svc_muzzleflash2);
GameBase.gi.WriteShort(self.index);
GameBase.gi.WriteByte(flashtype);
GameBase.gi.multicast(start, Defines.MULTICAST_PVS);
}
/** The Moster fires the grenade. */
public static void monster_fire_grenade(edict_t self, float[] start,
float[] aimdir, int damage, int speed, int flashtype) {
GameWeapon
.fire_grenade(self, start, aimdir, damage, speed, 2.5f,
damage + 40);
GameBase.gi.WriteByte(Defines.svc_muzzleflash2);
GameBase.gi.WriteShort(self.index);
GameBase.gi.WriteByte(flashtype);
GameBase.gi.multicast(start, Defines.MULTICAST_PVS);
}
/** The Moster fires the rocket. */
public static void monster_fire_rocket(edict_t self, float[] start,
float[] dir, int damage, int speed, int flashtype) {
GameWeapon.fire_rocket(self, start, dir, damage, speed, damage + 20, damage);
GameBase.gi.WriteByte(Defines.svc_muzzleflash2);
GameBase.gi.WriteShort(self.index);
GameBase.gi.WriteByte(flashtype);
GameBase.gi.multicast(start, Defines.MULTICAST_PVS);
}
/** The Moster fires the railgun. */
public static void monster_fire_railgun(edict_t self, float[] start,
float[] aimdir, int damage, int kick, int flashtype) {
GameWeapon.fire_rail(self, start, aimdir, damage, kick);
GameBase.gi.WriteByte(Defines.svc_muzzleflash2);
GameBase.gi.WriteShort(self.index);
GameBase.gi.WriteByte(flashtype);
GameBase.gi.multicast(start, Defines.MULTICAST_PVS);
}
/** The Moster fires the bfg. */
public static void monster_fire_bfg(edict_t self, float[] start,
float[] aimdir, int damage, int speed, int kick,
float damage_radius, int flashtype) {
GameWeapon.fire_bfg(self, start, aimdir, damage, speed, damage_radius);
GameBase.gi.WriteByte(Defines.svc_muzzleflash2);
GameBase.gi.WriteShort(self.index);
GameBase.gi.WriteByte(flashtype);
GameBase.gi.multicast(start, Defines.MULTICAST_PVS);
}
/*
* ================ monster_death_use
*
* When a monster dies, it fires all of its targets with the current enemy
* as activator. ================
*/
public static void monster_death_use(edict_t self) {
self.flags &= ~(Defines.FL_FLY | Defines.FL_SWIM);
self.monsterinfo.aiflags &= Defines.AI_GOOD_GUY;
if (self.item != null) {
GameItems.Drop_Item(self, self.item);
self.item = null;
}
if (self.deathtarget != null)
self.target = self.deathtarget;
if (self.target == null)
return;
GameUtil.G_UseTargets(self, self.enemy);
}
// ============================================================================
public static boolean monster_start(edict_t self) {
if (GameBase.deathmatch.value != 0) {
GameUtil.G_FreeEdict(self);
return false;
}
if ((self.spawnflags & 4) != 0
&& 0 == (self.monsterinfo.aiflags & Defines.AI_GOOD_GUY)) {
self.spawnflags &= ~4;
self.spawnflags |= 1;
// gi.dprintf("fixed spawnflags on %s at %s\n", self.classname,
// vtos(self.s.origin));
}
if (0 == (self.monsterinfo.aiflags & Defines.AI_GOOD_GUY))
GameBase.level.total_monsters++;
self.nextthink = GameBase.level.time + Defines.FRAMETIME;
self.svflags |= Defines.SVF_MONSTER;
self.s.renderfx |= Defines.RF_FRAMELERP;
self.takedamage = Defines.DAMAGE_AIM;
self.air_finished = GameBase.level.time + 12;
self.use = GameUtil.monster_use;
self.max_health = self.health;
self.clipmask = Defines.MASK_MONSTERSOLID;
self.s.skinnum = 0;
self.deadflag = Defines.DEAD_NO;
self.svflags &= ~Defines.SVF_DEADMONSTER;
if (null == self.monsterinfo.checkattack)
self.monsterinfo.checkattack = GameUtil.M_CheckAttack;
Math3D.VectorCopy(self.s.origin, self.s.old_origin);
if (GameBase.st.item != null && GameBase.st.item.length() > 0) {
self.item = GameItems.FindItemByClassname(GameBase.st.item);
if (self.item == null)
GameBase.gi.dprintf("monster_start:" + self.classname + " at "
+ Lib.vtos(self.s.origin) + " has bad item: "
+ GameBase.st.item + "\n");
}
// randomize what frame they start on
if (self.monsterinfo.currentmove != null)
self.s.frame = self.monsterinfo.currentmove.firstframe
+ (Lib.rand() % (self.monsterinfo.currentmove.lastframe
- self.monsterinfo.currentmove.firstframe + 1));
return true;
}
public static void monster_start_go(edict_t self) {
float[] v = { 0, 0, 0 };
if (self.health <= 0)
return;
// check for target to combat_point and change to combattarget
if (self.target != null) {
boolean notcombat;
boolean fixup;
edict_t target = null;
notcombat = false;
fixup = false;
/*
* if (true) { Com.Printf("all entities:\n");
*
* for (int n = 0; n < Game.globals.num_edicts; n++) { edict_t ent =
* GameBase.g_edicts[n]; Com.Printf( "|%4i | %25s
* |%8.2f|%8.2f|%8.2f||%8.2f|%8.2f|%8.2f||%8.2f|%8.2f|%8.2f|\n", new
* Vargs().add(n).add(ent.classname).
* add(ent.s.origin[0]).add(ent.s.origin[1]).add(ent.s.origin[2])
* .add(ent.mins[0]).add(ent.mins[1]).add(ent.mins[2])
* .add(ent.maxs[0]).add(ent.maxs[1]).add(ent.maxs[2])); }
* sleep(10); }
*/
EdictIterator edit = null;
while ((edit = GameBase.G_Find(edit, GameBase.findByTarget,
self.target)) != null) {
target = edit.o;
if (Lib.strcmp(target.classname, "point_combat") == 0) {
self.combattarget = self.target;
fixup = true;
} else {
notcombat = true;
}
}
if (notcombat && self.combattarget != null)
GameBase.gi.dprintf(self.classname + " at "
+ Lib.vtos(self.s.origin)
+ " has target with mixed types\n");
if (fixup)
self.target = null;
}
// validate combattarget
if (self.combattarget != null) {
edict_t target = null;
EdictIterator edit = null;
while ((edit = GameBase.G_Find(edit, GameBase.findByTarget,
self.combattarget)) != null) {
target = edit.o;
if (Lib.strcmp(target.classname, "point_combat") != 0) {
GameBase.gi.dprintf(self.classname + " at "
+ Lib.vtos(self.s.origin)
+ " has bad combattarget " + self.combattarget
+ " : " + target.classname + " at "
+ Lib.vtos(target.s.origin));
}
}
}
if (self.target != null) {
self.goalentity = self.movetarget = GameBase
.G_PickTarget(self.target);
if (null == self.movetarget) {
GameBase.gi
.dprintf(self.classname + " can't find target "
+ self.target + " at "
+ Lib.vtos(self.s.origin) + "\n");
self.target = null;
self.monsterinfo.pausetime = 100000000;
self.monsterinfo.stand.think(self);
} else if (Lib.strcmp(self.movetarget.classname, "path_corner") == 0) {
Math3D.VectorSubtract(self.goalentity.s.origin, self.s.origin,
v);
self.ideal_yaw = self.s.angles[Defines.YAW] = Math3D
.vectoyaw(v);
self.monsterinfo.walk.think(self);
self.target = null;
} else {
self.goalentity = self.movetarget = null;
self.monsterinfo.pausetime = 100000000;
self.monsterinfo.stand.think(self);
}
} else {
self.monsterinfo.pausetime = 100000000;
self.monsterinfo.stand.think(self);
}
self.think = Monster.monster_think;
self.nextthink = GameBase.level.time + Defines.FRAMETIME;
}
public static EntThinkAdapter monster_think = new EntThinkAdapter() {
public String getID() { return "monster_think";}
public boolean think(edict_t self) {
M.M_MoveFrame(self);
if (self.linkcount != self.monsterinfo.linkcount) {
self.monsterinfo.linkcount = self.linkcount;
M.M_CheckGround(self);
}
M.M_CatagorizePosition(self);
M.M_WorldEffects(self);
M.M_SetEffects(self);
return true;
}
};
public static EntThinkAdapter monster_triggered_spawn = new EntThinkAdapter() {
public String getID() { return "monster_trigger_spawn";}
public boolean think(edict_t self) {
self.s.origin[2] += 1;
GameUtil.KillBox(self);
self.solid = Defines.SOLID_BBOX;
self.movetype = Defines.MOVETYPE_STEP;
self.svflags &= ~Defines.SVF_NOCLIENT;
self.air_finished = GameBase.level.time + 12;
GameBase.gi.linkentity(self);
Monster.monster_start_go(self);
if (self.enemy != null && 0 == (self.spawnflags & 1)
&& 0 == (self.enemy.flags & Defines.FL_NOTARGET)) {
GameUtil.FoundTarget(self);
} else {
self.enemy = null;
}
return true;
}
};
// we have a one frame delay here so we don't telefrag the guy who activated
// us
public static EntUseAdapter monster_triggered_spawn_use = new EntUseAdapter() {
public String getID() { return "monster_trigger_spawn_use";}
public void use(edict_t self, edict_t other, edict_t activator) {
self.think = monster_triggered_spawn;
self.nextthink = GameBase.level.time + Defines.FRAMETIME;
if (activator.client != null)
self.enemy = activator;
self.use = GameUtil.monster_use;
}
};
public static EntThinkAdapter monster_triggered_start = new EntThinkAdapter() {
public String getID() { return "monster_triggered_start";}
public boolean think(edict_t self) {
if (self.index == 312)
Com.Printf("monster_triggered_start\n");
self.solid = Defines.SOLID_NOT;
self.movetype = Defines.MOVETYPE_NONE;
self.svflags |= Defines.SVF_NOCLIENT;
self.nextthink = 0;
self.use = monster_triggered_spawn_use;
return true;
}
};
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,531 @@
/*
* 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;
public class PlayerHud {
/*
* ======================================================================
*
* INTERMISSION
*
* ======================================================================
*/
public static void MoveClientToIntermission(edict_t 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.gunindex = 0;
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.breather_framenum = 0;
ent.client.enviro_framenum = 0;
ent.client.grenade_blew_up = false;
ent.client.grenade_time = 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_t targ) {
int i, n;
edict_t 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)
continue;
// strip players of all keys between units
for (n = 1; n < GameItemList.itemlist.length; n++) {
// null pointer exception fixed. (RST)
if (GameItemList.itemlist[n] != null)
if ((GameItemList.itemlist[n].flags & Defines.IT_KEY) != 0)
client.client.pers.inventory[n] = 0;
}
}
}
} 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_t ent, edict_t killer) {
StringBuffer string = new StringBuffer(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;
gclient_t cl;
edict_t 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_t ent) {
DeathmatchScoreboardMessage(ent, ent.enemy);
GameBase.gi.unicast(ent, true);
}
/*
* ==================
* Cmd_Score_f
*
* Display the scoreboard
* ==================
*/
public static void Cmd_Score_f(edict_t 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_t ent) {
gitem_t item;
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;
//
// ammo
//
if (0 == ent.client.ammo_index /*
* ||
* !ent.client.pers.inventory[ent.client.ammo_index]
*/
) {
ent.client.ps.stats[Defines.STAT_AMMO_ICON] = 0;
ent.client.ps.stats[Defines.STAT_AMMO] = 0;
} else {
item = GameItemList.itemlist[ent.client.ammo_index];
ent.client.ps.stats[Defines.STAT_AMMO_ICON] = (short) GameBase.gi
.imageindex(item.icon);
ent.client.ps.stats[Defines.STAT_AMMO] = (short) ent.client.pers.inventory[ent.client.ammo_index];
}
//
// armor
//
power_armor_type = GameItems.PowerArmorType(ent);
if (power_armor_type != 0) {
cells = ent.client.pers.inventory[GameItems.ITEM_INDEX(GameItems
.FindItem("cells"))];
if (cells == 0) { // ran out of cells for power armor
ent.flags &= ~Defines.FL_POWER_ARMOR;
GameBase.gi
.sound(ent, Defines.CHAN_ITEM, GameBase.gi
.soundindex("misc/power2.wav"), 1,
Defines.ATTN_NORM, 0);
power_armor_type = 0;
;
}
}
index = GameItems.ArmorIndex(ent);
if (power_armor_type != 0
&& (0 == index || 0 != (GameBase.level.framenum & 8))) { // flash
// between
// power
// armor
// and
// other
// armor
// icon
ent.client.ps.stats[Defines.STAT_ARMOR_ICON] = (short) GameBase.gi
.imageindex("i_powershield");
ent.client.ps.stats[Defines.STAT_ARMOR] = (short) cells;
} else if (index != 0) {
item = GameItems.GetItemByIndex(index);
ent.client.ps.stats[Defines.STAT_ARMOR_ICON] = (short) GameBase.gi
.imageindex(item.icon);
ent.client.ps.stats[Defines.STAT_ARMOR] = (short) ent.client.pers.inventory[index];
} else {
ent.client.ps.stats[Defines.STAT_ARMOR_ICON] = 0;
ent.client.ps.stats[Defines.STAT_ARMOR] = 0;
}
//
// 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 if (ent.client.breather_framenum > GameBase.level.framenum) {
ent.client.ps.stats[Defines.STAT_TIMER_ICON] = (short) GameBase.gi
.imageindex("p_rebreather");
ent.client.ps.stats[Defines.STAT_TIMER] = (short) ((ent.client.breather_framenum - GameBase.level.framenum) / 10);
} else {
ent.client.ps.stats[Defines.STAT_TIMER_ICON] = 0;
ent.client.ps.stats[Defines.STAT_TIMER] = 0;
}
//
// selected item
//
// bugfix rst
if (ent.client.pers.selected_item <= 0)
ent.client.ps.stats[Defines.STAT_SELECTED_ICON] = 0;
else
ent.client.ps.stats[Defines.STAT_SELECTED_ICON] = (short) GameBase.gi
.imageindex(GameItemList.itemlist[ent.client.pers.selected_item].icon);
ent.client.ps.stats[Defines.STAT_SELECTED_ITEM] = (short) ent.client.pers.selected_item;
//
// 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 if ((ent.client.pers.hand == Defines.CENTER_HANDED || ent.client.ps.fov > 91)
&& ent.client.pers.weapon != null)
ent.client.ps.stats[Defines.STAT_HELPICON] = (short) GameBase.gi
.imageindex(ent.client.pers.weapon.icon);
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_t ent) {
int i;
gclient_t 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_t ent) {
gclient_t 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_t ent) {
StringBuffer sb = new StringBuffer(256);
String sk;
if (GameBase.skill.value == 0)
sk = "easy";
else if (GameBase.skill.value == 1)
sk = "medium";
else if (GameBase.skill.value == 2)
sk = "hard";
else
sk = "hard+";
// send the layout
sb.append("xv 32 yv 8 picn help "); // background
sb.append("xv 202 yv 12 string2 \"").append(sk).append("\" "); // skill
sb.append("xv 0 yv 24 cstring2 \"").append(GameBase.level.level_name)
.append("\" "); // level name
sb.append("xv 0 yv 54 cstring2 \"").append(GameBase.game.helpmessage1)
.append("\" "); // help 1
sb.append("xv 0 yv 110 cstring2 \"").append(GameBase.game.helpmessage2)
.append("\" "); // help 2
sb.append("xv 50 yv 164 string2 \" kills goals secrets\" ");
sb.append("xv 50 yv 172 string2 \"");
sb.append(Com.sprintf("%3i/%3i %i/%i %i/%i\" ", new Vargs(6)
.add(GameBase.level.killed_monsters).add(
GameBase.level.total_monsters).add(
GameBase.level.found_goals).add(
GameBase.level.total_goals).add(
GameBase.level.found_secrets).add(
GameBase.level.total_secrets)));
GameBase.gi.WriteByte(Defines.svc_layout);
GameBase.gi.WriteString(sb.toString());
GameBase.gi.unicast(ent, true);
}
}

View File

@@ -0,0 +1,144 @@
/*
* 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 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
*/
static int TRAIL_LENGTH = 8;
static edict_t trail[] = new edict_t[TRAIL_LENGTH];
static int trail_head;
static boolean trail_active = false;
static {
//TODO: potential error
for (int n = 0; n < TRAIL_LENGTH; n++)
trail[n] = new edict_t(n);
}
static int NEXT(int n) {
return (n + 1) % PlayerTrail.TRAIL_LENGTH;
}
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 void New(float[] spot) {
if (!trail_active)
return;
Init();
Add(spot);
}
static edict_t PickFirst(edict_t self) {
if (!trail_active)
return null;
int marker = trail_head;
for (int n = PlayerTrail.TRAIL_LENGTH; n > 0; n--) {
if (PlayerTrail.trail[marker].timestamp <= self.monsterinfo.trail_time)
marker = NEXT(marker);
else
break;
}
if (GameUtil.visible(self, PlayerTrail.trail[marker])) {
return PlayerTrail.trail[marker];
}
if (GameUtil.visible(self, PlayerTrail.trail[PREV(marker)])) {
return PlayerTrail.trail[PREV(marker)];
}
return PlayerTrail.trail[marker];
}
static edict_t PickNext(edict_t self) {
int marker;
int n;
if (!trail_active)
return null;
for (marker = trail_head, n = PlayerTrail.TRAIL_LENGTH; n > 0; n--) {
if (PlayerTrail.trail[marker].timestamp <= self.monsterinfo.trail_time)
marker = NEXT(marker);
else
break;
}
return PlayerTrail.trail[marker];
}
static edict_t LastSpot() {
return PlayerTrail.trail[PREV(trail_head)];
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,54 @@
/*
* 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 {
/** Constructor, does the adapter registration. */
public SuperAdapter() {
register(this, getID());
}
/** Adapter registration. */
private static void register(SuperAdapter sa, String id) {
adapters.put(id, sa);
}
/** Adapter repository. */
private static Hashtable<String, SuperAdapter> adapters= new Hashtable<String, SuperAdapter>();
/** Returns the adapter from the repository given by its ID. */
public static SuperAdapter getFromID(String key) {
SuperAdapter sa= (SuperAdapter) 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();
}

View File

@@ -0,0 +1,156 @@
/*
* 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 {
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_bullets= from.max_bullets;
max_shells= from.max_shells;
max_rockets= from.max_rockets;
max_grenades= from.max_grenades;
max_cells= from.max_cells;
max_slugs= from.max_slugs;
weapon= from.weapon;
lastweapon= from.lastweapon;
power_cubes= from.power_cubes;
score= from.score;
game_helpchanged= from.game_helpchanged;
helpchanged= from.helpchanged;
spectator= from.spectator;
}
// client data that stays across multiple level loads
String userinfo= "";
String netname= "";
int hand;
boolean connected; // a loadgame will leave valid entities that
// just don't have a connection yet
// values saved and restored from edicts when changing levels
int health;
int max_health;
int savedFlags;
int selected_item;
int inventory[]= new int[Defines.MAX_ITEMS];
// ammo capacities
public int max_bullets;
public int max_shells;
public int max_rockets;
public int max_grenades;
public int max_cells;
public int max_slugs;
//pointer
gitem_t weapon;
//pointer
gitem_t lastweapon;
int power_cubes; // used for tracking the cubes in coop games
int score; // for calculating total unit score in coop games
int game_helpchanged;
int helpchanged;
boolean spectator; // client is a 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_bullets= f.readInt();
max_shells= f.readInt();
max_rockets= f.readInt();
max_grenades= f.readInt();
max_cells= f.readInt();
max_slugs= f.readInt();
weapon= f.readItem();
lastweapon= f.readItem();
power_cubes= f.readInt();
score= 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_bullets);
f.writeInt(max_shells);
f.writeInt(max_rockets);
f.writeInt(max_grenades);
f.writeInt(max_cells);
f.writeInt(max_slugs);
f.writeItem(weapon);
f.writeItem(lastweapon);
f.writeInt(power_cubes);
f.writeInt(score);
f.writeInt(game_helpchanged);
f.writeInt(helpchanged);
f.writeInt(spectator ? 1 : 0);
}
}

View File

@@ -0,0 +1,88 @@
/*
* 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
{
/** 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;
/** angles sent over in the last command. */
protected float cmd_angles[] = { 0, 0, 0 };
/** 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);
}
}

View File

@@ -0,0 +1,26 @@
/*
* 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;
}

View File

@@ -0,0 +1,27 @@
/*
* 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 float[] mins = { 0, 0, 0 };
public float[] maxs = { 0, 0, 0 };
public float[] origin = { 0, 0, 0 }; // for sounds or lights
public int headnode;
}

View File

@@ -0,0 +1,50 @@
/*
* 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 float normal[] = new float[3];
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 byte pad[] = { 0, 0 };
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;
}
}

View File

@@ -0,0 +1,26 @@
/*
* 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;
}

View File

@@ -0,0 +1,33 @@
/*
* 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;
/**
* cvar_t implements the struct cvar_t of the C version
*/
public final class cvar_t
{
public String name;
public String string;
public String latched_string;
public int flags = 0;
public boolean modified = false;
public float value = 0.0f;
public cvar_t next = null;
}

View File

@@ -0,0 +1,774 @@
/*
* 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 lombok.extern.slf4j.Slf4j;
import lwjake2.Defines;
import lwjake2.qcommon.Com;
import lwjake2.util.Lib;
import lwjake2.util.QuakeFile;
import java.io.IOException;
@Slf4j
public class edict_t {
/** Constructor. */
public edict_t(int i) {
s.number = i;
index = i;
}
/** Used during level loading. */
public void cleararealinks() {
area = new link_t(this);
}
/** 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 killtarget = null;
public String team = null;
public String pathtarget = null;
public String deathtarget = null;
public String combattarget = null;
public edict_t 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 edict_t goalentity = null;
public edict_t movetarget = null;
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;
public EntPainAdapter pain = null;
public EntDieAdapter die = 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 deadflag;
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 takedamage;
public int dmg;
public int radius_dmg;
public float dmg_radius;
/** make this a spawntemp var? */
public int sounds;
public int count;
public edict_t chain = null;
public edict_t enemy = null;
public edict_t oldenemy = null;
public edict_t activator = null;
public edict_t groundentity = null;
public int groundentity_linkcount;
public edict_t teamchain = null;
public edict_t teammaster = null;
/** can go in client only. */
public edict_t mynoise = null;
public edict_t 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
/** common integrated data blocks. */
public moveinfo_t moveinfo = new moveinfo_t();
public monsterinfo_t monsterinfo = new monsterinfo_t();
public gclient_t client;
public edict_t owner;
/** Introduced by rst. */
public int index;
/////////////////////////////////////////////////
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("deathtarget")) {
deathtarget = GameSpawn.ED_NewString(value);
return true;
} // F_LSTRING),
if (key.equals("killtarget")) {
killtarget = GameSpawn.ED_NewString(value);
return true;
} // F_LSTRING),
if (key.equals("combattarget")) {
combattarget = 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(killtarget);
f.writeString(team);
f.writeString(pathtarget);
f.writeString(deathtarget);
f.writeString(combattarget);
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.writeEdictRef(goalentity);
f.writeEdictRef(movetarget);
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.writeAdapter(pain);
f.writeAdapter(die);
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(deadflag);
f.writeInt(show_hostile);
f.writeFloat(powerarmor_time);
f.writeString(map);
f.writeInt(viewheight);
f.writeInt(takedamage);
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();
killtarget = f.readString();
team = f.readString();
pathtarget = f.readString();
deathtarget = f.readString();
combattarget = 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();
goalentity = f.readEdictRef();
movetarget = f.readEdictRef();
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();
pain = (EntPainAdapter) f.readAdapter();
die = (EntDieAdapter) 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();
deadflag = f.readInt();
show_hostile = f.readInt();
powerarmor_time = f.readFloat();
map = f.readString();
viewheight = f.readInt();
takedamage = 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();
item = f.readItem();
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)
log.error("ent load check failed for num {}", index);
}
}

View File

@@ -0,0 +1,164 @@
/*
* 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 java.io.IOException;
import lwjake2.util.Math3D;
import lwjake2.util.QuakeFile;
public class entity_state_t implements Cloneable
{
/** 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_t ent)
{
this.surrounding_ent = ent;
if (ent != null)
number = ent.index;
}
/** edict index. TODO: this is critical. The index has to be proper managed. */
public int number = 0;
// TODO: why was this introduced?
public edict_t 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
// 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;
}
}

View File

@@ -0,0 +1,212 @@
/*
* 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.CM;
import lwjake2.qcommon.Cbuf;
import lwjake2.qcommon.Com;
import lwjake2.qcommon.Cvar;
import lwjake2.qcommon.PMove;
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 {
// 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_t ent, int printlevel, String s) {
SV_GAME.PF_cprintf(ent, printlevel, s);
}
public void centerprintf(edict_t ent, String s) {
SV_GAME.PF_centerprintf(ent, s);
}
public void sound(edict_t 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_t 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);
}
public void error(int level, String err) {
SV_GAME.PF_error(level, 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_t ent, String name) {
SV_GAME.PF_setmodel(ent, name);
}
// collision detection
public trace_t trace(float[] start, float[] mins, float[] maxs,
float[] end, edict_t passent, int contentmask) {
return SV_WORLD.SV_Trace(start, mins, maxs, end, passent, contentmask);
}
public pmove_t.PointContentsAdapter pointcontents = new pmove_t.PointContentsAdapter() {
public int pointcontents(float[] o) {
return 0;
}
};
public boolean inPHS(float[] p1, float[] p2) {
return SV_GAME.PF_inPHS(p1, p2);
}
public void SetAreaPortalState(int portalnum, boolean open) {
CM.CM_SetAreaPortalState(portalnum, open);
}
public boolean AreasConnected(int area1, int area2) {
return CM.CM_AreasConnected(area1, area2);
}
// 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_t ent) {
SV_WORLD.SV_LinkEdict(ent);
}
public void unlinkentity(edict_t ent) {
SV_WORLD.SV_UnlinkEdict(ent);
}
// call before removing an interactive edict
public int BoxEdicts(float[] mins, float[] maxs, edict_t list[],
int maxcount, int areatype) {
return SV_WORLD.SV_AreaEdicts(mins, maxs, list, maxcount, areatype);
}
public void Pmove(pmove_t pmove) {
PMove.Pmove(pmove);
}
// 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_t 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 cvar_t cvar(String var_name, String value, int flags) {
return Cvar.Get(var_name, value, flags);
}
// console variable interaction
public cvar_t cvar_set(String var_name, String value) {
return Cvar.Set(var_name, value);
}
// console variable interaction
public cvar_t 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);
}
// concatenation of all argv >= 1
public String args() {
return Cmd.Args();
}
// add commands to the server console as if they were typed in
// for map changing, etc
public void AddCommandString(String text) {
Cbuf.AddText(text);
}
}

View File

@@ -0,0 +1,104 @@
/*
* 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 gclient_t clients[] = new gclient_t[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;
// items
public int num_items;
public boolean autosaved;
/** 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);
}
}

View File

@@ -0,0 +1,420 @@
/*
* 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 lombok.extern.slf4j.Slf4j;
import lwjake2.util.QuakeFile;
import java.io.IOException;
@Slf4j
public class gclient_t {
public gclient_t(int index)
{
this.index = index;
}
// this structure is cleared on each PutClientInServer(),
// except for 'client->pers'
// known to server
public player_state_t ps = new player_state_t(); // communicated by server to clients
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 ammo_index;
public int buttons;
public int oldbuttons;
public int latched_buttons;
public boolean weapon_thunk;
public gitem_t newweapon;
// sum up damage over an entire frame, so
// shotgun blasts give a single big kick
public int damage_armor; // damage absorbed by armor
public int damage_parmor; // damage absorbed by power armor
public int damage_blood; // damage taken out of health
public int damage_knockback; // impact damage
public float[] damage_from = { 0, 0, 0 }; // origin for vector calculation
public float killer_yaw; // when dead, look at killer
public int weaponstate;
public float[] kick_angles = { 0, 0, 0 }; // weapon kicks
public float[] kick_origin = { 0, 0, 0 };
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_alpha;
public float bonus_alpha;
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 breather_framenum;
public float enviro_framenum;
public boolean grenade_blew_up;
public float grenade_time;
public int silencer_shots;
public int weapon_sound;
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_t chase_target; // player we are chasing
public boolean update_chase; // need to update chase info?
public int 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;
ammo_index = 0;
buttons = oldbuttons = latched_buttons = 0;
weapon_thunk = false;
newweapon = null;
damage_armor = 0;
damage_parmor = 0;
damage_blood = 0;
damage_knockback = 0;
killer_yaw = 0;
damage_from = new float[3];
weaponstate = 0;
kick_angles = new float[3];
kick_origin = new float[3];
v_dmg_roll = v_dmg_pitch = v_dmg_time = 0;
fall_time = fall_value = 0;
damage_alpha = 0;
bonus_alpha = 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;
breather_framenum = 0;
enviro_framenum = 0;
grenade_blew_up = false;
grenade_time = 0;
silencer_shots = 0;
weapon_sound = 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;
ammo_index = f.readInt();
buttons = f.readInt();
oldbuttons = f.readInt();
latched_buttons = f.readInt();
weapon_thunk=f.readInt()!=0;
newweapon=f.readItem();
damage_armor = f.readInt();
damage_parmor = f.readInt();
damage_blood = f.readInt();
damage_knockback = f.readInt();
damage_from[0] = f.readFloat();
damage_from[1] = f.readFloat();
damage_from[2] = f.readFloat();
killer_yaw = f.readFloat();
weaponstate = f.readInt();
kick_angles[0] = f.readFloat();
kick_angles[1] = f.readFloat();
kick_angles[2] = f.readFloat();
kick_origin[0] = f.readFloat();
kick_origin[1] = f.readFloat();
kick_origin[2] = f.readFloat();
v_dmg_roll = f.readFloat();
v_dmg_pitch = f.readFloat();
v_dmg_time = f.readFloat();
fall_time = f.readFloat();
fall_value = f.readFloat();
damage_alpha = f.readFloat();
bonus_alpha = 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();
breather_framenum = f.readFloat();
enviro_framenum = f.readFloat();
grenade_blew_up = f.readInt() != 0;
grenade_time = f.readFloat();
silencer_shots = f.readInt();
weapon_sound = f.readInt();
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)
log.error("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(ammo_index);
f.writeInt(buttons);
f.writeInt(oldbuttons);
f.writeInt(latched_buttons);
f.writeInt(weapon_thunk?1:0);
f.writeItem(newweapon);
f.writeInt(damage_armor);
f.writeInt(damage_parmor);
f.writeInt(damage_blood);
f.writeInt(damage_knockback);
f.writeFloat(damage_from[0]);
f.writeFloat(damage_from[1]);
f.writeFloat(damage_from[2]);
f.writeFloat(killer_yaw);
f.writeInt(weaponstate);
f.writeFloat(kick_angles[0]);
f.writeFloat(kick_angles[1]);
f.writeFloat(kick_angles[2]);
f.writeFloat(kick_origin[0]);
f.writeFloat(kick_origin[1]);
f.writeFloat(kick_origin[2]);
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_alpha);
f.writeFloat(bonus_alpha);
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(breather_framenum);
f.writeFloat(enviro_framenum);
f.writeInt(grenade_blew_up?1:0);
f.writeFloat(grenade_time);
f.writeInt(silencer_shots);
f.writeInt(weapon_sound);
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);
}
}

View File

@@ -0,0 +1,41 @@
/*
* 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 {
public gitem_armor_t(
int base_count,
int max_count,
float normal_protection,
float energy_protection,
int armor) {
this.base_count= base_count;
this.max_count= max_count;
this.normal_protection= normal_protection;
this.energy_protection= energy_protection;
this.armor= armor;
}
int base_count;
int max_count;
float normal_protection;
float energy_protection;
int armor;
}

View File

@@ -0,0 +1,104 @@
/*
* 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 {
private static int id = 0;
public gitem_t(int xxx) {
index = xxx;
}
public gitem_t(String classname, EntInteractAdapter pickup,
ItemUseAdapter use, ItemDropAdapter drop,
EntThinkAdapter weaponthink) {
}
public gitem_t(String classname, EntInteractAdapter pickup,
ItemUseAdapter use, ItemDropAdapter drop,
EntThinkAdapter weaponthink, String pickup_sound,
String world_model, int world_model_flags, String view_model,
String icon, String pickup_name, int count_width, int quantity,
String ammo, int flags, int weapmodel, gitem_armor_t info, int tag,
String precaches) {
this.classname = classname;
this.pickup = pickup;
this.use = use;
this.drop = drop;
this.weaponthink = weaponthink;
this.pickup_sound = pickup_sound;
this.world_model = world_model;
this.world_model_flags = world_model_flags;
this.view_model = view_model;
this.icon = icon;
this.pickup_name = pickup_name;
this.count_width = count_width;
this.quantity = quantity;
this.ammo = ammo;
this.flags = flags;
this.weapmodel = weapmodel;
this.info = info;
this.tag = tag;
this.precaches = precaches;
this.index = id++;
}
String classname; // spawning name
EntInteractAdapter pickup;
ItemUseAdapter use;
ItemDropAdapter drop;
EntThinkAdapter weaponthink;
String pickup_sound;
String world_model;
int world_model_flags;
String view_model;
// client side info
String icon;
String pickup_name; // for printing on pickup
int count_width; // number of digits to display by icon
int quantity; // for ammo how much, for weapons how much is used per shot
String ammo; // for weapons
int flags; // IT_* flags
int weapmodel; // weapon model index (for weapons)
Object info;
int tag;
String precaches; // string of all models, sounds, and images this item will
// use
public int index;
}

View File

@@ -0,0 +1,154 @@
/*
* 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 lombok.extern.slf4j.Slf4j;
import lwjake2.util.QuakeFile;
import java.io.IOException;
@Slf4j
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_t sight_client; // changed once each frame for coop games
public edict_t sight_entity;
public int sight_entity_framenum;
public edict_t sound_entity;
public int sound_entity_framenum;
public edict_t sound2_entity;
public int sound2_entity_framenum;
public int pic_health;
public int total_secrets;
public int found_secrets;
public int total_goals;
public int found_goals;
public int total_monsters;
public int killed_monsters;
public edict_t current_entity; // entity running from G_RunFrame
public int body_que; // dead bodies
public 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.writeInt(total_monsters);
f.writeInt(killed_monsters);
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();
total_monsters = f.readInt();
killed_monsters = 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)
log.error("error in reading level_locals.");
}
}

View File

@@ -0,0 +1,27 @@
/*
* 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 link_t(Object o) {
this.o = o;
}
public link_t prev, next;
public Object o;
}

View File

@@ -0,0 +1,24 @@
/*
* 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 csurface_t c = new csurface_t();
public String rname;
}

Some files were not shown because too many files have changed in this diff Show More