- Updated scripting branch to latest version in trunk.

SVN r4337 (scripting)
This commit is contained in:
Randy Heit 2013-06-07 03:31:30 +00:00
commit 459ad5abff
282 changed files with 15905 additions and 3472 deletions

View File

@ -1,4 +1,5 @@
cmake_minimum_required( VERSION 2.4 )
project(ZDoom)
IF( NOT CMAKE_BUILD_TYPE )
SET( CMAKE_BUILD_TYPE Debug CACHE STRING

View File

@ -36,6 +36,7 @@ add_library( dumb
src/core/rendsig.c
src/core/unload.c
src/helpers/barray.c
src/helpers/blip_buf.c
src/helpers/clickrem.c
src/helpers/memfile.c
src/helpers/resample.c
@ -60,6 +61,8 @@ add_library( dumb
src/it/loadmod2.c
src/it/loadmtm.c
src/it/loadmtm2.c
src/it/loadokt.c
src/it/loadokt2.c
src/it/loadoldpsm.c
src/it/loadoldpsm2.c
src/it/loadpsm.c
@ -83,6 +86,8 @@ add_library( dumb
src/it/readmod.c
src/it/readmod2.c
src/it/readmtm.c
src/it/readokt.c
src/it/readokt2.c
src/it/readoldpsm.c
src/it/readpsm.c
src/it/readptm.c

View File

@ -233,7 +233,6 @@ int32 DUMBEXPORT duh_get_length(DUH *duh);
const char *DUMBEXPORT duh_get_tag(DUH *duh, const char *key);
/* Signal Rendering Functions */
typedef struct DUH_SIGRENDERER DUH_SIGRENDERER;
@ -396,6 +395,8 @@ void DUMBEXPORT dumb_it_set_global_volume_zero_callback(DUMB_IT_SIGRENDERER *sig
int DUMBCALLBACK dumb_it_callback_terminate(void *data);
int DUMBCALLBACK dumb_it_callback_midi_block(void *data, int channel, unsigned char midi_byte);
/* dumb_*_mod*: restrict |= 1-Don't read 15 sample files / 2-Use old pattern counting method */
DUH *DUMBEXPORT dumb_load_it(const char *filename);
DUH *DUMBEXPORT dumb_load_xm(const char *filename);
DUH *DUMBEXPORT dumb_load_s3m(const char *filename);
@ -408,6 +409,7 @@ DUH *DUMBEXPORT dumb_load_old_psm(const char * filename);
DUH *DUMBEXPORT dumb_load_mtm(const char *filename);
DUH *DUMBEXPORT dumb_load_riff(const char *filename);
DUH *DUMBEXPORT dumb_load_asy(const char *filename);
DUH *DUMBEXPORT dumb_load_okt(const char *filename);
DUH *DUMBEXPORT dumb_read_it(DUMBFILE *f);
DUH *DUMBEXPORT dumb_read_xm(DUMBFILE *f);
@ -421,6 +423,7 @@ DUH *DUMBEXPORT dumb_read_old_psm(DUMBFILE *f);
DUH *DUMBEXPORT dumb_read_mtm(DUMBFILE *f);
DUH *DUMBEXPORT dumb_read_riff(DUMBFILE *f);
DUH *DUMBEXPORT dumb_read_asy(DUMBFILE *f);
DUH *DUMBEXPORT dumb_read_okt(DUMBFILE *f);
DUH *DUMBEXPORT dumb_load_it_quick(const char *filename);
DUH *DUMBEXPORT dumb_load_xm_quick(const char *filename);
@ -434,6 +437,7 @@ DUH *DUMBEXPORT dumb_load_old_psm_quick(const char * filename);
DUH *DUMBEXPORT dumb_load_mtm_quick(const char *filename);
DUH *DUMBEXPORT dumb_load_riff_quick(const char *filename);
DUH *DUMBEXPORT dumb_load_asy_quick(const char *filename);
DUH *DUMBEXPORT dumb_load_okt_quick(const char *filename);
DUH *DUMBEXPORT dumb_read_it_quick(DUMBFILE *f);
DUH *DUMBEXPORT dumb_read_xm_quick(DUMBFILE *f);
@ -447,6 +451,7 @@ DUH *DUMBEXPORT dumb_read_old_psm_quick(DUMBFILE *f);
DUH *DUMBEXPORT dumb_read_mtm_quick(DUMBFILE *f);
DUH *DUMBEXPORT dumb_read_riff_quick(DUMBFILE *f);
DUH *DUMBEXPORT dumb_read_asy_quick(DUMBFILE *f);
DUH *DUMBEXPORT dumb_read_okt_quick(DUMBFILE *f);
int32 DUMBEXPORT dumb_it_build_checkpoints(DUMB_IT_SIGDATA *sigdata, int startorder);
void DUMBEXPORT dumb_it_do_initial_runthrough(DUH *duh);
@ -606,6 +611,8 @@ sigdata_t *DUMBEXPORT duh_get_raw_sigdata(DUH *duh, int sig, int32 type);
DUH_SIGRENDERER *DUMBEXPORT duh_encapsulate_raw_sigrenderer(sigrenderer_t *vsigrenderer, DUH_SIGTYPE_DESC *desc, int n_channels, int32 pos);
sigrenderer_t *DUMBEXPORT duh_get_raw_sigrenderer(DUH_SIGRENDERER *sigrenderer, int32 type);
int DUMBEXPORT duh_add_signal(DUH *duh, DUH_SIGTYPE_DESC *desc, sigdata_t *sigdata);
/* Standard Signal Types */
@ -662,6 +669,8 @@ typedef struct DUMB_VOLUME_RAMP_INFO DUMB_VOLUME_RAMP_INFO;
typedef void (*DUMB_RESAMPLE_PICKUP)(DUMB_RESAMPLER *resampler, void *data);
#include "internal/blip_buf.h"
struct DUMB_RESAMPLER
{
void *src;
@ -679,6 +688,9 @@ struct DUMB_RESAMPLER
signed char x8[3*2];
} x;
int overshot;
int last_clock;
int last_amp[2];
blip_t* blip_buffer[2];
};
struct DUMB_VOLUME_RAMP_INFO

View File

@ -0,0 +1,77 @@
/** \file
Sample buffer that resamples from input clock rate to output sample rate */
/* blip_buf 1.1.0 */
#ifndef BLIP_BUF_H
#define BLIP_BUF_H
#ifdef __cplusplus
extern "C" {
#endif
/** First parameter of most functions is blip_t*, or const blip_t* if nothing
is changed. */
typedef struct blip_t blip_t;
/** Creates new buffer that can hold at most sample_count samples. Sets rates
so that there are blip_max_ratio clocks per sample. Returns pointer to new
buffer, or NULL if insufficient memory. */
blip_t* blip_new( int sample_count );
blip_t* blip_dup( blip_t* );
/** Sets approximate input clock rate and output sample rate. For every
clock_rate input clocks, approximately sample_rate samples are generated. */
void blip_set_rates( blip_t*, double clock_rate, double sample_rate );
enum { /** Maximum clock_rate/sample_rate ratio. For a given sample_rate,
clock_rate must not be greater than sample_rate*blip_max_ratio. */
blip_max_ratio = 1 << 20 };
/** Clears entire buffer. Afterwards, blip_samples_avail() == 0. */
void blip_clear( blip_t* );
/** Adds positive/negative delta into buffer at specified clock time. */
void blip_add_delta( blip_t*, unsigned int clock_time, int delta );
/** Same as blip_add_delta(), but uses faster, lower-quality synthesis. */
void blip_add_delta_fast( blip_t*, unsigned int clock_time, int delta );
/** Length of time frame, in clocks, needed to make sample_count additional
samples available. */
int blip_clocks_needed( const blip_t*, int sample_count );
enum { /** Maximum number of samples that can be generated from one time frame. */
blip_max_frame = 4000 };
/** Makes input clocks before clock_duration available for reading as output
samples. Also begins new time frame at clock_duration, so that clock time 0 in
the new time frame specifies the same clock as clock_duration in the old time
frame specified. Deltas can have been added slightly past clock_duration (up to
however many clocks there are in two output samples). */
void blip_end_frame( blip_t*, unsigned int clock_duration );
/** Number of buffered samples available for reading. */
int blip_samples_avail( const blip_t* );
/** Reads and removes at most 'count' samples and writes them to 'out'. If
'stereo' is true, writes output to every other element of 'out', allowing easy
interleaving of two buffers into a stereo sample stream. Outputs 16-bit signed
samples. Returns number of samples actually read. */
int blip_read_samples( blip_t*, int out [], int count );
/** Reads the current integrator and returns it */
int blip_peek_sample( blip_t* );
/** Frees buffer. No effect if NULL is passed. */
void blip_delete( blip_t* );
/* Deprecated */
typedef blip_t blip_buffer_t;
#ifdef __cplusplus
}
#endif
#endif

View File

@ -54,7 +54,7 @@ sigdata->flags & IT_COMPATIBLE_GXX
* handle ambiguities in the format specification. The correct code in each
* case will be determined most likely by experimentation.
*/
#define STEREO_SAMPLES_COUNT_AS_TWO
//#define STEREO_SAMPLES_COUNT_AS_TWO
#define INVALID_ORDERS_END_SONG
#define INVALID_NOTES_CAUSE_NOTE_CUT
#define SUSTAIN_LOOP_OVERRIDES_NORMAL_LOOP
@ -300,7 +300,18 @@ struct IT_SAMPLE
#define IT_PTM_NOTE_SLIDE_DOWN_RETRIG 36
#define IT_PTM_NOTE_SLIDE_UP_RETRIG 37
#define IT_N_EFFECTS 38
/* More effects needed for OKT compatibility */
#define IT_OKT_NOTE_SLIDE_DOWN 38
#define IT_OKT_NOTE_SLIDE_DOWN_ROW 39
#define IT_OKT_NOTE_SLIDE_UP 40
#define IT_OKT_NOTE_SLIDE_UP_ROW 41
#define IT_OKT_ARPEGGIO_3 42
#define IT_OKT_ARPEGGIO_4 43
#define IT_OKT_ARPEGGIO_5 44
#define IT_OKT_VOLUME_SLIDE_DOWN 45
#define IT_OKT_VOLUME_SLIDE_UP 46
#define IT_N_EFFECTS 47
/* These represent the top nibble of the command value. */
#define IT_S_SET_FILTER 0 /* Greyed out in IT... */
@ -399,6 +410,10 @@ struct IT_PATTERN
#define IT_WAS_A_669 1024
#define IT_WAS_AN_OKT 2048
#define IT_WAS_AN_STM 4096
#define IT_ORDER_END 255
#define IT_ORDER_SKIP 254
@ -452,6 +467,7 @@ struct IT_PLAYING_ENVELOPE
#define IT_PLAYING_SUSTAINOFF 2
#define IT_PLAYING_FADING 4
#define IT_PLAYING_DEAD 8
#define IT_PLAYING_REVERSE 16
struct IT_PLAYING
{
@ -586,7 +602,8 @@ struct IT_CHANNEL
unsigned char new_note_action;
int arpeggio;
unsigned int arpeggio;
int arpeggio_shift;
unsigned char retrig;
unsigned char xm_retrig;
int retrig_tick;
@ -601,7 +618,7 @@ struct IT_CHANNEL
int portamento;
int toneporta;
int toneslide;
unsigned char toneslide_tick, last_toneslide_tick, ptm_toneslide, ptm_last_toneslide;
unsigned char toneslide_tick, last_toneslide_tick, ptm_toneslide, ptm_last_toneslide, okt_toneslide;
unsigned char destnote;
unsigned char toneslide_retrig;
@ -643,6 +660,10 @@ struct IT_CHANNEL
unsigned char xm_lastX1;
unsigned char xm_lastX2;
unsigned char inv_loop_delay;
unsigned char inv_loop_speed;
int inv_loop_offset;
IT_PLAYING *playing;
#ifdef BIT_ARRAY_BULLSHIT
@ -802,6 +823,7 @@ extern DUH_SIGTYPE_DESC _dumb_sigtype_it;
#define XM_E_NOTE_CUT 0xC
#define XM_E_NOTE_DELAY 0xD
#define XM_E_PATTERN_DELAY 0xE
#define XM_E_SET_MIDI_MACRO 0xF
#define XM_X_EXTRAFINE_PORTA_UP 1
#define XM_X_EXTRAFINE_PORTA_DOWN 2
@ -880,4 +902,6 @@ void _dumb_it_ptm_convert_effect(int effect, int value, IT_ENTRY *entry);
int32 _dumb_it_read_sample_data_adpcm4(IT_SAMPLE *sample, DUMBFILE *f);
void _dumb_it_interleave_stereo_sample(IT_SAMPLE *sample);
#endif /* INTERNAL_IT_H */

View File

@ -130,3 +130,22 @@ DUH *make_duh(
return duh;
}
int DUMBEXPORT duh_add_signal(DUH *duh, DUH_SIGTYPE_DESC *desc, sigdata_t *sigdata)
{
DUH_SIGNAL **signal;
if ( !duh || !desc || !sigdata ) return -1;
signal = ( DUH_SIGNAL ** ) realloc( duh->signal, ( duh->n_signals + 1 ) * sizeof( *duh->signal ) );
if ( !signal ) return -1;
duh->signal = signal;
memmove( signal + 1, signal, duh->n_signals * sizeof( *signal ) );
duh->n_signals++;
signal[ 0 ] = make_signal( desc, sigdata );
if ( !signal[ 0 ] ) return -1;
return 0;
}

View File

@ -29,16 +29,30 @@
*/
sigdata_t *DUMBEXPORT duh_get_raw_sigdata(DUH *duh, int sig, int32 type)
{
int i;
DUH_SIGNAL *signal;
if (!duh) return NULL;
if ((unsigned int)sig >= (unsigned int)duh->n_signals) return NULL;
if ( sig >= 0 )
{
if ((unsigned int)sig >= (unsigned int)duh->n_signals) return NULL;
signal = duh->signal[sig];
signal = duh->signal[sig];
if (signal && signal->desc->type == type)
return signal->sigdata;
if (signal && signal->desc->type == type)
return signal->sigdata;
}
else
{
for ( i = 0; i < duh->n_signals; i++ )
{
signal = duh->signal[i];
if (signal && signal->desc->type == type)
return signal->sigdata;
}
}
return NULL;
}

354
dumb/src/helpers/blip_buf.c Normal file
View File

@ -0,0 +1,354 @@
/* blip_buf 1.1.0. http://www.slack.net/~ant/ */
#include "internal/blip_buf.h"
#include <assert.h>
#include <limits.h>
#include <string.h>
#include <stdlib.h>
/* Library Copyright (C) 2003-2009 Shay Green. This library 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 2.1 of the License, or (at your option) any later version. This
library is distributed in the hope that 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 this module; if not, write to the Free Software Foundation,
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
#if defined (BLARGG_TEST) && BLARGG_TEST
#include "blargg_test.h"
#endif
/* Equivalent to ULONG_MAX >= 0xFFFFFFFF00000000.
Avoids constants that don't fit in 32 bits. */
#if ULONG_MAX/0xFFFFFFFF > 0xFFFFFFFF
typedef unsigned long fixed_t;
enum { pre_shift = 32 };
#elif defined(ULLONG_MAX)
typedef unsigned long long fixed_t;
enum { pre_shift = 32 };
#else
typedef unsigned fixed_t;
enum { pre_shift = 0 };
#endif
enum { time_bits = pre_shift + 20 };
static fixed_t const time_unit = (fixed_t) 1 << time_bits;
enum { bass_shift = 9 }; /* affects high-pass filter breakpoint frequency */
enum { end_frame_extra = 2 }; /* allows deltas slightly after frame length */
enum { half_width = 8 };
enum { buf_extra = half_width*2 + end_frame_extra };
enum { phase_bits = 5 };
enum { phase_count = 1 << phase_bits };
enum { delta_bits = 15 };
enum { delta_unit = 1 << delta_bits };
enum { frac_bits = time_bits - pre_shift };
/* We could eliminate avail and encode whole samples in offset, but that would
limit the total buffered samples to blip_max_frame. That could only be
increased by decreasing time_bits, which would reduce resample ratio accuracy.
*/
/** Sample buffer that resamples to output rate and accumulates samples
until they're read out */
struct blip_t
{
fixed_t factor;
fixed_t offset;
int avail;
int size;
int integrator;
};
typedef int buf_t;
/* probably not totally portable */
#define SAMPLES( buf ) ((buf_t*) ((buf) + 1))
/* Arithmetic (sign-preserving) right shift */
#define ARITH_SHIFT( n, shift ) \
((n) >> (shift))
enum { max_sample = +32767 };
enum { min_sample = -32768 };
#define CLAMP( n ) \
{\
if ( (short) n != n )\
n = ARITH_SHIFT( n, 16 ) ^ max_sample;\
}
static void check_assumptions( void )
{
int n;
#if INT_MAX < 0x7FFFFFFF || UINT_MAX < 0xFFFFFFFF
#error "int must be at least 32 bits"
#endif
assert( (-3 >> 1) == -2 ); /* right shift must preserve sign */
n = max_sample * 2;
CLAMP( n );
assert( n == max_sample );
n = min_sample * 2;
CLAMP( n );
assert( n == min_sample );
assert( blip_max_ratio <= time_unit );
assert( blip_max_frame <= (fixed_t) -1 >> time_bits );
}
blip_t* blip_new( int size )
{
blip_t* m;
assert( size >= 0 );
m = (blip_t*) malloc( sizeof *m + (size + buf_extra) * sizeof (buf_t) );
if ( m )
{
m->factor = time_unit / blip_max_ratio;
m->size = size;
blip_clear( m );
check_assumptions();
}
return m;
}
blip_t* blip_dup( blip_t* m )
{
size_t size = sizeof *m + (m->size + buf_extra) * sizeof(buf_t);
blip_t* r = (blip_t*) malloc( size );
if ( r ) memcpy( r, m, size );
return r;
}
void blip_delete( blip_t* m )
{
if ( m != NULL )
{
/* Clear fields in case user tries to use after freeing */
memset( m, 0, sizeof *m );
free( m );
}
}
void blip_set_rates( blip_t* m, double clock_rate, double sample_rate )
{
double factor = time_unit * sample_rate / clock_rate;
m->factor = (fixed_t) factor;
/* Fails if clock_rate exceeds maximum, relative to sample_rate */
assert( 0 <= factor - m->factor && factor - m->factor < 1 );
/* Avoid requiring math.h. Equivalent to
m->factor = (int) ceil( factor ) */
if ( m->factor < factor )
m->factor++;
/* At this point, factor is most likely rounded up, but could still
have been rounded down in the floating-point calculation. */
}
void blip_clear( blip_t* m )
{
/* We could set offset to 0, factor/2, or factor-1. 0 is suitable if
factor is rounded up. factor-1 is suitable if factor is rounded down.
Since we don't know rounding direction, factor/2 accommodates either,
with the slight loss of showing an error in half the time. Since for
a 64-bit factor this is years, the halving isn't a problem. */
m->offset = m->factor / 2;
m->avail = 0;
m->integrator = 0;
memset( SAMPLES( m ), 0, (m->size + buf_extra) * sizeof (buf_t) );
}
int blip_clocks_needed( const blip_t* m, int samples )
{
fixed_t needed;
/* Fails if buffer can't hold that many more samples */
assert( samples >= 0 && m->avail + samples <= m->size );
needed = (fixed_t) samples * time_unit;
if ( needed < m->offset )
return 0;
return (int)((needed - m->offset + m->factor - 1) / m->factor);
}
void blip_end_frame( blip_t* m, unsigned t )
{
fixed_t off = t * m->factor + m->offset;
m->avail += (int)(off >> time_bits);
m->offset = off & (time_unit - 1);
/* Fails if buffer size was exceeded */
assert( m->avail <= m->size );
}
int blip_samples_avail( const blip_t* m )
{
return m->avail;
}
static void remove_samples( blip_t* m, int count )
{
buf_t* buf = SAMPLES( m );
int remain = m->avail + buf_extra - count;
m->avail -= count;
memmove( &buf [0], &buf [count], remain * sizeof buf [0] );
memset( &buf [remain], 0, count * sizeof buf [0] );
}
int blip_read_samples( blip_t* m, int out [], int count )
{
assert( count >= 0 );
if ( count > m->avail )
count = m->avail;
if ( count )
{
buf_t const* in = SAMPLES( m );
buf_t const* end = in + count;
int sum = m->integrator;
do
{
/* Eliminate fraction */
int s = ARITH_SHIFT( sum, delta_bits - 8 );
sum += *in++;
*out = s;
out++;
/* High-pass filter */
sum -= s >> (8 - (delta_bits - bass_shift)); //<< (delta_bits - bass_shift - 8);
}
while ( in != end );
m->integrator = sum;
remove_samples( m, count );
}
return count;
}
int blip_peek_sample( blip_t* m )
{
return ARITH_SHIFT( m->integrator, delta_bits - 8 );
}
/* Things that didn't help performance on x86:
__attribute__((aligned(128)))
#define short int
restrict
*/
/* Sinc_Generator( 0.9, 0.55, 4.5 ) */
static short const bl_step [phase_count + 1] [half_width] =
{
{ 43, -115, 350, -488, 1136, -914, 5861,21022},
{ 44, -118, 348, -473, 1076, -799, 5274,21001},
{ 45, -121, 344, -454, 1011, -677, 4706,20936},
{ 46, -122, 336, -431, 942, -549, 4156,20829},
{ 47, -123, 327, -404, 868, -418, 3629,20679},
{ 47, -122, 316, -375, 792, -285, 3124,20488},
{ 47, -120, 303, -344, 714, -151, 2644,20256},
{ 46, -117, 289, -310, 634, -17, 2188,19985},
{ 46, -114, 273, -275, 553, 117, 1758,19675},
{ 44, -108, 255, -237, 471, 247, 1356,19327},
{ 43, -103, 237, -199, 390, 373, 981,18944},
{ 42, -98, 218, -160, 310, 495, 633,18527},
{ 40, -91, 198, -121, 231, 611, 314,18078},
{ 38, -84, 178, -81, 153, 722, 22,17599},
{ 36, -76, 157, -43, 80, 824, -241,17092},
{ 34, -68, 135, -3, 8, 919, -476,16558},
{ 32, -61, 115, 34, -60, 1006, -683,16001},
{ 29, -52, 94, 70, -123, 1083, -862,15422},
{ 27, -44, 73, 106, -184, 1152,-1015,14824},
{ 25, -36, 53, 139, -239, 1211,-1142,14210},
{ 22, -27, 34, 170, -290, 1261,-1244,13582},
{ 20, -20, 16, 199, -335, 1301,-1322,12942},
{ 18, -12, -3, 226, -375, 1331,-1376,12293},
{ 15, -4, -19, 250, -410, 1351,-1408,11638},
{ 13, 3, -35, 272, -439, 1361,-1419,10979},
{ 11, 9, -49, 292, -464, 1362,-1410,10319},
{ 9, 16, -63, 309, -483, 1354,-1383, 9660},
{ 7, 22, -75, 322, -496, 1337,-1339, 9005},
{ 6, 26, -85, 333, -504, 1312,-1280, 8355},
{ 4, 31, -94, 341, -507, 1278,-1205, 7713},
{ 3, 35, -102, 347, -506, 1238,-1119, 7082},
{ 1, 40, -110, 350, -499, 1190,-1021, 6464},
{ 0, 43, -115, 350, -488, 1136, -914, 5861}
};
/* Shifting by pre_shift allows calculation using unsigned int rather than
possibly-wider fixed_t. On 32-bit platforms, this is likely more efficient.
And by having pre_shift 32, a 32-bit platform can easily do the shift by
simply ignoring the low half. */
void blip_add_delta( blip_t* m, unsigned time, int delta )
{
unsigned fixed = (unsigned) ((time * m->factor + m->offset) >> pre_shift);
buf_t* out = SAMPLES( m ) + m->avail + (fixed >> frac_bits);
int const phase_shift = frac_bits - phase_bits;
int phase = fixed >> phase_shift & (phase_count - 1);
short const* in = bl_step [phase];
short const* rev = bl_step [phase_count - phase];
int interp = fixed >> (phase_shift - delta_bits) & (delta_unit - 1);
int delta2 = (delta * interp) >> delta_bits;
delta -= delta2;
/* Fails if buffer size was exceeded */
assert( out <= &SAMPLES( m ) [m->size + end_frame_extra] );
out [0] += in[0]*delta + in[half_width+0]*delta2;
out [1] += in[1]*delta + in[half_width+1]*delta2;
out [2] += in[2]*delta + in[half_width+2]*delta2;
out [3] += in[3]*delta + in[half_width+3]*delta2;
out [4] += in[4]*delta + in[half_width+4]*delta2;
out [5] += in[5]*delta + in[half_width+5]*delta2;
out [6] += in[6]*delta + in[half_width+6]*delta2;
out [7] += in[7]*delta + in[half_width+7]*delta2;
in = rev;
out [ 8] += in[7]*delta + in[7-half_width]*delta2;
out [ 9] += in[6]*delta + in[6-half_width]*delta2;
out [10] += in[5]*delta + in[5-half_width]*delta2;
out [11] += in[4]*delta + in[4-half_width]*delta2;
out [12] += in[3]*delta + in[3-half_width]*delta2;
out [13] += in[2]*delta + in[2-half_width]*delta2;
out [14] += in[1]*delta + in[1-half_width]*delta2;
out [15] += in[0]*delta + in[0-half_width]*delta2;
}
void blip_add_delta_fast( blip_t* m, unsigned time, int delta )
{
unsigned fixed = (unsigned) ((time * m->factor + m->offset) >> pre_shift);
buf_t* out = SAMPLES( m ) + m->avail + (fixed >> frac_bits);
int interp = fixed >> (frac_bits - delta_bits) & (delta_unit - 1);
int delta2 = delta * interp;
/* Fails if buffer size was exceeded */
assert( out <= &SAMPLES( m ) [m->size + end_frame_extra] );
out [7] += delta * delta_unit - delta2;
out [8] += delta2;
}

View File

@ -95,7 +95,8 @@ static int process_pickup(DUMB_RESAMPLER *resampler)
#define SET_VOLUME_VARIABLES SET_MONO_DEST_VOLUME_VARIABLES
#define RETURN_VOLUME_VARIABLES RETURN_MONO_DEST_VOLUME_VARIABLES
#define VOLUMES_ARE_ZERO MONO_DEST_VOLUMES_ARE_ZERO
#define MIX_ALIAS(op, upd, offset) MONO_DEST_MIX_ALIAS(op, upd, offset)
#define MIX_ALIAS(count) MONO_DEST_MIX_ALIAS(count)
#define PEEK_ALIAS MONO_DEST_PEEK_ALIAS
#define MIX_LINEAR(op, upd, o0, o1) MONO_DEST_MIX_LINEAR(op, upd, o0, o1)
#define MIX_CUBIC(op, upd, x0, x3, o0, o1, o2, o3) MONO_DEST_MIX_CUBIC(op, upd, x0, x3, o0, o1, o2, o3)
#define MIX_ZEROS(op) *dst++ op 0
@ -137,7 +138,8 @@ static int process_pickup(DUMB_RESAMPLER *resampler)
if ( volume_right ) volume_right->volume = (float)rvolr / 16777216.0f; \
}
#define VOLUMES_ARE_ZERO (lvol == 0 && lvolt == 0 && rvol == 0 && rvolt == 0)
#define MIX_ALIAS(op, upd, offset) STEREO_DEST_MIX_ALIAS(op, upd, offset)
#define MIX_ALIAS(count) STEREO_DEST_MIX_ALIAS(count)
#define PEEK_ALIAS STEREO_DEST_PEEK_ALIAS
#define MIX_LINEAR(op, upd, o0, o1) STEREO_DEST_MIX_LINEAR(op, upd, o0, o1)
#define MIX_CUBIC(op, upd, x0, x3, o0, o1, o2, o3) STEREO_DEST_MIX_CUBIC(op, upd, x0, x3, o0, o1, o2, o3)
#define MIX_ZEROS(op) { *dst++ op 0; *dst++ op 0; }
@ -157,6 +159,9 @@ static int process_pickup(DUMB_RESAMPLER *resampler)
#undef MONO_DEST_VOLUME_ZEROS
#undef MONO_DEST_VOLUME_VARIABLES
#undef MONO_DEST_VOLUME_PARAMETERS
#undef STEREO_DEST_PEEK_ALIAS
#undef MONO_DEST_PEEK_ALIAS
#undef POKE_ALIAS
#undef COPYSRC2
#undef COPYSRC
#undef DIVIDE_BY_SRC_CHANNELS

View File

@ -46,12 +46,13 @@
int32 dumb_resample(DUMB_RESAMPLER *resampler, sample_t *dst, int32 dst_size, VOLUME_PARAMETERS, double delta)
{
int dt;
int dt, inv_dt;
int VOLUME_VARIABLES;
long done;
long todo;
LONG_LONG todo64;
int quality;
int blip_samples[256*SRC_CHANNELS];
if (!resampler || resampler->dir == 0) return 0;
ASSERT(resampler->dir == -1 || resampler->dir == 1);
@ -59,6 +60,7 @@ int32 dumb_resample(DUMB_RESAMPLER *resampler, sample_t *dst, int32 dst_size, VO
done = 0;
dt = (int)(delta * 65536.0 + 0.5);
if (dt == 0 || dt == 0x80000000) return 0;
inv_dt = (int)(1.0 / delta * 65536.0 + 0.5);
SET_VOLUME_VARIABLES;
if (VOLUMES_ARE_ZERO) dst = NULL;
@ -104,29 +106,33 @@ int32 dumb_resample(DUMB_RESAMPLER *resampler, sample_t *dst, int32 dst_size, VO
subpos = (long)new_subpos & 65535;
} else if (quality <= DUMB_RQ_ALIASING) {
/* Aliasing, backwards */
SRCTYPE xbuf[2*SRC_CHANNELS];
int todo_clocks = todo << 16, todo_clocks_set = todo_clocks;
SRCTYPE xbuf[2*SRC_CHANNELS];
SRCTYPE *x = &xbuf[0];
SRCTYPE *xstart;
COPYSRC(xbuf, 0, resampler->X, 1);
COPYSRC(xbuf, 1, resampler->X, 2);
while (todo && x < &xbuf[2*SRC_CHANNELS]) {
if ( todo_clocks_set > 256 * 65536 ) todo_clocks_set = 256 * 65536;
while (resampler->last_clock < todo_clocks_set && x < &xbuf[2*SRC_CHANNELS]) {
// TODO: check what happens when multiple tempo slides occur per row
HEAVYASSERT(pos >= resampler->start);
MIX_ALIAS(+=, 1, 0);
subpos += dt;
pos += subpos >> 16;
x -= (subpos >> 16) * SRC_CHANNELS;
subpos &= 65535;
todo--;
POKE_ALIAS(0);
pos--;
x += SRC_CHANNELS;
}
x = &src[pos*SRC_CHANNELS];
while ( todo_clocks ) {
todo_clocks_set = todo_clocks;
if ( todo_clocks_set > 256 * 65536 ) todo_clocks_set = 256 * 65536;
todo_clocks -= todo_clocks_set;
while ( resampler->last_clock < todo_clocks_set )
{
POKE_ALIAS(2);
pos--;
x -= SRC_CHANNELS;
}
todo = todo_clocks_set >> 16;
MIX_ALIAS( todo );
}
x = xstart = &src[pos*SRC_CHANNELS];
LOOP4(todo,
MIX_ALIAS(+=, 1, 2);
subpos += dt;
x += (subpos >> 16) * SRC_CHANNELS;
subpos &= 65535;
);
pos += DIVIDE_BY_SRC_CHANNELS((long)(x - xstart));
} else if (quality <= DUMB_RQ_LINEAR) {
/* Linear interpolation, backwards */
SRCTYPE xbuf[3*SRC_CHANNELS];
@ -205,28 +211,32 @@ int32 dumb_resample(DUMB_RESAMPLER *resampler, sample_t *dst, int32 dst_size, VO
subpos = (long)new_subpos & 65535;
} else if (quality <= DUMB_RQ_ALIASING) {
/* Aliasing, forwards */
int todo_clocks = todo << 16, todo_clocks_set = todo_clocks;
SRCTYPE xbuf[2*SRC_CHANNELS];
SRCTYPE *x = &xbuf[0];
SRCTYPE *xstart;
COPYSRC(xbuf, 0, resampler->X, 1);
COPYSRC(xbuf, 1, resampler->X, 2);
while (todo && x < &xbuf[2*SRC_CHANNELS]) {
if ( todo_clocks_set > 256 * 65536 ) todo_clocks_set = 256 * 65536;
while (resampler->last_clock < todo_clocks_set && x < &xbuf[2*SRC_CHANNELS]) {
HEAVYASSERT(pos < resampler->end);
MIX_ALIAS(+=, 1, 0);
subpos += dt;
pos += subpos >> 16;
x += (subpos >> 16) * SRC_CHANNELS;
subpos &= 65535;
todo--;
POKE_ALIAS(0);
pos++;
x += SRC_CHANNELS;
}
x = &src[pos*SRC_CHANNELS];
while ( todo_clocks ) {
todo_clocks_set = todo_clocks;
if ( todo_clocks_set > 256 * 65536 ) todo_clocks_set = 256 * 65536;
todo_clocks -= todo_clocks_set;
while ( resampler->last_clock < todo_clocks_set )
{
POKE_ALIAS(-2);
pos++;
x += SRC_CHANNELS;
}
todo = todo_clocks_set >> 16;
MIX_ALIAS( todo );
}
x = xstart = &src[pos*SRC_CHANNELS];
LOOP4(todo,
MIX_ALIAS(+=, 1, -2);
subpos += dt;
x += (subpos >> 16) * SRC_CHANNELS;
subpos &= 65535;
);
pos += DIVIDE_BY_SRC_CHANNELS((long)(x - xstart));
} else if (quality <= DUMB_RQ_LINEAR) {
/* Linear interpolation, forwards */
SRCTYPE xbuf[3*SRC_CHANNELS];
@ -339,7 +349,7 @@ void dumb_resample_get_current_sample(DUMB_RESAMPLER *resampler, VOLUME_PARAMETE
HEAVYASSERT(pos >= resampler->start);
if (dumb_resampling_quality <= DUMB_RQ_ALIASING) {
/* Aliasing, backwards */
MIX_ALIAS(=, 0, 1);
PEEK_ALIAS;
} else if (quality <= DUMB_RQ_LINEAR) {
/* Linear interpolation, backwards */
MIX_LINEAR(=, 0, 2, 1);
@ -351,7 +361,7 @@ void dumb_resample_get_current_sample(DUMB_RESAMPLER *resampler, VOLUME_PARAMETE
HEAVYASSERT(pos < resampler->end);
if (dumb_resampling_quality <= DUMB_RQ_ALIASING) {
/* Aliasing */
MIX_ALIAS(=, 0, 1);
PEEK_ALIAS;
} else if (dumb_resampling_quality <= DUMB_RQ_LINEAR) {
/* Linear interpolation, forwards */
MIX_LINEAR(=, 0, 1, 2);
@ -368,6 +378,7 @@ void dumb_resample_get_current_sample(DUMB_RESAMPLER *resampler, VOLUME_PARAMETE
#undef MIX_CUBIC
#undef MIX_LINEAR
#undef MIX_ALIAS
#undef PEEK_ALIAS
#undef VOLUMES_ARE_ZERO
#undef SET_VOLUME_VARIABLES
#undef RETURN_VOLUME_VARIABLES

View File

@ -117,9 +117,9 @@ int dumb_resampling_quality = DUMB_RQ_CUBIC;
*/
#define LOOP4(iterator, CONTENT) \
{ \
while (iterator) { \
while ( (iterator)-- ) \
{ \
CONTENT; \
(iterator)--; \
} \
}
#endif
@ -189,7 +189,7 @@ static void init_cubic(void)
#define SRCTYPE sample_t
#define SRCBITS 24
#define ALIAS(x, vol) MULSC(x, vol)
#define ALIAS(x) (x >> 8)
#define LINEAR(x0, x1) (x0 + MULSC(x1 - x0, subpos))
/*
#define SET_CUBIC_COEFFICIENTS(x0, x1, x2, x3) { \
@ -225,7 +225,7 @@ static void init_cubic(void)
#define SUFFIX _16
#define SRCTYPE short
#define SRCBITS 16
#define ALIAS(x, vol) (x * vol >> 8)
#define ALIAS(x) (x)
#define LINEAR(x0, x1) ((x0 << 8) + MULSC16(x1 - x0, subpos))
/*
#define SET_CUBIC_COEFFICIENTS(x0, x1, x2, x3) { \
@ -247,7 +247,7 @@ static void init_cubic(void)
#define SUFFIX _8
#define SRCTYPE signed char
#define SRCBITS 8
#define ALIAS(x, vol) (x * vol)
#define ALIAS(x) (x << 8)
#define LINEAR(x0, x1) ((x0 << 16) + (x1 - x0) * subpos)
/*
#define SET_CUBIC_COEFFICIENTS(x0, x1, x2, x3) { \

View File

@ -69,6 +69,11 @@ void dumb_reset_resampler(DUMB_RESAMPLER *resampler, SRCTYPE *src, int src_chann
}
for (i = 0; i < src_channels*3; i++) resampler->X[i] = 0;
resampler->overshot = -1;
resampler->last_clock = 0;
resampler->last_amp[0] = 0;
resampler->last_amp[1] = 0;
blip_clear(resampler->blip_buffer[0]);
blip_clear(resampler->blip_buffer[1]);
}
@ -77,6 +82,21 @@ DUMB_RESAMPLER *dumb_start_resampler(SRCTYPE *src, int src_channels, int32 pos,
{
DUMB_RESAMPLER *resampler = malloc(sizeof(*resampler));
if (!resampler) return NULL;
resampler->blip_buffer[0] = blip_new( 256 );
if (!resampler->blip_buffer[0])
{
free(resampler);
return NULL;
}
resampler->blip_buffer[1] = blip_new( 256 );
if (!resampler->blip_buffer[1])
{
free(resampler->blip_buffer[0]);
free(resampler);
return NULL;
}
blip_set_rates(resampler->blip_buffer[0], 65536, 1);
blip_set_rates(resampler->blip_buffer[1], 65536, 1);
dumb_reset_resampler(resampler, src, src_channels, pos, start, end, quality);
return resampler;
}
@ -123,16 +143,41 @@ DUMB_RESAMPLER *dumb_start_resampler(SRCTYPE *src, int src_channels, int32 pos,
}
#define RETURN_MONO_DEST_VOLUME_VARIABLES if ( volume ) volume->volume = (float)volr / 16777216.0f
#define MONO_DEST_VOLUMES_ARE_ZERO (vol == 0 && volt == 0)
#define MONO_DEST_MIX_ALIAS(op, upd, offset) { \
*dst++ op ALIAS(x[offset], vol); \
if ( upd ) UPDATE_VOLUME( volume, vol ); \
#define POKE_ALIAS(offset) { \
int delta = ALIAS(x[offset]) - resampler->last_amp[0]; \
resampler->last_amp[0] += delta; \
if ( delta ) blip_add_delta( resampler->blip_buffer[0], resampler->last_clock, delta ); \
resampler->last_clock += inv_dt; \
}
#define STEREO_DEST_MIX_ALIAS(op, upd, offset) { \
int xm = x[offset]; \
*dst++ op ALIAS(xm, lvol); \
*dst++ op ALIAS(xm, rvol); \
if ( upd ) UPDATE_VOLUME( volume_left, lvol ); \
if ( upd ) UPDATE_VOLUME( volume_right, rvol ); \
#define MONO_DEST_PEEK_ALIAS *dst = MULSC( blip_peek_sample( resampler->blip_buffer[0] ), vol )
#define MONO_DEST_MIX_ALIAS(count) { \
int n = 0; \
resampler->last_clock -= count * 65536; \
blip_end_frame( resampler->blip_buffer[0], count * 65536 ); \
blip_read_samples( resampler->blip_buffer[0], blip_samples, count ); \
LOOP4( count, \
*dst++ += MULSC( blip_samples[n], vol ); \
n++; \
UPDATE_VOLUME( volume, vol ); \
); \
}
#define STEREO_DEST_PEEK_ALIAS { \
int sample = blip_peek_sample( resampler->blip_buffer[0] ); \
*dst++ = MULSC( sample, lvol ); \
*dst++ = MULSC( sample, rvol ); \
}
#define STEREO_DEST_MIX_ALIAS(count) { \
int sample, n = 0; \
resampler->last_clock -= count * 65536; \
blip_end_frame( resampler->blip_buffer[0], count * 65536 ); \
blip_read_samples( resampler->blip_buffer[0], blip_samples, count ); \
LOOP4( count, \
sample = blip_samples[n++]; \
*dst++ += MULSC( sample, lvol ); \
*dst++ += MULSC( sample, rvol ); \
UPDATE_VOLUME( volume_left, lvol ); \
UPDATE_VOLUME( volume_right, rvol ); \
); \
}
#define MONO_DEST_MIX_LINEAR(op, upd, o0, o1) { \
*dst++ op MULSC(LINEAR(x[o0], x[o1]), vol); \
@ -208,16 +253,51 @@ DUMB_RESAMPLER *dumb_start_resampler(SRCTYPE *src, int src_channels, int32 pos,
if ( volume_right ) volume_right->volume = (float)rvolr / 16777216.0f; \
}
#define MONO_DEST_VOLUMES_ARE_ZERO (lvol == 0 && lvolt == 0 && rvol == 0 && rvolt == 0)
#define MONO_DEST_MIX_ALIAS(op, upd, offset) { \
*dst++ op ALIAS(x[(offset)*2], lvol) + ALIAS(x[(offset)*2+1], rvol); \
if ( upd ) UPDATE_VOLUME( volume_left, lvol ); \
if ( upd ) UPDATE_VOLUME( volume_right, rvol ); \
#define POKE_ALIAS(offset) { \
int deltal = ALIAS(x[(offset)*2+0]) - resampler->last_amp[0]; \
int deltar = ALIAS(x[(offset)*2+1]) - resampler->last_amp[1]; \
resampler->last_amp[0] += deltal; \
resampler->last_amp[1] += deltar; \
if ( deltal ) blip_add_delta( resampler->blip_buffer[0], resampler->last_clock, deltal ); \
if ( deltar ) blip_add_delta( resampler->blip_buffer[1], resampler->last_clock, deltar ); \
resampler->last_clock += inv_dt; \
}
#define STEREO_DEST_MIX_ALIAS(op, upd, offset) { \
*dst++ op ALIAS(x[(offset)*2], lvol); \
*dst++ op ALIAS(x[(offset)*2+1], rvol); \
if ( upd ) UPDATE_VOLUME( volume_left, lvol ); \
if ( upd ) UPDATE_VOLUME( volume_right, rvol ); \
#define MONO_DEST_PEEK_ALIAS { \
*dst = MULSC( blip_peek_sample( resampler->blip_buffer[0] ), lvol ) + \
MULSC( blip_peek_sample( resampler->blip_buffer[1] ), rvol ); \
}
#define MONO_DEST_MIX_ALIAS(count) { \
int n = 0; \
resampler->last_clock -= count * 65536; \
blip_end_frame( resampler->blip_buffer[0], count * 65536 ); \
blip_end_frame( resampler->blip_buffer[1], count * 65536 ); \
blip_read_samples( resampler->blip_buffer[0], blip_samples, count ); \
blip_read_samples( resampler->blip_buffer[1], blip_samples + 256, count ); \
LOOP4( count, \
*dst++ += MULSC( blip_samples[n], lvol ) + MULSC( blip_samples[256+n], rvol ); \
n++; \
UPDATE_VOLUME( volume_left, lvol ); \
UPDATE_VOLUME( volume_right, rvol ); \
); \
}
#define STEREO_DEST_PEEK_ALIAS { \
*dst++ = MULSC( blip_peek_sample( resampler->blip_buffer[0] ), lvol ); \
*dst++ = MULSC( blip_peek_sample( resampler->blip_buffer[1] ), rvol ); \
}
#define STEREO_DEST_MIX_ALIAS(count) { \
int n = 0; \
resampler->last_clock -= count * 65536; \
blip_end_frame( resampler->blip_buffer[0], count * 65536 ); \
blip_end_frame( resampler->blip_buffer[1], count * 65536 ); \
blip_read_samples( resampler->blip_buffer[0], blip_samples, count ); \
blip_read_samples( resampler->blip_buffer[1], blip_samples + 256, count ); \
LOOP4( count, \
*dst++ += MULSC( blip_samples[n], lvol); \
*dst++ += MULSC( blip_samples[256+n], rvol); \
n++; \
UPDATE_VOLUME( volume_left, lvol ); \
UPDATE_VOLUME( volume_right, rvol ); \
); \
}
#define MONO_DEST_MIX_LINEAR(op, upd, o0, o1) { \
*dst++ op MULSC(LINEAR(x[(o0)*2], x[(o1)*2]), lvol) + MULSC(LINEAR(x[(o0)*2+1], x[(o1)*2+1]), rvol); \

View File

@ -24,7 +24,7 @@
DUMB_IT_SIGDATA *DUMBEXPORT duh_get_it_sigdata(DUH *duh)
{
return duh_get_raw_sigdata(duh, 0, SIGTYPE_IT);
return duh_get_raw_sigdata(duh, -1, SIGTYPE_IT);
}

View File

@ -31,18 +31,71 @@
//#define INVESTIGATE_OLD_INSTRUMENTS
static int it_seek(DUMBFILE *f, int32 offset)
typedef struct tdumbfile_mem_status
{
int32 pos = dumbfile_pos(f);
const unsigned char * ptr;
unsigned offset, size;
} dumbfile_mem_status;
if (pos > offset)
static int dumbfile_mem_skip(void * f, int32 n)
{
dumbfile_mem_status * s = (dumbfile_mem_status *) f;
s->offset += n;
if (s->offset > s->size)
{
s->offset = s->size;
return 1;
}
return 0;
}
static int dumbfile_mem_getc(void * f)
{
dumbfile_mem_status * s = (dumbfile_mem_status *) f;
if (s->offset < s->size)
{
return *(s->ptr + s->offset++);
}
return -1;
}
static int32 dumbfile_mem_getnc(char * ptr, int32 n, void * f)
{
dumbfile_mem_status * s = (dumbfile_mem_status *) f;
int32 max = s->size - s->offset;
if (max > n) max = n;
if (max)
{
memcpy(ptr, s->ptr + s->offset, max);
s->offset += max;
}
return max;
}
static DUMBFILE_SYSTEM mem_dfs = {
NULL, // open
&dumbfile_mem_skip,
&dumbfile_mem_getc,
&dumbfile_mem_getnc,
NULL // close
};
static int it_seek(dumbfile_mem_status * s, int32 offset)
{
if ( (unsigned)offset > s->size )
return -1;
if (pos < offset)
if (dumbfile_skip(f, offset - pos))
return -1;
s->offset = offset;
return 0;
}
@ -306,6 +359,8 @@ static int it_read_envelope(IT_ENVELOPE *envelope, DUMBFILE *f)
envelope->loop_end = dumbfile_getc(f);
envelope->sus_loop_start = dumbfile_getc(f);
envelope->sus_loop_end = dumbfile_getc(f);
if (envelope->n_nodes > 25)
envelope->n_nodes = 25;
for (n = 0; n < envelope->n_nodes; n++) {
envelope->node_y[n] = dumbfile_getc(f);
envelope->node_t[n] = dumbfile_igetw(f);
@ -629,7 +684,7 @@ int32 _dumb_it_read_sample_data_adpcm4(IT_SAMPLE *sample, DUMBFILE *f)
signed char * ptr, * end;
signed char compression_table[16];
if (dumbfile_getnc(compression_table, 16, f) != 16)
return -1;
return -1;
ptr = (signed char *) sample->data;
delta = 0;
@ -682,15 +737,36 @@ static int32 it_read_sample_data(int cmwt, IT_SAMPLE *sample, unsigned char conv
else
decompress8(f, sample->data, datasize, ((cmwt >= 0x215) && (convert & 4)));
} else if (sample->flags & IT_SAMPLE_16BIT) {
if (convert & 2)
if (sample->flags & IT_SAMPLE_STEREO) {
if (convert & 2) {
for (n = 0; n < datasize; n += 2)
((short *)sample->data)[n] = dumbfile_mgetw(f);
for (n = 1; n < datasize; n += 2)
((short *)sample->data)[n] = dumbfile_mgetw(f);
} else {
for (n = 0; n < datasize; n += 2)
((short *)sample->data)[n] = dumbfile_igetw(f);
for (n = 1; n < datasize; n += 2)
((short *)sample->data)[n] = dumbfile_igetw(f);
}
} else {
if (convert & 2)
for (n = 0; n < datasize; n++)
((short *)sample->data)[n] = dumbfile_mgetw(f);
else
for (n = 0; n < datasize; n++)
((short *)sample->data)[n] = dumbfile_igetw(f);
}
} else {
if (sample->flags & IT_SAMPLE_STEREO) {
for (n = 0; n < datasize; n += 2)
((signed char *)sample->data)[n] = dumbfile_getc(f);
for (n = 1; n < datasize; n += 2)
((signed char *)sample->data)[n] = dumbfile_getc(f);
} else
for (n = 0; n < datasize; n++)
((short *)sample->data)[n] = dumbfile_mgetw(f);
else
for (n = 0; n < datasize; n++)
((short *)sample->data)[n] = dumbfile_igetw(f);
} else
for (n = 0; n < datasize; n++)
((signed char *)sample->data)[n] = dumbfile_getc(f);
((signed char *)sample->data)[n] = dumbfile_getc(f);
}
if (dumbfile_error(f))
return -1;
@ -934,13 +1010,58 @@ static sigdata_t *it_load_sigdata(DUMBFILE *f)
unsigned char *buffer;
unsigned char *file_buffer = NULL;
unsigned int file_size = 0;
int block_size;
dumbfile_mem_status memdata;
do
{
void * temp = realloc( file_buffer, file_size + 32768 );
if ( !temp )
{
if ( file_buffer ) free( file_buffer );
return NULL;
}
file_buffer = temp;
block_size = dumbfile_getnc( file_buffer + file_size, 32768, f );
if ( block_size < 0 )
{
free( file_buffer );
return NULL;
}
file_size += block_size;
}
while ( block_size == 32768 );
memdata.ptr = file_buffer;
memdata.offset = 0;
memdata.size = file_size;
f = dumbfile_open_ex(&memdata, &mem_dfs);
if ( !f )
{
free( file_buffer );
return NULL;
}
if (dumbfile_mgetl(f) != IT_SIGNATURE)
{
dumbfile_close(f);
free(file_buffer);
return NULL;
}
sigdata = malloc(sizeof(*sigdata));
if (!sigdata)
{
dumbfile_close(f);
free(file_buffer);
return NULL;
}
sigdata->song_message = NULL;
sigdata->order = NULL;
@ -989,12 +1110,16 @@ static sigdata_t *it_load_sigdata(DUMBFILE *f)
// XXX sample count
if (dumbfile_error(f) || sigdata->n_orders <= 0 || sigdata->n_instruments > 256 || sigdata->n_samples > 4000 || sigdata->n_patterns > 256) {
_dumb_it_unload_sigdata(sigdata);
dumbfile_close(f);
free(file_buffer);
return NULL;
}
sigdata->order = malloc(sigdata->n_orders);
if (!sigdata->order) {
_dumb_it_unload_sigdata(sigdata);
dumbfile_close(f);
free(file_buffer);
return NULL;
}
@ -1002,6 +1127,8 @@ static sigdata_t *it_load_sigdata(DUMBFILE *f)
sigdata->instrument = malloc(sigdata->n_instruments * sizeof(*sigdata->instrument));
if (!sigdata->instrument) {
_dumb_it_unload_sigdata(sigdata);
dumbfile_close(f);
free(file_buffer);
return NULL;
}
}
@ -1010,6 +1137,8 @@ static sigdata_t *it_load_sigdata(DUMBFILE *f)
sigdata->sample = malloc(sigdata->n_samples * sizeof(*sigdata->sample));
if (!sigdata->sample) {
_dumb_it_unload_sigdata(sigdata);
dumbfile_close(f);
free(file_buffer);
return NULL;
}
for (n = 0; n < sigdata->n_samples; n++)
@ -1020,6 +1149,8 @@ static sigdata_t *it_load_sigdata(DUMBFILE *f)
sigdata->pattern = malloc(sigdata->n_patterns * sizeof(*sigdata->pattern));
if (!sigdata->pattern) {
_dumb_it_unload_sigdata(sigdata);
dumbfile_close(f);
free(file_buffer);
return NULL;
}
for (n = 0; n < sigdata->n_patterns; n++)
@ -1032,6 +1163,8 @@ static sigdata_t *it_load_sigdata(DUMBFILE *f)
component = malloc(769 * sizeof(*component));
if (!component) {
_dumb_it_unload_sigdata(sigdata);
dumbfile_close(f);
free(file_buffer);
return NULL;
}
@ -1076,6 +1209,8 @@ static sigdata_t *it_load_sigdata(DUMBFILE *f)
if (dumbfile_error(f)) {
free(component);
_dumb_it_unload_sigdata(sigdata);
dumbfile_close(f);
free(file_buffer);
return NULL;
}
@ -1096,6 +1231,8 @@ static sigdata_t *it_load_sigdata(DUMBFILE *f)
if (!sigdata->midi) {
free(component);
_dumb_it_unload_sigdata(sigdata);
dumbfile_close(f);
free(file_buffer);
return NULL;
// Should we be happy with this outcome in some situations?
}
@ -1104,6 +1241,8 @@ static sigdata_t *it_load_sigdata(DUMBFILE *f)
if (dumbfile_error(f) || dumbfile_skip(f, 8*i)) {
free(component);
_dumb_it_unload_sigdata(sigdata);
dumbfile_close(f);
free(file_buffer);
return NULL;
}
/* Read embedded MIDI configuration */
@ -1111,6 +1250,8 @@ static sigdata_t *it_load_sigdata(DUMBFILE *f)
if (dumbfile_skip(f, 32*9)) {
free(component);
_dumb_it_unload_sigdata(sigdata);
dumbfile_close(f);
free(file_buffer);
return NULL;
}
for (i = 0; i < 16; i++) {
@ -1119,6 +1260,8 @@ static sigdata_t *it_load_sigdata(DUMBFILE *f)
if (dumbfile_getnc(mididata, 32, f) < 32) {
free(component);
_dumb_it_unload_sigdata(sigdata);
dumbfile_close(f);
free(file_buffer);
return NULL;
}
sigdata->midi->SFmacroz[i] = 0;
@ -1174,12 +1317,14 @@ static sigdata_t *it_load_sigdata(DUMBFILE *f)
sigdata->flags &= IT_REAL_FLAGS;
qsort(component, n_components, sizeof(IT_COMPONENT), &it_component_compare);
qsort(component, n_components, sizeof(IT_COMPONENT), &it_component_compare);
buffer = malloc(65536);
if (!buffer) {
free(component);
_dumb_it_unload_sigdata(sigdata);
dumbfile_close(f);
free(file_buffer);
return NULL;
}
@ -1208,10 +1353,12 @@ static sigdata_t *it_load_sigdata(DUMBFILE *f)
continue;
}
if (it_seek(f, component[n].offset)) {
if (it_seek(&memdata, component[n].offset)) {
free(buffer);
free(component);
_dumb_it_unload_sigdata(sigdata);
dumbfile_close(f);
free(file_buffer);
return NULL;
}
@ -1227,6 +1374,8 @@ static sigdata_t *it_load_sigdata(DUMBFILE *f)
free(buffer);
free(component);
_dumb_it_unload_sigdata(sigdata);
dumbfile_close(f);
free(file_buffer);
return NULL;
}
sigdata->song_message[message_length] = 0;
@ -1243,6 +1392,8 @@ static sigdata_t *it_load_sigdata(DUMBFILE *f)
free(buffer);
free(component);
_dumb_it_unload_sigdata(sigdata);
dumbfile_close(f);
free(file_buffer);
return NULL;
}
break;
@ -1252,6 +1403,8 @@ static sigdata_t *it_load_sigdata(DUMBFILE *f)
free(buffer);
free(component);
_dumb_it_unload_sigdata(sigdata);
dumbfile_close(f);
free(file_buffer);
return NULL;
}
break;
@ -1261,6 +1414,8 @@ static sigdata_t *it_load_sigdata(DUMBFILE *f)
free(buffer);
free(component);
_dumb_it_unload_sigdata(sigdata);
dumbfile_close(f);
free(file_buffer);
return NULL;
}
@ -1287,10 +1442,12 @@ static sigdata_t *it_load_sigdata(DUMBFILE *f)
m = component[n].sampfirst;
while (m >= 0) {
if (it_seek(f, component[m].offset)) {
if (it_seek(&memdata, component[m].offset)) {
free(buffer);
free(component);
_dumb_it_unload_sigdata(sigdata);
dumbfile_close(f);
free(file_buffer);
return NULL;
}
@ -1298,16 +1455,79 @@ static sigdata_t *it_load_sigdata(DUMBFILE *f)
free(buffer);
free(component);
_dumb_it_unload_sigdata(sigdata);
dumbfile_close(f);
free(file_buffer);
return NULL;
}
m = component[m].sampnext;
}
}
}
free(buffer);
for ( n = 0; n < 10; n++ )
{
if ( dumbfile_getc( f ) == 'X' )
{
if ( dumbfile_getc( f ) == 'T' )
{
if ( dumbfile_getc( f ) == 'P' )
{
if ( dumbfile_getc( f ) == 'M' )
{
break;
}
}
}
}
}
if ( !dumbfile_error( f ) && n < 10 )
{
unsigned int mptx_id = dumbfile_igetl( f );
while ( !dumbfile_error( f ) && mptx_id != DUMB_ID('M','P','T','S') )
{
unsigned int size = dumbfile_igetw( f );
switch (mptx_id)
{
/* TODO: Add instrument extension readers */
default:
dumbfile_skip(f, size * sigdata->n_instruments);
break;
}
mptx_id = dumbfile_igetl( f );
}
mptx_id = dumbfile_igetl( f );
while ( memdata.offset < file_size )
{
unsigned int size = dumbfile_igetw( f );
switch (mptx_id)
{
/* TODO: Add more song extension readers */
case DUMB_ID('D','T','.','.'):
if ( size == 2 )
sigdata->tempo = dumbfile_igetw( f );
else if ( size == 4 )
sigdata->tempo = dumbfile_igetl( f );
break;
default:
dumbfile_skip(f, size);
break;
}
mptx_id = dumbfile_igetl( f );
}
}
free(buffer);
free(component);
dumbfile_close(f);
free(file_buffer);
_dumb_it_fix_invalid_orders(sigdata);
return sigdata;
@ -1327,9 +1547,11 @@ DUH *DUMBEXPORT dumb_read_it_quick(DUMBFILE *f)
return NULL;
{
const char *tag[1][2];
const char *tag[2][2];
tag[0][0] = "TITLE";
tag[0][1] = ((DUMB_IT_SIGDATA *)sigdata)->name;
return make_duh(-1, 1, (const char *const (*)[2])tag, 1, &descptr, &sigdata);
tag[1][0] = "FORMAT";
tag[1][1] = "IT";
return make_duh(-1, 2, (const char *const (*)[2])tag, 1, &descptr, &sigdata);
}
}

File diff suppressed because it is too large Load Diff

42
dumb/src/it/loadokt.c Normal file
View File

@ -0,0 +1,42 @@
/* _______ ____ __ ___ ___
* \ _ \ \ / \ / \ \ / / ' ' '
* | | \ \ | | || | \/ | . .
* | | | | | | || ||\ /| |
* | | | | | | || || \/ | | ' ' '
* | | | | | | || || | | . .
* | |_/ / \ \__// || | |
* /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
* / \
* / . \
* loadokt.c - Code to read an Oktalyzer module / / \ \
* file, opening and closing it for | < / \_
* you. | \/ /\ /
* \_ / > /
* By Chris Moeller. | \ / /
* | ' /
* \__/
*/
#include "dumb.h"
#include "internal/it.h"
/* dumb_load_okt_quick(): loads an OKT file into a DUH struct, returning a
* pointer to the DUH struct. When you have finished with it, you must
* pass the pointer to unload_duh() so that the memory can be freed.
*/
DUH *DUMBEXPORT dumb_load_okt_quick(const char *filename)
{
DUH *duh;
DUMBFILE *f = dumbfile_open(filename);
if (!f)
return NULL;
duh = dumb_read_okt_quick(f);
dumbfile_close(f);
return duh;
}

29
dumb/src/it/loadokt2.c Normal file
View File

@ -0,0 +1,29 @@
/* _______ ____ __ ___ ___
* \ _ \ \ / \ / \ \ / / ' ' '
* | | \ \ | | || | \/ | . .
* | | | | | | || ||\ /| |
* | | | | | | || || \/ | | ' ' '
* | | | | | | || || | | . .
* | |_/ / \ \__// || | |
* /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
* / \
* / . \
* loadokt2.c - Function to read an Oktalyzer / / \ \
* module file, opening and closing | < / \_
* it for you, and do an initial run- | \/ /\ /
* through. \_ / > /
* | \ / /
* By Chris Moeller. | ' /
* \__/
*/
#include "dumb.h"
DUH *DUMBEXPORT dumb_load_okt(const char *filename)
{
DUH *duh = dumb_load_okt_quick(filename);
dumb_it_do_initial_runthrough(duh);
return duh;
}

View File

@ -331,6 +331,7 @@ static DUMB_IT_SIGDATA *it_riff_amff_load_sigdata( struct riff * stream )
sigdata->n_instruments = 0;
sigdata->n_orders = 0;
sigdata->restart_position = 0;
memset(sigdata->channel_volume, 64, DUMB_IT_N_CHANNELS);
@ -553,6 +554,7 @@ static DUMB_IT_SIGDATA *it_riff_am_load_sigdata( struct riff * stream )
sigdata->n_instruments = 0;
sigdata->n_orders = 0;
sigdata->restart_position = 0;
memset(sigdata->channel_volume, 64, DUMB_IT_N_CHANNELS);

View File

@ -71,7 +71,7 @@ static int it_asy_read_pattern( IT_PATTERN *pattern, DUMBFILE *f, unsigned char
entry->mask |= IT_ENTRY_INSTRUMENT;
}
_dumb_it_xm_convert_effect( buffer[ pos + 2 ] & 0x0F, buffer[ pos + 3 ], entry, 1 );
_dumb_it_xm_convert_effect( buffer[ pos + 2 ], buffer[ pos + 3 ], entry, 1 );
if ( entry->mask ) ++entry;
}
@ -90,7 +90,7 @@ static int it_asy_read_pattern( IT_PATTERN *pattern, DUMBFILE *f, unsigned char
static int it_asy_read_sample_header( IT_SAMPLE *sample, DUMBFILE *f )
{
int finetune;
int finetune, key_offset;
/**
21 22 Chars Sample 1 name. If the name is not a full
@ -111,7 +111,7 @@ assumed not to be an instrument name, and is probably a message.
sample->default_volume = dumbfile_getc( f ); // Should we be setting global_volume to this instead?
sample->global_volume = 64;
if ( sample->default_volume > 64 ) sample->default_volume = 64;
dumbfile_skip( f, 1 ); /* XXX unknown */
key_offset = ( signed char ) dumbfile_getc( f ); /* base key offset */
sample->length = dumbfile_igetl( f );
sample->loop_start = dumbfile_igetl( f );
sample->loop_end = sample->loop_start + dumbfile_igetl( f );
@ -124,7 +124,7 @@ assumed not to be an instrument name, and is probably a message.
sample->flags = IT_SAMPLE_EXISTS;
sample->default_pan = 0;
sample->C5_speed = (int)( AMIGA_CLOCK / 214.0 );//( int32 )( 16726.0 * pow( DUMB_PITCH_BASE, finetune * 32 ) );
sample->C5_speed = (int)( AMIGA_CLOCK / 214.0 * pow( DUMB_SEMITONE_BASE, key_offset ) );//( long )( 16726.0 * pow( DUMB_PITCH_BASE, finetune * 32 ) );
sample->finetune = finetune * 32;
// the above line might be wrong

View File

@ -265,6 +265,7 @@ static DUMB_IT_SIGDATA *it_riff_dsmf_load_sigdata( struct riff * stream )
sigdata->n_instruments = 0;
sigdata->n_orders = 0;
sigdata->restart_position = 0;
memset(sigdata->channel_volume, 64, DUMB_IT_N_CHANNELS);

View File

@ -393,10 +393,12 @@ static DUMBFILE *dumbfile_buffer_mod(DUMBFILE *f, uint32 *fft)
return dumbfile_open_ex(bm, &buffer_mod_dfs);
}
static DUMBFILE *dumbfile_buffer_mod_2(DUMBFILE *f, int32 *remain)
static DUMBFILE *dumbfile_buffer_mod_2(DUMBFILE *f, int n_samples, IT_SAMPLE *sample, int32 *total_sample_size, int32 *remain)
{
int32 read;
int sample_number;
BUFFERED_MOD *bm = malloc(sizeof(*bm));
unsigned char *ptr;
if (!bm) return NULL;
bm->buffered = malloc(32768);
@ -430,6 +432,21 @@ static DUMBFILE *dumbfile_buffer_mod_2(DUMBFILE *f, int32 *remain)
if (*remain) {
bm->ptr = 0;
ptr = bm->buffered + *remain;
sample_number = n_samples - 1;
*total_sample_size = 0;
while (ptr > bm->buffered && sample_number >= 0) {
if (sample[sample_number].flags & IT_SAMPLE_EXISTS) {
ptr -= (sample[sample_number].length + 1) / 2 + 5 + 16;
if (ptr >= bm->buffered && !memcmp(ptr, "ADPCM", 5)) { /* BAH */
*total_sample_size += (sample[sample_number].length + 1) / 2 + 5 + 16;
} else {
*total_sample_size += sample[sample_number].length;
ptr -= sample[sample_number].length - ((sample[sample_number].length + 1) / 2 + 5 + 16);
}
}
sample_number--;
}
} else {
free(bm->buffered);
bm->buffered = NULL;
@ -447,7 +464,7 @@ static DUMB_IT_SIGDATA *it_mod_load_sigdata(DUMBFILE *f, int rstrict)
int n_channels;
int i;
uint32 fft = 0;
DUMBFILE *rem;
DUMBFILE *rem = NULL;
f = dumbfile_buffer_mod(f, &fft);
if (!f)
@ -550,7 +567,7 @@ static DUMB_IT_SIGDATA *it_mod_load_sigdata(DUMBFILE *f, int rstrict)
}
// moo
if ( rstrict && sigdata->n_samples == 15 )
if ( ( rstrict & 1 ) && sigdata->n_samples == 15 )
{
free(sigdata);
dumbfile_close(f);
@ -629,12 +646,48 @@ static DUMB_IT_SIGDATA *it_mod_load_sigdata(DUMBFILE *f, int rstrict)
if (sigdata->n_samples == 31)
dumbfile_skip(f, 4);
/* Work out how many patterns there are. */
sigdata->n_patterns = -1;
for (i = 0; i < 128; i++)
if (sigdata->n_patterns < sigdata->order[i])
sigdata->n_patterns = sigdata->order[i];
sigdata->n_patterns++;
if ( ( rstrict & 2 ) )
{
long total_sample_size;
long remain;
rem = f;
f = dumbfile_buffer_mod_2(rem, sigdata->n_samples, sigdata->sample, &total_sample_size, &remain);
if (!f) {
_dumb_it_unload_sigdata(sigdata);
dumbfile_close(rem);
return NULL;
}
if (remain > total_sample_size) {
sigdata->n_patterns = ( remain - total_sample_size + 4 ) / ( 256 * sigdata->n_pchannels );
if (fft == DUMB_ID('M',0,0,0) || fft == DUMB_ID('8',0,0,0)) {
remain -= sigdata->n_patterns * 256 * sigdata->n_pchannels;
if (dumbfile_skip(f, remain - total_sample_size)) {
_dumb_it_unload_sigdata(sigdata);
dumbfile_close(f);
dumbfile_close(rem);
return NULL;
}
}
}
}
else
{
for (i = 0; i < 128; i++)
{
if (sigdata->order[i] > sigdata->n_patterns)
sigdata->n_patterns = sigdata->order[i];
}
sigdata->n_patterns++;
}
if ( sigdata->n_patterns <= 0 ) {
_dumb_it_unload_sigdata(sigdata);
dumbfile_close(f);
if (rem) dumbfile_close(rem);
return NULL;
}
/* May as well try to save a tiny bit of memory. */
if (sigdata->n_orders < 128) {
@ -646,6 +699,7 @@ static DUMB_IT_SIGDATA *it_mod_load_sigdata(DUMBFILE *f, int rstrict)
if (!sigdata->pattern) {
_dumb_it_unload_sigdata(sigdata);
dumbfile_close(f);
if (rem) dumbfile_close(rem);
return NULL;
}
for (i = 0; i < sigdata->n_patterns; i++)
@ -653,10 +707,11 @@ static DUMB_IT_SIGDATA *it_mod_load_sigdata(DUMBFILE *f, int rstrict)
/* Read in the patterns */
{
unsigned char *buffer = malloc(256 * n_channels); /* 64 rows * 4 bytes */
unsigned char *buffer = malloc(256 * sigdata->n_pchannels); /* 64 rows * 4 bytes */
if (!buffer) {
_dumb_it_unload_sigdata(sigdata);
dumbfile_close(f);
if (rem) dumbfile_close(rem);
return NULL;
}
for (i = 0; i < sigdata->n_patterns; i++) {
@ -664,40 +719,13 @@ static DUMB_IT_SIGDATA *it_mod_load_sigdata(DUMBFILE *f, int rstrict)
free(buffer);
_dumb_it_unload_sigdata(sigdata);
dumbfile_close(f);
if (rem) dumbfile_close(rem);
return NULL;
}
}
free(buffer);
}
rem = NULL;
/* uggly */
if (fft == DUMB_ID('M',0,0,0) || fft == DUMB_ID('8',0,0,0)) {
int32 skip;
int32 remain;
rem = f;
f = dumbfile_buffer_mod_2(rem, &remain);
if (!f) {
_dumb_it_unload_sigdata(sigdata);
dumbfile_close(rem);
return NULL;
}
for (skip = 0, i = 0; i < sigdata->n_samples; i++) {
if (sigdata->sample[i].flags & IT_SAMPLE_EXISTS) {
skip += sigdata->sample[i].length;
}
}
if (remain - skip) {
if (dumbfile_skip(f, remain - skip)) {
_dumb_it_unload_sigdata(sigdata);
dumbfile_close(f);
dumbfile_close(rem);
return NULL;
}
}
}
/* And finally, the sample data */
for (i = 0; i < sigdata->n_samples; i++) {
if (it_mod_read_sample_data(&sigdata->sample[i], f, fft)) {
@ -727,8 +755,8 @@ static DUMB_IT_SIGDATA *it_mod_load_sigdata(DUMBFILE *f, int rstrict)
}*/
dumbfile_close(f); /* Destroy the BUFFERED_MOD DUMBFILE we were using. */
if (rem) dumbfile_close(rem); /* And the BUFFERED_MOD DUMBFILE used to pre-read the signature. */
/* The DUMBFILE originally passed to our function is intact. */
if (rem) dumbfile_close(rem);
/* Now let's initialise the remaining variables, and we're done! */
sigdata->flags = IT_WAS_AN_XM | IT_WAS_A_MOD | IT_OLD_EFFECTS | IT_COMPATIBLE_GXX | IT_STEREO;

558
dumb/src/it/readokt.c Normal file
View File

@ -0,0 +1,558 @@
/* _______ ____ __ ___ ___
* \ _ \ \ / \ / \ \ / / ' ' '
* | | \ \ | | || | \/ | . .
* | | | | | | || ||\ /| |
* | | | | | | || || \/ | | ' ' '
* | | | | | | || || | | . .
* | |_/ / \ \__// || | |
* /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
* / \
* / . \
* readokt.c - Code to read an Oktalyzer module / / \ \
* from an open file. | < / \_
* | \/ /\ /
* By Chris Moeller. \_ / > /
* | \ / /
* | ' /
* \__/
*/
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "dumb.h"
#include "internal/it.h"
static int it_okt_read_pattern(IT_PATTERN *pattern, const unsigned char *data, int length, int n_channels)
{
int pos;
int channel;
int row;
int n_rows;
IT_ENTRY *entry;
if (length < 2) return -1;
n_rows = (data[0] << 8) | data[1];
if (!n_rows) n_rows = 64;
if (length < 2 + (n_rows * n_channels * 4)) return -1;
pattern->n_rows = n_rows;
/* compute number of entries */
pattern->n_entries = n_rows; /* Account for the row end markers */
pos = 2;
for (row = 0; row < pattern->n_rows; row++) {
for (channel = 0; channel < n_channels; channel++) {
if (data[pos+0] | data[pos+2])
pattern->n_entries++;
pos += 4;
}
}
pattern->entry = (IT_ENTRY *) malloc(pattern->n_entries * sizeof(*pattern->entry));
if (!pattern->entry)
return -1;
entry = pattern->entry;
pos = 2;
for (row = 0; row < n_rows; row++) {
for (channel = 0; channel < n_channels; channel++) {
if (data[pos+0] | data[pos+2]) {
entry->channel = channel;
entry->mask = 0;
if (data[pos+0] > 0 && data[pos+0] <= 36) {
entry->mask |= IT_ENTRY_NOTE | IT_ENTRY_INSTRUMENT;
entry->note = data[pos+0] + 35;
entry->instrument = data[pos+1] + 1;
}
entry->effect = 0;
entry->effectvalue = data[pos+3];
switch (data[pos+2]) {
case 2: if (data[pos+3]) entry->effect = IT_PORTAMENTO_DOWN; break; // XXX code calls this rs_portu, but it's adding to the period, which decreases the pitch
case 13: if (data[pos+3]) entry->effect = IT_OKT_NOTE_SLIDE_DOWN; break;
case 21: if (data[pos+3]) entry->effect = IT_OKT_NOTE_SLIDE_DOWN_ROW; break;
case 1: if (data[pos+3]) entry->effect = IT_PORTAMENTO_UP; break; // XXX same deal here, increasing the pitch
case 17: if (data[pos+3]) entry->effect = IT_OKT_NOTE_SLIDE_UP; break;
case 30: if (data[pos+3]) entry->effect = IT_OKT_NOTE_SLIDE_UP_ROW; break;
case 10: if (data[pos+3]) entry->effect = IT_OKT_ARPEGGIO_3; break;
case 11: if (data[pos+3]) entry->effect = IT_OKT_ARPEGGIO_4; break;
case 12: if (data[pos+3]) entry->effect = IT_OKT_ARPEGGIO_5; break;
case 15: entry->effect = IT_S; entry->effectvalue = EFFECT_VALUE(IT_S_SET_FILTER, data[pos+3] & 0x0F); break;
case 25: entry->effect = IT_JUMP_TO_ORDER; break;
case 27: entry->note = IT_NOTE_OFF; entry->mask |= IT_ENTRY_NOTE; break;
case 28: entry->effect = IT_SET_SPEED; break;
case 31:
if ( data[pos+3] <= 0x40 ) entry->effect = IT_SET_CHANNEL_VOLUME;
else if ( data[pos+3] <= 0x50 ) { entry->effect = IT_OKT_VOLUME_SLIDE_DOWN; entry->effectvalue = data[pos+3] - 0x40; }
else if ( data[pos+3] <= 0x60 ) { entry->effect = IT_OKT_VOLUME_SLIDE_UP; entry->effectvalue = data[pos+3] - 0x50; }
else if ( data[pos+3] <= 0x70 ) { entry->effect = IT_OKT_VOLUME_SLIDE_DOWN; entry->effectvalue = data[pos+3] - 0x50; }
else if ( data[pos+3] <= 0x80 ) { entry->effect = IT_OKT_VOLUME_SLIDE_UP; entry->effectvalue = data[pos+3] - 0x60; }
break;
}
if ( entry->effect ) entry->mask |= IT_ENTRY_EFFECT;
entry++;
}
pos += 4;
}
IT_SET_END_ROW(entry);
entry++;
}
return 0;
}
static void it_okt_read_sample_header(IT_SAMPLE *sample, const unsigned char * data)
{
int loop_start, loop_length;
memcpy(sample->name, data, 20);
sample->name[20] = 0;
sample->filename[0] = 0;
sample->length = (data[20] << 24) | (data[21] << 16) | (data[22] << 8) | data[23];
sample->global_volume = 64;
sample->default_volume = data[29];
loop_start = ((data[24] << 8) | data[25]) << 1;
loop_length = ((data[26] << 8) | data[27]) << 1;
sample->sus_loop_start = loop_start;
sample->sus_loop_end = loop_start + loop_length;
if (sample->length <= 0) {
sample->flags = 0;
return;
}
sample->flags = IT_SAMPLE_EXISTS;
sample->default_pan = 0;
sample->C5_speed = (int)( AMIGA_CLOCK / 214.0 ); //(long)(16726.0*pow(DUMB_PITCH_BASE, finetune*32));
sample->finetune = 0;
if (sample->sus_loop_end > sample->length)
sample->sus_loop_end = sample->length;
if (loop_length > 2)
sample->flags |= IT_SAMPLE_SUS_LOOP;
sample->vibrato_speed = 0;
sample->vibrato_depth = 0;
sample->vibrato_rate = 0;
sample->vibrato_waveform = 0; // do we have to set _all_ these?
sample->max_resampling_quality = -1;
}
static int it_okt_read_sample_data(IT_SAMPLE *sample, const char * data, int length)
{
if (length && sample->length) {
if (length < sample->length) {
sample->length = length;
if (length < sample->sus_loop_end) sample->sus_loop_end = length;
}
sample->data = malloc(length);
if (!sample->data)
return -1;
memcpy(sample->data, data, length);
}
return 0;
}
typedef struct IFF_CHUNK IFF_CHUNK;
typedef struct IFF_CHUNKED IFF_CHUNKED;
struct IFF_CHUNK
{
unsigned type;
unsigned char * data;
unsigned size;
};
struct IFF_CHUNKED
{
unsigned chunk_count;
IFF_CHUNK * chunks;
};
static IFF_CHUNKED *dumbfile_read_okt(DUMBFILE *f)
{
IFF_CHUNKED *mod = (IFF_CHUNKED *) malloc(sizeof(*mod));
if (!mod) return NULL;
mod->chunk_count = 0;
mod->chunks = 0;
for (;;)
{
long bytes_read;
IFF_CHUNK * chunk = ( IFF_CHUNK * ) realloc( mod->chunks, ( mod->chunk_count + 1 ) * sizeof( IFF_CHUNK ) );
if ( !chunk )
{
if ( mod->chunks ) free( mod->chunks );
free( mod );
return NULL;
}
mod->chunks = chunk;
chunk += mod->chunk_count;
bytes_read = dumbfile_mgetl( f );
if ( bytes_read < 0 ) break;
chunk->type = bytes_read;
chunk->size = dumbfile_mgetl( f );
if ( dumbfile_error( f ) ) break;
chunk->data = (unsigned char *) malloc( chunk->size );
if ( !chunk->data )
{
free( mod->chunks );
free( mod );
return NULL;
}
bytes_read = dumbfile_getnc( ( char * ) chunk->data, chunk->size, f );
if ( bytes_read < (long)chunk->size )
{
if ( bytes_read <= 0 ) {
free( chunk->data );
break;
} else {
chunk->size = bytes_read;
mod->chunk_count++;
break;
}
}
mod->chunk_count++;
}
if ( !mod->chunk_count ) {
if ( mod->chunks ) free(mod->chunks);
free(mod);
mod = NULL;
}
return mod;
}
void free_okt(IFF_CHUNKED * mod)
{
unsigned i;
if (mod)
{
if (mod->chunks)
{
for (i = 0; i < mod->chunk_count; i++)
{
if (mod->chunks[i].data) free(mod->chunks[i].data);
}
free(mod->chunks);
}
free(mod);
}
}
const IFF_CHUNK * get_chunk_by_type(IFF_CHUNKED * mod, unsigned type, unsigned offset)
{
unsigned i;
if (mod)
{
if (mod->chunks)
{
for (i = 0; i < mod->chunk_count; i++)
{
if (mod->chunks[i].type == type)
{
if (!offset) return &mod->chunks[i];
else offset--;
}
}
}
}
return NULL;
}
unsigned get_chunk_count(IFF_CHUNKED *mod, unsigned type)
{
unsigned i, count = 0;
if (mod)
{
if (mod->chunks)
{
for (i = 0; i < mod->chunk_count; i++)
{
if (mod->chunks[i].type == type) count++;
}
}
}
return count;
}
static DUMB_IT_SIGDATA *it_okt_load_sigdata(DUMBFILE *f)
{
DUMB_IT_SIGDATA *sigdata;
unsigned n_channels;
unsigned i, j, k, l;
IFF_CHUNKED *mod;
const IFF_CHUNK *chunk;
char signature[8];
if (dumbfile_getnc(signature, 8, f) < 8 ||
memcmp(signature, "OKTASONG", 8)) {
return NULL;
}
mod = dumbfile_read_okt(f);
if (!mod)
return NULL;
sigdata = (DUMB_IT_SIGDATA *) malloc(sizeof(*sigdata));
if (!sigdata) {
free_okt(mod);
return NULL;
}
sigdata->name[0] = 0;
chunk = get_chunk_by_type(mod, DUMB_ID('S','P','E','E'), 0);
if (!chunk || chunk->size < 2) {
free(sigdata);
free_okt(mod);
return NULL;
}
sigdata->speed = (chunk->data[0] << 8) | chunk->data[1];
chunk = get_chunk_by_type(mod, DUMB_ID('S','A','M','P'), 0);
if (!chunk || chunk->size < 32) {
free(sigdata);
free_okt(mod);
return NULL;
}
sigdata->n_samples = chunk->size / 32;
chunk = get_chunk_by_type(mod, DUMB_ID('C','M','O','D'), 0);
if (!chunk || chunk->size < 8) {
free(sigdata);
free_okt(mod);
return NULL;
}
n_channels = 0;
for (i = 0; i < 4; i++) {
j = (chunk->data[i * 2] << 8) | chunk->data[i * 2 + 1];
if (!j) n_channels++;
else if (j == 1) n_channels += 2;
}
if (!n_channels) {
free(sigdata);
free_okt(mod);
return NULL;
}
sigdata->n_pchannels = n_channels;
sigdata->sample = (IT_SAMPLE *) malloc(sigdata->n_samples * sizeof(*sigdata->sample));
if (!sigdata->sample) {
free(sigdata);
free_okt(mod);
return NULL;
}
sigdata->song_message = NULL;
sigdata->order = NULL;
sigdata->instrument = NULL;
sigdata->pattern = NULL;
sigdata->midi = NULL;
sigdata->checkpoint = NULL;
sigdata->n_instruments = 0;
for (i = 0; i < (unsigned)sigdata->n_samples; i++)
sigdata->sample[i].data = NULL;
chunk = get_chunk_by_type(mod, DUMB_ID('S','A','M','P'), 0);
for (i = 0; i < (unsigned)sigdata->n_samples; i++) {
it_okt_read_sample_header(&sigdata->sample[i], chunk->data + 32 * i);
}
sigdata->restart_position = 0;
chunk = get_chunk_by_type(mod, DUMB_ID('P','L','E','N'), 0);
if (!chunk || chunk->size < 2) {
_dumb_it_unload_sigdata(sigdata);
free_okt(mod);
return NULL;
}
sigdata->n_orders = (chunk->data[0] << 8) | chunk->data[1];
// what if this is > 128?
if (sigdata->n_orders <= 0 || sigdata->n_orders > 128) {
_dumb_it_unload_sigdata(sigdata);
free_okt(mod);
return NULL;
}
chunk = get_chunk_by_type(mod, DUMB_ID('P','A','T','T'), 0);
if (!chunk || chunk->size < (unsigned)sigdata->n_orders) {
_dumb_it_unload_sigdata(sigdata);
free_okt(mod);
return NULL;
}
sigdata->order = (unsigned char *) malloc(sigdata->n_orders);
if (!sigdata->order) {
_dumb_it_unload_sigdata(sigdata);
free_okt(mod);
return NULL;
}
memcpy(sigdata->order, chunk->data, sigdata->n_orders);
/* Work out how many patterns there are. */
chunk = get_chunk_by_type(mod, DUMB_ID('S','L','E','N'), 0);
if (!chunk || chunk->size < 2) {
_dumb_it_unload_sigdata(sigdata);
free_okt(mod);
return NULL;
}
sigdata->n_patterns = (chunk->data[0] << 8) | chunk->data[1];
j = get_chunk_count(mod, DUMB_ID('P','B','O','D'));
if (sigdata->n_patterns > (int)j) sigdata->n_patterns = (int)j;
if (!sigdata->n_patterns) {
_dumb_it_unload_sigdata(sigdata);
free_okt(mod);
return NULL;
}
sigdata->pattern = (IT_PATTERN *) malloc(sigdata->n_patterns * sizeof(*sigdata->pattern));
if (!sigdata->pattern) {
_dumb_it_unload_sigdata(sigdata);
free_okt(mod);
return NULL;
}
for (i = 0; i < (unsigned)sigdata->n_patterns; i++)
sigdata->pattern[i].entry = NULL;
/* Read in the patterns */
for (i = 0; i < (unsigned)sigdata->n_patterns; i++) {
chunk = get_chunk_by_type(mod, DUMB_ID('P','B','O','D'), i);
if (it_okt_read_pattern(&sigdata->pattern[i], chunk->data, chunk->size, n_channels) != 0) {
_dumb_it_unload_sigdata(sigdata);
free_okt(mod);
return NULL;
}
}
/* And finally, the sample data */
k = get_chunk_count(mod, DUMB_ID('S','B','O','D'));
for (i = 0, j = 0; i < (unsigned)sigdata->n_samples, j < k; i++) {
if (sigdata->sample[i].flags & IT_SAMPLE_EXISTS) {
chunk = get_chunk_by_type(mod, DUMB_ID('S','B','O','D'), j);
if (it_okt_read_sample_data(&sigdata->sample[i], (const char *)chunk->data, chunk->size)) {
_dumb_it_unload_sigdata(sigdata);
free_okt(mod);
return NULL;
}
j++;
}
}
for (; i < (unsigned)sigdata->n_samples; i++) {
sigdata->sample[i].flags = 0;
}
chunk = get_chunk_by_type(mod, DUMB_ID('C','M','O','D'), 0);
for (i = 0, j = 0; i < n_channels, j < 4; j++) {
k = (chunk->data[j * 2] << 8) | chunk->data[j * 2 + 1];
l = (j == 1 || j == 2) ? 48 : 16;
if (k == 0) {
sigdata->channel_pan[i++] = l;
}
else if (k == 1) {
sigdata->channel_pan[i++] = l;
sigdata->channel_pan[i++] = l;
}
}
free_okt(mod);
/* Now let's initialise the remaining variables, and we're done! */
sigdata->flags = IT_WAS_AN_OKT | IT_WAS_AN_XM | IT_WAS_A_MOD | IT_OLD_EFFECTS | IT_COMPATIBLE_GXX | IT_STEREO;
sigdata->global_volume = 128;
sigdata->mixing_volume = 48;
/* We want 50 ticks per second; 50/6 row advances per second;
* 50*10=500 row advances per minute; 500/4=125 beats per minute.
*/
sigdata->tempo = 125;
sigdata->pan_separation = 128;
memset(sigdata->channel_volume, 64, DUMB_IT_N_CHANNELS);
memset(sigdata->channel_pan + n_channels, 32, DUMB_IT_N_CHANNELS - n_channels);
_dumb_it_fix_invalid_orders(sigdata);
return sigdata;
}
DUH *DUMBEXPORT dumb_read_okt_quick(DUMBFILE *f)
{
sigdata_t *sigdata;
DUH_SIGTYPE_DESC *descptr = &_dumb_sigtype_it;
sigdata = it_okt_load_sigdata(f);
if (!sigdata)
return NULL;
{
const char *tag[1][2];
tag[0][0] = "FORMAT";
tag[0][1] = "Oktalyzer";
return make_duh(-1, 1, (const char *const (*)[2])tag, 1, &descptr, &sigdata);
}
}

29
dumb/src/it/readokt2.c Normal file
View File

@ -0,0 +1,29 @@
/* _______ ____ __ ___ ___
* \ _ \ \ / \ / \ \ / / ' ' '
* | | \ \ | | || | \/ | . .
* | | | | | | || ||\ /| |
* | | | | | | || || \/ | | ' ' '
* | | | | | | || || | | . .
* | |_/ / \ \__// || | |
* /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
* / \
* / . \
* readokt2.c - Function to read an Oktalyzer / / \ \
* module from an open file and do | < / \_
* an initial run-through. | \/ /\ /
* \_ / > /
* | \ / /
* By Chris Moeller. | ' /
* \__/
*/
#include "dumb.h"
DUH *DUMBEXPORT dumb_read_okt(DUMBFILE *f)
{
DUH *duh = dumb_read_okt_quick(f);
dumb_it_do_initial_runthrough(duh);
return duh;
}

View File

@ -296,7 +296,7 @@ static int it_old_psm_read_patterns(IT_PATTERN * pattern, DUMBFILE * f, int num,
if (flags & 0x80) {
if ((*ptr < 60) && (channel < pchans)) {
entry->mask |= IT_ENTRY_NOTE;
entry->note = *ptr + 36;
entry->note = *ptr + 35;
}
ptr++;
if (*ptr) {

View File

@ -557,8 +557,8 @@ static DUMB_IT_SIGDATA *it_s3m_load_sigdata(DUMBFILE *f, int * cwtv)
return NULL;
}
sigdata->global_volume = dumbfile_getc(f) << 1;
if ( !sigdata->global_volume || sigdata->global_volume > 128 ) sigdata->global_volume = 128;
sigdata->global_volume = dumbfile_getc(f) * 16 / 11;
if ( !sigdata->global_volume || sigdata->global_volume > 93 ) sigdata->global_volume = 93;
sigdata->speed = dumbfile_getc(f);
if (sigdata->speed == 0) sigdata->speed = 6; // Should we? What about tempo?
sigdata->tempo = dumbfile_getc(f);

View File

@ -20,6 +20,7 @@
// IT_STEREO... :o
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include "dumb.h"
#include "internal/it.h"
@ -28,14 +29,16 @@
#define strnicmp strncasecmp
#endif
static int it_stm_read_sample_header( IT_SAMPLE *sample, DUMBFILE *f )
static int it_stm_read_sample_header( IT_SAMPLE *sample, DUMBFILE *f, unsigned short *offset )
{
dumbfile_getnc( sample->filename, 12, f );
sample->filename[12] = 0;
memcpy( sample->name, sample->filename, 13 );
dumbfile_skip( f, 2 + 2 );
dumbfile_skip( f, 2 );
*offset = dumbfile_igetw( f );
sample->length = dumbfile_igetw( f );
sample->loop_start = dumbfile_igetw( f );
@ -78,24 +81,15 @@ static int it_stm_read_sample_header( IT_SAMPLE *sample, DUMBFILE *f )
return dumbfile_error(f);
}
static int it_stm_read_sample_data( IT_SAMPLE *sample, DUMBFILE *f )
static int it_stm_read_sample_data( IT_SAMPLE *sample, void *data_block, long offset )
{
int32 n;
if ( ! sample->length ) return 0;
n = dumbfile_pos( f );
if ( n & 15 ) {
if ( dumbfile_skip( f, 16 - ( n & 15 ) ) )
return -1;
}
sample->data = malloc( sample->length );
if (!sample->data)
return -1;
if ( dumbfile_getnc( sample->data, sample->length, f ) != sample->length )
return -1;
memcpy( sample->data, (unsigned char*)data_block + offset, sample->length );
return 0;
}
@ -141,10 +135,6 @@ static int it_stm_read_pattern( IT_PATTERN *pattern, DUMBFILE *f, unsigned char
entry->effectvalue = buffer[ pos + 3 ];
if ( entry->instrument && entry->instrument < 32 )
entry->mask |= IT_ENTRY_INSTRUMENT;
if ( note == 0xFC || note == 0xFE ) {
entry->mask |= IT_ENTRY_NOTE;
entry->note = IT_NOTE_CUT;
}
if ( note < 251 ) {
entry->mask |= IT_ENTRY_NOTE;
entry->note = ( note >> 4 ) * 12 + ( note & 0x0F );
@ -153,9 +143,9 @@ static int it_stm_read_pattern( IT_PATTERN *pattern, DUMBFILE *f, unsigned char
entry->mask |= IT_ENTRY_VOLPAN;
entry->mask |= IT_ENTRY_EFFECT;
switch ( entry->effect ) {
case IT_SET_SPEED:
entry->effectvalue >>= 4;
break;
case IT_SET_SPEED:
/* taken care of in the renderer */
break;
case IT_BREAK_TO_ROW:
entry->effectvalue -= (entry->effectvalue >> 4) * 6;
@ -192,14 +182,20 @@ static int it_stm_read_pattern( IT_PATTERN *pattern, DUMBFILE *f, unsigned char
static DUMB_IT_SIGDATA *it_stm_load_sigdata(DUMBFILE *f /*, int * version*/)
static DUMB_IT_SIGDATA *it_stm_load_sigdata(DUMBFILE *f, int * version)
{
DUMB_IT_SIGDATA *sigdata;
char tracker_name[ 8 ];
unsigned short sample_offset[ 31 ];
void *data_block;
int n;
long o, p, q;
sigdata = malloc(sizeof(*sigdata));
if (!sigdata) return NULL;
@ -227,8 +223,7 @@ static DUMB_IT_SIGDATA *it_stm_load_sigdata(DUMBFILE *f /*, int * version*/)
return NULL;
}
/* *version = dumbfile_mgetw(f); */
dumbfile_skip( f, 2 );
*version = dumbfile_mgetw(f);
sigdata->song_message = NULL;
sigdata->order = NULL;
@ -242,16 +237,17 @@ static DUMB_IT_SIGDATA *it_stm_load_sigdata(DUMBFILE *f /*, int * version*/)
sigdata->n_samples = 31;
sigdata->n_pchannels = 4;
sigdata->tempo = 125;
sigdata->mixing_volume = 48;
sigdata->tempo = 125;
sigdata->mixing_volume = 48;
sigdata->pan_separation = 128;
/** WARNING: which ones? */
sigdata->flags = IT_OLD_EFFECTS | IT_COMPATIBLE_GXX | IT_WAS_AN_S3M;
sigdata->flags = IT_OLD_EFFECTS | IT_COMPATIBLE_GXX | IT_WAS_AN_S3M | IT_WAS_AN_STM | IT_STEREO;
sigdata->speed = dumbfile_getc(f) >> 4;
if ( sigdata->speed < 1 ) sigdata->speed = 1;
sigdata->n_patterns = dumbfile_getc(f);
n = dumbfile_getc(f);
if ( n < 32 ) n = 32;
sigdata->speed = n;
sigdata->n_patterns = dumbfile_getc(f);
sigdata->global_volume = dumbfile_getc(f) << 1;
if ( sigdata->global_volume > 128 ) sigdata->global_volume = 128;
@ -287,7 +283,7 @@ static DUMB_IT_SIGDATA *it_stm_load_sigdata(DUMBFILE *f /*, int * version*/)
sigdata->channel_pan[ 3 ] = 16;
for ( n = 0; n < sigdata->n_samples; ++n ) {
if ( it_stm_read_sample_header( &sigdata->sample[ n ], f ) ) {
if ( it_stm_read_sample_header( &sigdata->sample[ n ], f, &sample_offset[ n ] ) ) {
_dumb_it_unload_sigdata( sigdata );
return NULL;
}
@ -300,7 +296,8 @@ static DUMB_IT_SIGDATA *it_stm_load_sigdata(DUMBFILE *f /*, int * version*/)
}
/* Orders, byte each, length = sigdata->n_orders (should be even) */
dumbfile_getnc( sigdata->order, 128, f );
dumbfile_getnc( sigdata->order, *version >= 0x200 ? 128 : 64, f );
if (*version < 0x200) memset( sigdata->order + 64, 0xFF, 64 );
sigdata->restart_position = 0;
for ( n = 127; n >= 0; --n ) {
@ -332,54 +329,105 @@ static DUMB_IT_SIGDATA *it_stm_load_sigdata(DUMBFILE *f /*, int * version*/)
free( buffer );
}
o = LONG_MAX;
p = 0;
for ( n = 0; n < sigdata->n_samples; ++n ) {
if ( it_stm_read_sample_data( &sigdata->sample[ n ], f ) ) {
if ((sigdata->sample[ n ].flags & IT_SAMPLE_EXISTS) && sample_offset[ n ]) {
q = ((long)sample_offset[ n ]) * 16;
if (q < o) {
o = q;
}
if (q + sigdata->sample[ n ].length > p) {
p = q + sigdata->sample[ n ].length;
}
}
else {
sigdata->sample[ n ].flags = 0;
sigdata->sample[ n ].length = 0;
}
}
data_block = malloc( p - o );
if ( !data_block ) {
_dumb_it_unload_sigdata( sigdata );
return NULL;
}
for ( n = 0, q = o / 16; n < sigdata->n_samples; ++n ) {
if ( sample_offset[ n ] ) {
sample_offset[ n ] = (unsigned short)(sample_offset[ n ] - q);
}
}
q = o - dumbfile_pos( f );
p -= o;
o = 0;
if ( q >= 0 ) dumbfile_skip( f, q );
else {
o = -q;
memset ( data_block, 0, o );
}
if ( dumbfile_getnc( (char*)data_block + o, p - o, f ) != p - o ) {
free( data_block );
_dumb_it_unload_sigdata( sigdata );
return NULL;
}
for ( n = 0; n < sigdata->n_samples; ++n ) {
if ( it_stm_read_sample_data( &sigdata->sample[ n ], data_block, ((long)sample_offset[ n ]) * 16 ) ) {
free( data_block );
_dumb_it_unload_sigdata( sigdata );
return NULL;
}
}
free( data_block );
_dumb_it_fix_invalid_orders(sigdata);
return sigdata;
}
/*static char hexdigit(int in)
{
if (in < 10) return in + '0';
else return in + 'A' - 10;
}*/
DUH *DUMBEXPORT dumb_read_stm_quick(DUMBFILE *f)
{
sigdata_t *sigdata;
/*int ver;*/
int ver;
DUH_SIGTYPE_DESC *descptr = &_dumb_sigtype_it;
sigdata = it_stm_load_sigdata(f /*, &ver*/);
sigdata = it_stm_load_sigdata(f , &ver);
if (!sigdata)
return NULL;
{
/*char version[16];*/
char version[16];
const char *tag[2][2];
tag[0][0] = "TITLE";
tag[0][1] = ((DUMB_IT_SIGDATA *)sigdata)->name;
tag[1][0] = "FORMAT";
tag[1][1] = "STM";
/*version[0] = 'S';
version[0] = 'S';
version[1] = 'T';
version[2] = 'M';
version[3] = ' ';
version[4] = 'v';
version[5] = hexdigit((ver >> 8) & 15);
version[5] = '0' + ((ver >> 8) & 15);
version[6] = '.';
version[7] = hexdigit((ver >> 4) & 15);
version[8] = hexdigit(ver & 15);
version[9] = 0;
tag[1][1] = (const char *) &version;*/
if ((ver & 255) > 99)
{
version[7] = '0' + ((ver & 255) / 100 );
version[8] = '0' + (((ver & 255) / 10) % 10);
version[9] = '0' + ((ver & 255) % 10);
version[10] = 0;
}
else
{
version[7] = '0' + ((ver & 255) / 10);
version[8] = '0' + ((ver & 255) % 10);
version[9] = 0;
}
tag[1][1] = (const char *) &version;
return make_duh(-1, 2, (const char *const (*)[2])tag, 1, &descptr, &sigdata);
}
}

View File

@ -111,11 +111,20 @@ typedef struct XM_INSTRUMENT_EXTRA
int vibrato_sweep; /* 0-0xFF */
int vibrato_depth; /* 0-0x0F */
int vibrato_speed; /* 0-0x3F */
int sample_header_size;
}
XM_INSTRUMENT_EXTRA;
/* Trims off trailing white space, usually added by the tracker on file creation
*/
static void trim_whitespace(char *ptr, size_t size)
{
char *p = ptr + size - 1;
while (p >= ptr && *p <= 0x20) *p-- = '\0';
}
/* Frees the original block if it can't resize it or if size is 0, and acts
* as malloc if ptr is NULL.
*/
@ -329,7 +338,7 @@ static int it_xm_read_pattern(IT_PATTERN *pattern, DUMBFILE *f, int n_channels,
static int it_xm_make_envelope(IT_ENVELOPE *envelope, const unsigned short *data, int y_offset)
{
int i, pos;
int i, pos, val;
if (envelope->n_nodes > 12) {
/* XXX
@ -346,12 +355,13 @@ static int it_xm_make_envelope(IT_ENVELOPE *envelope, const unsigned short *data
pos = 0;
for (i = 0; i < envelope->n_nodes; i++) {
envelope->node_t[i] = data[pos++];
if (data[pos] > 64) {
TRACE("XM error: out-of-range envelope node (node_y[%d]=%d)\n", i, data[pos]);
envelope->n_nodes = 0;
return -1;
val = data[pos++];
if (val > 64) {
TRACE("XM error: out-of-range envelope node (node_y[%d]=%d)\n", i, val);
/* FT2 seems to simply clip the value */
val = 64;
}
envelope->node_y[i] = (signed char)(data[pos++] + y_offset);
envelope->node_y[i] = (signed char)(val + y_offset);
}
return 0;
@ -359,21 +369,153 @@ static int it_xm_make_envelope(IT_ENVELOPE *envelope, const unsigned short *data
typedef struct LIMITED_XM LIMITED_XM;
struct LIMITED_XM
{
unsigned char *buffered;
long ptr, limit, allocated;
DUMBFILE *remaining;
};
/* XXX */
struct DUMBFILE
{
DUMBFILE_SYSTEM *dfs;
void *file;
long pos;
};
static int limit_xm_resize(void *f, long n)
{
DUMBFILE *df = f;
LIMITED_XM *lx = df->file;
if (lx->buffered || n) {
if (n > lx->allocated) {
unsigned char *buffered = realloc( lx->buffered, n );
if ( !buffered ) return -1;
lx->buffered = buffered;
memset( buffered + lx->allocated, 0, n - lx->allocated );
lx->allocated = n;
}
if ( dumbfile_getnc( lx->buffered, n, lx->remaining ) < n ) return -1;
} else if (!n) {
if ( lx->buffered ) free( lx->buffered );
lx->buffered = NULL;
lx->allocated = 0;
}
lx->limit = n;
lx->ptr = 0;
return 0;
}
static int limit_xm_skip_end(void *f, int32 n)
{
DUMBFILE *df = f;
LIMITED_XM *lx = df->file;
return dumbfile_skip( lx->remaining, n );
}
static int limit_xm_skip(void *f, int32 n)
{
LIMITED_XM *lx = f;
lx->ptr += n;
return 0;
}
static int limit_xm_getc(void *f)
{
LIMITED_XM *lx = f;
if (lx->ptr >= lx->allocated) {
return 0;
}
return lx->buffered[lx->ptr++];
}
static long limit_xm_getnc(char *ptr, int32 n, void *f)
{
LIMITED_XM *lx = f;
int left;
left = lx->allocated - lx->ptr;
if (n > left) {
if (left > 0) {
memcpy( ptr, lx->buffered + lx->ptr, left );
memset( ptr + left, 0, n - left );
} else {
memset( ptr, 0, n );
}
} else {
memcpy( ptr, lx->buffered + lx->ptr, n );
}
lx->ptr += n;
return n;
}
static void limit_xm_close(void *f)
{
LIMITED_XM *lx = f;
if (lx->buffered) free(lx->buffered);
/* Do NOT close lx->remaining */
free(f);
}
DUMBFILE_SYSTEM limit_xm_dfs = {
NULL,
&limit_xm_skip,
&limit_xm_getc,
&limit_xm_getnc,
&limit_xm_close
};
static DUMBFILE *dumbfile_limit_xm(DUMBFILE *f)
{
LIMITED_XM * lx = malloc(sizeof(*lx));
lx->remaining = f;
lx->buffered = NULL;
lx->ptr = 0;
lx->limit = 0;
lx->allocated = 0;
return dumbfile_open_ex( lx, &limit_xm_dfs );
}
static int it_xm_read_instrument(IT_INSTRUMENT *instrument, XM_INSTRUMENT_EXTRA *extra, DUMBFILE *f)
{
uint32 size, bytes_read;
unsigned short vol_points[24];
unsigned short pan_points[24];
int i, type;
const unsigned long max_size = 4 + 22 + 1 + 2 + 4 + 96 + 48 + 48 + 1 * 14 + 2 + 2;
unsigned long skip_end = 0;
/* Header size. Tends to be more than the actual size of the structure.
* So unread bytes must be skipped before reading the first sample
* header.
*/
if ( limit_xm_resize( f, 4 ) < 0 ) return -1;
size = dumbfile_igetl(f);
if ( size == 0 ) size = max_size;
else if ( size > max_size )
{
skip_end = size - max_size;
size = max_size;
}
if ( limit_xm_resize( f, size - 4 ) < 0 ) return -1;
dumbfile_getnc(instrument->name, 22, f);
instrument->name[22] = 0;
trim_whitespace(instrument->name, 22);
instrument->filename[0] = 0;
dumbfile_skip(f, 1); /* Instrument type. Should be 0, but seems random. */
extra->n_samples = dumbfile_igetw(f);
@ -385,12 +527,11 @@ static int it_xm_read_instrument(IT_INSTRUMENT *instrument, XM_INSTRUMENT_EXTRA
if (extra->n_samples) {
/* sample header size */
dumbfile_skip(f, 4); // XXX can't be trusted, as there are trackers that write the wrong value here
/*i = dumbfile_igetl(f);
if (i && i != 0x28) { // XXX some crap with 0 here
TRACE("XM error: unexpected sample header size\n");
return -1;
}*/
if (!i || i > 0x28) i = 0x28;*/
dumbfile_skip(f, 4);
i = 0x28;
extra->sample_header_size = i;
/* sample map */
for (i = 0; i < 96; i++) {
@ -476,7 +617,10 @@ static int it_xm_read_instrument(IT_INSTRUMENT *instrument, XM_INSTRUMENT_EXTRA
for (i = 0; i < 96; i++)
instrument->map_sample[i] = 0;
if (dumbfile_skip(f, size - bytes_read))
if (size > bytes_read && dumbfile_skip(f, size - bytes_read))
return -1;
if (skip_end && limit_xm_skip_end(f, skip_end))
return -1;
instrument->new_note_action = NNA_NOTE_CUT;
@ -531,6 +675,7 @@ static int it_xm_read_sample_header(IT_SAMPLE *sample, DUMBFILE *f)
dumbfile_getnc(sample->name, 22, f);
sample->name[22] = 0;
trim_whitespace(sample->name, 22);
sample->filename[0] = 0;
@ -785,6 +930,7 @@ static DUMB_IT_SIGDATA *it_xm_load_sigdata(DUMBFILE *f, int * version)
return NULL;
}
sigdata->name[20] = 0;
trim_whitespace(sigdata->name, 20);
if (dumbfile_getc(f) != 0x1A) {
TRACE("XM error: 0x1A not found\n");
@ -924,16 +1070,24 @@ static DUMB_IT_SIGDATA *it_xm_load_sigdata(DUMBFILE *f, int * version)
for (i = 0; i < sigdata->n_instruments; i++) {
XM_INSTRUMENT_EXTRA extra;
if (it_xm_read_instrument(&sigdata->instrument[i], &extra, f) < 0) {
DUMBFILE * lf = dumbfile_limit_xm( f );
if ( !lf ) {
_dumb_it_unload_sigdata(sigdata);
return NULL;
}
if (it_xm_read_instrument(&sigdata->instrument[i], &extra, lf) < 0) {
// XXX
if ( ! i )
{
TRACE("XM error: instrument %d\n", i+1);
dumbfile_close( lf );
_dumb_it_unload_sigdata(sigdata);
return NULL;
}
else
{
dumbfile_close( lf );
sigdata->n_instruments = i;
break;
}
@ -948,17 +1102,31 @@ static DUMB_IT_SIGDATA *it_xm_load_sigdata(DUMBFILE *f, int * version)
sigdata->sample = safe_realloc(sigdata->sample, sizeof(*sigdata->sample)*(total_samples+extra.n_samples));
if (!sigdata->sample) {
dumbfile_close( lf );
_dumb_it_unload_sigdata(sigdata);
return NULL;
}
for (j = total_samples; j < total_samples+extra.n_samples; j++)
sigdata->sample[j].data = NULL;
if ( limit_xm_resize( lf, 0 ) < 0 ) {
dumbfile_close( lf );
_dumb_it_unload_sigdata( sigdata );
return NULL;
}
/* read instrument's samples */
for (j = 0; j < extra.n_samples; j++) {
IT_SAMPLE *sample = &sigdata->sample[total_samples+j];
int b = it_xm_read_sample_header(sample, f);
int b;
if ( limit_xm_resize( lf, extra.sample_header_size ) < 0 ) {
dumbfile_close( lf );
_dumb_it_unload_sigdata( sigdata );
return NULL;
}
b = it_xm_read_sample_header(sample, lf);
if (b < 0) {
dumbfile_close( lf );
_dumb_it_unload_sigdata(sigdata);
return NULL;
}
@ -975,12 +1143,15 @@ static DUMB_IT_SIGDATA *it_xm_load_sigdata(DUMBFILE *f, int * version)
}
for (j = 0; j < extra.n_samples; j++) {
if (it_xm_read_sample_data(&sigdata->sample[total_samples+j], roguebytes[j], f) != 0) {
dumbfile_close( lf );
_dumb_it_unload_sigdata(sigdata);
return NULL;
}
}
total_samples += extra.n_samples;
}
dumbfile_close( lf );
}
sigdata->n_samples = total_samples;
@ -1012,8 +1183,16 @@ static DUMB_IT_SIGDATA *it_xm_load_sigdata(DUMBFILE *f, int * version)
for (i = 0; i < sigdata->n_instruments; i++) {
XM_INSTRUMENT_EXTRA extra;
if (it_xm_read_instrument(&sigdata->instrument[i], &extra, f) < 0) {
DUMBFILE * lf = dumbfile_limit_xm( f );
if ( !lf ) {
free(roguebytes);
_dumb_it_unload_sigdata(sigdata);
return NULL;
}
if (it_xm_read_instrument(&sigdata->instrument[i], &extra, lf) < 0) {
TRACE("XM error: instrument %d\n", i+1);
dumbfile_close(lf);
free(roguebytes);
_dumb_it_unload_sigdata(sigdata);
return NULL;
@ -1026,6 +1205,7 @@ static DUMB_IT_SIGDATA *it_xm_load_sigdata(DUMBFILE *f, int * version)
sigdata->sample = safe_realloc(sigdata->sample, sizeof(*sigdata->sample)*(total_samples+extra.n_samples));
if (!sigdata->sample) {
dumbfile_close( lf );
free(roguebytes);
_dumb_it_unload_sigdata(sigdata);
return NULL;
@ -1033,10 +1213,24 @@ static DUMB_IT_SIGDATA *it_xm_load_sigdata(DUMBFILE *f, int * version)
for (j = total_samples; j < total_samples+extra.n_samples; j++)
sigdata->sample[j].data = NULL;
if ( limit_xm_resize( lf, 0 ) < 0 ) {
dumbfile_close( lf );
free( roguebytes );
_dumb_it_unload_sigdata( sigdata );
return NULL;
}
/* read instrument's samples */
for (j = 0; j < extra.n_samples; j++) {
IT_SAMPLE *sample = &sigdata->sample[total_samples+j];
int b = it_xm_read_sample_header(sample, f);
int b;
if ( limit_xm_resize( lf, extra.sample_header_size ) < 0 ) {
dumbfile_close( lf );
free( roguebytes );
_dumb_it_unload_sigdata( sigdata );
return NULL;
}
b = it_xm_read_sample_header(sample, lf);
if (b < 0) {
free(roguebytes);
_dumb_it_unload_sigdata(sigdata);
@ -1055,6 +1249,8 @@ static DUMB_IT_SIGDATA *it_xm_load_sigdata(DUMBFILE *f, int * version)
}
total_samples += extra.n_samples;
}
dumbfile_close( lf );
}
sigdata->n_samples = total_samples;

View File

@ -167,6 +167,7 @@ if (log) printf(" - %2d %02X", effect, value);
case XM_SET_GLOBAL_VOLUME:
effect = IT_SET_GLOBAL_VOLUME;
value *= 2;
if (value > 128) value = 128;
break;
case XM_KEY_OFF:
@ -187,6 +188,7 @@ if (log) printf(" - %2d %02X", effect, value);
case EBASE+XM_E_SET_PANNING: effect = SBASE+IT_S_SET_PAN; break;
case EBASE+XM_E_FINE_VOLSLIDE_UP: effect = IT_XM_FINE_VOLSLIDE_UP; break;
case EBASE+XM_E_FINE_VOLSLIDE_DOWN: effect = IT_XM_FINE_VOLSLIDE_DOWN; break;
case EBASE+XM_E_SET_MIDI_MACRO: effect = SBASE+IT_S_SET_MIDI_MACRO; break;
case EBASE + XM_E_FINE_PORTA_UP:
effect = IT_PORTAMENTO_UP;

View File

@ -877,6 +877,10 @@
RelativePath="..\..\src\helpers\barray.c"
>
</File>
<File
RelativePath="..\..\src\helpers\blip_buf.c"
>
</File>
<File
RelativePath="..\..\src\helpers\clickrem.c"
>
@ -1560,6 +1564,14 @@
RelativePath="..\..\src\it\loadmtm2.c"
>
</File>
<File
RelativePath="..\..\src\it\loadokt.c"
>
</File>
<File
RelativePath="..\..\src\it\loadokt2.c"
>
</File>
<File
RelativePath="..\..\src\it\loadoldpsm.c"
>
@ -1778,6 +1790,14 @@
RelativePath="..\..\src\it\readmtm.c"
>
</File>
<File
RelativePath="..\..\src\it\readokt.c"
>
</File>
<File
RelativePath="..\..\src\it\readokt2.c"
>
</File>
<File
RelativePath="..\..\src\it\readoldpsm.c"
>
@ -1960,6 +1980,10 @@
<Filter
Name="internal"
>
<File
RelativePath="..\..\include\internal\blip_buf.h"
>
</File>
<File
RelativePath="..\..\include\internal\dumb.h"
>

View File

@ -141,6 +141,9 @@ Note: All <bool> fields default to false unless mentioned otherwise.
light = <integer>; // This side's light level. Default is 0.
lightabsolute = <bool>; // true = 'light' is an absolute value. Default is
// relative to the owning sector's light level.
lightfog = <bool>; // true = This side's relative lighting is used even in
// foggy sectors. Default is to disable relative
// lighting in foggy sectors.
nofakecontrast = <bool>; // Disables use of fake contrast on this sidedef.
smoothlighting = <bool>; // Use smooth fake contrast.
clipmidtex = <bool>; // Side's mid textures are clipped to floor and ceiling.

View File

@ -1,5 +1,5 @@
===============================================================================
Universal Strife Dialog Format Specification v2.0 - 08/20/10
Universal Strife Dialog Format Specification v2.1 - 01/06/13
Written by Braden "Blzut3" Obrzut - admin@maniacsvault.net
@ -11,7 +11,7 @@ Graf Zahl
Quasar
et al.
Copyright (c) 2010 Braden Obrzut.
Copyright (c) 2013 Braden Obrzut.
Permission is granted to copy, distribute and/or modify this document
under the terms of the GNU Free Documentation License, Version 1.2
or any later version published by the Free Software Foundation;
@ -19,6 +19,14 @@ et al.
===============================================================================
=======================================
Changes in v2.1
=======================================
* Pages are specified as starting as being indexed from 1 instead of 0. While
this technically renders the spec incompatible, all known implementations
used this convention as it was inline with the binary format.
=======================================
I. Grammar / Syntax
=======================================
@ -77,7 +85,7 @@ conversation // Starts a dialog.
actor = <integer>; // mobj for this conversation's actor. If previously
// used, this will override the previous conversation.
page // Starts a new page. Pages are automatically numbered starting at 0.
page // Starts a new page. Pages are automatically numbered starting at 1.
{
name = <string>; // Name that goes in the upper left hand corner
panel = <string>; // Name of lump to render as the background.

View File

@ -485,8 +485,10 @@ endif( BACKPATCH )
# Update svnrevision.h
get_target_property( UPDATEREVISION_EXE updaterevision LOCATION )
add_custom_target( revision_check ALL
COMMAND ${CMAKE_BINARY_DIR}/tools/updaterevision/updaterevision . src/svnrevision.h
COMMAND ${UPDATEREVISION_EXE} . src/svnrevision.h
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
DEPENDS updaterevision )
@ -568,9 +570,12 @@ else( NO_ASM )
endif( X64 )
endif( NO_ASM )
get_target_property( LEMON_EXE lemon LOCATION )
get_target_property( RE2C_EXE re2c LOCATION )
add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/xlat_parser.c ${CMAKE_CURRENT_BINARY_DIR}/xlat_parser.h
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_SOURCE_DIR}/xlat/xlat_parser.y .
COMMAND ${CMAKE_BINARY_DIR}/tools/lemon/lemon xlat_parser.y
COMMAND ${LEMON_EXE} xlat_parser.y
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
DEPENDS lemon ${CMAKE_CURRENT_SOURCE_DIR}/xlat/xlat_parser.y )
@ -581,7 +586,7 @@ add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/zcc-parse.c ${CMAKE_CURRE
DEPENDS lemon ${CMAKE_CURRENT_SOURCE_DIR}/zscript/zcc-parse.lemon )
add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/sc_man_scanner.h
COMMAND ${CMAKE_BINARY_DIR}/tools/re2c/re2c --no-generation-date -s -o ${CMAKE_CURRENT_BINARY_DIR}/sc_man_scanner.h ${CMAKE_CURRENT_SOURCE_DIR}/sc_man_scanner.re
COMMAND ${RE2C_EXE} --no-generation-date -s -o ${CMAKE_CURRENT_BINARY_DIR}/sc_man_scanner.h ${CMAKE_CURRENT_SOURCE_DIR}/sc_man_scanner.re
DEPENDS re2c ${CMAKE_CURRENT_SOURCE_DIR}/sc_man_scanner.re )
include_directories( ${CMAKE_CURRENT_BINARY_DIR} )
@ -604,7 +609,37 @@ elseif( FLUIDSYNTH_FOUND )
add_definitions( -DHAVE_FLUIDSYNTH )
endif( DYN_FLUIDSYNTH )
# Project files should be aware of the header files. We can GLOB these since
# there's generally a new cpp for every header so this file will get changed
if( WIN32 )
set( EXTRA_HEADER_DIRS win32/*.h )
else( WIN32 )
set( EXTRA_HEADER_DIRS sdl/*.h )
endif( WIN32 )
file( GLOB HEADER_FILES
${EXTRA_HEADER_DIRS}
fragglescript/*.h
g_doom/*.h
g_heretic/*.h
g_hexen/*.h
g_raven/*.h
g_shared/*.h
g_strife/*.h
intermission/*.h
menu/*.h
oplsynth/*.h
r_data/*.h
resourcefiles/*.h
sfmt/*.h
sound/*.h
textures/*.h
thingdef/*.h
xlat/*.h
*.h
)
add_executable( zdoom WIN32
${HEADER_FILES}
autostart.cpp
${ASM_SOURCES}
${SYSTEM_SOURCES}
@ -818,6 +853,8 @@ add_executable( zdoom WIN32
oplsynth/music_opldumper_mididevice.cpp
oplsynth/music_opl_mididevice.cpp
oplsynth/opl_mus_player.cpp
oplsynth/dosbox/opl.cpp
oplsynth/OPL3.cpp
resourcefiles/ancientzip.cpp
resourcefiles/file_7z.cpp
resourcefiles/file_grp.cpp
@ -965,3 +1002,7 @@ if( CMAKE_COMPILER_IS_GNUCXX )
set_source_files_properties( x86.cpp PROPERTIES COMPILE_FLAGS "-msse2 -mmmx" )
endif( SSE_MATTERS )
endif( CMAKE_COMPILER_IS_GNUCXX )
if( MSVC )
set_target_properties(zdoom PROPERTIES LINK_FLAGS "/MANIFEST:NO")
endif( MSVC )

View File

@ -97,7 +97,7 @@ DEFINE_SPECIAL(FloorAndCeiling_LowerByValue, 95, 3, 3, 3)
DEFINE_SPECIAL(FloorAndCeiling_RaiseByValue, 96, 3, 3, 3)
DEFINE_SPECIAL(Ceiling_LowerAndCrushDist, 97, 3, 5, 5)
DEFINE_SPECIAL(Sector_SetTranslucent, 98, 3, 4, 4)
DEFINE_SPECIAL(Floor_RaiseAndCrushDoom, 99, 3, 4, 4)
DEFINE_SPECIAL(Scroll_Texture_Left, 100, -1, -1, 2)
DEFINE_SPECIAL(Scroll_Texture_Right, 101, -1, -1, 2)
DEFINE_SPECIAL(Scroll_Texture_Up, 102, -1, -1, 2)

View File

@ -416,6 +416,7 @@ enum EBounceFlags
// for them that are not present in ZDoom, so it is necessary to identify it properly.
BOUNCE_MBF = 1<<12, // This in itself is not a valid mode, but replaces MBF's MF_BOUNCE flag.
BOUNCE_AutoOffFloorOnly = 1<<13, // like BOUNCE_AutoOff, but only on floors
BOUNCE_UseBounceState = 1<<14, // Use Bounce[.*] states
BOUNCE_TypeMask = BOUNCE_Walls | BOUNCE_Floors | BOUNCE_Ceilings | BOUNCE_Actors | BOUNCE_AutoOff | BOUNCE_HereticType | BOUNCE_MBF,
@ -666,7 +667,7 @@ public:
virtual bool Massacre ();
// Transforms the actor into a finely-ground paste
bool Grind(bool items);
virtual bool Grind(bool items);
// Is the other actor on my team?
bool IsTeammate (AActor *other);
@ -976,6 +977,11 @@ public:
return GetClass()->FindState(2, names, exact);
}
FState *FindState(int numnames, FName *names, bool exact = false) const
{
return GetClass()->FindState(numnames, names, exact);
}
bool HasSpecialDeathStates () const;
};

View File

@ -40,6 +40,8 @@
#include "c_bind.h"
#include "farchive.h"
#include "r_renderer.h"
#include "r_sky.h"
#include "sbar.h"
#include "m_cheat.h"
#include "i_system.h"
@ -1080,7 +1082,7 @@ void AM_Stop ()
{
automapactive = false;
stopped = true;
BorderNeedRefresh = screen->GetPageCount ();
V_SetBorderNeedRefresh();
viewactive = true;
}
@ -1179,7 +1181,7 @@ void AM_ToggleMap ()
if (dmflags2 & DF2_NO_AUTOMAP)
return;
SB_state = screen->GetPageCount ();
ST_SetNeedRefresh();
if (!automapactive)
{
AM_Start ();
@ -1190,7 +1192,7 @@ void AM_ToggleMap ()
if (am_overlay==1 && viewactive)
{
viewactive = false;
SB_state = screen->GetPageCount ();
ST_SetNeedRefresh();
}
else
{
@ -1611,9 +1613,10 @@ void AM_drawSubsectors()
angle_t rotation;
sector_t tempsec;
int floorlight, ceilinglight;
fixed_t scalex, scaley;
double originx, originy;
FDynamicColormap *colormap;
mpoint_t originpt;
for (int i = 0; i < numsubsectors; ++i)
{
@ -1642,27 +1645,17 @@ void AM_drawSubsectors()
// For lighting and texture determination
sector_t *sec = Renderer->FakeFlat (subsectors[i].render_sector, &tempsec, &floorlight, &ceilinglight, false);
// Find texture origin.
mpoint_t originpt = { -sec->GetXOffset(sector_t::floor) >> FRACTOMAPBITS,
sec->GetYOffset(sector_t::floor) >> FRACTOMAPBITS };
originpt.x = -sec->GetXOffset(sector_t::floor) >> FRACTOMAPBITS;
originpt.y = sec->GetYOffset(sector_t::floor) >> FRACTOMAPBITS;
rotation = 0 - sec->GetAngle(sector_t::floor);
// Apply the floor's rotation to the texture origin.
if (rotation != 0)
{
AM_rotate(&originpt.x, &originpt.y, rotation);
}
// Apply the automap's rotation to the texture origin.
if (am_rotate == 1 || (am_rotate == 2 && viewactive))
{
rotation += ANG90 - players[consoleplayer].camera->angle;
AM_rotatePoint(&originpt.x, &originpt.y);
}
originx = f_x + ((originpt.x - m_x) * scale / float(1 << 24));
originy = f_y + (f_h - (originpt.y - m_y) * scale / float(1 << 24));
// Coloring for the polygon
colormap = sec->ColorMap;
FTextureID maptex = sec->GetTexture(sector_t::floor);
scalex = sec->GetXScale(sector_t::floor);
scaley = sec->GetYScale(sector_t::floor);
#ifdef _3DFLOORS
if (sec->e->XFloor.ffloors.Size())
@ -1698,6 +1691,7 @@ void AM_drawSubsectors()
F3DFloor *rover = sec->e->XFloor.ffloors[i];
if (!(rover->flags & FF_EXISTS)) continue;
if (rover->flags & FF_FOG) continue;
if (!(rover->flags & FF_RENDERPLANES)) continue;
if (rover->alpha == 0) continue;
double roverz = rover->top.plane->ZatPoint(secx, secy);
// Ignore 3D floors that are above or below the sector itself:
@ -1709,6 +1703,13 @@ void AM_drawSubsectors()
{
maptex = *(rover->top.texture);
floorplane = rover->top.plane;
sector_t *model = rover->top.model;
int selector = (rover->flags & FF_INVERTPLANES) ? sector_t::floor : sector_t::ceiling;
rotation = 0 - model->GetAngle(selector);
scalex = model->GetXScale(selector);
scaley = model->GetYScale(selector);
originpt.x = -model->GetXOffset(selector) >> FRACTOMAPBITS;
originpt.y = model->GetYOffset(selector) >> FRACTOMAPBITS;
break;
}
}
@ -1718,6 +1719,24 @@ void AM_drawSubsectors()
colormap = light->extra_colormap;
}
#endif
if (maptex == skyflatnum)
{
continue;
}
// Apply the floor's rotation to the texture origin.
if (rotation != 0)
{
AM_rotate(&originpt.x, &originpt.y, rotation);
}
// Apply the automap's rotation to the texture origin.
if (am_rotate == 1 || (am_rotate == 2 && viewactive))
{
rotation += ANG90 - players[consoleplayer].camera->angle;
AM_rotatePoint(&originpt.x, &originpt.y);
}
originx = f_x + ((originpt.x - m_x) * scale / float(1 << 24));
originy = f_y + (f_h - (originpt.y - m_y) * scale / float(1 << 24));
// If this subsector has not actually been seen yet (because you are cheating
// to see it on the map), tint and desaturate it.
@ -1740,8 +1759,8 @@ void AM_drawSubsectors()
screen->FillSimplePoly(TexMan(maptex),
&points[0], points.Size(),
originx, originy,
scale / (FIXED2FLOAT(sec->GetXScale(sector_t::floor)) * float(1 << MAPBITS)),
scale / (FIXED2FLOAT(sec->GetYScale(sector_t::floor)) * float(1 << MAPBITS)),
scale / (FIXED2DBL(scalex) * float(1 << MAPBITS)),
scale / (FIXED2DBL(scaley) * float(1 << MAPBITS)),
rotation,
colormap,
floorlight

View File

@ -51,15 +51,16 @@ void FCajunMaster::ClearPlayer (int i, bool keepTeam)
players[i].mo = NULL;
}
botinfo_t *bot = botinfo;
while (bot && stricmp (players[i].userinfo.netname, bot->name))
while (bot && stricmp (players[i].userinfo.GetName(), bot->name))
bot = bot->next;
if (bot)
{
bot->inuse = false;
bot->lastteam = keepTeam ? players[i].userinfo.team : TEAM_NONE;
bot->lastteam = keepTeam ? players[i].userinfo.GetTeam() : TEAM_NONE;
}
players[i].~player_t();
::new(&players[i]) player_t;
players[i].userinfo.Reset();
playeringame[i] = false;
}

View File

@ -140,7 +140,7 @@ void FCajunMaster::Main (int buf)
//Check if player should go observer. Or un observe
if (bot_observer && !observer && !netgame)
{
Printf ("%s is now observer\n", players[consoleplayer].userinfo.netname);
Printf ("%s is now observer\n", players[consoleplayer].userinfo.GetName());
observer = true;
players[consoleplayer].mo->UnlinkFromWorld ();
players[consoleplayer].mo->flags = MF_DROPOFF|MF_NOBLOCKMAP|MF_NOCLIP|MF_NOTDMATCH|MF_NOGRAVITY|MF_FRIENDLY;
@ -149,7 +149,7 @@ void FCajunMaster::Main (int buf)
}
else if (!bot_observer && observer && !netgame) //Go back
{
Printf ("%s returned to the fray\n", players[consoleplayer].userinfo.netname);
Printf ("%s returned to the fray\n", players[consoleplayer].userinfo.GetName());
observer = false;
players[consoleplayer].mo->UnlinkFromWorld ();
players[consoleplayer].mo->flags = MF_SOLID|MF_SHOOTABLE|MF_DROPOFF|MF_PICKUP|MF_NOTDMATCH|MF_FRIENDLY;
@ -218,7 +218,7 @@ void FCajunMaster::End ()
{
if (deathmatch)
{
getspawned.Push(players[i].userinfo.netname);
getspawned.Push(players[i].userinfo.GetName());
}
CleanBotstuff (&players[i]);
}
@ -353,7 +353,7 @@ void FCajunMaster::DoAddBot (int bnum, char *info)
if (!deathmatch && playerstarts[bnum].type == 0)
{
Printf ("%s tried to join, but there was no player %d start\n",
players[bnum].userinfo.netname, bnum+1);
players[bnum].userinfo.GetName(), bnum+1);
ClearPlayer (bnum, false); // Make the bot inactive again
if (botnum > 0)
{
@ -370,9 +370,9 @@ void FCajunMaster::DoAddBot (int bnum, char *info)
botingame[bnum] = true;
if (teamplay)
Printf ("%s joined the %s team\n", players[bnum].userinfo.netname,Teams[players[bnum].userinfo.team].GetName ());
Printf ("%s joined the %s team\n", players[bnum].userinfo.GetName(), Teams[players[bnum].userinfo.GetTeam()].GetName());
else
Printf ("%s joined the game\n", players[bnum].userinfo.netname);
Printf ("%s joined the game\n", players[bnum].userinfo.GetName());
G_DoReborn (bnum, true);
if (StatusBar != NULL)

View File

@ -231,7 +231,7 @@ bool AnnounceKill (AActor *killer, AActor *killee)
if (killer == NULL)
{ // The world killed the player
if (killee->player->userinfo.gender == GENDER_MALE)
if (killee->player->userinfo.GetGender() == GENDER_MALE)
{ // Only males have scrotums to separate
choice = &WorldKillSounds[rannum % 3];
}
@ -244,11 +244,11 @@ bool AnnounceKill (AActor *killer, AActor *killee)
else if (killer == killee)
{ // The player killed self
choice = &SuicideSounds[rannum & 3];
killerName = killer->player->userinfo.netname;
killerName = killer->player->userinfo.GetName();
}
else
{ // Another player did the killing
if (killee->player->userinfo.gender == GENDER_MALE)
if (killee->player->userinfo.GetGender() == GENDER_MALE)
{ // Only males can be castrated
choice = &KillSounds[rannum % countof(KillSounds)];
}
@ -256,7 +256,7 @@ bool AnnounceKill (AActor *killer, AActor *killee)
{
choice = &KillSounds[rannum % (countof(KillSounds) - 1)];
}
killerName = killer->player->userinfo.netname;
killerName = killer->player->userinfo.GetName();
// Blood only plays the announcement sound on the killer's
// computer. I think it sounds neater to also hear it on
@ -269,8 +269,8 @@ bool AnnounceKill (AActor *killer, AActor *killee)
{
char assembled[1024];
SexMessage (message, assembled, killee->player->userinfo.gender,
killee->player->userinfo.netname, killerName);
SexMessage (message, assembled, killee->player->userinfo.GetGender(),
killee->player->userinfo.GetName(), killerName);
Printf (PRINT_MEDIUM, "%s\n", assembled);
}
if (playSound)
@ -301,8 +301,8 @@ bool AnnounceTelefrag (AActor *killer, AActor *killee)
{
char assembled[1024];
SexMessage (message, assembled, killee->player->userinfo.gender,
killee->player->userinfo.netname, killer->player->userinfo.netname);
SexMessage (message, assembled, killee->player->userinfo.GetGender(),
killee->player->userinfo.GetName(), killer->player->userinfo.GetName());
Printf (PRINT_MEDIUM, "%s\n", assembled);
}
if (killee->CheckLocalView (consoleplayer) ||

View File

@ -327,7 +327,7 @@ CCMD (hxvisit)
CCMD (changemap)
{
if (who == NULL)
if (who == NULL || !usergame)
{
Printf ("Use the map command when not in a game.\n");
return;

View File

@ -1134,7 +1134,7 @@ void C_DrawConsole (bool hw2d)
(viewwindowx || viewwindowy) &&
viewactive)
{
BorderNeedRefresh = screen->GetPageCount ();
V_SetBorderNeedRefresh();
}
oldbottom = ConBottom;
@ -1224,8 +1224,8 @@ void C_DrawConsole (bool hw2d)
{
screen->Dim (PalEntry ((unsigned char)(player->BlendR*255), (unsigned char)(player->BlendG*255), (unsigned char)(player->BlendB*255)),
player->BlendA, 0, ConBottom, screen->GetWidth(), screen->GetHeight() - ConBottom);
SB_state = screen->GetPageCount ();
BorderNeedRefresh = screen->GetPageCount ();
ST_SetNeedRefresh();
V_SetBorderNeedRefresh();
}
}
}

View File

@ -132,10 +132,10 @@ FBaseCVar::~FBaseCVar ()
}
}
void FBaseCVar::ForceSet (UCVarValue value, ECVarType type)
void FBaseCVar::ForceSet (UCVarValue value, ECVarType type, bool nouserinfosend)
{
DoSet (value, type);
if (Flags & CVAR_USERINFO)
if ((Flags & CVAR_USERINFO) && !nouserinfosend && !(Flags & CVAR_IGNORE))
D_UserInfoChanged (this);
if (m_UseCallback)
Callback ();
@ -266,7 +266,7 @@ static GUID cGUID;
static char truestr[] = "true";
static char falsestr[] = "false";
char *FBaseCVar::ToString (UCVarValue value, ECVarType type)
const char *FBaseCVar::ToString (UCVarValue value, ECVarType type)
{
switch (type)
{
@ -849,9 +849,7 @@ UCVarValue FStringCVar::GetFavoriteRepDefault (ECVarType *type) const
void FStringCVar::SetGenericRepDefault (UCVarValue value, ECVarType type)
{
if (DefaultValue)
delete[] DefaultValue;
DefaultValue = ToString (value, type);
ReplaceString(&DefaultValue, ToString(value, type));
if (Flags & CVAR_ISDEFAULT)
{
SetGenericRep (value, type);
@ -1274,52 +1272,56 @@ static int STACK_ARGS sortcvars (const void *a, const void *b)
void FilterCompactCVars (TArray<FBaseCVar *> &cvars, DWORD filter)
{
FBaseCVar *cvar = CVars;
while (cvar)
// Accumulate all cvars that match the filter flags.
for (FBaseCVar *cvar = CVars; cvar != NULL; cvar = cvar->m_Next)
{
if (cvar->Flags & filter)
cvars.Push (cvar);
cvar = cvar->m_Next;
if ((cvar->Flags & filter) && !(cvar->Flags & CVAR_IGNORE))
cvars.Push(cvar);
}
if (cvars.Size () > 0)
// Now sort them, so they're in a deterministic order and not whatever
// order the linker put them in.
if (cvars.Size() > 0)
{
cvars.ShrinkToFit ();
qsort (&cvars[0], cvars.Size(), sizeof(FBaseCVar *), sortcvars);
qsort(&cvars[0], cvars.Size(), sizeof(FBaseCVar *), sortcvars);
}
}
void C_WriteCVars (BYTE **demo_p, DWORD filter, bool compact)
{
FBaseCVar *cvar = CVars;
BYTE *ptr = *demo_p;
FString dump = C_GetMassCVarString(filter, compact);
size_t dumplen = dump.Len() + 1; // include terminating \0
memcpy(*demo_p, dump.GetChars(), dumplen);
*demo_p += dumplen;
}
FString C_GetMassCVarString (DWORD filter, bool compact)
{
FBaseCVar *cvar;
FString dump;
if (compact)
{
TArray<FBaseCVar *> cvars;
ptr += sprintf ((char *)ptr, "\\\\%ux", filter);
FilterCompactCVars (cvars, filter);
dump.AppendFormat("\\\\%ux", filter);
FilterCompactCVars(cvars, filter);
while (cvars.Pop (cvar))
{
UCVarValue val = cvar->GetGenericRep (CVAR_String);
ptr += sprintf ((char *)ptr, "\\%s", val.String);
UCVarValue val = cvar->GetGenericRep(CVAR_String);
dump << '\\' << val.String;
}
}
else
{
cvar = CVars;
while (cvar)
for (cvar = CVars; cvar != NULL; cvar = cvar->m_Next)
{
if ((cvar->Flags & filter) && !(cvar->Flags & CVAR_NOSAVE))
if ((cvar->Flags & filter) && !(cvar->Flags & (CVAR_NOSAVE|CVAR_IGNORE)))
{
UCVarValue val = cvar->GetGenericRep (CVAR_String);
ptr += sprintf ((char *)ptr, "\\%s\\%s",
cvar->GetName (), val.String);
UCVarValue val = cvar->GetGenericRep(CVAR_String);
dump << '\\' << cvar->GetName() << '\\' << val.String;
}
cvar = cvar->m_Next;
}
}
*demo_p = ptr + 1;
return dump;
}
void C_ReadCVars (BYTE **demo_p)
@ -1390,58 +1392,42 @@ void C_ReadCVars (BYTE **demo_p)
*demo_p += strlen (*((char **)demo_p)) + 1;
}
static struct backup_s
struct FCVarBackup
{
char *name, *string;
} CVarBackups[MAX_DEMOCVARS];
static int numbackedup = 0;
FString Name, String;
};
static TArray<FCVarBackup> CVarBackups;
void C_BackupCVars (void)
{
struct backup_s *backup = CVarBackups;
FBaseCVar *cvar = CVars;
assert(CVarBackups.Size() == 0);
CVarBackups.Clear();
while (cvar)
FCVarBackup backup;
for (FBaseCVar *cvar = CVars; cvar != NULL; cvar = cvar->m_Next)
{
if ((cvar->Flags & (CVAR_SERVERINFO|CVAR_DEMOSAVE))
&& !(cvar->Flags & CVAR_LATCH))
if ((cvar->Flags & (CVAR_SERVERINFO|CVAR_DEMOSAVE)) && !(cvar->Flags & CVAR_LATCH))
{
if (backup == &CVarBackups[MAX_DEMOCVARS])
I_Error ("C_BackupDemoCVars: Too many cvars to save (%d)", MAX_DEMOCVARS);
backup->name = copystring (cvar->GetName());
backup->string = copystring (cvar->GetGenericRep (CVAR_String).String);
backup++;
backup.Name = cvar->GetName();
backup.String = cvar->GetGenericRep(CVAR_String).String;
CVarBackups.Push(backup);
}
cvar = cvar->m_Next;
}
numbackedup = int(backup - CVarBackups);
}
void C_RestoreCVars (void)
{
struct backup_s *backup = CVarBackups;
int i;
for (i = numbackedup; i; i--, backup++)
for (unsigned int i = 0; i < CVarBackups.Size(); ++i)
{
cvar_set (backup->name, backup->string);
cvar_set(CVarBackups[i].Name, CVarBackups[i].String);
}
C_ForgetCVars();
}
void C_ForgetCVars (void)
{
struct backup_s *backup = CVarBackups;
int i;
for (i = numbackedup; i; i--, backup++)
{
delete[] backup->name;
delete[] backup->string;
backup->name = backup->string = NULL;
}
numbackedup = 0;
CVarBackups.Clear();
}
FBaseCVar *FindCVar (const char *var_name, FBaseCVar **prev)
@ -1489,6 +1475,30 @@ FBaseCVar *FindCVarSub (const char *var_name, int namelen)
return var;
}
//===========================================================================
//
// C_CreateCVar
//
// Create a new cvar with the specified name and type. It should not already
// exist.
//
//===========================================================================
FBaseCVar *C_CreateCVar(const char *var_name, ECVarType var_type, DWORD flags)
{
assert(FindCVar(var_name, NULL) == NULL);
flags |= CVAR_AUTO;
switch (var_type)
{
case CVAR_Bool: return new FBoolCVar(var_name, 0, flags);
case CVAR_Int: return new FIntCVar(var_name, 0, flags);
case CVAR_Float: return new FFloatCVar(var_name, 0, flags);
case CVAR_String: return new FStringCVar(var_name, NULL, flags);
case CVAR_Color: return new FColorCVar(var_name, 0, flags);
default: return NULL;
}
}
void UnlatchCVars (void)
{
FLatchedValue var;
@ -1522,33 +1532,14 @@ void C_SetCVarsToDefaults (void)
}
}
void C_ArchiveCVars (FConfigFile *f, int type)
void C_ArchiveCVars (FConfigFile *f, uint32 filter)
{
// type 0: Game-specific cvars
// type 1: Global cvars
// type 2: Unknown cvars
// type 3: Unknown global cvars
// type 4: User info cvars
// type 5: Server info cvars
static const DWORD filters[6] =
{
CVAR_ARCHIVE,
CVAR_ARCHIVE|CVAR_GLOBALCONFIG,
CVAR_ARCHIVE|CVAR_AUTO,
CVAR_ARCHIVE|CVAR_GLOBALCONFIG|CVAR_AUTO,
CVAR_ARCHIVE|CVAR_USERINFO,
CVAR_ARCHIVE|CVAR_SERVERINFO
};
FBaseCVar *cvar = CVars;
DWORD filter;
filter = filters[type];
while (cvar)
{
if ((cvar->Flags &
(CVAR_GLOBALCONFIG|CVAR_ARCHIVE|CVAR_AUTO|CVAR_USERINFO|CVAR_SERVERINFO|CVAR_NOSAVE))
(CVAR_GLOBALCONFIG|CVAR_ARCHIVE|CVAR_MOD|CVAR_AUTO|CVAR_USERINFO|CVAR_SERVERINFO|CVAR_NOSAVE))
== filter)
{
UCVarValue val;
@ -1679,14 +1670,16 @@ void FBaseCVar::ListVars (const char *filter, bool plain)
else
{
++count;
Printf ("%c%c%c %s : :%s\n",
Printf ("%c%c%c%c%c %s = %s\n",
flags & CVAR_ARCHIVE ? 'A' : ' ',
flags & CVAR_USERINFO ? 'U' :
flags & CVAR_SERVERINFO ? 'S' :
flags & CVAR_AUTO ? 'C' : ' ',
flags & CVAR_SERVERINFO ? 'S' :
flags & CVAR_AUTO ? 'C' : ' ',
flags & CVAR_NOSET ? '-' :
flags & CVAR_LATCH ? 'L' :
flags & CVAR_UNSETTABLE ? '*' : ' ',
flags & CVAR_LATCH ? 'L' :
flags & CVAR_UNSETTABLE ? '*' : ' ',
flags & CVAR_MOD ? 'M' : ' ',
flags & CVAR_IGNORE ? 'X' : ' ',
var->GetName(),
var->GetGenericRep (CVAR_String).String);
}

View File

@ -61,6 +61,8 @@ enum
CVAR_GLOBALCONFIG = 1024, // cvar is saved to global config section
CVAR_VIDEOCONFIG = 2048, // cvar is saved to video config section (not implemented)
CVAR_NOSAVE = 4096, // when used with CVAR_SERVERINFO, do not save var to savegame
CVAR_MOD = 8192, // cvar was defined by a mod
CVAR_IGNORE = 16384,// do not send cvar across the network/inaccesible from ACS (dummy mod cvar)
};
union UCVarValue
@ -68,7 +70,7 @@ union UCVarValue
bool Bool;
int Int;
float Float;
char *String;
const char *String;
const GUID *pGUID;
};
@ -96,9 +98,10 @@ public:
inline const char *GetName () const { return Name; }
inline uint32 GetFlags () const { return Flags; }
inline FBaseCVar *GetNext() const { return m_Next; }
void CmdSet (const char *newval);
void ForceSet (UCVarValue value, ECVarType type);
void ForceSet (UCVarValue value, ECVarType type, bool nouserinfosend=false);
void SetGenericRep (UCVarValue value, ECVarType type);
void ResetToDefault ();
void SetArchiveBit () { Flags |= CVAR_ARCHIVE; }
@ -129,7 +132,7 @@ protected:
static bool ToBool (UCVarValue value, ECVarType type);
static int ToInt (UCVarValue value, ECVarType type);
static float ToFloat (UCVarValue value, ECVarType type);
static char *ToString (UCVarValue value, ECVarType type);
static const char *ToString (UCVarValue value, ECVarType type);
static const GUID *ToGUID (UCVarValue value, ECVarType type);
static UCVarValue FromBool (bool value, ECVarType type);
static UCVarValue FromInt (int value, ECVarType type);
@ -150,18 +153,22 @@ private:
static bool m_UseCallback;
static bool m_DoNoSet;
friend void C_WriteCVars (BYTE **demo_p, uint32 filter, bool compact);
friend FString C_GetMassCVarString (uint32 filter, bool compact);
friend void C_ReadCVars (BYTE **demo_p);
friend void C_BackupCVars (void);
friend FBaseCVar *FindCVar (const char *var_name, FBaseCVar **prev);
friend FBaseCVar *FindCVarSub (const char *var_name, int namelen);
friend void UnlatchCVars (void);
friend void C_ArchiveCVars (FConfigFile *f, int type);
friend void C_ArchiveCVars (FConfigFile *f, uint32 filter);
friend void C_SetCVarsToDefaults (void);
friend void FilterCompactCVars (TArray<FBaseCVar *> &cvars, uint32 filter);
friend void C_DeinitConsole();
};
// Returns a string with all cvars whose flags match filter. In compact mode,
// the cvar names are omitted to save space.
FString C_GetMassCVarString (uint32 filter, bool compact=false);
// Writes all cvars that could effect demo sync to *demo_p. These are
// cvars that have either CVAR_SERVERINFO or CVAR_DEMOSAVE set.
void C_WriteCVars (BYTE **demo_p, uint32 filter, bool compact=false);
@ -177,11 +184,14 @@ void C_BackupCVars (void);
FBaseCVar *FindCVar (const char *var_name, FBaseCVar **prev);
FBaseCVar *FindCVarSub (const char *var_name, int namelen);
// Create a new cvar with the specified name and type
FBaseCVar *C_CreateCVar(const char *var_name, ECVarType var_type, DWORD flags);
// Called from G_InitNew()
void UnlatchCVars (void);
// archive cvars to FILE f
void C_ArchiveCVars (FConfigFile *f, int type);
void C_ArchiveCVars (FConfigFile *f, uint32 filter);
// initialize cvars to default values after they are created
void C_SetCVarsToDefaults (void);
@ -404,10 +414,6 @@ inline FBaseCVar *cvar_forceset (const char *var_name, const BYTE *value) { retu
// Maximum number of cvars that can be saved across a demo. If you need
// to save more, bump this up.
#define MAX_DEMOCVARS 32
// Restore demo cvars. Called after demo playback to restore all cvars
// that might possibly have been changed during the course of demo playback.
void C_RestoreCVars (void);
@ -425,5 +431,6 @@ void C_ForgetCVars (void);
#define EXTERN_CVAR(type,name) extern F##type##CVar name;
extern FBaseCVar *CVars;
#endif //__C_CVARS_H__

View File

@ -1019,32 +1019,7 @@ FConsoleAlias::~FConsoleAlias ()
m_Command[1] = m_Command[0] = FString();
}
FString BuildString (int argc, char **argv)
{
if (argc == 1)
{
return *argv;
}
else
{
FString buf;
int arg;
for (arg = 0; arg < argc; arg++)
{
if (strchr (argv[arg], ' '))
{
buf.AppendFormat ("\"%s\" ", argv[arg]);
}
else
{
buf.AppendFormat ("%s ", argv[arg]);
}
}
return buf;
}
}
// Given an argument vector, reconstitute the command line it could have been produced from.
FString BuildString (int argc, FString *argv)
{
if (argc == 1)
@ -1058,8 +1033,23 @@ FString BuildString (int argc, FString *argv)
for (arg = 0; arg < argc; arg++)
{
if (strchr (argv[arg], ' '))
{
if (strchr(argv[arg], '"'))
{ // If it contains one or more quotes, we need to escape them.
buf << '"';
long substr_start = 0, quotepos;
while ((quotepos = argv[arg].IndexOf('"', substr_start)) >= 0)
{
if (substr_start < quotepos)
{
buf << argv[arg].Mid(substr_start, quotepos - substr_start);
}
buf << "\\\"";
substr_start = quotepos + 1;
}
buf << argv[arg].Mid(substr_start) << "\" ";
}
else if (strchr(argv[arg], ' '))
{ // If it contains a space, it needs to be quoted.
buf << '"' << argv[arg] << "\" ";
}
else

View File

@ -61,7 +61,6 @@ void C_SetAlias (const char *name, const char *cmd);
void C_ClearAliases ();
// build a single string out of multiple strings
FString BuildString (int argc, char **argv);
FString BuildString (int argc, FString *argv);
// Class that can parse command lines

View File

@ -537,6 +537,7 @@ void CreatePath(const char *fn)
}
if (mkdir(copy, 0755) == -1)
{ // failed
free(copy);
return;
}
exists: if (p != NULL)
@ -755,7 +756,7 @@ FString strbin1 (const char *start)
//
//==========================================================================
void CleanseString(char *str)
char *CleanseString(char *str)
{
char *escape = strrchr(str, TEXTCOLOR_ESCAPE);
if (escape != NULL)
@ -773,6 +774,7 @@ void CleanseString(char *str)
}
}
}
return str;
}
//==========================================================================

View File

@ -47,7 +47,7 @@ const char *myasctime ();
int strbin (char *str);
FString strbin1 (const char *start);
void CleanseString (char *str);
char *CleanseString (char *str);
void CreatePath(const char * fn);

View File

@ -50,6 +50,7 @@
#include "g_level.h"
#include "p_lnspec.h"
#include "r_state.h"
#include "w_wad.h"
// MACROS ------------------------------------------------------------------
@ -58,7 +59,7 @@
struct FCompatOption
{
const char *Name;
int CompatFlags;
DWORD CompatFlags;
int WhichSlot;
};
@ -76,7 +77,9 @@ enum
CP_SETFLAGS,
CP_SETSPECIAL,
CP_CLEARSPECIAL,
CP_SETACTIVATION
CP_SETACTIVATION,
CP_SECTORFLOOROFFSET,
CP_SETWALLYSCALE,
};
// EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
@ -99,6 +102,7 @@ static FCompatOption Options[] =
{ "resetplayerspeed", BCOMPATF_RESETPLAYERSPEED, SLOT_BCOMPAT },
{ "vileghosts", BCOMPATF_VILEGHOSTS, SLOT_BCOMPAT },
{ "ignoreteleporttags", BCOMPATF_BADTELEPORTERS, SLOT_BCOMPAT },
{ "rebuildnodes", BCOMPATF_REBUILDNODES, SLOT_BCOMPAT },
// list copied from g_mapinfo.cpp
{ "shorttex", COMPATF_SHORTTEX, SLOT_COMPAT },
@ -136,6 +140,16 @@ static FCompatOption Options[] =
{ NULL, 0, 0 }
};
static const char *const LineSides[] =
{
"Front", "Back", NULL
};
static const char *const WallTiers[] =
{
"Top", "Mid", "Bot", NULL
};
static TArray<int> CompatParams;
static int ii_compatparams;
@ -258,6 +272,28 @@ void ParseCompatibility()
sc.MustGetNumber();
CompatParams.Push(sc.Number);
}
else if (sc.Compare("sectorflooroffset"))
{
if (flags.ExtCommandIndex == ~0u) flags.ExtCommandIndex = CompatParams.Size();
CompatParams.Push(CP_SECTORFLOOROFFSET);
sc.MustGetNumber();
CompatParams.Push(sc.Number);
sc.MustGetFloat();
CompatParams.Push(FLOAT2FIXED(sc.Float));
}
else if (sc.Compare("setwallyscale"))
{
if (flags.ExtCommandIndex == ~0u) flags.ExtCommandIndex = CompatParams.Size();
CompatParams.Push(CP_SETWALLYSCALE);
sc.MustGetNumber();
CompatParams.Push(sc.Number);
sc.MustGetString();
CompatParams.Push(sc.MustMatchString(LineSides));
sc.MustGetString();
CompatParams.Push(sc.MustMatchString(WallTiers));
sc.MustGetFloat();
CompatParams.Push(FLOAT2FIXED(sc.Float));
}
else
{
sc.UnGet();
@ -437,6 +473,30 @@ void SetCompatibilityParams()
i += 3;
break;
}
case CP_SECTORFLOOROFFSET:
{
if (CompatParams[i+1] < numsectors)
{
sector_t *sec = &sectors[CompatParams[i+1]];
sec->floorplane.ChangeHeight(CompatParams[i+2]);
sec->ChangePlaneTexZ(sector_t::floor, CompatParams[i+2]);
}
i += 3;
break;
}
case CP_SETWALLYSCALE:
{
if (CompatParams[i+1] < numlines)
{
side_t *side = lines[CompatParams[i+1]].sidedef[CompatParams[i+2]];
if (side != NULL)
{
side->SetTextureYScale(CompatParams[i+3], CompatParams[i+4]);
}
}
i += 5;
break;
}
}
}
}

View File

@ -38,9 +38,12 @@
#include "doomtype.h"
#include "configfile.h"
#include "m_random.h"
#define READBUFFERSIZE 256
static FRandom pr_endtag;
//====================================================================
//
// FConfigFile Constructor
@ -679,13 +682,68 @@ bool FConfigFile::ReadConfig (void *file)
whiteprobe++;
}
*(whiteprobe - 1) = 0;
NewConfigEntry (section, start, whiteprobe);
// Check for multi-line value
if (whiteprobe[0] == '<' && whiteprobe[1] == '<' && whiteprobe[2] == '<' && whiteprobe[3] != '\0')
{
ReadMultiLineValue (file, section, start, whiteprobe + 3);
}
else
{
NewConfigEntry (section, start, whiteprobe);
}
}
}
}
return true;
}
//====================================================================
//
// FConfigFile :: ReadMultiLineValue
//
// Reads a multi-line value, with format as follows:
//
// key=<<<ENDTAG
// ... blah blah blah ...
// >>>ENDTAG
//
// The final ENDTAG must be on a line all by itself.
//
//====================================================================
FConfigFile::FConfigEntry *FConfigFile::ReadMultiLineValue(void *file, FConfigSection *section, const char *key, const char *endtag)
{
char readbuf[READBUFFERSIZE];
FString value;
size_t endlen = strlen(endtag);
// Keep on reading lines until we reach a line that matches >>>endtag
while (ReadLine(readbuf, READBUFFERSIZE, file) != NULL)
{
// Does the start of this line match the endtag?
if (readbuf[0] == '>' && readbuf[1] == '>' && readbuf[2] == '>' &&
strncmp(readbuf + 3, endtag, endlen) == 0)
{ // Is there nothing but line break characters after the match?
size_t i;
for (i = endlen + 3; readbuf[i] != '\0'; ++i)
{
if (readbuf[i] != '\n' && readbuf[i] != '\r')
{ // Not a line break character
break;
}
}
if (readbuf[i] == '\0')
{ // We're done; strip the previous line's line breaks, since it's not part of the value.
value.StripRight("\n\r");
}
break;
}
// Append this line to the value.
value << readbuf;
}
return NewConfigEntry(section, key, value);
}
//====================================================================
//
// FConfigFile :: ReadLine
@ -732,7 +790,16 @@ bool FConfigFile::WriteConfigFile () const
fprintf (file, "[%s]\n", section->Name);
while (entry != NULL)
{
fprintf (file, "%s=%s\n", entry->Key, entry->Value);
if (strpbrk(entry->Value, "\r\n") == NULL)
{ // Single-line value
fprintf (file, "%s=%s\n", entry->Key, entry->Value);
}
else
{ // Multi-line value
const char *endtag = GenerateEndTag(entry->Value);
fprintf (file, "%s=<<<%s\n%s\n>>>%s\n", entry->Key,
endtag, entry->Value, endtag);
}
entry = entry->Next;
}
section = section->Next;
@ -742,6 +809,44 @@ bool FConfigFile::WriteConfigFile () const
return true;
}
//====================================================================
//
// FConfigFile :: GenerateEndTag
//
// Generates a terminator sequence for multi-line values that does
// not appear anywhere in the value.
//
//====================================================================
const char *FConfigFile::GenerateEndTag(const char *value)
{
static const char Base64Table[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789._";
static char EndTag[25] = "EOV-";
// Try different 20-character sequences until we find one that
// isn't in the value. We create the sequences by generating two
// 64-bit random numbers and Base64 encoding the first 15 bytes
// from them.
union { QWORD rand_num[2]; BYTE rand_bytes[16]; };
do
{
rand_num[0] = pr_endtag.GenRand64();
rand_num[1] = pr_endtag.GenRand64();
for (int i = 0; i < 5; ++i)
{
DWORD three_bytes = (rand_bytes[i*3] << 16) | (rand_bytes[i*3+1] << 8) | (rand_bytes[i*3+2]);
EndTag[4+i*4 ] = Base64Table[rand_bytes[i*3] >> 2];
EndTag[4+i*4+1] = Base64Table[((rand_bytes[i*3] & 3) << 4) | (rand_bytes[i*3+1] >> 4)];
EndTag[4+i*4+2] = Base64Table[((rand_bytes[i*3+1] & 15) << 2) | (rand_bytes[i*3+2] >> 6)];
EndTag[4+i*4+3] = Base64Table[rand_bytes[i*3+2] & 63];
}
}
while (strstr(value, EndTag) != NULL);
return EndTag;
}
//====================================================================
//
// FConfigFile :: WriteCommentHeader

View File

@ -78,6 +78,7 @@ protected:
virtual char *ReadLine (char *string, int n, void *file) const;
bool ReadConfig (void *file);
static const char *GenerateEndTag(const char *value);
bool OkayToWrite;
bool FileExisted;
@ -110,6 +111,7 @@ private:
FConfigEntry *FindEntry (FConfigSection *section, const char *key) const;
FConfigSection *NewConfigSection (const char *name);
FConfigEntry *NewConfigEntry (FConfigSection *section, const char *key, const char *value);
FConfigEntry *ReadMultiLineValue (void *file, FConfigSection *section, const char *key, const char *terminator);
void SetSectionNote (FConfigSection *section, const char *note);
public:

View File

@ -441,7 +441,8 @@ int FIWadManager::IdentifyVersion (TArray<FString> &wadfiles, const char *iwad,
"heretic shadow of the serpent riders/base",
"hexen/base",
"hexen deathkings of the dark citadel/base",
"ultimate doom/base"
"ultimate doom/base",
"DOOM 3 BFG Edition/base/wads"
};
steam_path += "/SteamApps/common/";
for (i = 0; i < countof(steam_dirs); ++i)

View File

@ -158,7 +158,6 @@ EXTERN_CVAR (Bool, sv_unlimited_pickup)
extern int testingmode;
extern bool setmodeneeded;
extern bool netdemo;
extern int NewWidth, NewHeight, NewBits, DisplayBits;
EXTERN_CVAR (Bool, st_scale)
extern bool gameisdead;
@ -698,21 +697,21 @@ void D_Display ()
if (screen->Lock (false))
{
SB_state = screen->GetPageCount ();
BorderNeedRefresh = screen->GetPageCount ();
ST_SetNeedRefresh();
V_SetBorderNeedRefresh();
}
// [RH] Allow temporarily disabling wipes
if (NoWipe)
{
BorderNeedRefresh = screen->GetPageCount ();
V_SetBorderNeedRefresh();
NoWipe--;
wipe = false;
wipegamestate = gamestate;
}
else if (gamestate != wipegamestate && gamestate != GS_FULLCONSOLE && gamestate != GS_TITLELEVEL)
{ // save the current screen if about to wipe
BorderNeedRefresh = screen->GetPageCount ();
V_SetBorderNeedRefresh();
switch (wipegamestate)
{
default:
@ -775,15 +774,14 @@ void D_Display ()
}
screen->SetBlendingRect(viewwindowx, viewwindowy,
viewwindowx + viewwidth, viewwindowy + viewheight);
P_CheckPlayerSprites();
P_PredictPlayer(&players[consoleplayer]);
Renderer->RenderView(&players[consoleplayer]);
P_UnPredictPlayer();
if ((hw2d = screen->Begin2D(viewactive)))
{
// Redraw everything every frame when using 2D accel
SB_state = screen->GetPageCount();
BorderNeedRefresh = screen->GetPageCount();
ST_SetNeedRefresh();
V_SetBorderNeedRefresh();
}
Renderer->DrawRemainingPlayerSprites();
screen->DrawBlendingRect();
@ -804,9 +802,10 @@ void D_Display ()
if (hud_althud && viewheight == SCREENHEIGHT && screenblocks > 10)
{
StatusBar->DrawBottomStuff (HUD_None);
StatusBar->DrawBottomStuff (HUD_AltHud);
if (DrawFSHUD || automapactive) DrawHUD();
StatusBar->DrawTopStuff (HUD_None);
StatusBar->Draw (HUD_AltHud);
StatusBar->DrawTopStuff (HUD_AltHud);
}
else
if (viewheight == SCREENHEIGHT && viewactive && screenblocks > 10)
@ -1279,7 +1278,7 @@ void D_DoAdvanceDemo (void)
Advisory = NULL;
if (!M_DemoNoPlay)
{
BorderNeedRefresh = screen->GetPageCount ();
V_SetBorderNeedRefresh();
democount++;
mysnprintf (demoname + 4, countof(demoname) - 4, "%d", democount);
if (Wads.CheckNumForName (demoname) < 0)
@ -1359,6 +1358,136 @@ CCMD (endgame)
}
}
//==========================================================================
//
// ParseCVarInfo
//
//==========================================================================
void ParseCVarInfo()
{
int lump, lastlump = 0;
bool addedcvars = false;
while ((lump = Wads.FindLump("CVARINFO", &lastlump)) != -1)
{
FScanner sc(lump);
sc.SetCMode(true);
while (sc.GetToken())
{
FString cvarname;
char *cvardefault = NULL;
ECVarType cvartype = CVAR_Dummy;
int cvarflags = CVAR_MOD|CVAR_ARCHIVE;
FBaseCVar *cvar;
// Check for flag tokens.
while (sc.TokenType == TK_Identifier)
{
if (stricmp(sc.String, "server") == 0)
{
cvarflags |= CVAR_SERVERINFO;
}
else if (stricmp(sc.String, "user") == 0)
{
cvarflags |= CVAR_USERINFO;
}
else if (stricmp(sc.String, "noarchive") == 0)
{
cvarflags &= ~CVAR_ARCHIVE;
}
else
{
sc.ScriptError("Unknown cvar attribute '%s'", sc.String);
}
sc.MustGetAnyToken();
}
// Do some sanity checks.
if ((cvarflags & (CVAR_SERVERINFO|CVAR_USERINFO)) == 0 ||
(cvarflags & (CVAR_SERVERINFO|CVAR_USERINFO)) == (CVAR_SERVERINFO|CVAR_USERINFO))
{
sc.ScriptError("One of 'server' or 'user' must be specified");
}
// The next token must be the cvar type.
if (sc.TokenType == TK_Bool)
{
cvartype = CVAR_Bool;
}
else if (sc.TokenType == TK_Int)
{
cvartype = CVAR_Int;
}
else if (sc.TokenType == TK_Float)
{
cvartype = CVAR_Float;
}
else if (sc.TokenType == TK_Color)
{
cvartype = CVAR_Color;
}
else if (sc.TokenType == TK_String)
{
cvartype = CVAR_String;
}
else
{
sc.ScriptError("Bad cvar type '%s'", sc.String);
}
// The next token must be the cvar name.
sc.MustGetToken(TK_Identifier);
if (FindCVar(sc.String, NULL) != NULL)
{
sc.ScriptError("cvar '%s' already exists", sc.String);
}
cvarname = sc.String;
// A default value is optional and signalled by a '=' token.
if (sc.CheckToken('='))
{
switch (cvartype)
{
case CVAR_Bool:
if (!sc.CheckToken(TK_True) && !sc.CheckToken(TK_False))
{
sc.ScriptError("Expected true or false");
}
cvardefault = sc.String;
break;
case CVAR_Int:
sc.MustGetNumber();
cvardefault = sc.String;
break;
case CVAR_Float:
sc.MustGetFloat();
cvardefault = sc.String;
break;
default:
sc.MustGetString();
cvardefault = sc.String;
break;
}
}
// Now create the cvar.
cvar = C_CreateCVar(cvarname, cvartype, cvarflags);
if (cvardefault != NULL)
{
UCVarValue val;
val.String = cvardefault;
cvar->SetGenericRepDefault(val, CVAR_String);
}
// To be like C and ACS, require a semicolon after everything.
sc.MustGetToken(';');
addedcvars = true;
}
}
// Only load mod cvars from the config if we defined some, so we don't
// clutter up the cvar space when not playing mods with custom cvars.
if (addedcvars)
{
GameConfig->DoModSetup (gameinfo.ConfigName);
}
}
//==========================================================================
//
// D_AddFile
@ -2167,7 +2296,10 @@ void D_DoomMain (void)
allwads.Clear();
allwads.ShrinkToFit();
SetMapxxFlag();
// Now that wads are loaded, define mod-specific cvars.
ParseCVarInfo();
// [RH] Initialize localizable strings.
GStrings.LoadStrings (false);

View File

@ -108,6 +108,8 @@ int resendcount[MAXNETNODES];
unsigned int lastrecvtime[MAXPLAYERS]; // [RH] Used for pings
unsigned int currrecvtime[MAXPLAYERS];
unsigned int lastglobalrecvtime; // Identify the last time a packet was recieved.
bool hadlate;
int nodeforplayer[MAXPLAYERS];
int playerfornode[MAXNETNODES];
@ -311,6 +313,8 @@ void Net_ClearBuffers ()
oldentertics = entertic;
gametic = 0;
maketic = 0;
lastglobalrecvtime = 0;
}
//
@ -596,12 +600,12 @@ void PlayerIsGone (int netnode, int netconsole)
if (deathmatch)
{
Printf ("%s left the game with %d frags\n",
players[netconsole].userinfo.netname,
players[netconsole].fragcount);
players[netconsole].userinfo.GetName(),
players[netconsole].fragcount);
}
else
{
Printf ("%s left the game\n", players[netconsole].userinfo.netname);
Printf ("%s left the game\n", players[netconsole].userinfo.GetName());
}
// [RH] Revert each player to their own view if spying through the player who left
@ -646,7 +650,7 @@ void PlayerIsGone (int netnode, int netconsole)
{
Net_Arbitrator = i;
players[i].settings_controller = true;
Printf ("%s is the new arbitrator\n", players[i].userinfo.netname);
Printf ("%s is the new arbitrator\n", players[i].userinfo.GetName());
break;
}
}
@ -700,6 +704,8 @@ void GetPackets (void)
}
continue; // extra setup packet
}
lastglobalrecvtime = I_GetTime (false); //Update the last time a packet was recieved
netnode = doomcom.remotenode;
netconsole = playerfornode[netnode] & ~PL_DRONE;
@ -1361,7 +1367,7 @@ bool DoArbitrate (void *userdata)
data->playersdetected[0] |= 1 << netbuffer[1];
StartScreen->NetMessage ("Found %s (node %d, player %d)",
players[netbuffer[1]].userinfo.netname,
players[netbuffer[1]].userinfo.GetName(),
node, netbuffer[1]+1);
}
}
@ -1820,6 +1826,33 @@ void TryRunTics (void)
if (lowtic < gametic)
I_Error ("TryRunTics: lowtic < gametic");
// [Ed850] Check to see the last time a packet was recieved.
// If it's longer then 3 seconds, a node has likely stalled. Check which one and re-request its last packet.
if(I_GetTime(false) - lastglobalrecvtime >= TICRATE*3)
{
int latenode = 0; // Node 0 is the local player, and should always be the highest
lastglobalrecvtime = I_GetTime(false); //Bump the count
if(NetMode == NET_PeerToPeer || consoleplayer == Net_Arbitrator)
{
for (i = 0; i < doomcom.numnodes; i++)
if (nodeingame[i] && nettics[i] < nettics[latenode])
latenode = i;
}
else if (nodeingame[nodeforplayer[Net_Arbitrator]] &&
nettics[nodeforplayer[Net_Arbitrator]] < nettics[0])
{ // Likely a packet server game. Only check the packet host.
latenode = Net_Arbitrator;
}
if (debugfile)
fprintf (debugfile, "lost tics from %i (%i to %i)\n",
latenode, nettics[latenode], gametic);
if(latenode != 0) // Send resend request to late node (if not yourself... somehow). Also mark the node as waiting to display it in the hud.
remoteresend[latenode] = players[playerfornode[latenode]].waiting = hadlate = true;
}
// don't stay in here forever -- give the menu a chance to work
if (I_GetTime (false) - entertic >= TICRATE/3)
{
@ -1829,6 +1862,13 @@ void TryRunTics (void)
}
}
if (hadlate)
{
hadlate = false;
for (i = 0; i < MAXPLAYERS; i++)
players[i].waiting = false;
}
// run the count tics
if (counts > 0)
{
@ -1968,12 +2008,12 @@ void Net_DoCommand (int type, BYTE **stream, int player)
{
case DEM_SAY:
{
const char *name = players[player].userinfo.netname;
const char *name = players[player].userinfo.GetName();
BYTE who = ReadByte (stream);
s = ReadString (stream);
CleanseString (s);
if (((who & 1) == 0) || players[player].userinfo.team == TEAM_NONE)
if (((who & 1) == 0) || players[player].userinfo.GetTeam() == TEAM_NONE)
{ // Said to everyone
if (who & 2)
{
@ -1985,7 +2025,7 @@ void Net_DoCommand (int type, BYTE **stream, int player)
}
S_Sound (CHAN_VOICE | CHAN_UI, gameinfo.chatSound, 1, ATTN_NONE);
}
else if (players[player].userinfo.team == players[consoleplayer].userinfo.team)
else if (players[player].userinfo.GetTeam() == players[consoleplayer].userinfo.GetTeam())
{ // Said only to members of the player's team
if (who & 2)
{
@ -2245,7 +2285,7 @@ void Net_DoCommand (int type, BYTE **stream, int player)
paused = player + 1;
S_PauseSound (false, false);
}
BorderNeedRefresh = screen->GetPageCount ();
V_SetBorderNeedRefresh();
}
break;
@ -2392,7 +2432,7 @@ void Net_DoCommand (int type, BYTE **stream, int player)
players[playernum].settings_controller = true;
if (consoleplayer == playernum || consoleplayer == Net_Arbitrator)
Printf ("%s has been added to the controller list.\n", players[playernum].userinfo.netname);
Printf ("%s has been added to the controller list.\n", players[playernum].userinfo.GetName());
}
break;
@ -2402,7 +2442,7 @@ void Net_DoCommand (int type, BYTE **stream, int player)
players[playernum].settings_controller = false;
if (consoleplayer == playernum || consoleplayer == Net_Arbitrator)
Printf ("%s has been removed from the controller list.\n", players[playernum].userinfo.netname);
Printf ("%s has been removed from the controller list.\n", players[playernum].userinfo.GetName());
}
break;
@ -2653,7 +2693,7 @@ CCMD (pings)
for (i = 0; i < MAXPLAYERS; i++)
if (playeringame[i])
Printf ("% 4d %s\n", currrecvtime[i] - lastrecvtime[i],
players[i].userinfo.netname);
players[i].userinfo.GetName());
}
//==========================================================================
@ -2675,13 +2715,13 @@ static void Network_Controller (int playernum, bool add)
if (players[playernum].settings_controller && add)
{
Printf ("%s is already on the setting controller list.\n", players[playernum].userinfo.netname);
Printf ("%s is already on the setting controller list.\n", players[playernum].userinfo.GetName());
return;
}
if (!players[playernum].settings_controller && !add)
{
Printf ("%s is not on the setting controller list.\n", players[playernum].userinfo.netname);
Printf ("%s is not on the setting controller list.\n", players[playernum].userinfo.GetName());
return;
}
@ -2780,7 +2820,7 @@ CCMD (net_listcontrollers)
if (players[i].settings_controller)
{
Printf ("- %s\n", players[i].userinfo.netname);
Printf ("- %s\n", players[i].userinfo.GetName());
}
}
}

View File

@ -92,22 +92,6 @@ enum
const char *GenderNames[3] = { "male", "female", "other" };
static const char *UserInfoStrings[] =
{
"name",
"autoaim",
"color",
"skin",
"team",
"gender",
"neverswitchonpickup",
"movebob",
"stillbob",
"playerclass",
"colorset",
NULL
};
// Replace \ with %/ and % with %%
FString D_EscapeUserInfo (const char *str)
{
@ -192,11 +176,12 @@ void D_GetPlayerColor (int player, float *h, float *s, float *v, FPlayerColorSet
{
userinfo_t *info = &players[player].userinfo;
FPlayerColorSet *colorset = NULL;
int color;
uint32 color;
int team;
if (players[player].mo != NULL)
{
colorset = players[player].mo->GetClass()->GetColorSet(info->colorset);
colorset = players[player].mo->GetClass()->GetColorSet(info->GetColorSet());
}
if (colorset != NULL)
{
@ -204,25 +189,28 @@ void D_GetPlayerColor (int player, float *h, float *s, float *v, FPlayerColorSet
}
else
{
color = info->color;
color = info->GetColor();
}
RGBtoHSV (RPART(color)/255.f, GPART(color)/255.f, BPART(color)/255.f,
h, s, v);
if (teamplay && TeamLibrary.IsValidTeam(info->team) && !Teams[info->team].GetAllowCustomPlayerColor ())
if (teamplay && TeamLibrary.IsValidTeam((team = info->GetTeam())) && !Teams[team].GetAllowCustomPlayerColor())
{
// In team play, force the player to use the team's hue
// and adjust the saturation and value so that the team
// hue is visible in the final color.
float ts, tv;
int tcolor = Teams[info->team].GetPlayerColor ();
int tcolor = Teams[team].GetPlayerColor ();
RGBtoHSV (RPART(tcolor)/255.f, GPART(tcolor)/255.f, BPART(tcolor)/255.f,
h, &ts, &tv);
*s = clamp(ts + *s * 0.15f - 0.075f, 0.f, 1.f);
*v = clamp(tv + *v * 0.5f - 0.25f, 0.f, 1.f);
// Make sure not to pass back any colorset in teamplay.
colorset = NULL;
}
if (set != NULL)
{
@ -261,9 +249,10 @@ int D_PickRandomTeam ()
{
if (playeringame[i])
{
if (TeamLibrary.IsValidTeam (players[i].userinfo.team))
team = players[i].userinfo.GetTeam();
if (TeamLibrary.IsValidTeam(team))
{
if (Teams[players[i].userinfo.team].m_iPresent++ == 0)
if (Teams[team].m_iPresent++ == 0)
{
numTeams++;
}
@ -275,7 +264,7 @@ int D_PickRandomTeam ()
{
do
{
team = pr_pickteam() % Teams.Size ();
team = pr_pickteam() % Teams.Size();
} while (Teams[team].m_iPresent != 0);
}
else
@ -316,7 +305,7 @@ static void UpdateTeam (int pnum, int team, bool update)
{
userinfo_t *info = &players[pnum].userinfo;
if ((dmflags2 & DF2_NO_TEAM_SWITCH) && (alwaysapplydmflags || deathmatch) && TeamLibrary.IsValidTeam (info->team))
if ((dmflags2 & DF2_NO_TEAM_SWITCH) && (alwaysapplydmflags || deathmatch) && TeamLibrary.IsValidTeam (info->GetTeam()))
{
Printf ("Team changing has been disabled!\n");
return;
@ -328,19 +317,15 @@ static void UpdateTeam (int pnum, int team, bool update)
{
team = TEAM_NONE;
}
oldteam = info->team;
info->team = team;
oldteam = info->GetTeam();
team = info->TeamChanged(team);
if (teamplay && !TeamLibrary.IsValidTeam (info->team))
{ // Force players onto teams in teamplay mode
info->team = D_PickRandomTeam ();
}
if (update && oldteam != info->team)
if (update && oldteam != team)
{
if (TeamLibrary.IsValidTeam (info->team))
Printf ("%s joined the %s team\n", info->netname, Teams[info->team].GetName ());
if (TeamLibrary.IsValidTeam (team))
Printf ("%s joined the %s team\n", info->GetName(), Teams[team].GetName ());
else
Printf ("%s is now a loner\n", info->netname);
Printf ("%s is now a loner\n", info->GetName());
}
// Let the player take on the team's color
R_BuildPlayerTranslation (pnum);
@ -348,25 +333,28 @@ static void UpdateTeam (int pnum, int team, bool update)
{
StatusBar->AttachToPlayer (&players[pnum]);
}
if (!TeamLibrary.IsValidTeam (info->team))
info->team = TEAM_NONE;
// Double-check
if (!TeamLibrary.IsValidTeam (team))
{
*static_cast<FIntCVar *>((*info)[NAME_Team]) = TEAM_NONE;
}
}
int D_GetFragCount (player_t *player)
{
if (!teamplay || !TeamLibrary.IsValidTeam (player->userinfo.team))
const int team = player->userinfo.GetTeam();
if (!teamplay || !TeamLibrary.IsValidTeam(team))
{
return player->fragcount;
}
else
{
// Count total frags for this player's team
const int team = player->userinfo.team;
int count = 0;
for (int i = 0; i < MAXPLAYERS; ++i)
{
if (playeringame[i] && players[i].userinfo.team == team)
if (playeringame[i] && players[i].userinfo.GetTeam() == team)
{
count += players[i].fragcount;
}
@ -378,37 +366,146 @@ int D_GetFragCount (player_t *player)
void D_SetupUserInfo ()
{
int i;
userinfo_t *coninfo = &players[consoleplayer].userinfo;
userinfo_t *coninfo;
// Reset everybody's userinfo to a default state.
for (i = 0; i < MAXPLAYERS; i++)
memset (&players[i].userinfo, 0, sizeof(userinfo_t));
{
players[i].userinfo.Reset();
}
// Initialize the console player's user info
coninfo = &players[consoleplayer].userinfo;
strncpy (coninfo->netname, name, MAXPLAYERNAME);
if (teamplay && !TeamLibrary.IsValidTeam (team))
for (FBaseCVar *cvar = CVars; cvar != NULL; cvar = cvar->GetNext())
{
coninfo->team = D_PickRandomTeam ();
if ((cvar->GetFlags() & (CVAR_USERINFO|CVAR_IGNORE)) == CVAR_USERINFO)
{
FBaseCVar **newcvar;
FName cvarname(cvar->GetName());
switch (cvarname.GetIndex())
{
// Some cvars don't copy their original value directly.
case NAME_Team: coninfo->TeamChanged(team); break;
case NAME_Skin: coninfo->SkinChanged(skin); break;
case NAME_Gender: coninfo->GenderChanged(gender); break;
case NAME_PlayerClass: coninfo->PlayerClassChanged(playerclass); break;
// The rest do.
default:
newcvar = coninfo->CheckKey(cvarname);
(*newcvar)->SetGenericRep(cvar->GetGenericRep(CVAR_String), CVAR_String);
break;
}
}
}
else
R_BuildPlayerTranslation(consoleplayer);
}
void userinfo_t::Reset()
{
// Clear this player's userinfo.
TMapIterator<FName, FBaseCVar *> it(*this);
TMap<FName, FBaseCVar *>::Pair *pair;
while (it.NextPair(pair))
{
coninfo->team = team;
delete pair->Value;
}
if (autoaim > 35.f || autoaim < 0.f)
Clear();
// Create userinfo vars for this player, initialized to their defaults.
for (FBaseCVar *cvar = CVars; cvar != NULL; cvar = cvar->GetNext())
{
coninfo->aimdist = ANGLE_1*35;
if ((cvar->GetFlags() & (CVAR_USERINFO|CVAR_IGNORE)) == CVAR_USERINFO)
{
ECVarType type;
FName cvarname(cvar->GetName());
FBaseCVar *newcvar;
// Some cvars have different types for their shadow copies.
switch (cvarname.GetIndex())
{
case NAME_Skin: type = CVAR_Int; break;
case NAME_Gender: type = CVAR_Int; break;
case NAME_PlayerClass: type = CVAR_Int; break;
default: type = cvar->GetRealType(); break;
}
newcvar = C_CreateCVar(NULL, type, cvar->GetFlags() & CVAR_MOD);
newcvar->SetGenericRepDefault(cvar->GetGenericRepDefault(CVAR_String), CVAR_String);
Insert(cvarname, newcvar);
}
}
else
{
coninfo->aimdist = abs ((int)(autoaim * (float)ANGLE_1));
}
int userinfo_t::TeamChanged(int team)
{
if (teamplay && !TeamLibrary.IsValidTeam(team))
{ // Force players onto teams in teamplay mode
team = D_PickRandomTeam();
}
coninfo->color = color;
coninfo->colorset = colorset;
coninfo->skin = R_FindSkin (skin, 0);
coninfo->gender = D_GenderToInt (gender);
coninfo->neverswitch = neverswitchonpickup;
coninfo->MoveBob = (fixed_t)(65536.f * movebob);
coninfo->StillBob = (fixed_t)(65536.f * stillbob);
coninfo->PlayerClass = D_PlayerClassToInt (playerclass);
R_BuildPlayerTranslation (consoleplayer);
*static_cast<FIntCVar *>((*this)[NAME_Team]) = team;
return team;
}
int userinfo_t::SkinChanged(const char *skinname)
{
int skinnum = R_FindSkin(skinname, 0);
*static_cast<FIntCVar *>((*this)[NAME_Skin]) = skinnum;
return skinnum;
}
int userinfo_t::SkinNumChanged(int skinnum)
{
*static_cast<FIntCVar *>((*this)[NAME_Skin]) = skinnum;
return skinnum;
}
int userinfo_t::GenderChanged(const char *gendername)
{
int gendernum = D_GenderToInt(gendername);
*static_cast<FIntCVar *>((*this)[NAME_Gender]) = gendernum;
return gendernum;
}
int userinfo_t::PlayerClassChanged(const char *classname)
{
int classnum = D_PlayerClassToInt(classname);
*static_cast<FIntCVar *>((*this)[NAME_PlayerClass]) = classnum;
return classnum;
}
int userinfo_t::PlayerClassNumChanged(int classnum)
{
*static_cast<FIntCVar *>((*this)[NAME_PlayerClass]) = classnum;
return classnum;
}
int userinfo_t::ColorSetChanged(int setnum)
{
*static_cast<FIntCVar *>((*this)[NAME_ColorSet]) = setnum;
return setnum;
}
uint32 userinfo_t::ColorChanged(const char *colorname)
{
FColorCVar *color = static_cast<FColorCVar *>((*this)[NAME_Color]);
assert(color != NULL);
UCVarValue val;
val.String = const_cast<char *>(colorname);
color->SetGenericRep(val, CVAR_String);
*static_cast<FIntCVar *>((*this)[NAME_ColorSet]) = -1;
return *color;
}
uint32 userinfo_t::ColorChanged(uint32 colorval)
{
FColorCVar *color = static_cast<FColorCVar *>((*this)[NAME_Color]);
assert(color != NULL);
UCVarValue val;
val.Int = colorval;
color->SetGenericRep(val, CVAR_Int);
// This version is called by the menu code. Do not implicitly set colorset.
return colorval;
}
void D_UserInfoChanged (FBaseCVar *cvar)
@ -500,7 +597,7 @@ static const char *SetServerVar (char *name, ECVarType type, BYTE **stream, bool
{
if (playeringame[i])
{
UpdateTeam (i, players[i].userinfo.team, true);
UpdateTeam (i, players[i].userinfo.GetTeam(), true);
}
}
}
@ -569,111 +666,131 @@ void D_DoServerInfoChange (BYTE **stream, bool singlebit)
}
}
void D_WriteUserInfoStrings (int i, BYTE **stream, bool compact)
static int STACK_ARGS userinfosortfunc(const void *a, const void *b)
{
if (i >= MAXPLAYERS)
{
WriteByte (0, stream);
}
else
{
userinfo_t *info = &players[i].userinfo;
PClassPlayerPawn *type = PlayerClasses[info->PlayerClass].Type;
if (!compact)
{
sprintf (*((char **)stream),
"\\name\\%s"
"\\autoaim\\%g"
"\\color\\%x %x %x"
"\\colorset\\%d"
"\\skin\\%s"
"\\team\\%d"
"\\gender\\%s"
"\\neverswitchonpickup\\%d"
"\\movebob\\%g"
"\\stillbob\\%g"
"\\playerclass\\%s"
,
D_EscapeUserInfo(info->netname).GetChars(),
(double)info->aimdist / (float)ANGLE_1,
RPART(info->color), GPART(info->color), BPART(info->color),
info->colorset,
D_EscapeUserInfo(skins[info->skin].name).GetChars(),
info->team,
info->gender == GENDER_FEMALE ? "female" :
info->gender == GENDER_NEUTER ? "other" : "male",
info->neverswitch,
(float)(info->MoveBob) / 65536.f,
(float)(info->StillBob) / 65536.f,
info->PlayerClass == -1 ? "Random" : D_EscapeUserInfo(type->DisplayName).GetChars()
);
}
else
{
sprintf (*((char **)stream),
"\\"
"\\%s" // name
"\\%g" // autoaim
"\\%x %x %x" // color
"\\%s" // skin
"\\%d" // team
"\\%s" // gender
"\\%d" // neverswitchonpickup
"\\%g" // movebob
"\\%g" // stillbob
"\\%s" // playerclass
"\\%d" // colorset
,
D_EscapeUserInfo(info->netname).GetChars(),
(double)info->aimdist / (float)ANGLE_1,
RPART(info->color), GPART(info->color), BPART(info->color),
D_EscapeUserInfo(skins[info->skin].name).GetChars(),
info->team,
info->gender == GENDER_FEMALE ? "female" :
info->gender == GENDER_NEUTER ? "other" : "male",
info->neverswitch,
(float)(info->MoveBob) / 65536.f,
(float)(info->StillBob) / 65536.f,
info->PlayerClass == -1 ? "Random" : D_EscapeUserInfo(type->DisplayName).GetChars(),
info->colorset
);
}
}
*stream += strlen (*((char **)stream)) + 1;
TMap<FName, FBaseCVar *>::ConstPair *pair1 = *(TMap<FName, FBaseCVar *>::ConstPair **)a;
TMap<FName, FBaseCVar *>::ConstPair *pair2 = *(TMap<FName, FBaseCVar *>::ConstPair **)b;
return stricmp(pair1->Key.GetChars(), pair2->Key.GetChars());
}
void D_ReadUserInfoStrings (int i, BYTE **stream, bool update)
static int STACK_ARGS namesortfunc(const void *a, const void *b)
{
userinfo_t *info = &players[i].userinfo;
FName *name1 = (FName *)a;
FName *name2 = (FName *)b;
return stricmp(name1->GetChars(), name2->GetChars());
}
void D_WriteUserInfoStrings (int pnum, BYTE **stream, bool compact)
{
if (pnum >= MAXPLAYERS)
{
WriteByte (0, stream);
return;
}
userinfo_t *info = &players[pnum].userinfo;
TArray<TMap<FName, FBaseCVar *>::Pair *> userinfo_pairs(info->CountUsed());
TMap<FName, FBaseCVar *>::Iterator it(*info);
TMap<FName, FBaseCVar *>::Pair *pair;
UCVarValue cval;
// Create a simple array of all userinfo cvars
while (it.NextPair(pair))
{
userinfo_pairs.Push(pair);
}
// For compact mode, these need to be sorted. Verbose mode doesn't matter.
if (compact)
{
qsort(&userinfo_pairs[0], userinfo_pairs.Size(), sizeof(pair), userinfosortfunc);
// Compact mode is signified by starting the string with two backslash characters.
// We output one now. The second will be output as part of the first value.
*(*stream)++ = '\\';
}
for (unsigned int i = 0; i < userinfo_pairs.Size(); ++i)
{
pair = userinfo_pairs[i];
if (!compact)
{ // In verbose mode, prepend the cvar's name
*stream += sprintf(*((char **)stream), "\\%s\\", pair->Key.GetChars());
}
// A few of these need special handling for compatibility reasons.
switch (pair->Key.GetIndex())
{
case NAME_Gender:
*stream += sprintf(*((char **)stream), "\\%s",
*static_cast<FIntCVar *>(pair->Value) == GENDER_FEMALE ? "female" :
*static_cast<FIntCVar *>(pair->Value) == GENDER_NEUTER ? "other" : "male");
break;
case NAME_PlayerClass:
*stream += sprintf(*((char **)stream), "\\%s", info->GetPlayerClassNum() == -1 ? "Random" :
D_EscapeUserInfo(info->GetPlayerClassType()->DisplayName.GetChars()));
break;
case NAME_Skin:
*stream += sprintf(*((char **)stream), "\\%s", D_EscapeUserInfo(skins[info->GetSkin()].name).GetChars());
break;
default:
cval = pair->Value->GetGenericRep(CVAR_String);
*stream += sprintf(*((char **)stream), "\\%s", cval.String);
break;
}
}
*(*stream)++ = '\0';
}
void D_ReadUserInfoStrings (int pnum, BYTE **stream, bool update)
{
userinfo_t *info = &players[pnum].userinfo;
TArray<FName> compact_names(info->CountUsed());
FBaseCVar **cvar_ptr;
const char *ptr = *((const char **)stream);
const char *breakpt;
FString value;
bool compact;
int infotype = -1;
FName keyname;
unsigned int infotype = 0;
if (*ptr++ != '\\')
return;
compact = (*ptr == '\\') ? ptr++, true : false;
if (i < MAXPLAYERS)
// We need the cvar names in sorted order for compact mode
if (compact)
{
for (;;)
{
int j;
TMap<FName, FBaseCVar *>::Iterator it(*info);
TMap<FName, FBaseCVar *>::Pair *pair;
breakpt = strchr (ptr, '\\');
while (it.NextPair(pair))
{
compact_names.Push(pair->Key);
}
qsort(&compact_names[0], compact_names.Size(), sizeof(FName), namesortfunc);
}
if (pnum < MAXPLAYERS)
{
for (breakpt = ptr; breakpt != NULL; ptr = breakpt + 1)
{
breakpt = strchr(ptr, '\\');
if (compact)
{
// Compact has just the value.
if (infotype >= compact_names.Size())
{ // Too many entries! OMG!
break;
}
keyname = compact_names[infotype++];
value = D_UnescapeUserInfo(ptr, breakpt != NULL ? breakpt - ptr : strlen(ptr));
infotype++;
}
else
{
// Verbose has both the key name and its value.
assert(breakpt != NULL);
// A malicious remote machine could invalidate the above assert.
if (breakpt == NULL)
@ -689,156 +806,187 @@ void D_ReadUserInfoStrings (int i, BYTE **stream, bool update)
{
value = D_UnescapeUserInfo(valstart, strlen(valstart));
}
for (j = 0;
UserInfoStrings[j] && strnicmp (UserInfoStrings[j], ptr, valstart - ptr - 1) != 0;
++j)
{ }
if (UserInfoStrings[j] == NULL)
{
infotype = -1;
}
else
{
infotype = j;
}
keyname = FName(ptr, valstart - ptr - 1, true);
}
switch (infotype)
// A few of these need special handling.
switch (keyname)
{
case INFO_Autoaim: {
double angles;
angles = atof (value);
if (angles > 35.f || angles < 0.f)
{
info->aimdist = ANGLE_1*35;
}
else
{
info->aimdist = abs ((int)(angles * (float)ANGLE_1));
}
}
case NAME_Gender:
info->GenderChanged(value);
break;
case INFO_Name:
{
char oldname[MAXPLAYERNAME+1];
strcpy (oldname, info->netname);
strncpy (info->netname, value, MAXPLAYERNAME);
info->netname[MAXPLAYERNAME] = 0;
CleanseString(info->netname);
if (update && strcmp (oldname, info->netname) != 0)
{
Printf ("%s is now known as %s\n", oldname, info->netname);
}
}
case NAME_PlayerClass:
info->PlayerClassChanged(value);
break;
case INFO_Team:
UpdateTeam (i, atoi(value), update);
break;
case INFO_Color:
case INFO_ColorSet:
if (infotype == INFO_Color)
case NAME_Skin:
info->SkinChanged(value);
if (players[pnum].mo != NULL)
{
info->color = V_GetColorFromString (NULL, value);
info->colorset = -1;
}
else
{
info->colorset = atoi(value);
}
R_BuildPlayerTranslation (i);
if (StatusBar != NULL && i == StatusBar->GetPlayer())
{
StatusBar->AttachToPlayer (&players[i]);
}
break;
case INFO_Skin:
info->skin = R_FindSkin (value, players[i].CurrentPlayerClass);
if (players[i].mo != NULL)
{
if (players[i].cls != NULL &&
!(players[i].mo->flags4 & MF4_NOSKIN) &&
players[i].mo->state->sprite ==
GetDefaultByType (players[i].cls)->SpawnState->sprite)
if (players[pnum].cls != NULL &&
!(players[pnum].mo->flags4 & MF4_NOSKIN) &&
players[pnum].mo->state->sprite ==
GetDefaultByType (players[pnum].cls)->SpawnState->sprite)
{ // Only change the sprite if the player is using a standard one
players[i].mo->sprite = skins[info->skin].sprite;
players[i].mo->scaleX = skins[info->skin].ScaleX;
players[i].mo->scaleY = skins[info->skin].ScaleY;
players[pnum].mo->sprite = skins[info->GetSkin()].sprite;
}
}
// Rebuild translation in case the new skin uses a different range
// than the old one.
R_BuildPlayerTranslation (i);
R_BuildPlayerTranslation(pnum);
break;
case INFO_Gender:
info->gender = D_GenderToInt (value);
case NAME_Team:
UpdateTeam(pnum, atoi(value), update);
break;
case INFO_NeverSwitchOnPickup:
if (value[0] >= '0' && value[0] <= '9')
{
info->neverswitch = atoi (value) ? true : false;
}
else if (stricmp (value, "true") == 0)
{
info->neverswitch = 1;
}
else
{
info->neverswitch = 0;
}
break;
case INFO_MoveBob:
info->MoveBob = (fixed_t)(atof (value) * 65536.f);
break;
case INFO_StillBob:
info->StillBob = (fixed_t)(atof (value) * 65536.f);
break;
case INFO_PlayerClass:
info->PlayerClass = D_PlayerClassToInt (value);
case NAME_Color:
info->ColorChanged(value);
break;
default:
cvar_ptr = info->CheckKey(keyname);
if (cvar_ptr != NULL)
{
assert(*cvar_ptr != NULL);
UCVarValue val;
FString oldname;
if (keyname == NAME_Name)
{
val = (*cvar_ptr)->GetGenericRep(CVAR_String);
oldname = val.String;
}
val.String = CleanseString(value.LockBuffer());
(*cvar_ptr)->SetGenericRep(val, CVAR_String);
value.UnlockBuffer();
if (keyname == NAME_Name && update && oldname != value)
{
Printf("%s is now known as %s\n", oldname.GetChars(), value.GetChars());
}
}
break;
}
if (breakpt)
if (keyname == NAME_Color || keyname == NAME_ColorSet)
{
ptr = breakpt + 1;
R_BuildPlayerTranslation(pnum);
if (StatusBar != NULL && pnum == StatusBar->GetPlayer())
{
StatusBar->AttachToPlayer(&players[pnum]);
}
}
else
}
}
*stream += strlen (*((char **)stream)) + 1;
}
void ReadCompatibleUserInfo(FArchive &arc, userinfo_t &info)
{
char netname[MAXPLAYERNAME + 1];
BYTE team;
int aimdist, color, colorset, skin, gender;
bool neverswitch;
//fixed_t movebob, stillbob; These were never serialized!
//int playerclass; "
info.Reset();
arc.Read(&netname, sizeof(netname));
arc << team << aimdist << color << skin << gender << neverswitch << colorset;
*static_cast<FStringCVar *>(info[NAME_Name]) = netname;
*static_cast<FIntCVar *>(info[NAME_Team]) = team;
*static_cast<FFloatCVar *>(info[NAME_Autoaim]) = (float)aimdist / ANGLE_1;
*static_cast<FIntCVar *>(info[NAME_Skin]) = skin;
*static_cast<FIntCVar *>(info[NAME_Gender]) = gender;
*static_cast<FBoolCVar *>(info[NAME_NeverSwitchOnPickup]) = neverswitch;
*static_cast<FIntCVar *>(info[NAME_ColorSet]) = colorset;
UCVarValue val;
val.Int = color;
static_cast<FColorCVar *>(info[NAME_Color])->SetGenericRep(val, CVAR_Int);
}
void WriteUserInfo(FArchive &arc, userinfo_t &info)
{
TMapIterator<FName, FBaseCVar *> it(info);
TMap<FName, FBaseCVar *>::Pair *pair;
FName name;
UCVarValue val;
int i;
while (it.NextPair(pair))
{
name = pair->Key;
arc << name;
switch (name.GetIndex())
{
case NAME_Skin:
arc.WriteString(skins[info.GetSkin()].name);
break;
case NAME_PlayerClass:
i = info.GetPlayerClassNum();
arc.WriteString(i == -1 ? "Random" : PlayerClasses[i].Type->DisplayName);
break;
default:
val = pair->Value->GetGenericRep(CVAR_String);
arc.WriteString(val.String);
break;
}
}
name = NAME_None;
arc << name;
}
void ReadUserInfo(FArchive &arc, userinfo_t &info)
{
FName name;
FBaseCVar **cvar;
char *str = NULL;
UCVarValue val;
info.Reset();
for (arc << name; name != NAME_None; arc << name)
{
cvar = info.CheckKey(name);
arc << str;
if (cvar != NULL && *cvar != NULL)
{
switch (name)
{
case NAME_Team: info.TeamChanged(atoi(str)); break;
case NAME_Skin: info.SkinChanged(str); break;
case NAME_PlayerClass: info.PlayerClassChanged(str); break;
default:
val.String = str;
(*cvar)->SetGenericRep(val, CVAR_String);
break;
}
}
}
*stream += strlen (*((char **)stream)) + 1;
if (str != NULL)
{
delete[] str;
}
}
FArchive &operator<< (FArchive &arc, userinfo_t &info)
{
if (arc.IsStoring ())
if (SaveVersion < 4253)
{
arc.Write (&info.netname, sizeof(info.netname));
ReadCompatibleUserInfo(arc, info);
}
else if (arc.IsStoring())
{
WriteUserInfo(arc, info);
}
else
{
arc.Read (&info.netname, sizeof(info.netname));
ReadUserInfo(arc, info);
}
arc << info.team << info.aimdist << info.color
<< info.skin << info.gender << info.neverswitch
<< info.colorset;
return arc;
}
@ -852,27 +1000,51 @@ CCMD (playerinfo)
{
if (playeringame[i])
{
Printf ("%d. %s\n", i, players[i].userinfo.netname);
Printf("%d. %s\n", i, players[i].userinfo.GetName());
}
}
}
else
{
int i = atoi (argv[1]);
int i = atoi(argv[1]);
if (i < 0 || i >= MAXPLAYERS)
{
Printf("Bad player number\n");
return;
}
userinfo_t *ui = &players[i].userinfo;
Printf ("Name: %s\n", ui->netname);
Printf ("Team: %s (%d)\n", ui->team == TEAM_NONE ? "None" : Teams[ui->team].GetName (), ui->team);
Printf ("Aimdist: %d\n", ui->aimdist);
Printf ("Color: %06x\n", ui->color);
Printf ("ColorSet: %d\n", ui->colorset);
Printf ("Skin: %s (%d)\n", skins[ui->skin].name, ui->skin);
Printf ("Gender: %s (%d)\n", GenderNames[ui->gender], ui->gender);
Printf ("NeverSwitch: %d\n", ui->neverswitch);
Printf ("MoveBob: %g\n", ui->MoveBob/65536.f);
Printf ("StillBob: %g\n", ui->StillBob/65536.f);
Printf ("PlayerClass: %s (%d)\n",
ui->PlayerClass == -1 ? "Random" : PlayerClasses[ui->PlayerClass].Type->DisplayName.GetChars(),
ui->PlayerClass);
if (argv.argc() > 2) PrintMiscActorInfo(players[i].mo);
if (!playeringame[i])
{
Printf(TEXTCOLOR_ORANGE "Player %d is not in the game\n", i);
}
// Print special info
Printf("%20s: %s\n", "Name", ui->GetName());
Printf("%20s: %s (%d)\n", "Team", ui->GetTeam() == TEAM_NONE ? "None" : Teams[ui->GetTeam()].GetName(), ui->GetTeam());
Printf("%20s: %s (%d)\n", "Skin", skins[ui->GetSkin()].name, ui->GetSkin());
Printf("%20s: %s (%d)\n", "Gender", GenderNames[ui->GetGender()], ui->GetGender());
Printf("%20s: %s (%d)\n", "PlayerClass",
ui->GetPlayerClassNum() == -1 ? "Random" : ui->GetPlayerClassType()->DisplayName.GetChars(),
ui->GetPlayerClassNum());
// Print generic info
TMapIterator<FName, FBaseCVar *> it(*ui);
TMap<FName, FBaseCVar *>::Pair *pair;
while (it.NextPair(pair))
{
if (pair->Key != NAME_Name && pair->Key != NAME_Team && pair->Key != NAME_Skin &&
pair->Key != NAME_Gender && pair->Key != NAME_PlayerClass)
{
UCVarValue val = pair->Value->GetGenericRep(CVAR_String);
Printf("%20s: %s\n", pair->Key.GetChars(), val.String);
}
}
if (argv.argc() > 2)
{
PrintMiscActorInfo(players[i].mo);
}
}
}

View File

@ -183,6 +183,7 @@ enum
{
PPF_NOTHRUSTWHENINVUL = 1, // Attacks do not thrust the player if they are invulnerable.
PPF_CANSUPERMORPH = 2, // Being remorphed into this class can give you a Tome of Power
PPF_CROUCHABLEMORPH = 4, // This morphed player can crouch
};
//
@ -215,7 +216,6 @@ typedef enum
CF_INSTANTWEAPSWITCH= 1 << 11, // [RH] Switch weapons instantly
CF_TOTALLYFROZEN = 1 << 12, // [RH] All players can do is press +use
CF_PREDICTING = 1 << 13, // [RH] Player movement is being predicted
CF_WEAPONREADY = 1 << 14, // [RH] Weapon is in the ready state and can fire its primary attack
CF_DRAIN = 1 << 16, // Player owns a drain powerup
CF_HIGHJUMP = 1 << 18, // more Skulltag flags. Implementation not guaranteed though. ;)
CF_REFLECTION = 1 << 19,
@ -223,15 +223,22 @@ typedef enum
CF_DOUBLEFIRINGSPEED= 1 << 21, // Player owns a double firing speed artifact
CF_EXTREMELYDEAD = 1 << 22, // [RH] Reliably let the status bar know about extreme deaths.
CF_INFINITEAMMO = 1 << 23, // Player owns an infinite ammo artifact
CF_WEAPONBOBBING = 1 << 24, // [HW] Bob weapon while the player is moving
CF_WEAPONREADYALT = 1 << 25, // Weapon can fire its secondary attack
CF_WEAPONSWITCHOK = 1 << 26, // It is okay to switch away from this weapon
CF_BUDDHA = 1 << 27, // [SP] Buddha mode - take damage, but don't die
CF_WEAPONRELOADOK = 1 << 28, // [XA] Okay to reload this weapon.
CF_WEAPONZOOMOK = 1 << 29, // [XA] Okay to use weapon zoom function.
CF_NOCLIP2 = 1 << 30, // [RH] More Quake-like noclip
} cheat_t;
enum
{
WF_WEAPONREADY = 1 << 0, // [RH] Weapon is in the ready state and can fire its primary attack
WF_WEAPONBOBBING = 1 << 1, // [HW] Bob weapon while the player is moving
WF_WEAPONREADYALT = 1 << 2, // Weapon can fire its secondary attack
WF_WEAPONSWITCHOK = 1 << 3, // It is okay to switch away from this weapon
WF_DISABLESWITCH = 1 << 4, // Disable weapon switching completely
WF_WEAPONRELOADOK = 1 << 5, // [XA] Okay to reload this weapon.
WF_WEAPONZOOMOK = 1 << 6, // [XA] Okay to use weapon zoom function.
WF_REFIRESWITCHOK = 1 << 7, // Mirror WF_WEAPONSWITCHOK for A_ReFire
};
#define WPIECE1 1
#define WPIECE2 2
#define WPIECE3 4
@ -241,6 +248,29 @@ typedef enum
#define MAXPLAYERNAME 15
// [GRB] Custom player classes
enum
{
PCF_NOMENU = 1, // Hide in new game menu
};
class FPlayerClass
{
public:
FPlayerClass ();
FPlayerClass (const FPlayerClass &other);
~FPlayerClass ();
bool CheckSkin (int skin);
PClassPlayerPawn *Type;
DWORD Flags;
TArray<int> Skins;
};
extern TArray<FPlayerClass> PlayerClasses;
// User info (per-player copies of each CVAR_USERINFO cvar)
enum
{
GENDER_MALE,
@ -248,20 +278,80 @@ enum
GENDER_NEUTER
};
struct userinfo_t
struct userinfo_t : TMap<FName,FBaseCVar *>
{
char netname[MAXPLAYERNAME+1];
BYTE team;
int aimdist;
int color;
int colorset;
int skin;
int gender;
bool neverswitch;
fixed_t MoveBob, StillBob;
int PlayerClass;
int GetAimDist() const
{
if (dmflags2 & DF2_NOAUTOAIM)
{
return 0;
}
int GetAimDist() const { return (dmflags2 & DF2_NOAUTOAIM)? 0 : aimdist; }
float aim = *static_cast<FFloatCVar *>(*CheckKey(NAME_Autoaim));
if (aim > 35 || aim < 0)
{
return ANGLE_1*35;
}
else
{
return xs_RoundToInt(fabs(aim * ANGLE_1));
}
}
const char *GetName() const
{
return *static_cast<FStringCVar *>(*CheckKey(NAME_Name));
}
int GetTeam() const
{
return *static_cast<FIntCVar *>(*CheckKey(NAME_Team));
}
int GetColorSet() const
{
return *static_cast<FIntCVar *>(*CheckKey(NAME_ColorSet));
}
uint32 GetColor() const
{
return *static_cast<FColorCVar *>(*CheckKey(NAME_Color));
}
bool GetNeverSwitch() const
{
return *static_cast<FBoolCVar *>(*CheckKey(NAME_NeverSwitchOnPickup));
}
fixed_t GetMoveBob() const
{
return FLOAT2FIXED(*static_cast<FFloatCVar *>(*CheckKey(NAME_MoveBob)));
}
fixed_t GetStillBob() const
{
return FLOAT2FIXED(*static_cast<FFloatCVar *>(*CheckKey(NAME_StillBob)));
}
int GetPlayerClassNum() const
{
return *static_cast<FIntCVar *>(*CheckKey(NAME_PlayerClass));
}
PClassPlayerPawn *GetPlayerClassType() const
{
return PlayerClasses[GetPlayerClassNum()].Type;
}
int GetSkin() const
{
return *static_cast<FIntCVar *>(*CheckKey(NAME_Skin));
}
int GetGender() const
{
return *static_cast<FIntCVar *>(*CheckKey(NAME_Gender));
}
void Reset();
int TeamChanged(int team);
int SkinChanged(const char *skinname);
int SkinNumChanged(int skinnum);
int GenderChanged(const char *gendername);
int PlayerClassChanged(const char *classname);
int PlayerClassNumChanged(int classnum);
uint32 ColorChanged(const char *colorname);
uint32 ColorChanged(uint32 colorval);
int ColorSetChanged(int setnum);
};
FArchive &operator<< (FArchive &arc, userinfo_t &info);
@ -325,6 +415,7 @@ public:
int lastkilltime; // [RH] For multikills
BYTE multicount;
BYTE spreecount; // [RH] Keep track of killing sprees
BYTE WeaponState;
AWeapon *ReadyWeapon;
AWeapon *PendingWeapon; // WP_NOCHANGE if not changing
@ -333,6 +424,7 @@ public:
int timefreezer; // Player has an active time freezer
short refire; // refired shots are less accurate
short inconsistant;
bool waiting;
int killcount, itemcount, secretcount; // for intermission
int damagecount, bonuscount;// for screen flashing
int hazardcount; // for delayed Strife damage
@ -438,6 +530,11 @@ public:
crouching = 0;
crouchviewdelta = 0;
}
bool CanCrouch() const
{
return morphTics == 0 || mo->PlayerFlags & PPF_CROUCHABLEMORPH;
}
int GetSpawnClass();
};
@ -447,7 +544,7 @@ extern player_t players[MAXPLAYERS];
FArchive &operator<< (FArchive &arc, player_t *&p);
void P_CheckPlayerSprites();
void P_CheckPlayerSprite(AActor *mo, int &spritenum, fixed_t &scalex, fixed_t &scaley);
inline void AActor::SetFriendPlayer(player_t *player)
{
@ -474,26 +571,4 @@ inline bool AActor::IsNoClip2() const
bool P_IsPlayerTotallyFrozen(const player_t *player);
// [GRB] Custom player classes
enum
{
PCF_NOMENU = 1, // Hide in new game menu
};
class FPlayerClass
{
public:
FPlayerClass ();
FPlayerClass (const FPlayerClass &other);
~FPlayerClass ();
bool CheckSkin (int skin);
PClassPlayerPawn *Type;
DWORD Flags;
TArray<int> Skins;
};
extern TArray<FPlayerClass> PlayerClasses;
#endif // __D_PLAYER_H__

View File

@ -631,7 +631,10 @@ void FDecalLib::ParseGenerator (FScanner &sc)
}
actor->DecalGenerator = decal;
decal->Users.Push (type);
if (decal != NULL)
{
decal->Users.Push (type);
}
}
void FDecalLib::ParseFader (FScanner &sc)

View File

@ -325,6 +325,7 @@ static void MarkRoot()
DThinker::MarkRoots();
FCanvasTextureInfo::Mark();
Mark(DACSThinker::ActiveThinker);
Mark(level.DefaultSkybox);
// Mark dead bodies.
for (i = 0; i < BODYQUESIZE; ++i)
{

View File

@ -333,7 +333,7 @@ enum
COMPATF_HITSCAN = 1 << 28, // Hitscans use original blockmap anf hit check code.
COMPATF_LIGHT = 1 << 29, // Find neighboring light level like Doom
COMPATF_POLYOBJ = 1 << 30, // Draw polyobjects the old fashioned way
COMPATF_MASKEDMIDTEX = 1 << 31, // Ignore compositing when drawing masked midtextures
COMPATF_MASKEDMIDTEX = 1u << 31, // Ignore compositing when drawing masked midtextures
COMPATF2_BADANGLES = 1 << 0, // It is impossible to face directly NSEW.
COMPATF2_FLOORMOVE = 1 << 1, // Use the same floor motion behavior as Doom.
@ -348,6 +348,7 @@ enum
BCOMPATF_VILEGHOSTS = 1 << 2, // Monsters' radius and height aren't restored properly when resurrected.
BCOMPATF_BADTELEPORTERS = 1 << 3, // Ignore tags on Teleport specials
BCOMPATF_BADPORTALS = 1 << 4, // Restores the old unstable portal behavior
BCOMPATF_REBUILDNODES = 1 << 5, // Force node rebuild
};
// phares 3/20/98:

View File

@ -450,7 +450,7 @@ int DThinker::TickThinkers (FThinkerList *list, FThinkerList *dest)
NextToThink = node->NextThinker;
if (node->ObjectFlags & OF_JustSpawned)
{
node->ObjectFlags &= ~OF_JustSpawned;
// Leave OF_JustSpawn set until after Tick() so the ticker can check it.
if (dest != NULL)
{ // Move thinker from this list to the destination list
node->Remove();
@ -465,7 +465,8 @@ int DThinker::TickThinkers (FThinkerList *list, FThinkerList *dest)
if (!(node->ObjectFlags & OF_EuthanizeMe))
{ // Only tick thinkers not scheduled for destruction
node->Tick ();
node->Tick();
node->ObjectFlags &= ~OF_JustSpawned;
GC::CheckGC();
}
node = NextToThink;

View File

@ -774,7 +774,7 @@ void FParser::SF_PlayerName(void)
if(plnum !=-1)
{
t_return.type = svt_string;
t_return.string = players[plnum].userinfo.netname;
t_return.string = players[plnum].userinfo.GetName();
}
else
{
@ -4256,7 +4256,7 @@ void FParser::SF_SpawnShot2(void)
S_Sound (mo, CHAN_VOICE, mo->SeeSound, 1, ATTN_NORM);
mo->target = source;
P_ThrustMobj(mo, mo->angle = source->angle, mo->Speed);
if (!P_CheckMissileSpawn(mo)) mo = NULL;
if (!P_CheckMissileSpawn(mo, source->radius)) mo = NULL;
}
t_return.value.mobj = mo;
}

View File

@ -132,8 +132,8 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_VileAttack)
return 0;
S_Sound (self, CHAN_WEAPON, snd, 1, ATTN_NORM);
P_TraceBleed (dmg, target);
P_DamageMobj (target, self, self, dmg, NAME_None);
int newdam = P_DamageMobj (target, self, self, dmg, NAME_None);
P_TraceBleed (newdam > 0 ? newdam : dmg, target);
an = self->angle >> ANGLETOFINESHIFT;
fire = self->tracer;

View File

@ -88,16 +88,22 @@ DEFINE_ACTION_FUNCTION(AActor, A_BrainDie)
// New dmflag: Kill all boss spawned monsters before ending the level.
if (dmflags2 & DF2_KILLBOSSMONST)
{
TThinkerIterator<AActor> it;
AActor *mo;
while ((mo = it.Next()))
int count; // Repeat until we have no more boss-spawned monsters.
do // (e.g. Pain Elementals can spawn more to kill upon death.)
{
if (mo->flags4 & MF4_BOSSSPAWNED)
TThinkerIterator<AActor> it;
AActor *mo;
count = 0;
while ((mo = it.Next()))
{
P_DamageMobj(mo, self, self, mo->health, NAME_None,
DMG_NO_ARMOR|DMG_FORCED|DMG_THRUSTLESS|DMG_NO_FACTOR);
if (mo->health > 0 && mo->flags4 & MF4_BOSSSPAWNED)
{
P_DamageMobj(mo, self, self, mo->health, NAME_None,
DMG_NO_ARMOR|DMG_FORCED|DMG_THRUSTLESS|DMG_NO_FACTOR);
count++;
}
}
}
} while (count != 0);
}
G_ExitLevel (0, false);

View File

@ -12,8 +12,8 @@ DEFINE_ACTION_FUNCTION(AActor, A_BruisAttack)
{
int damage = (pr_bruisattack()%8+1)*10;
S_Sound (self, CHAN_WEAPON, "baron/melee", 1, ATTN_NORM);
P_DamageMobj (self->target, self, self, damage, NAME_Melee);
P_TraceBleed (damage, self->target, self);
int newdam = P_DamageMobj (self->target, self, self, damage, NAME_Melee);
P_TraceBleed (newdam > 0 ? newdam : damage, self->target, self);
return 0;
}

View File

@ -24,8 +24,8 @@ DEFINE_ACTION_FUNCTION(AActor, A_HeadAttack)
{
int damage = (pr_headattack()%6+1)*10;
S_Sound (self, CHAN_WEAPON, self->AttackSound, 1, ATTN_NORM);
P_DamageMobj (self->target, self, self, damage, NAME_Melee);
P_TraceBleed (damage, self->target, self);
int newdam = P_DamageMobj (self->target, self, self, damage, NAME_Melee);
P_TraceBleed (newdam > 0 ? newdam : damage, self->target, self);
return 0;
}

View File

@ -22,8 +22,8 @@ DEFINE_ACTION_FUNCTION(AActor, A_SargAttack)
if (self->CheckMeleeRange ())
{
int damage = ((pr_sargattack()%10)+1)*4;
P_DamageMobj (self->target, self, self, damage, NAME_Melee);
P_TraceBleed (damage, self->target, self);
int newdam = P_DamageMobj (self->target, self, self, damage, NAME_Melee);
P_TraceBleed (newdam > 0 ? newdam : damage, self->target, self);
}
return 0;
}

View File

@ -27,8 +27,8 @@ DEFINE_ACTION_FUNCTION(AActor, A_TroopAttack)
{
int damage = (pr_troopattack()%8+1)*3;
S_Sound (self, CHAN_WEAPON, "imp/melee", 1, ATTN_NORM);
P_DamageMobj (self->target, self, self, damage, NAME_Melee);
P_TraceBleed (damage, self->target, self);
int newdam = P_DamageMobj (self->target, self, self, damage, NAME_Melee);
P_TraceBleed (newdam > 0 ? newdam : damage, self->target, self);
return 0;
}

View File

@ -367,9 +367,13 @@ void P_SetSafeFlash(AWeapon *weapon, player_t *player, FState *flashstate, int i
}
// if we get here the state doesn't seem to belong to any class in the inheritance chain
// This can happen with Dehacked if the flash states are remapped.
// The only way to check this would be to go through all Dehacked modifiable actors and
// find the correct one.
// For now let's assume that it will work.
// The only way to check this would be to go through all Dehacked modifiable actors, convert
// their states into a single flat array and find the correct one.
// Rather than that, just check to make sure it belongs to something.
if (FState::StaticFindStateOwner(flashstate + index) == NULL)
{ // Invalid state. With no index offset, it should at least be valid.
index = 0;
}
P_SetPsprite (player, ps_flash, flashstate + index);
}
@ -506,7 +510,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_FirePlasma)
//
// [RH] A_FireRailgun
//
static void FireRailgun(AActor *self, int RailOffset)
static void FireRailgun(AActor *self, int offset_xy)
{
int damage;
player_t *player;
@ -531,7 +535,7 @@ static void FireRailgun(AActor *self, int RailOffset)
damage = deathmatch ? 100 : 150;
P_RailAttack (self, damage, RailOffset);
P_RailAttack (self, damage, offset_xy);
}
@ -638,8 +642,8 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_BFGSpray)
damage += (pr_bfgspray() & 7) + 1;
thingToHit = linetarget;
P_DamageMobj (thingToHit, self->target, self->target, damage, spray != NULL? FName(spray->DamageType) : FName(NAME_BFGSplash));
P_TraceBleed (damage, thingToHit, self->target);
int newdam = P_DamageMobj (thingToHit, self->target, self->target, damage, spray != NULL? FName(spray->DamageType) : FName(NAME_BFGSplash));
P_TraceBleed (newdam > 0 ? newdam : damage, thingToHit, self->target);
}
return 0;
}

View File

@ -12,7 +12,6 @@
#include "thingdef/thingdef.h"
*/
FRandom pr_lost ("LostMissileRange");
FRandom pr_oldsoul ("BetaLostSoul");
//

View File

@ -120,7 +120,6 @@ void A_PainShootSkull (AActor *self, angle_t angle, PClassActor *spawntype, int
other = Spawn (spawntype, x, y, z, ALLOW_REPLACE);
// Check to see if the new Lost Soul's z value is above the
// ceiling of its new sector, or below the floor. If so, kill it.

View File

@ -157,8 +157,8 @@ DEFINE_ACTION_FUNCTION(AActor, A_SkelFist)
{
int damage = ((pr_skelfist()%10)+1)*6;
S_Sound (self, CHAN_WEAPON, "skeleton/melee", 1, ATTN_NORM);
P_DamageMobj (self->target, self, self, damage, NAME_Melee);
P_TraceBleed (damage, self->target, self);
int newdam = P_DamageMobj (self->target, self, self, damage, NAME_Melee);
P_TraceBleed (newdam > 0 ? newdam : damage, self->target, self);
}
return 0;
}

View File

@ -153,9 +153,10 @@ void AScriptedMarine::Tick ()
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_M_Refire)
DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_M_Refire)
{
PARAM_ACTION_PROLOGUE;
PARAM_BOOL_OPT(ignoremissile) { ignoremissile = false; }
if (self->target == NULL || self->target->health <= 0)
{
@ -169,7 +170,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_M_Refire)
self->SetState (self->state + 1);
return 0;
}
if ((self->MissileState == NULL && !self->CheckMeleeRange ()) ||
if (((ignoremissile || self->MissileState == NULL) && !self->CheckMeleeRange ()) ||
!P_CheckSight (self, self->target) ||
pr_m_refire() < 4) // Small chance of stopping even when target not dead
{

View File

@ -161,10 +161,9 @@ int consoleplayer; // player taking events
int gametic;
CVAR(Bool, demo_compress, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG);
char demoname[256];
FString demoname;
bool demorecording;
bool demoplayback;
bool netdemo;
bool demonew; // [RH] Only used around G_InitNew for demos
int demover;
BYTE* demobuffer;
@ -415,7 +414,7 @@ CCMD (invuseall)
CCMD (invuse)
{
if (players[consoleplayer].inventorytics == 0 || gameinfo.gametype == GAME_Strife)
if (players[consoleplayer].inventorytics == 0)
{
if (players[consoleplayer].mo) SendItemUse = players[consoleplayer].mo->InvSel;
}
@ -1131,10 +1130,10 @@ void G_Ticker ()
if (cmd->ucmd.forwardmove > TURBOTHRESHOLD &&
!(gametic&31) && ((gametic>>5)&(MAXPLAYERS-1)) == i )
{
Printf ("%s is turbo!\n", players[i].userinfo.netname);
Printf ("%s is turbo!\n", players[i].userinfo.GetName());
}
if (netgame && !players[i].isbot && !netdemo && (gametic%ticdup) == 0)
if (netgame && !players[i].isbot && !demoplayback && (gametic%ticdup) == 0)
{
//players[i].inconsistant = 0;
if (gametic > BACKUPTICS*ticdup && consistancy[i][buf] != cmd->consistancy)
@ -1316,7 +1315,7 @@ void G_PlayerReborn (int player)
int chasecam;
BYTE currclass;
userinfo_t userinfo; // [RH] Save userinfo
botskill_t b_skill;//Added by MC:
botskill_t b_skill; //Added by MC:
APlayerPawn *actor;
PClassPlayerPawn *cls;
FString log;
@ -1330,7 +1329,7 @@ void G_PlayerReborn (int player)
secretcount = p->secretcount;
currclass = p->CurrentPlayerClass;
b_skill = p->skill; //Added by MC:
memcpy (&userinfo, &p->userinfo, sizeof(userinfo));
userinfo.TransferFrom(p->userinfo);
actor = p->mo;
cls = p->cls;
log = p->LogText;
@ -1346,7 +1345,7 @@ void G_PlayerReborn (int player)
p->itemcount = itemcount;
p->secretcount = secretcount;
p->CurrentPlayerClass = currclass;
memcpy (&p->userinfo, &userinfo, sizeof(userinfo));
p->userinfo.TransferFrom(userinfo);
p->mo = actor;
p->cls = cls;
p->LogText = log;
@ -1389,6 +1388,10 @@ bool G_CheckSpot (int playernum, FPlayerStart *mthing)
y = mthing->y;
z = mthing->z;
if (!(level.flags & LEVEL_USEPLAYERSTARTZ))
{
z = 0;
}
z += P_PointInSector (x, y)->floorplane.ZatPoint (x, y);
if (!players[playernum].mo)
@ -2052,7 +2055,6 @@ static void PutSavePic (FILE *file, int width, int height)
}
else
{
P_CheckPlayerSprites();
Renderer->WriteSavePic(&players[consoleplayer], file, width, height);
}
}
@ -2097,11 +2099,8 @@ void G_DoSaveGame (bool okForQuicksave, FString filename, const char *descriptio
G_WriteHubInfo(stdfile);
{
BYTE vars[4096], *vars_p;
vars_p = vars;
C_WriteCVars (&vars_p, CVAR_SERVERINFO);
*vars_p = 0;
M_AppendPNGText (stdfile, "Important CVARs", (char *)vars);
FString vars = C_GetMassCVarString(CVAR_SERVERINFO);
M_AppendPNGText (stdfile, "Important CVARs", vars.GetChars());
}
if (level.time != 0 || level.maptime != 0)
@ -2274,7 +2273,7 @@ void G_WriteDemoTiccmd (ticcmd_t *cmd, int player, int buf)
void G_RecordDemo (const char* name)
{
usergame = false;
strcpy (demoname, name);
demoname = name;
FixPathSeperator (demoname);
DefaultExtension (demoname, ".lmp");
maxdemosize = 0x20000;
@ -2507,7 +2506,7 @@ bool G_ProcessIFFDemo (char *mapname)
}
if (numPlayers > 1)
multiplayer = netgame = netdemo = true;
multiplayer = netgame = true;
if (uncompSize > 0)
{
@ -2645,7 +2644,6 @@ bool G_CheckDemoStatus (void)
P_SetupWeapons_ntohton();
demoplayback = false;
netdemo = false;
netgame = false;
multiplayer = false;
singletics = false;
@ -2713,11 +2711,18 @@ bool G_CheckDemoStatus (void)
formlen = demobuffer + 4;
WriteLong (int(demo_p - demobuffer - 8), &formlen);
M_WriteFile (demoname, demobuffer, int(demo_p - demobuffer));
bool saved = M_WriteFile (demoname, demobuffer, int(demo_p - demobuffer));
M_Free (demobuffer);
demorecording = false;
stoprecording = false;
Printf ("Demo %s recorded\n", demoname);
if (saved)
{
Printf ("Demo %s recorded\n", demoname.GetChars());
}
else
{
Printf ("Demo %s could not be saved\n", demoname.GetChars());
}
}
return false;

View File

@ -75,8 +75,8 @@ DEFINE_ACTION_FUNCTION(AActor, A_ChicAttack)
if (self->CheckMeleeRange())
{
int damage = 1 + (pr_chicattack() & 1);
P_DamageMobj (self->target, self, self, damage, NAME_Melee);
P_TraceBleed (damage, self->target, self);
int newdam = P_DamageMobj (self->target, self, self, damage, NAME_Melee);
P_TraceBleed (newdam > 0 ? newdam : damage, self->target, self);
}
return 0;
}

View File

@ -78,8 +78,8 @@ DEFINE_ACTION_FUNCTION(AActor, A_Srcr1Attack)
if (self->CheckMeleeRange ())
{
int damage = pr_scrc1atk.HitDice (8);
P_DamageMobj (self->target, self, self, damage, NAME_Melee);
P_TraceBleed (damage, self->target, self);
int newdam = P_DamageMobj (self->target, self, self, damage, NAME_Melee);
P_TraceBleed (newdam > 0 ? newdam : damage, self->target, self);
return 0;
}
@ -219,8 +219,8 @@ DEFINE_ACTION_FUNCTION(AActor, A_Srcr2Attack)
if (self->CheckMeleeRange())
{
int damage = pr_s2a.HitDice (20);
P_DamageMobj (self->target, self, self, damage, NAME_Melee);
P_TraceBleed (damage, self->target, self);
int newdam = P_DamageMobj (self->target, self, self, damage, NAME_Melee);
P_TraceBleed (newdam > 0 ? newdam : damage, self->target, self);
return 0;
}
chance = self->health < self->SpawnHealth()/2 ? 96 : 48;

View File

@ -187,7 +187,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_VolcanoBlast)
blast->vely = FixedMul (1*FRACUNIT, finesine[angle]);
blast->velz = (FRACUNIT*5/2) + (pr_blast() << 10);
S_Sound (blast, CHAN_BODY, "world/volcano/shoot", 1, ATTN_NORM);
P_CheckMissileSpawn (blast);
P_CheckMissileSpawn (blast, self->radius);
}
return 0;
}
@ -224,7 +224,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_VolcBallImpact)
tiny->velx = FixedMul (FRACUNIT*7/10, finecosine[angle]);
tiny->vely = FixedMul (FRACUNIT*7/10, finesine[angle]);
tiny->velz = FRACUNIT + (pr_volcimpact() << 9);
P_CheckMissileSpawn (tiny);
P_CheckMissileSpawn (tiny, self->radius);
}
return 0;
}

View File

@ -414,7 +414,7 @@ void FireMacePL1B (AActor *actor)
ball->velx = (actor->velx>>1) + FixedMul(ball->Speed, finecosine[angle]);
ball->vely = (actor->vely>>1) + FixedMul(ball->Speed, finesine[angle]);
S_Sound (ball, CHAN_BODY, "weapons/maceshoot", 1, ATTN_NORM);
P_CheckMissileSpawn (ball);
P_CheckMissileSpawn (ball, actor->radius);
}
//----------------------------------------------------------------------------
@ -568,7 +568,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_MaceBallImpact2)
tiny->velx = (self->velx>>1) + FixedMul(self->velz-FRACUNIT, finecosine[angle]);
tiny->vely = (self->vely>>1) + FixedMul(self->velz-FRACUNIT, finesine[angle]);
tiny->velz = self->velz;
P_CheckMissileSpawn (tiny);
P_CheckMissileSpawn (tiny, self->radius);
tiny = Spawn("MaceFX3", self->x, self->y, self->z, ALLOW_REPLACE);
angle = self->angle-ANG90;
@ -578,7 +578,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_MaceBallImpact2)
tiny->velx = (self->velx>>1) + FixedMul(self->velz-FRACUNIT, finecosine[angle]);
tiny->vely = (self->vely>>1) + FixedMul(self->velz-FRACUNIT, finesine[angle]);
tiny->velz = self->velz;
P_CheckMissileSpawn (tiny);
P_CheckMissileSpawn (tiny, self->radius);
}
else
{ // Explode
@ -851,7 +851,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_SpawnRippers)
angle >>= ANGLETOFINESHIFT;
ripper->velx = FixedMul (ripper->Speed, finecosine[angle]);
ripper->vely = FixedMul (ripper->Speed, finesine[angle]);
P_CheckMissileSpawn (ripper);
P_CheckMissileSpawn (ripper, self->radius);
}
return 0;
}
@ -1118,7 +1118,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_SkullRodStorm)
mo->velx = 1; // Force collision detection
mo->velz = -mo->Speed;
mo->special2 = self->special2; // Transfer player number
P_CheckMissileSpawn (mo);
P_CheckMissileSpawn (mo, self->radius);
if (self->special1 != -1 && !S_IsActorPlayingSomething (self, CHAN_BODY, -1))
{
S_Sound (self, CHAN_BODY|CHAN_LOOP, self->special1, 1, ATTN_NORM);
@ -1386,7 +1386,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_FirePhoenixPL2)
{
S_Sound (self, CHAN_WEAPON|CHAN_LOOP, soundid, 1, ATTN_NORM);
}
P_CheckMissileSpawn (mo);
P_CheckMissileSpawn (mo, self->radius);
return 0;
}

View File

@ -85,8 +85,8 @@ DEFINE_ACTION_FUNCTION(AActor, A_LichAttack)
if (self->CheckMeleeRange ())
{
int damage = pr_atk.HitDice (6);
P_DamageMobj (target, self, self, damage, NAME_Melee);
P_TraceBleed (damage, target, self);
int newdam = P_DamageMobj (target, self, self, damage, NAME_Melee);
P_TraceBleed (newdam > 0 ? newdam : damage, target, self);
return 0;
}
dist = P_AproxDistance (self->x-target->x, self->y-target->y)
@ -118,7 +118,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_LichAttack)
fire->velz = baseFire->velz;
fire->Damage = NULL;
fire->health = (i+1) * 2;
P_CheckMissileSpawn (fire);
P_CheckMissileSpawn (fire, self->radius);
}
}
}
@ -193,7 +193,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_LichIceImpact)
shard->velx = FixedMul (shard->Speed, finecosine[angle]);
shard->vely = FixedMul (shard->Speed, finesine[angle]);
shard->velz = -FRACUNIT*6/10;
P_CheckMissileSpawn (shard);
P_CheckMissileSpawn (shard, self->radius);
}
return 0;
}

View File

@ -52,8 +52,8 @@ DEFINE_ACTION_FUNCTION(AActor, A_KnightAttack)
if (self->CheckMeleeRange ())
{
int damage = pr_knightatk.HitDice (3);
P_DamageMobj (self->target, self, self, damage, NAME_Melee);
P_TraceBleed (damage, self->target, self);
int newdam = P_DamageMobj (self->target, self, self, damage, NAME_Melee);
P_TraceBleed (newdam > 0 ? newdam : damage, self->target, self);
S_Sound (self, CHAN_BODY, "hknight/melee", 1, ATTN_NORM);
return 0;
}

View File

@ -80,8 +80,8 @@ DEFINE_ACTION_FUNCTION(AActor, A_WizAtk3)
if (self->CheckMeleeRange())
{
int damage = pr_wizatk3.HitDice (4);
P_DamageMobj (self->target, self, self, damage, NAME_Melee);
P_TraceBleed (damage, self->target, self);
int newdam = P_DamageMobj (self->target, self, self, damage, NAME_Melee);
P_TraceBleed (newdam > 0 ? newdam : damage, self->target, self);
return 0;
}
PClassActor *fx = PClass::FindActor("WizardFX1");

View File

@ -34,8 +34,8 @@ DEFINE_ACTION_FUNCTION(AActor, A_BishopAttack)
if (self->CheckMeleeRange())
{
int damage = pr_atk.HitDice (4);
P_DamageMobj (self->target, self, self, damage, NAME_Melee);
P_TraceBleed (damage, self->target, self);
int newdam = P_DamageMobj (self->target, self, self, damage, NAME_Melee);
P_TraceBleed (newdam > 0 ? newdam : damage, self->target, self);
return 0;
}
self->special1 = (pr_atk() & 3) + 5;

View File

@ -87,8 +87,8 @@ static void DragonSeek (AActor *actor, angle_t thresh, angle_t turnMax)
if (actor->CheckMeleeRange ())
{
int damage = pr_dragonseek.HitDice (10);
P_DamageMobj (actor->target, actor, actor, damage, NAME_Melee);
P_TraceBleed (damage, actor->target, actor);
int newdam = P_DamageMobj (actor->target, actor, actor, damage, NAME_Melee);
P_TraceBleed (newdam > 0 ? newdam : damage, actor->target, actor);
S_Sound (actor, CHAN_WEAPON, actor->AttackSound, 1, ATTN_NORM);
}
else if (pr_dragonseek() < 128 && P_CheckMissileRange(actor))
@ -206,8 +206,8 @@ DEFINE_ACTION_FUNCTION(AActor, A_DragonFlight)
if (abs(self->angle-angle) < ANGLE_45/2 && self->CheckMeleeRange())
{
int damage = pr_dragonflight.HitDice (8);
P_DamageMobj (self->target, self, self, damage, NAME_Melee);
P_TraceBleed (damage, self->target, self);
int newdam = P_DamageMobj (self->target, self, self, damage, NAME_Melee);
P_TraceBleed (newdam > 0 ? newdam : damage, self->target, self);
S_Sound (self, CHAN_WEAPON, self->AttackSound, 1, ATTN_NORM);
}
else if (abs(self->angle-angle) <= ANGLE_1*20)

View File

@ -128,7 +128,7 @@ bool AArtiPoisonBag3::Use (bool pickup)
mo->target = Owner;
mo->tics -= pr_poisonbag()&3;
P_CheckMissileSpawn(mo);
P_CheckMissileSpawn(mo, Owner->radius);
return true;
}
return false;

View File

@ -72,13 +72,14 @@ DEFINE_ACTION_FUNCTION(AActor, A_PotteryExplode)
}
}
S_Sound (mo, CHAN_BODY, "PotteryExplode", 1, ATTN_NORM);
if (self->args[0]>=0 && self->args[0]<=255 && SpawnableThings[self->args[0]])
{ // Spawn an item
// Spawn an item?
PClassActor *type = P_GetSpawnableType(self->args[0]);
if (type != NULL)
{
if (!((level.flags2 & LEVEL2_NOMONSTERS) || (dmflags & DF_NO_MONSTERS))
|| !(GetDefaultByType (SpawnableThings[self->args[0]])->flags3 & MF3_ISMONSTER))
|| !(GetDefaultByType (type)->flags3 & MF3_ISMONSTER))
{ // Only spawn monsters if not -nomonsters
Spawn (SpawnableThings[self->args[0]],
self->x, self->y, self->z, ALLOW_REPLACE);
Spawn (type, self->x, self->y, self->z, ALLOW_REPLACE);
}
}
return 0;
@ -317,13 +318,14 @@ DEFINE_ACTION_FUNCTION(AActor, A_SoAExplode)
mo->vely = pr_soaexplode.Random2()<<(FRACBITS-6);
}
}
if (self->args[0]>=0 && self->args[0]<=255 && SpawnableThings[self->args[0]])
{ // Spawn an item
// Spawn an item?
PClassActor *type = P_GetSpawnableType(self->args[0]);
if (type != NULL)
{
if (!((level.flags2 & LEVEL2_NOMONSTERS) || (dmflags & DF_NO_MONSTERS))
|| !(GetDefaultByType (SpawnableThings[self->args[0]])->flags3 & MF3_ISMONSTER))
|| !(GetDefaultByType (type)->flags3 & MF3_ISMONSTER))
{ // Only spawn monsters if not -nomonsters
Spawn (SpawnableThings[self->args[0]],
self->x, self->y, self->z, ALLOW_REPLACE);
Spawn (type, self->x, self->y, self->z, ALLOW_REPLACE);
}
}
S_Sound (self, CHAN_BODY, self->DeathSound, 1, ATTN_NORM);

View File

@ -532,5 +532,5 @@ AActor *P_SpawnKoraxMissile (fixed_t x, fixed_t y, fixed_t z,
dist = 1;
}
th->velz = (dest->z-z+(30*FRACUNIT))/dist;
return (P_CheckMissileSpawn(th) ? th : NULL);
return (P_CheckMissileSpawn(th, source->radius) ? th : NULL);
}

View File

@ -197,8 +197,8 @@ DEFINE_ACTION_FUNCTION(AActor, A_SerpentMeleeAttack)
if (self->CheckMeleeRange ())
{
int damage = pr_serpentmeattack.HitDice (5);
P_DamageMobj (self->target, self, self, damage, NAME_Melee);
P_TraceBleed (damage, self->target, self);
int newdam = P_DamageMobj (self->target, self, self, damage, NAME_Melee);
P_TraceBleed (newdam > 0 ? newdam : damage, self->target, self);
S_Sound (self, CHAN_BODY, "SerpentMeleeHit", 1, ATTN_NORM);
}
if (pr_serpentmeattack() < 96)

View File

@ -168,8 +168,8 @@ DEFINE_ACTION_FUNCTION(AActor, A_ThrustImpale)
if (thing == self)
continue; // don't clip against self
P_DamageMobj (thing, self, self, 10001, NAME_Crush);
P_TraceBleed (10001, thing);
int newdam = P_DamageMobj (thing, self, self, 10001, NAME_Crush);
P_TraceBleed (newdam > 0 ? newdam : 10001, thing);
self->args[1] = 1; // Mark thrust thing as bloody
}
return 0;

View File

@ -77,6 +77,7 @@
#include "d_netinf.h"
#include "v_palette.h"
#include "menu/menu.h"
#include "a_sharedglobal.h"
#include "a_strifeglobal.h"
#include "r_data/colormaps.h"
#include "farchive.h"
@ -114,7 +115,6 @@ extern bool timingdemo;
int starttime;
extern bool netdemo;
extern FString BackupSaveName;
bool savegamerestore;
@ -228,9 +228,8 @@ void G_NewInit ()
int i;
G_ClearSnapshots ();
SB_state = screen->GetPageCount ();
ST_SetNeedRefresh();
netgame = false;
netdemo = false;
multiplayer = false;
if (demoplayback)
{
@ -241,14 +240,15 @@ void G_NewInit ()
for (i = 0; i < MAXPLAYERS; ++i)
{
player_t *p = &players[i];
userinfo_t saved_ui = players[i].userinfo;
userinfo_t saved_ui;
saved_ui.TransferFrom(players[i].userinfo);
int chasecam = p->cheats & CF_CHASECAM;
p->~player_t();
::new(p) player_t;
players[i].cheats |= chasecam;
players[i].playerstate = PST_DEAD;
playeringame[i] = 0;
players[i].userinfo = saved_ui;
players[i].userinfo.TransferFrom(saved_ui);
}
BackupSaveName = "";
consoleplayer = 0;
@ -288,7 +288,7 @@ static void InitPlayerClasses ()
{
for (int i = 0; i < MAXPLAYERS; ++i)
{
SinglePlayerClass[i] = players[i].userinfo.PlayerClass;
SinglePlayerClass[i] = players[i].userinfo.GetPlayerClassNum();
if (SinglePlayerClass[i] < 0 || !playeringame[i])
{
SinglePlayerClass[i] = (pr_classchoice()) % PlayerClasses.Size ();
@ -426,7 +426,7 @@ void G_InitNew (const char *mapname, bool bTitleLevel)
demoplayback = false;
automapactive = false;
viewactive = true;
BorderNeedRefresh = screen->GetPageCount ();
V_SetBorderNeedRefresh();
//Added by MC: Initialize bots.
if (!deathmatch)
@ -491,7 +491,7 @@ void G_ChangeLevel(const char *levelname, int position, int flags, int nextSkill
}
else if (strncmp(levelname, "enDSeQ", 6) != 0)
{
nextinfo = FindLevelInfo (levelname);
nextinfo = FindLevelInfo (levelname, false);
if (nextinfo != NULL)
{
level_info_t *nextredir = nextinfo->CheckLevelRedirect();
@ -657,17 +657,14 @@ void G_DoCompleted (void)
}
else
{
if (strncmp (nextlevel, "enDSeQ", 6) == 0)
level_info_t *nextinfo = FindLevelInfo (nextlevel, false);
if (nextinfo == NULL || strncmp (nextlevel, "enDSeQ", 6) == 0)
{
wminfo.next = nextlevel;
wminfo.LName1 = NULL;
}
else
{
level_info_t *nextinfo = FindLevelInfo (nextlevel);
wminfo.next = nextinfo->mapname;
wminfo.LName1 = TexMan[TexMan.CheckForTexture(nextinfo->pname, FTexture::TEX_MiscPatch)];
}
@ -740,6 +737,9 @@ void G_DoCompleted (void)
if (!(level.flags2 & LEVEL2_FORGETSTATE))
{
G_SnapshotLevel ();
// Do not free any global strings this level might reference
// while it's not loaded.
FBehavior::StaticLockLevelVarStrings();
}
else
{ // Make sure we don't have a snapshot lying around from before.
@ -882,6 +882,10 @@ void G_DoLoadLevel (int position, bool autosave)
{
level.flags2 &= ~LEVEL2_NOMONSTERS;
}
if (changeflags & CHANGELEVEL_PRERAISEWEAPON)
{
level.flags2 |= LEVEL2_PRERAISEWEAPON;
}
level.maptime = 0;
P_SetupLevel (level.mapname, position);
@ -928,7 +932,7 @@ void G_DoLoadLevel (int position, bool autosave)
G_UnSnapshotLevel (!savegamerestore); // [RH] Restore the state of the level.
G_FinishTravel ();
// For each player, if they are viewing through a player, make sure it is themselves.
for (int ii = 0; i < MAXPLAYERS; ++i)
for (int ii = 0; ii < MAXPLAYERS; ++ii)
{
if (playeringame[ii] && (players[ii].camera == NULL || players[ii].camera->player != NULL))
{
@ -1128,7 +1132,7 @@ void G_FinishTravel ()
// The player being spawned here is a short lived dummy and
// must not start any ENTER script or big problems will happen.
pawndup = P_SpawnPlayer (&playerstarts[pawn->player - players], int(pawn->player - players), true);
pawndup = P_SpawnPlayer (&playerstarts[pawn->player - players], int(pawn->player - players), SPF_TEMPPLAYER);
if (!(changeflags & CHANGELEVEL_KEEPFACING))
{
pawn->angle = pawndup->angle;
@ -1273,6 +1277,7 @@ void G_InitLevelLocals ()
NormalLight.ChangeFade (level.fadeto);
level.DefaultEnvironment = info->DefaultEnvironment;
level.DefaultSkybox = NULL;
}
//==========================================================================
@ -1436,6 +1441,11 @@ void G_SerializeLevel (FArchive &arc, bool hubLoad)
P_SerializeSubsectors(arc);
StatusBar->Serialize (arc);
if (SaveVersion >= 4222)
{ // This must be done *after* thinkers are serialized.
arc << level.DefaultSkybox;
}
arc << level.total_monsters << level.total_items << level.total_secrets;
// Does this level have custom translations?
@ -1568,6 +1578,11 @@ void G_UnSnapshotLevel (bool hubLoad)
}
// No reason to keep the snapshot around once the level's been entered.
level.info->ClearSnapshot();
if (hubLoad)
{
// Unlock ACS global strings that were locked when the snapshot was made.
FBehavior::StaticUnlockLevelVarStrings();
}
}
//==========================================================================

View File

@ -179,7 +179,7 @@ enum ELevelFlags
LEVEL2_KEEPFULLINVENTORY = 0x00000040, // doesn't reduce the amount of inventory items to 1
/* = 0x00000080, */
LEVEL2_PRERAISEWEAPON = 0x00000080, // players should spawn with their weapons fully raised (but not when respawning it multiplayer)
LEVEL2_MONSTERFALLINGDAMAGE = 0x00000100,
LEVEL2_CLIPMIDTEX = 0x00000200,
LEVEL2_WRAPMIDTEX = 0x00000400,
@ -424,6 +424,8 @@ struct FLevelLocals
int airsupply;
int DefaultEnvironment; // Default sound environment.
TObjPtr<class ASkyViewpoint> DefaultSkybox;
FSectorScrollValues *Scrolls; // NULL if no DScrollers in this level
SBYTE WallVertLight; // Light diffs for vert/horiz walls
@ -498,6 +500,7 @@ enum
CHANGELEVEL_CHANGESKILL = 8,
CHANGELEVEL_NOINTERMISSION = 16,
CHANGELEVEL_RESETHEALTH = 32,
CHANGELEVEL_PRERAISEWEAPON = 64,
};
void G_ChangeLevel(const char *levelname, int position, int flags, int nextSkill=-1);
@ -512,7 +515,7 @@ void G_InitLevelLocals (void);
void G_AirControlChanged ();
cluster_info_t *FindClusterInfo (int cluster);
level_info_t *FindLevelInfo (const char *mapname);
level_info_t *FindLevelInfo (const char *mapname, bool allowdefault=true);
level_info_t *FindLevelByNum (int num);
level_info_t *CheckLevelRedirect (level_info_t *info);

View File

@ -82,7 +82,7 @@ static int FindWadLevelInfo (const char *name)
//
//==========================================================================
level_info_t *FindLevelInfo (const char *mapname)
level_info_t *FindLevelInfo (const char *mapname, bool allowdefault)
{
int i;
@ -90,7 +90,7 @@ level_info_t *FindLevelInfo (const char *mapname)
{
return &wadlevelinfos[i];
}
else
else if (allowdefault)
{
if (TheDefaultLevelInfo.LevelName.IsEmpty())
{
@ -100,6 +100,7 @@ level_info_t *FindLevelInfo (const char *mapname)
}
return &TheDefaultLevelInfo;
}
return NULL;
}
//==========================================================================
@ -197,6 +198,9 @@ void G_ClearSnapshots (void)
{
wadlevelinfos[i].ClearSnapshot();
}
// Since strings are only locked when snapshotting a level, unlock them
// all now, since we got rid of all the snapshots that cared about them.
GlobalACSStrings.UnlockAll();
}
//==========================================================================
@ -305,6 +309,10 @@ FString level_info_t::LookupLevelName()
{
mysnprintf (checkstring, countof(checkstring), "%d: ", atoi(mapname + 3));
}
else if (mapname[0] == 'L' && mapname[1] == 'E' && mapname[2] == 'V' && mapname[3] == 'E' && mapname[4] == 'L')
{
mysnprintf (checkstring, countof(checkstring), "%d: ", atoi(mapname + 5));
}
thename = strstr (lookedup, checkstring);
if (thename == NULL)
{
@ -1267,6 +1275,7 @@ MapFlagHandlers[] =
{ "forgetstate", MITYPE_SETFLAG2, LEVEL2_FORGETSTATE, 0 },
{ "rememberstate", MITYPE_CLRFLAG2, LEVEL2_FORGETSTATE, 0 },
{ "unfreezesingleplayerconversations",MITYPE_SETFLAG2, LEVEL2_CONV_SINGLE_UNFREEZE, 0 },
{ "spawnwithweaponraised", MITYPE_SETFLAG2, LEVEL2_PRERAISEWEAPON, 0 },
{ "nobotnodes", MITYPE_IGNORE, 0, 0 }, // Skulltag option: nobotnodes
{ "compat_shorttex", MITYPE_COMPATFLAG, COMPATF_SHORTTEX, 0 },
{ "compat_stairs", MITYPE_COMPATFLAG, COMPATF_STAIRINDEX, 0 },

View File

@ -154,8 +154,8 @@ DEFINE_ACTION_FUNCTION(AActor, A_MinotaurAtk1)
if (self->CheckMeleeRange())
{
int damage = pr_minotauratk1.HitDice (4);
P_DamageMobj (self->target, self, self, damage, NAME_Melee);
P_TraceBleed (damage, self->target, self);
int newdam = P_DamageMobj (self->target, self, self, damage, NAME_Melee);
P_TraceBleed (newdam > 0 ? newdam : damage, self->target, self);
if ((player = self->target->player) != NULL &&
player->mo == self->target)
{ // Squish the player
@ -297,8 +297,8 @@ DEFINE_ACTION_FUNCTION(AActor, A_MinotaurAtk2)
{
int damage;
damage = pr_atk.HitDice (friendly ? 3 : 5);
P_DamageMobj (self->target, self, self, damage, NAME_Melee);
P_TraceBleed (damage, self->target, self);
int newdam = P_DamageMobj (self->target, self, self, damage, NAME_Melee);
P_TraceBleed (newdam > 0 ? newdam : damage, self->target, self);
return 0;
}
z = self->z + 40*FRACUNIT;
@ -346,8 +346,8 @@ DEFINE_ACTION_FUNCTION(AActor, A_MinotaurAtk3)
int damage;
damage = pr_minotauratk3.HitDice (friendly ? 3 : 5);
P_DamageMobj (self->target, self, self, damage, NAME_Melee);
P_TraceBleed (damage, self->target, self);
int newdam = P_DamageMobj (self->target, self, self, damage, NAME_Melee);
P_TraceBleed (newdam > 0 ? newdam : damage, self->target, self);
if ((player = self->target->player) != NULL &&
player->mo == self->target)
{ // Squish the player
@ -397,7 +397,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_MntrFloorFire)
mo = Spawn("MinotaurFX3", x, y, self->floorz, ALLOW_REPLACE);
mo->target = self->target;
mo->velx = 1; // Force block checking
P_CheckMissileSpawn (mo);
P_CheckMissileSpawn (mo, self->radius);
return 0;
}
@ -419,8 +419,8 @@ void P_MinotaurSlam (AActor *source, AActor *target)
target->velx += FixedMul (thrust, finecosine[angle]);
target->vely += FixedMul (thrust, finesine[angle]);
damage = pr_minotaurslam.HitDice (static_cast<AMinotaur *>(source) ? 4 : 6);
P_DamageMobj (target, NULL, NULL, damage, NAME_Melee);
P_TraceBleed (damage, target, angle, 0);
int newdam = P_DamageMobj (target, NULL, NULL, damage, NAME_Melee);
P_TraceBleed (newdam > 0 ? newdam : damage, target, angle, 0);
if (target->player)
{
target->reactiontime = 14+(pr_minotaurslam()&7);
@ -589,7 +589,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_MinotaurChase)
if (!self1->target || (self1->target->health <= 0) ||
!(self1->target->flags&MF_SHOOTABLE))
{ // look for a new target
self1->SetState (self1->FindState ("Spawn"));
self1->SetIdle();
return 0;
}

View File

@ -380,7 +380,6 @@ CUSTOM_CVAR(Int, sv_corpsequeuesize, 64, CVAR_ARCHIVE|CVAR_SERVERINFO)
while (first != NULL && first->Count > (DWORD)self)
{
DCorpsePointer *next = iterator.Next ();
next->Count = first->Count;
first->Destroy ();
first = next;
}
@ -403,9 +402,8 @@ DCorpsePointer::DCorpsePointer (AActor *ptr)
if (first->Count >= (DWORD)sv_corpsequeuesize)
{
DCorpsePointer *next = iterator.Next ();
next->Count = first->Count;
first->Destroy ();
return;
first = next;
}
}
++first->Count;

View File

@ -1107,7 +1107,6 @@ void APowerWeaponLevel2::EndEffect ()
Super::EndEffect();
if (player != NULL)
{
if (player->ReadyWeapon != NULL &&
player->ReadyWeapon->WeaponFlags & WIF_POWERED_UP)
{
@ -1156,6 +1155,25 @@ void APlayerSpeedTrail::Tick ()
IMPLEMENT_CLASS (APowerSpeed)
//===========================================================================
//
// APowerSpeed :: Serialize
//
//===========================================================================
void APowerSpeed::Serialize(FArchive &arc)
{
Super::Serialize (arc);
if (SaveVersion < 4146)
{
SpeedFlags = 0;
}
else
{
arc << SpeedFlags;
}
}
//===========================================================================
//
// APowerSpeed :: GetSpeedFactor
@ -1164,8 +1182,10 @@ IMPLEMENT_CLASS (APowerSpeed)
fixed_t APowerSpeed ::GetSpeedFactor ()
{
if (Inventory != NULL) return FixedMul(Speed, Inventory->GetSpeedFactor());
else return Speed;
if (Inventory != NULL)
return FixedMul(Speed, Inventory->GetSpeedFactor());
else
return Speed;
}
//===========================================================================
@ -1184,12 +1204,22 @@ void APowerSpeed::DoEffect ()
if (Owner->player->cheats & CF_PREDICTING)
return;
if (SpeedFlags & PSF_NOTRAIL)
return;
if (level.time & 1)
return;
// check if another speed item is present to avoid multiple drawing of the speed trail.
if (Inventory != NULL && Inventory->GetSpeedFactor() > FRACUNIT)
return;
// Check if another speed item is present to avoid multiple drawing of the speed trail.
// Only the last PowerSpeed without PSF_NOTRAIL set will actually draw the trail.
for (AInventory *item = Inventory; item != NULL; item = item->Inventory)
{
if (item->IsKindOf(RUNTIME_CLASS(APowerSpeed)) &&
!(static_cast<APowerSpeed *>(item)->SpeedFlags & PSF_NOTRAIL))
{
return;
}
}
if (P_AproxDistance (Owner->velx, Owner->vely) <= 12*FRACUNIT)
return;
@ -1411,7 +1441,8 @@ void APowerTimeFreezer::DoEffect()
Super::DoEffect();
// [RH] Do not change LEVEL_FROZEN on odd tics, or the Revenant's tracer
// will get thrown off.
if (level.time & 1)
// [ED850] Don't change it if the player is predicted either.
if (level.time & 1 || (Owner != NULL && Owner->player != NULL && Owner->player->cheats & CF_PREDICTING))
{
return;
}
@ -1503,7 +1534,7 @@ void APowerDamage::EndEffect( )
//===========================================================================
//
// APowerDamage :: AbsorbDamage
// APowerDamage :: ModifyDamage
//
//===========================================================================

View File

@ -30,6 +30,9 @@ protected:
virtual void InitEffect ();
virtual void DoEffect ();
virtual void EndEffect ();
friend void EndAllPowerupEffects(AInventory *item);
friend void InitAllPowerupEffects(AInventory *item);
};
// An artifact is an item that gives the player a powerup when activated.
@ -143,9 +146,14 @@ class APowerSpeed : public APowerup
DECLARE_CLASS (APowerSpeed, APowerup)
protected:
void DoEffect ();
void Serialize(FArchive &arc);
fixed_t GetSpeedFactor();
public:
int SpeedFlags;
};
#define PSF_NOTRAIL 1
class APowerMinotaur : public APowerup
{
DECLARE_CLASS (APowerMinotaur, APowerup)

View File

@ -755,6 +755,48 @@ CCMD (spray)
Net_WriteString (argv[1]);
}
DBaseDecal *ShootDecal(const FDecalTemplate *tpl, AActor *basisactor, sector_t *sec, fixed_t x, fixed_t y, fixed_t z, angle_t angle, fixed_t tracedist, bool permanent)
{
if (tpl == NULL || (tpl = tpl->GetDecal()) == NULL)
{
return NULL;
}
FTraceResults trace;
DBaseDecal *decal;
side_t *wall;
angle >>= ANGLETOFINESHIFT;
Trace(x, y, z, sec,
finecosine[angle], finesine[angle], 0,
tracedist, 0, 0, NULL, trace, TRACE_NoSky);
if (trace.HitType == TRACE_HitWall)
{
if (permanent)
{
decal = new DBaseDecal(trace.Z);
wall = trace.Line->sidedef[trace.Side];
decal->StickToWall(wall, trace.X, trace.Y, trace.ffloor);
tpl->ApplyToDecal(decal, wall);
// Spread decal to nearby walls if it does not all fit on this one
if (cl_spreaddecals)
{
decal->Spread(tpl, wall, trace.X, trace.Y, trace.Z, trace.ffloor);
}
return decal;
}
else
{
return DImpactDecal::StaticCreate(tpl,
trace.X, trace.Y, trace.Z,
trace.Line->sidedef[trace.Side], NULL);
}
}
return NULL;
}
class ADecal : public AActor
{
DECLARE_CLASS (ADecal, AActor);
@ -767,9 +809,6 @@ IMPLEMENT_CLASS (ADecal)
void ADecal::BeginPlay ()
{
const FDecalTemplate *tpl;
FTraceResults trace;
DBaseDecal *decal;
side_t *wall;
Super::BeginPlay ();
@ -781,31 +820,13 @@ void ADecal::BeginPlay ()
if (!tpl->PicNum.Exists())
{
Printf("Decal actor at (%d,%d) does not have a valid texture\n", x>>FRACBITS, y>>FRACBITS);
}
else
{
// Look for a wall within 64 units behind the actor. If none can be
// found, then no decal is created, and this actor is destroyed
// without effectively doing anything.
Trace (x, y, z, Sector,
finecosine[(angle+ANGLE_180)>>ANGLETOFINESHIFT],
finesine[(angle+ANGLE_180)>>ANGLETOFINESHIFT], 0,
64*FRACUNIT, 0, 0, NULL, trace, TRACE_NoSky);
if (trace.HitType == TRACE_HitWall)
{
decal = new DBaseDecal (this);
wall = trace.Line->sidedef[trace.Side];
decal->StickToWall (wall, trace.X, trace.Y, trace.ffloor);
tpl->ApplyToDecal (decal, wall);
// Spread decal to nearby walls if it does not all fit on this one
if (cl_spreaddecals)
{
decal->Spread (tpl, wall, trace.X, trace.Y, z, trace.ffloor);
}
}
else
if (NULL == ShootDecal(tpl, this, Sector, x, y, z, angle + ANGLE_180, 64*FRACUNIT, true))
{
DPrintf ("Could not find a wall to stick decal to at (%d,%d)\n", x>>FRACBITS, y>>FRACBITS);
}

View File

@ -15,6 +15,9 @@
static FRandom pr_morphmonst ("MorphMonster");
void EndAllPowerupEffects(AInventory *item);
void InitAllPowerupEffects(AInventory *item);
//---------------------------------------------------------------------------
//
// FUNC P_MorphPlayer
@ -74,6 +77,7 @@ bool P_MorphPlayer (player_t *activator, player_t *p, PClassPlayerPawn *spawntyp
}
morphed = static_cast<APlayerPawn *>(Spawn (spawntype, actor->x, actor->y, actor->z, NO_REPLACE));
EndAllPowerupEffects(actor->Inventory);
DObject::StaticPointerSubstitution (actor, morphed);
if ((actor->tid != 0) && (style & MORPH_NEWTIDBEHAVIOUR))
{
@ -144,6 +148,7 @@ bool P_MorphPlayer (player_t *activator, player_t *p, PClassPlayerPawn *spawntyp
}
item = next;
}
InitAllPowerupEffects(morphed->Inventory);
morphed->ActivateMorphWeapon ();
if (p->camera == actor)
{
@ -201,10 +206,8 @@ bool P_UndoPlayerMorph (player_t *activator, player_t *player, int unmorphflag,
}
pmo->player = NULL;
mo->ObtainInventory (pmo);
DObject::StaticPointerSubstitution (pmo, mo);
// Remove the morph power if the morph is being undone prematurely.
for (AInventory *item = mo->Inventory, *next = NULL; item != NULL; item = next)
for (AInventory *item = pmo->Inventory, *next = NULL; item != NULL; item = next)
{
next = item->Inventory;
if (item->IsKindOf(RUNTIME_CLASS(APowerMorph)))
@ -213,6 +216,9 @@ bool P_UndoPlayerMorph (player_t *activator, player_t *player, int unmorphflag,
item->Destroy();
}
}
EndAllPowerupEffects(pmo->Inventory);
mo->ObtainInventory (pmo);
DObject::StaticPointerSubstitution (pmo, mo);
if ((pmo->tid != 0) && (player->MorphStyle & MORPH_NEWTIDBEHAVIOUR))
{
mo->tid = pmo->tid;
@ -235,6 +241,7 @@ bool P_UndoPlayerMorph (player_t *activator, player_t *player, int unmorphflag,
mo->flags2 = (mo->flags2 & ~MF2_FLY) | (pmo->flags2 & MF2_FLY);
mo->flags3 = (mo->flags3 & ~MF3_GHOST) | (pmo->flags3 & MF3_GHOST);
mo->Score = pmo->Score;
InitAllPowerupEffects(mo->Inventory);
PClassActor *exit_flash = player->MorphExitFlash;
bool correctweapon = !!(player->MorphStyle & MORPH_LOSEACTUALWEAPON);
@ -280,11 +287,11 @@ bool P_UndoPlayerMorph (player_t *activator, player_t *player, int unmorphflag,
size_t skinindex = 0;
// If a custom skin was in use, then reload it
// or else the base skin for the player class.
if ((unsigned int)player->userinfo.skin >= PlayerClasses.Size () &&
(size_t)player->userinfo.skin < numskins)
if ((unsigned int)player->userinfo.GetSkin() >= PlayerClasses.Size () &&
(size_t)player->userinfo.GetSkin() < numskins)
{
skinindex = player->userinfo.skin;
skinindex = player->userinfo.GetSkin();
}
else if (PlayerClasses.Size () > 1)
{
@ -366,7 +373,7 @@ bool P_MorphMonster (AActor *actor, PClassActor *spawntype, int duration, int st
{
AMorphedMonster *morphed;
if (actor->player || spawntype == NULL ||
if (actor == NULL || actor->player || spawntype == NULL ||
actor->flags3 & MF3_DONTMORPH ||
!(actor->flags3 & MF3_ISMONSTER) ||
!spawntype->IsDescendantOf (RUNTIME_CLASS(AMorphedMonster)))
@ -387,8 +394,8 @@ bool P_MorphMonster (AActor *actor, PClassActor *spawntype, int duration, int st
morphed->MorphStyle = style;
morphed->MorphExitFlash = (exit_flash) ? exit_flash : RUNTIME_CLASS(ATeleportFog);
morphed->FlagsSave = actor->flags & ~MF_JUSTHIT;
//morphed->special = actor->special;
//memcpy (morphed->args, actor->args, sizeof(actor->args));
morphed->special = actor->special;
memcpy (morphed->args, actor->args, sizeof(actor->args));
morphed->CopyFriendliness (actor, true);
morphed->flags |= actor->flags & MF_SHADOW;
morphed->flags3 |= actor->flags3 & MF3_GHOST;
@ -398,6 +405,7 @@ bool P_MorphMonster (AActor *actor, PClassActor *spawntype, int duration, int st
}
morphed->AddToHash ();
actor->RemoveFromHash ();
actor->special = 0;
actor->tid = 0;
actor->flags &= ~(MF_SOLID|MF_SHOOTABLE);
actor->flags |= MF_UNMORPHED;
@ -420,7 +428,8 @@ bool P_UndoMonsterMorph (AMorphedMonster *beast, bool force)
if (beast->UnmorphTime == 0 ||
beast->UnmorphedMe == NULL ||
beast->flags3 & MF3_STAYMORPHED)
beast->flags3 & MF3_STAYMORPHED ||
beast->UnmorphedMe->flags3 & MF3_STAYMORPHED)
{
return false;
}
@ -428,10 +437,13 @@ bool P_UndoMonsterMorph (AMorphedMonster *beast, bool force)
actor->SetOrigin (beast->x, beast->y, beast->z);
actor->flags |= MF_SOLID;
beast->flags &= ~MF_SOLID;
int beastflags6 = beast->flags6;
beast->flags6 &= ~MF6_TOUCHY;
if (!force && !P_TestMobjLocation (actor))
{ // Didn't fit
actor->flags &= ~MF_SOLID;
beast->flags |= MF_SOLID;
beast->flags6 = beastflags6;
beast->UnmorphTime = level.time + 5*TICRATE; // Next try in 5 seconds
return false;
}
@ -538,6 +550,46 @@ bool P_MorphedDeath(AActor *actor, AActor **morphed, int *morphedstyle, int *mor
return false;
}
//===========================================================================
//
// EndAllPowerupEffects
//
// Calls EndEffect() on every Powerup in the inventory list.
//
//===========================================================================
void EndAllPowerupEffects(AInventory *item)
{
while (item != NULL)
{
if (item->IsKindOf(RUNTIME_CLASS(APowerup)))
{
static_cast<APowerup *>(item)->EndEffect();
}
item = item->Inventory;
}
}
//===========================================================================
//
// InitAllPowerupEffects
//
// Calls InitEffect() on every Powerup in the inventory list.
//
//===========================================================================
void InitAllPowerupEffects(AInventory *item)
{
while (item != NULL)
{
if (item->IsKindOf(RUNTIME_CLASS(APowerup)))
{
static_cast<APowerup *>(item)->InitEffect();
}
item = item->Inventory;
}
}
// Base class for morphing projectiles --------------------------------------
IMPLEMENT_CLASS(AMorphProjectile)

View File

@ -561,6 +561,33 @@ void AInventory::BeginPlay ()
flags |= MF_DROPPED; // [RH] Items are dropped by default
}
//===========================================================================
//
// AInventory :: Grind
//
//===========================================================================
bool AInventory::Grind(bool items)
{
// Does this grind request even care about items?
if (!items)
{
return false;
}
// Dropped items are normally destroyed by crushers. Set the DONTGIB flag,
// and they'll act like corpses with it set and be immune to crushers.
if (flags & MF_DROPPED)
{
if (!(flags3 & MF3_DONTGIB))
{
Destroy();
}
return false;
}
// Non-dropped items call the super method for compatibility.
return Super::Grind(items);
}
//===========================================================================
//
// AInventory :: DoEffect
@ -984,6 +1011,8 @@ void AInventory::Touch (AActor *toucher)
toucher = toucher->player->mo;
}
bool localview = toucher->CheckLocalView(consoleplayer);
if (!CallTryPickup (toucher, &toucher)) return;
// This is the only situation when a pickup flash should ever play.
@ -996,7 +1025,7 @@ void AInventory::Touch (AActor *toucher)
{
const char * message = PickupMessage ();
if (message != NULL && *message != 0 && toucher->CheckLocalView (consoleplayer)
if (message != NULL && *message != 0 && localview
&& (StaticLastMessageTic != gametic || StaticLastMessage != message))
{
StaticLastMessageTic = gametic;
@ -1010,7 +1039,10 @@ void AInventory::Touch (AActor *toucher)
if (toucher->player != NULL)
{
PlayPickupSound (toucher->player->mo);
toucher->player->bonuscount = BONUSADD;
if (!(ItemFlags & IF_NOSCREENFLASH))
{
toucher->player->bonuscount = BONUSADD;
}
}
else
{

View File

@ -124,6 +124,7 @@ enum
IF_PERSISTENTPOWER = 1<<18, // Powerup is kept when travelling between levels
IF_RESTRICTABSOLUTELY = 1<<19, // RestrictedTo and ForbiddenTo do not allow pickup in any form by other classes
IF_NEVERRESPAWN = 1<<20, // Never, ever respawns
IF_NOSCREENFLASH = 1<<21, // No pickup flash on the player's screen
};
@ -160,6 +161,7 @@ public:
virtual bool SpecialDropAction (AActor *dropper);
virtual bool DrawPowerup (int x, int y);
virtual void DoEffect ();
virtual bool Grind(bool items);
virtual const char *PickupMessage ();
virtual void PlayPickupSound (AActor *toucher);
@ -283,6 +285,7 @@ public:
PClassActor *ProjectileType; // Projectile used by primary attack
PClassActor *AltProjectileType; // Projectile used by alternate attack
int SelectionOrder; // Lower-numbered weapons get picked first
int MinSelAmmo1, MinSelAmmo2; // Ignore in BestWeapon() if inadequate ammo
fixed_t MoveCombatDist; // Used by bots, but do they *really* need it?
int ReloadCounter; // For A_CheckForReload
int BobStyle; // [XA] Bobbing style. Defines type of bobbing (e.g. Normal, Alpha)
@ -376,7 +379,17 @@ enum
WIF_BOT_BFG = 1<<28, // this is a BFG
};
#define S_LIGHTDONE 0
class AWeaponGiver : public AWeapon
{
DECLARE_CLASS(AWeaponGiver, AWeapon)
public:
bool TryPickup(AActor *&toucher);
void Serialize(FArchive &arc);
fixed_t DropAmmoFactor;
};
// Health is some item that gives the player health when picked up.
class PClassHealth : public PClassInventory

View File

@ -31,6 +31,7 @@ class ARandomSpawner : public AActor
DDropItem *di; // di will be our drop item list iterator
DDropItem *drop; // while drop stays as the reference point.
int n = 0;
bool nomonsters = (dmflags & DF_NO_MONSTERS) || (level.flags2 & LEVEL2_NOMONSTERS);
Super::BeginPlay();
drop = di = GetDropItems();
@ -40,29 +41,46 @@ class ARandomSpawner : public AActor
{
if (di->Name != NAME_None)
{
if (di->Amount < 0) di->Amount = 1; // default value is -1, we need a positive value.
n += di->Amount; // this is how we can weight the list.
if (!nomonsters || !(GetDefaultByType(PClass::FindClass(di->Name))->flags3 & MF3_ISMONSTER))
{
if (di->Amount < 0) di->Amount = 1; // default value is -1, we need a positive value.
n += di->Amount; // this is how we can weight the list.
}
di = di->Next;
}
}
if (n == 0)
{ // Nothing left to spawn. They must have all been monsters, and monsters are disabled.
Destroy();
return;
}
// Then we reset the iterator to the start position...
di = drop;
// Take a random number...
n = pr_randomspawn(n);
// And iterate in the array up to the random number chosen.
while (n > -1)
while (n > -1 && di != NULL)
{
if (di->Name != NAME_None)
if (di->Name != NAME_None &&
(!nomonsters || !(GetDefaultByType(PClass::FindClass(di->Name))->flags3 & MF3_ISMONSTER)))
{
n -= di->Amount;
if ((di->Next != NULL) && (n > -1)) di = di->Next; else n = -1;
if ((di->Next != NULL) && (n > -1))
di = di->Next;
else
n = -1;
}
else
{
di = di->Next;
}
}
// So now we can spawn the dropped item.
if (bouncecount >= MAX_RANDOMSPAWNERS_RECURSION) // Prevents infinite recursions
if (di == NULL || bouncecount >= MAX_RANDOMSPAWNERS_RECURSION) // Prevents infinite recursions
{
Spawn("Unknown", x, y, z, NO_REPLACE); // Show that there's a problem.
Destroy(); return;
Destroy();
return;
}
else if (pr_randomspawn() <= di->Probability) // prob 255 = always spawn, prob 0 = almost never spawn.
{
@ -167,7 +185,7 @@ class ARandomSpawner : public AActor
newmobj->z += SpawnPoint[2];
}
if (newmobj->flags & MF_MISSILE)
P_CheckMissileSpawn(newmobj);
P_CheckMissileSpawn(newmobj, 0);
// Bouncecount is used to count how many recursions we're in.
if (newmobj->IsKindOf(PClass::FindClass("RandomSpawner")))
newmobj->bouncecount = ++bouncecount;
@ -179,8 +197,10 @@ class ARandomSpawner : public AActor
if (rep && ((rep->flags4 & MF4_BOSSDEATH) || (rep->flags2 & MF2_BOSS)))
boss = true;
}
if (boss) this->tracer = newmobj;
else Destroy(); // "else" because a boss-replacing spawner must wait until it can call A_BossDeath.
if (boss)
this->tracer = newmobj;
else // "else" because a boss-replacing spawner must wait until it can call A_BossDeath.
Destroy();
}
void Tick() // This function is needed for handling boss replacers

View File

@ -9,7 +9,8 @@ struct vertex_t;
struct side_t;
struct F3DFloor;
extern void P_SpawnDirt (AActor *actor, fixed_t radius);
void P_SpawnDirt (AActor *actor, fixed_t radius);
class DBaseDecal *ShootDecal(const FDecalTemplate *tpl, AActor *basisactor, sector_t *sec, fixed_t x, fixed_t y, fixed_t z, angle_t angle, fixed_t tracedist, bool permanent);
class DBaseDecal : public DThinker
{

View File

@ -49,21 +49,9 @@ void ASkyViewpoint::BeginPlay ()
{
Super::BeginPlay ();
if (tid == 0)
if (tid == 0 && level.DefaultSkybox == NULL)
{
int i;
for (i = 0; i <numsectors; i++)
{
if (sectors[i].FloorSkyBox == NULL)
{
sectors[i].FloorSkyBox = this;
}
if (sectors[i].CeilingSkyBox == NULL)
{
sectors[i].CeilingSkyBox = this;
}
}
level.DefaultSkybox = this;
}
}
@ -87,6 +75,10 @@ void ASkyViewpoint::Destroy ()
sectors[i].CeilingSkyBox = NULL;
}
}
if (level.DefaultSkybox == this)
{
level.DefaultSkybox = NULL;
}
Super::Destroy();
}

View File

@ -83,6 +83,10 @@ void AWeapon::Serialize (FArchive &arc)
}
arc << FOVScale
<< Crosshair;
if (SaveVersion >= 4203)
{
arc << MinSelAmmo1 << MinSelAmmo2;
}
}
//===========================================================================
@ -328,7 +332,7 @@ void AWeapon::AttachToOwner (AActor *other)
SisterWeapon = AddWeapon (SisterWeaponType);
if (Owner->player != NULL)
{
if (!Owner->player->userinfo.neverswitch && !(WeaponFlags & WIF_NO_AUTO_SWITCH))
if (!Owner->player->userinfo.GetNeverSwitch() && !(WeaponFlags & WIF_NO_AUTO_SWITCH))
{
Owner->player->PendingWeapon = this;
}
@ -719,16 +723,17 @@ FState *AWeapon::GetZoomState ()
/* Weapon giver ***********************************************************/
class AWeaponGiver : public AWeapon
{
DECLARE_CLASS(AWeaponGiver, AWeapon)
public:
bool TryPickup(AActor *&toucher);
};
IMPLEMENT_CLASS(AWeaponGiver)
void AWeaponGiver::Serialize(FArchive &arc)
{
Super::Serialize(arc);
if (SaveVersion >= 4246)
{
arc << DropAmmoFactor;
}
}
bool AWeaponGiver::TryPickup(AActor *&toucher)
{
DDropItem *di = GetDropItems();
@ -746,8 +751,17 @@ bool AWeaponGiver::TryPickup(AActor *&toucher)
{
weap->ItemFlags &= ~IF_ALWAYSPICKUP; // use the flag of this item only.
weap->flags = (weap->flags & ~MF_DROPPED) | (this->flags & MF_DROPPED);
// If our ammo gives are non-negative, transfer them to the real weapon.
if (AmmoGive1 >= 0) weap->AmmoGive1 = AmmoGive1;
if (AmmoGive2 >= 0) weap->AmmoGive2 = AmmoGive2;
// If DropAmmoFactor is non-negative, modify the given ammo amounts.
if (DropAmmoFactor > 0)
{
weap->AmmoGive1 = FixedMul(weap->AmmoGive1, DropAmmoFactor);
weap->AmmoGive2 = FixedMul(weap->AmmoGive2, DropAmmoFactor);
}
weap->BecomeItem();
}
else return false;

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