quakespasm-spike-r5

This commit is contained in:
Spike 2016-10-27 19:35:10 -06:00 committed by Eric Wasylishen
parent 04673200eb
commit 97bccd3be6
32 changed files with 8496 additions and 338 deletions

2037
e1m1_effects/maps/e1m1.ent Executable file

File diff suppressed because it is too large Load Diff

BIN
e1m1_effects/particles/flare.tga Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View File

@ -0,0 +1,316 @@
// License:
//
// So that single player releases are not overwriting each other's particle script files by using the same names ...
// And that everyone's maps play nice with each other ...
// ... and NOT stomping each others files by people using the same name.
//
// You agree to the following
//
// 1) You will include these comments at the top of your effects file to help make sure others do the same
// 2) You agree to rename this file to the following convention if you do a single player map release ...
//
// * lib_effects_0_<your gamedir>.cfg // For a gamedir release
// Example: lib_effects_0_travail.cfg // If your intended gamedir is "travail"
// Example: lib_effects_0_travail_mymap.cfg // If you are one to contributor releasing a map to a group pack
//
// * lib_effects_0_<your mapname>.cfg // For a map release that doesn't use a gamedir
// Example: lib_effects_0_mymap.cfg // If your map name is "mymap.bsp"
//
// Have fun!!! Enjoy!
//
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Lib Effects 0 - October 5, 2016
//
// The file contains specific effects.
// If your map is named "mymap.bsp", you should also have a "mymap.cfg" to indicate which models call the effects in this file.
//
r_part waterpour
{
texture ball
type smoke
scale 12
scalefactor 1
count 300
velwrand 24 14 8
veladd 200
alpha 1
rgb 192 192 192
rgbrand 0 0 64
die 2
gravity 200
cliptype waterpoursplash
clipbounce 100
clipcount 5
}
r_part waterpoursplash
{
texture ball
type texturedspark
randomvel 50 50
count 1
scalefactor 1
alpha 0.1
rgb 255 255 255
die 0.4
scale 50
stretchfactor -2.5
veladd 1
scale 1
gravity 800
}
r_part waterdrip
{
texture ball
type texturedspark
scale 1
stretchfactor -1.5
scalefactor 1
count 2
spawnchance 0.25
orgbias 0 0 0
veladd 0
velbias 0 0 -200
alpha 1
rgb 192 192 192
rgbrand 0 0 64
die 3
cliptype waterdripsplash
clipbounce 100
clipcount 5
}
r_part waterdripsplash
{
texture ball
type texturedspark
randomvel 50 50
count 1
scalefactor 1
alpha 0.1
rgb 255 255 255
die 0.4
scale 50
stretchfactor -2.5
veladd 1
scale 1
gravity 800
}
r_part misty
{
type normal
count 80
scale 40
scalerand 15
scalefactor 1
scaledelta -15
alpha 0
die 4
randomvel 4 12
veladd 50
rgb 255 255 255
rgbdelta 0 -11 -5
blend alpha
spawnorg 20 5
spawnmode box
spawnvel 6 0
up 15
rampmode delta
ramp 0 0 0 -0.7
ramp 0 0 0 0.4
ramp 0 0 0 0.3
cliptype misty
clipbounce -1
clipcount 0
}
r_part smokey
{
type normal
count 64
scale 40
scalerand 15
scalefactor 1
scaledelta -15
alpha 0
die 3
velwrand 0 0 0
velbias 0 0 50
gravity 0
up 15
rgb 8 8 8
rgbrand 2 2 2
blend modulate
spawnorg 10 5
spawnmode box
spawnvel 6 0
rampmode delta
ramp 0 0 0 -1
ramp 0 0 0 0.4
ramp 0 0 0 0.3
cliptype smokey
clipbounce -1
clipcount 0
}
r_part mystical
{
texture "particles/flare"
count 40
scale 40
scalerand 15
scalefactor 1
scaledelta -15
alpha 0
die 2
randomvel 8 12
veladd 5 -10
rgb 255 255 255
blend modulate
spawnorg 10 5
spawnmode box
spawnvel 6 0
up 15
rampmode delta
ramp 0 0 0 -0.7 // Changes scale
ramp 0 0 0 0.4 // Changes scale
ramp 0 0 0 2400 // Changes scale
}
r_part sparkcascadelite
{
type spark
count 25
spawnchance 0.10
scale 40
scalefactor 1
spawnmode box
spawnorg 0 0
orgbias 0 0 0
velwrand 70 70 8
gravity 400
die 1
cliptype sparkcascadelite
clipbounce -1
clipcount 10
alpha 1
blend add
rgb 255 128 76
rampmode delta
ramp 0 0 0 1
}
r_part sparkcascadeheavy
{
type spark
count 500
spawnchance 0.10
scale 40
scalefactor 1
spawnmode box
spawnorg 0 0
orgbias 0 0 0
randomvel 140 0
gravity 400
die 1
cliptype sparkcascadeheavy
clipbounce -1
clipcount 10
alpha 1
blend add
rgb 255 128 76
rampmode delta
ramp 0 0 0 1
}
r_part liquidfog
{
type normal
scale 80
scalerand 20
up -6
count 2
alpha 0
blend add
rgb 32 255 32
rgbrand 8 8 8
die 3.5
veladd 0
randomvel 11
gravity -15
rgb 32 32 32
rgbrand 2 2 2
spawnmode ball
cliptype liquidfog
clipbounce -1
clipcount 0
rampmode delta
ramp 0 0 0 -0.25
ramp 0 0 0 0.125
ramp 0 0 0 0.125
}
//snow from sky surfaces is often within a box
//this means we don't want the snow to ever settle.
//we also have no volume, so make the snow fall a little further.
r_part skysnow
{
texture ball
scalefactor 1
rainfrequency 2
count 1
alpha 1
rgb 255 255 255
die 8
veladd 0
velbias 0 0 -100
scale 5
flurry 40
gravity 400
friction 5
cliptype skysnow
clipbounce 0
}
r_part skyrain
{
texture ball
type texturedspark
scale 1
stretchfactor -40
scalefactor 1
rainfrequency 10
count 1
veladd 0
velbias -200 -200 -2000
alpha 0.1
rgb 255 255 255
die 2
cliptype skyrainsplash
clipbounce 100
clipcount 5
inwater skyrainsplash
}
r_part skyrainsplash
{
randomvel 50 50
count 1
texture ball
scalefactor 1
alpha 0.1
rgb 255 255 255
die 0.4
scale 50
stretchfactor -2.5
veladd 1
scale 1
type texturedspark
gravity 800
}

View File

@ -0,0 +1,123 @@
r_part railtrail
{
//blue spiral, mimicing quake2
texture "classicparticle"
tcoords 0 0 16 16 32
scale 0.5
alpha 1
scalefactor 0.8
step 1
spawnmode spiral 64
spawnorg 3
spawnvel 6
die 1 1.2
// colorindex 116 7 //this would be quake2's palette, which we can't use, so we have to make it up
rgb 1 31 42
rgbrand 27 52 69
rgbrandsync 1
}
r_part +railtrail
{
//grey filler, mimicing quake2
texture "classicparticle"
tcoords 0 0 16 16 32
scale 0.5
alpha 1
scalefactor 0.8
step 0.75
spawnorg 3
spawnvel 3
die 0.6 0.8
colorindex 0 15
}
//circley effect when the slugs impacts something
r_part railslugimpact
{
type normal
count 120
die 1
scale 5
alpha 1
spawnmode circle
spawnvel 30 0
spawnorg 1 0
rgb 28 83 111
friction 1.5
}
//invisible particle that leaves our trail
r_part railslug
{
rainfrequency 0.2
count 5
scale 0
scalefactor 1
spawnmode box
spawnorg 0 0
orgbias 0 0 0
randomvel 300
veladd 600
// gravity 800
die 3
alpha 0
blend add
bounce 1.5
clipcount 120
cliptype railslugimpact
//give it a trail
emit railtrail
emitinterval -1
//sound
sound "enforcer/enfire.wav" 1 1 0 0
//light
lightrgb .25 .5 4
lightradius 300
lightradiusfade 600
lighttime 0.333
// lightcorona 1 0.25
// lightshadows 0
// lightcubemap 0 //cubemaps/%i (not supported by quakespasm)
// lightscales 0 1 1 //ambient, diffuse, specular (not supported by quakespasm)
}
//small proxy effect that spawns our real effect only periodically.
//(thereby avoiding a sound every single frame)
r_part sparkcascadeheavy
{
count 2
spawnchance 0.20
scale 10
die 0
alpha 1
rgb 255 255 255
veladd 1
emit railslug
}
//example if you want to spew particles from certain models, in this case grunts. unfortunately it includes corpses.
//r_part modelspew
//{
// type normal
// count 800
// scale 10
// scalefactor 1
// die 1
// alpha .5
// rgb 255 255 128
// veladd 300
// bounce 1.5
// randomvel 100 100
// gravity 800
//}
//r_effect "progs/soldier.mdl" modelspew forwards

3174
haze.cfg Executable file

File diff suppressed because it is too large Load Diff

915
high.cfg Executable file
View File

@ -0,0 +1,915 @@
///////////////////////////////
//rain
r_part te_rain
{
texture ball; scalefactor 1; count 1; alpha 0.4; rgb 255 255 255; die 2; veladd 2; scale 2; type texturedspark
cliptype rainsplash
clipbounce 1
clipcount 5
}
r_part rainsplash
{
randomvel 50 50
count 1;
texture ball; scalefactor 1; alpha 0.1; rgb 255 255 255; die 0.4; scale 50;
stretchfactor 4
veladd 50; scale 1; type texturedspark
gravity 400
}
///////////////////////////////
//rocket trail
// flame trail
r_part tr_rocket
{
texture "particles/fteparticlefont.tga"
tcoords 97 97 191 191 256
step 1
scale 12
alpha 0.4
die 0.5
rgb 255 127 100
rgbdelta -14 -300 -300
blend add
scalefactor 1
scaledelta -15
}
// smoke puffs
r_part +tr_rocket
{
texture "particles/fteparticlefont.tga"
tcoords 97 97 191 191 256
step 5
scale 30
alpha 0.2
die 0.75
//diesubrand 10.25
randomvel 0.2
rgb 5 5 5
//rgbdelta -230 -45 -9
gravity -15
scalefactor 1
scaledelta 20
spawnvel 5
}
// burst sparks
r_part +tr_rocket
{
type texturedspark
texture "particles/fteparticlefont.tga"
tcoords 1 65 31 95 256 8 32
count 1
scale 2
scalefactor 1
scaledelta -15
alpha 0.2
die 0.25
rgb 255 128 0
blend add
spawnmode ball
spawnorg 1
spawnvel 50
veladd 500
friction 0.01
gravity 100
}
///////////////////////////////////////////
//alternate rocket trail, which is used by a handful of qw players.
//r_part tr_altrocket
//{
//}
///////////////////////////////////////////
//grenade trail
r_part tr_grenade
{
texture "particles/fteparticlefont.tga"
tcoords 97 97 191 191 256
step 6
scale 32
scaledelta 12
alpha 0.3
die 1.25
randomvel 2
veladd 15
rgb 75 75 75
//rgb 255 50 50
//rgbdelta -255 -75 -75
gravity -25
scalefactor 1
blend modulate
}
r_part +tr_grenade
{
texture "particles/fteparticlefont.tga"
tcoords 97 97 191 191 256
scale 1
scaledelta 0.25
alpha 0.2
step 4
die 0.8
randomvel 0
rgb 255 150 150
rgbdelta 0 -150 -150
type beam
blend add
}
//////////////////////////////////
//shotgun impacts
r_part gunshotsmoke
{
texture "particles/fteparticlefont.tga"
tcoords 1 65 31 95 256 8 32
count 3
scale 25
scalefactor 1
die 0.8
alpha 0.12
rgb 32 32 32
blend add
spawnmode ball
spawnorg 2
spawnvel 20
veladd -20
}
r_part te_gunshot
{
type texturedspark
texture "particles/fteparticlefont.tga"
tcoords 1 65 31 95 256 8 32
count 3
scale 2
scalefactor 1
alpha 0.5
die 0.8
rgb 255 128 0
blend add
spawnmode ball
spawnorg 1
spawnvel 100
veladd -80
friction 0.3
gravity 400
assoc gunshotsmoke
}
//////////////////////////////////
//nail impacts
r_part te_spike
{
type sparkfan
count 10
scale 1
scalefactor 1
alpha 0.5
die 0.2
rgb 255 128 0
blend add
spawnmode ball
spawnorg 12
spawnvel 300
}
r_part +te_spike
{
texture "particles/fteparticlefont.tga"
tcoords 1 97 95 191 256
count 1
scale 1
scalefactor 1
scaledelta 190
die 0.1
alpha 0.6
rgb 255 128 0
blend add
assoc gunshotsmoke
}
r_part te_superspike
{
type sparkfan
count 20
scale 1
scalefactor 1
alpha 0.5
die 0.2
rgb 255 128 0
blend add
spawnmode ball
spawnorg 12
spawnvel 300
}
r_part +te_superspike
{
texture "particles/fteparticlefont.tga"
tcoords 1 97 95 191 256
count 1
scale 1
scalefactor 1
scaledelta 190
die 0.1
alpha 0.6
rgb 255 128 0
blend add
assoc gunshotsmoke
}
////////////////////////////////////////////////
//explosion
//red bit
r_part te_explosion
{
texture "particles/fteparticlefont.tga"
tcoords 97 97 191 191 256
count 1
scale 500
alpha 0.4
die 0.2
rgb 255 127 100
rgbdelta -14 -300 -300
blend add
scalefactor 1
scaledelta -15
randomvel 0
// lightradius 350
// lightrgb 1.4 1.2 1.05
// lighttime 0.5
// lightradiusfade 350
// lightrgbfade 2 2 2
}
//smoke
r_part +te_explosion
{
texture "particles/fteparticlefont.tga"
tcoords 97 97 191 191 256
count 7
scale 300
alpha 0.2
die 0.8
//diesubrand 10.25
randomvel 100
rgb 5 5 5
//rgbdelta -230 -45 -9
gravity -15
scalefactor 1
scaledelta 40
spawnvel 5
}
// burst sparks
r_part +te_explosion
{
type texturedspark
texture "particles/fteparticlefont.tga"
tcoords 1 65 31 95 256 8 32
count 100
scale 5
scalefactor 1
scaledelta -15
alpha 0.2
die 0.5
rgb 255 128 0
blend add
spawnmode ball
spawnorg 1
randomvel 1000
friction 0.01
gravity 100
stretchfactor -80
}
//hide lights in explosions.
//r_explosionlight 0
//hide the explosion sprite in qw
cl_expsprite 0
//hide it in nq - WARNING: some mods use this sprite as a flame thrower.
//r_effect "progs/s_explod.spr" hidden 1
//////////////////////////////////////////
//rogue te_explosion2 effect
//note: if not otherwise defined, te_explosion2_BASE_RAND maps to this, and specifies the palette colours.
r_part te_explosion2
{
type texturedspark
texture "particles/fteparticlefont.tga"
tcoords 1 65 31 95 256 8 32
count 256
scale 5
scalefactor 1
scaledelta -15
alpha 0.2
die 0.5
blend add
spawnmode ball
spawnorg 1
randomvel 1000
friction 0.01
gravity 100
stretchfactor -80
}
//dragon fireball
//r_part te_explosion2_228_5
//rogue multigrenade sub explosion
//also triggered from a shielded rogue player touching another player (and doing some damage)
//also used during the ending.
//red particles
//r_part te_explosion2_230_5
//rogue plasma explosion
//also rogue timemachine explosion
//white particles splaying outwards
//r_part te_explosion2_244_3
//////////////////////////////////////////
//for when a spawn dies.
//also used by TF for emp explosions.
r_part te_tarexplosion
{
type texturedspark
texture "particles/fteparticlefont.tga"
tcoords 1 65 31 95 256 8 32
count 128
scale 5
scalefactor 1
scaledelta -15
rgb 0 0 17
alpha 0.5
die 0.5
spawnmode ball
spawnorg 1
randomvel 500
friction 0.01
gravity 100
stretchfactor -80
}
r_part +te_tarexplosion
{
type texturedspark
texture "particles/fteparticlefont.tga"
tcoords 1 65 31 95 256 8 32
count 256
scale 5
scalefactor 1
scaledelta -15
rgb 83 67 115
alpha 0.3
die 0.5
blend add
spawnmode ball
spawnorg 1
randomvel 500
friction 0.01
gravity 100
stretchfactor -80
}
//////////////////////////////////////////
//cthon falling into lava.
//often also used for TF gas grenades.
r_part te_lavasplash
{
texture "particles/fteparticlefont.tga"
tcoords 129 1 191 63 256
count 654
scale 15
alpha 0.7
die 4
randomvel 64
rgb 255 128 128
gravity 50
blend add
spawnorg 192 64
up 48
}
//////////////////////////////////////////
//FIXME: what if we don't have glsl support?
r_part te_teleport
{
scale 250
count 1
alpha 0.3
die 0.5
scalefactor 1
rotationstart 45
rotationspeed 0
shader
{
surfaceparm noshadows
surfaceparm nodlight
glslprogram
{
varying vec2 tcoord;
varying vec4 scoord;
varying float alph;
#ifdef VERTEX_SHADER
attribute vec2 v_texcoord;
attribute vec4 v_colour;
void main(void)
{
scoord = ftetransform();
tcoord = (v_texcoord.st - 0.5)*2.0;
alph = v_colour.a;
gl_Position = scoord;
}
#endif
#ifdef FRAGMENT_SHADER
uniform sampler2D s_t0;
void main(void)
{
vec2 nst;
float f;
nst = scoord.xy / scoord.w;
nst = (1.0 + nst)/2.0;
f = 1.0 - length(tcoord);
// f = 1.0 - tcoord*tcoord;
if (f < 0.0) discard;
f *= alph;
gl_FragColor = texture2D(s_t0, nst - tcoord*f);
}
#endif
}
{
map $currentrender
blendfunc blend
}
}
}
//////////////////////////////////////////
//hellknight
r_part tr_knightspike
{
texture "particles/fteparticlefont.tga"
tcoords 1 97 95 191 256
scale 15
step 1
alpha 0.6
die 0.2
rgb 192 96 48
veladd 0
randomvel 2
friction 4
scalefactor 0.825
blend add
spawnmode spiral
spawnvel -50
lighttime 0
lightshadows 0
lightradius 150
lightrgb 0.75 0.37 0.18
}
r_part te_knightspike
{
type sparkfan
count 200
scale 3
scalefactor 1
alpha 0.5
die 0.5
rgb 192 96 48
blend add
spawnmode ball
spawnorg 12
spawnvel 100
stretchfactor 10
}
/////////////////////////////////////////
//vore missiles
r_part tr_vorespike
{
texture "particles/fteparticlefont.tga"
tcoords 1 97 95 191 256
scale 15
step 1
alpha 0.6
die 0.5
rgb 192 96 192
veladd 15
spawnmode spiral
spawnvel 50
randomvel 0
friction 0
scalefactor 1
blend add
lighttime 0
lightshadows 0
lightradius 150
lightrgb 0.75 0.37 0.75
}
//rygel's pack sucks
r_trail "progs/v_spike.mdl" tr_vorespike
////////////////////
//enforcer laser effect
r_part tr_enforcerlaser
{
type texturedspark
texture "particles/fteparticlefont.tga"
tcoords 1 97 95 191 256
scale 15
step 4
alpha 0.3
die 0.5
rgb 255 69 0
veladd -32
spawnmode spiral
spawnvel 16
randomvel 32
friction 0
scalefactor 1
blend add
lighttime 0.2
lightshadows 0
lightradius 150
lightrgb 1 0.27 0
lightrgbfade 5 1 0
lightcorona 2 0.25
}
r_trail "progs/laser.mdl" tr_enforcerlaser
/////////////////////////////////////////
//rogue wrath enemy's projectiles
r_part tr_wrathball
{
type texturedspark
texture "particles/fteparticlefont.tga"
tcoords 1 97 95 191 256
scale 15
step 4
alpha 0.3
die 0.5
rgb 255 0 0
veladd -32
spawnmode spiral
spawnvel 16
randomvel 32
friction 0
scalefactor 1
blend add
lighttime 0.2
lightshadows 0
lightradius 150
lightrgb 1 0.27 0
lightrgbfade 5 1 0
lightcorona 2 0.5
}
r_trail "progs/w_ball.mdl" tr_wrathball
//wrath death
//grey particles
//no difference from the fallback except for the blend mode. this should ensure that we are not quite so invisible.
r_part te_explosion2_0_4
{
type texturedspark
texture "particles/fteparticlefont.tga"
tcoords 1 65 31 95 256 8 32
count 256
scale 5
scalefactor 1
scaledelta -15
alpha 0.2
die 0.5
spawnmode ball
spawnorg 1
randomvel 1000
friction 0.01
gravity 100
stretchfactor -80
}
/////////////////////////////////////////
//rogue lavaspikes
r_part tr_lavaspike
{
type spark
texture "particles/fteparticlefont.tga"
tcoords 1 97 95 191 256
scale 15
step 4
alpha 0.3
die 0.5
rgb 255 0 0
veladd -32
spawnmode spiral
spawnvel 16
randomvel 32
friction 0
scalefactor 1
blend add
}
r_trail "progs/lspike.mdl" tr_lavaspike
/////////////////////////////////////////
//rogue plasma gun
r_part tr_plasma
{
type texturedspark
texture "particles/fteparticlefont.tga"
tcoords 1 97 95 191 256
scale 15
step 4
alpha 0.3
die 0.25
rgb 128 128 255
veladd -32
spawnmode spiral
spawnvel 16
randomvel 32
friction 0
scalefactor 1
blend add
lighttime 0.2
lightshadows 0
lightradius 150
lightrgb 1 1 2
lightrgbfade 5 1 0.5
lightcorona 2 0.5
}
r_trail "progs/plasma.mdl" tr_plasma
/////////////////////////////////////////
//scrag missiles.
r_part tr_wizspike
{
texture "particles/fteparticlefont.tga"
tcoords 1 97 95 191 256
scale 15
step 4
alpha 0.6
die 0.2
rgb 25 200 25
veladd 0
randomvel 2
friction 4
scalefactor 0.825
spawnmode spiral
spawnvel 25
blend add
lighttime 2
lightradiusfade 75
lightshadows 0
lightradius 150
lightrgb 0.1 0.7 0.1
}
r_part tr_wizspike2
{
texture "particles/fteparticlefont.tga"
tcoords 1 97 95 191 256
scale 4
step 1
alpha 0.6
die 0.2
rgb 25 200 25
veladd 64
randomvel 64
friction 4
scalefactor 0.825
spawnmode spiral
spawnvel 25
blend add
}
//scrag impact
r_part te_wizspike
{
texture "particles/fteparticlefont.tga"
tcoords 1 97 95 191 256
scale 15
alpha 0.6
rgb 25 200 25
friction 0
scalefactor 0.825
blend add
count 5
veladd -256
randomvel 256
die 1
diesubrand 0.5
gravity 800
emit tr_wizspike2
emitinterval -1
bounce 1.5
}
/////////////////////////////////////////
//shambler stuff
r_part shambercharging
{
spawnmode ball
count 200
spawnorg 128
spawnvel -256
texture "particles/fteparticlefont.tga"
tcoords 1 1 63 63 256 2 64
scale 4
alpha 1
die 0.5
orgadd -64
rotationspeed 90
rotationstart 0 360
rgb 100 100 250
rgbrand 0 0 0
gravity 0
scalefactor 0.4
lighttime 0
lightshadows 0
lightradius 400
lightrgb 2 2 2
}
r_effect progs/s_light.mdl shambercharging 0
/////////////////////////////////////////
//blood effects
r_part te_blood
{
texture fte_bloodparticle
blend subtract
count 1
scale 32
alpha 0
die 1
randomvel 64
veladd 10
rotationspeed 90
rotationstart 0 360
rgb 32 64 64
rgbdelta -32 -64 -64
gravity 200
scalefactor 0.8
}
r_part high.pe_73
{
texture fte_bloodparticle
blend subtract
count 1
scale 32
alpha 0
die 1
randomvel 64
veladd 10
rotationspeed 90
rotationstart 0 360
rgb 32 64 64
rgbdelta -32 -64 -64
gravity 200
scalefactor 0.8
}
r_part te_lightningblood
{
texture fte_bloodparticle
blend subtract
count 1
scale 32
alpha 0
die 1
randomvel 32
veladd 5
rotationspeed 90
rotationstart 0 360
rgb 64 128 128
rgbdelta -64 -128 -128
gravity 200
scalefactor 0.8
}
r_part high.pe_225
{
texture fte_bloodparticle
blend subtract
count 0.5
scale 32
alpha 0
die 1
randomvel 32
veladd 5
rotationspeed 90
rotationstart 0 360
rgb 64 128 128
rgbdelta -64 -128 -128
gravity 200
scalefactor 0.8
}
/////////////////////////////////////////
//zombie body-part blood trails
r_part tr_slightblood
{
texture fte_bloodparticle
blend subtract
// tcoords 1 1 63 63 256 2 64
step 16
scale 64
alpha 0
die 1
randomvel 32
veladd 10
rotationspeed 90
rotationstart 0 360
rgb 64 128 128
rgbdelta -64 -128 -128
gravity 200
scalefactor 0.8
scaledelta -10
stains -0.5
}
//////////////////////////////////////////
//regular ol' blood trails
r_part tr_blood
{
texture fte_bloodparticle
blend subtract
step 8
scale 64
alpha 0
die 1
randomvel 32
veladd 10
rotationspeed 90
rotationstart 0 360
rgb 32 128 128
rgbdelta -32 -128 -128
gravity 200
scalefactor 0.8
scaledelta -10
stains -0.5
}
//////////////////////////////////
//fallbacks
r_part pe_default
{
texture "particles/fteparticlefont.tga"
tcoords 1 97 95 191 256
count 1
scale 4
veladd 15
die 0.4
alphadelta 0
diesubrand 0.4
gravity 40
spawnorg 8
}
r_part pe_defaulttrail
{
texture "particles/fteparticlefont.tga"
tcoords 1 97 95 191 256
scale 15
step 1
alpha 0.6
die 0.2
rgb 192 96 48
veladd 0
randomvel 2
friction 4
scalefactor 0.825
spawnmode spiral
spawnvel 25
blend add
}
//////////////////////////////////
//map debugging
r_part pe_pointfile
{
texture "particles/fteparticlefont.tga"
tcoords 1 97 95 191 256
count 1
scale 50
die 30
alphadelta 0
rgb 255 255 0
}

335
particles.txt Executable file
View File

@ -0,0 +1,335 @@
Example:
r_part blah
{
texture textures/particles/myimage.tga
tcoords 0 0 64 64 64
die 2
scalefactor 1
scale 20
alpha 0.5
rgb 255 0 0
rgbdelta 0 128 128
spawnmode ball
spawnorg 32
}
will give transparent (0.5) red particles that will fade to fully white when they go fully transparent 2 seconds later.
they will spawn within a ball of 32 units radius.
Types:
Always try to include a type. The engine will guess what your particle is meant to be otherwise, based on textures and other nonsense. That stuff is generally unreliable, and probably not what you want.
Chaining:
You can add multiple particle descriptions to a single effect. The first should be 'r_part foo', and the others should be 'r_part +foo'. Particles without a + will reset the chain.
Effect naming:
Effects loaded by r_particledesc will be given an internal prefix, eg: foo.cfg.
If the gamecode explicitly states foo.bar, your foo.cfg will automatically be executed, and will automatically use the bar effect from it.
The effect can still be overriden from a custom config by explicitly naming the effect foo.bar - the effect bar in the config foo will not override this, but would override bar on its own.
Scale:
scale values are defined in 1/4th qu, for some reason.
scalefactor typically needs to be explicitly set to 1. this value affects how the particle scales with distance from view, rather than the actual size of the particle.
texture <TEXTURENAME>
specifies to use an image named TEXTURENAME for this effect.
tcoords <s1> <t1> <s2> <t2> [tscale] [rsmax] [rsstep]
specifies to use a subsection of the image.
if tscale is set, all units are divided by this. it is the virtual size of your texture. So a value of 1 means that your texture coords must be between 0 and 1. But if it properly matches your texture's size, the coords are in pixels.
if rsmax is present, each particle will use a random image. These images must be on a single row in your particle font.
rsstep specifies the stride (gap from one to the next) in your particle font, and is only needed if rsmax is present and greater than 1.
rotationstart <min> [max]
the particle will start with a rotation rotated between min and max.
if max is missing, the particle will always start with the min value.
beamtexstep <value>
only valid if the effect is a beam.
specifies the number of quake units per beam texture repitition.
beamtexspeed <value>
only valid if the effect is a beam.
controls how fast the texture scrolls on the beam.
scale <min> [max]
particles will start with a diameter of this many quake units.
actual scale will be randomly chosen between min and max (max defaults to equal if min is missing)
scalerand <extra>
obsolete
overrides the scale max value
actual scale will be now be randomly chosen between min and min+extra
scalefactor <frac>
controls how the particle scales with distance.
1 makes the particle scale the same as anything else
0 makes the particle not change size no matter how far it is
scaledelta <value>
controls how the particle scales over time
specifies the change in the particle scale per second.
step <min> <max>
trails/beams only
specifies the distance between each particle in the trail (or beam).
count <min> <max>
point/box effects only (not trails or beams)
specifies how many particles are spawned per effect (some classic effects contain an extra scaler which is multiplied by the resulting value)
alpha <value>
specifies the initial alpha value of the effect
alphadelta <value>
specifies how much the alpha value of the effect changes per second (subtracted)
die <maximum age> <minimum age>
specifies the maximum age of the particle
diesubrand <value>
obsolete (set by die)
specifies the maximum starting age of the particle.
basically the particle will live up to this much less time. the alpha value will also be aged by the amount chosen by this value
randomvel <horiz> [vert]
controls how fast the particle moves when it spawns. This works regardless of any requested velocities.
if vert is not specified, horiz is used instead.
veladd <value>
controls how much of the effect's spawn velocity is used, can be greater than 1, or negative.
orgadd <value>
biases how much to add to the starting origin relative to the requested velocity.
friction <<xyz>|<xy> <z> | <x> <y> <z>>
Proportion of the particle's speed that should be lost from friction. Negative values are accepted.
gravity <value>
amount that the particle's velocity changes per second, in quake units.
clipbounce <value>
how much of the particle's velocity to use if the particle is clipped. See cliptype.
Defaults to 0.8
cliptype <effectname>
Specifies which new effect to spawn when the particle hits something.
The origin and velocity of the particle are used to spawn the new effect.
The clipbounce value is used as a scaler for the reflected velocity.
If the effect named is the effect itself, the particle will merely bounce, instead of spawning a new effect.
FIXME: make default to bounce if clipbounce is set without cliptype.
clipcount <count>
The scaler to use for the number of particles to spawn upon a clip event.
Only valid in conjunction with cliptype.
assoc <effectname>
Specifies another effect to spawn at the same time that this effect is spawned.
Thus allowing two sets of particles from one effect.
inwater <effectname>
obsolete
Specifies a replacement effect to use when this one is spawned underwater.
assoc used is the replacement effect. the assoc value from the replaced effect is ignored (this includes +foo chains).
overwater [content names]
Specifies that this particle should ONLY be spawned when out of water.
The particle will not spawn under water (this does not affect assoc chains).
Content names are a space-separated list of: water slime lava sky solid fluid. Default is fluid if not specified.
The r_part_contentswitch cvar must be enabled for this to function correctly.
underwater [content names]
Specifies that this particle should ONLY be spawned when underwater.
The particle will not spawn if the spawn position is non-water (this does not affect assoc chains).
colorindex <index> [rand]
Specifies a palette index to spawn the particle with.
The index used is between index and index+rand.
overrides the normal starting colours.
colorrand <rand>
obsolete.
replaces the [rand] part of the colorindex setting.
citracer
only valid if colorindex is set.
adds a palette index between 0 and 3, based on the particle index in the effect or trail.
red <value>
green <value>
blue <value>
rgb <red> <green> <blue>
rgb <value>
Specifies the initial red, green, and/or blue values for each particle.
Fully opaque is 255 or above.
Values above 255 are valid, but will remain opaque until the value drops below 255 from the colour deltas.
redrand <value>
greenrand <value>
bluerand <value>
rgbrand <red> <green> <blue>
rgbrand <value>
Specifies how much extra red, green, and/or blue there may be for particles.
The initial colour will be multiplied by this amount before addition.
Each componant is separately randomized. EG, red might add nothing, while the full green is added, and only half the blue.
Fully opaque is 255 or above.
redrandsync <value>
greenrandsync <value>
bluerandsync <value>
rgbrandsync <red> <green> <blue>
rgbrandsync <value>
Specifies how much extra red, green, and/or blue there may be for particles.
The initial colour will be multiplied by this amount before addition.
Componants are syncronised. EG, if the full amount of red is added, the full amount of green and blue will also be added.
Fully opaque is 255 or above.
reddelta <value>
greendelta <value>
bluedelta <value>
rgbdelta <red> <green> <blue>
rgbdelta <value>
Specifies how much the red, green, and/or blue values of each particle change over time.
The value 255 is the value required to go from opaque to invisible in 1 second.
rgbdeltatime <value>
Specifies for how long the particle may change colours for. After this many seconds, the particle may no longer change colours (delta becomes 0).
rampmode <mode>
mode may be one of:
none: uses rgb+rand+sync+delta+scale+scaledelta values.
absolute: the ramp overrides all colour+scale values. The effect moves from one absolute ramp index to the next.
delta: uses rgb+rand+sync+scale, but not delta values. All delta values come from the colour ramp instead.
if not none, the ramp index used is based upon the particle's age, its lifetime, and how many ramp elements there are.
rampindexlist <idx1> [<idx2> [idx3 ...]]
Scale used is the currently set scale value.
Specifies a set of palette index values to use for the effect as part of the effect's colour ramp.
rampindex <idx> <scale>
Specifies an individual palette index value and particle scale to use for the effect as part of the effect's colour ramp
ramp <red> <green> <blue> [alpha] [scale]
Specifies a ramp index in rgb terms, regardless of palette.
stains <value>
How much the effect discolours the wall upon impact.
The stained colour is based upon the colour of the particle upon impact.
blend <mode>
mode may be one of: add, subtract, blendcolour/blendcolor, blend
if the texture used is actually a shader, this is ignored.
spawnmode <mode> [arg1] [arg2]
This affects how particles are positioned when they first spawn, and their initial velocities.
for point effects, mode may be one of:
box: simple axially aligned box of particles.
circle: particles spawn within a ball with uniform distance from the center. none will appear in the middle.
arg1: percentage of the circle to cover. a value of 5 will go around the circle 5 separate times. this can be used for weird beam effects.
areaspread: the radius of the ball
areaspreadvert: the height of the ball. if 0, will be a flat circle
ball: particles spawn randomly within a ball.
areaspread: the radius of the ball
areaspreadvert: the height of the ball. if 0, will be a flat circle.
telebox: matches quake's telebox
lavasplash: like chthon's lava splash
uniformcircle: particles are spawned in a circle with uniform distance between and from the center. z=0.
syncfield: particles spawn at predictable locations based upon time within a rough sphere. Only useful for effects that are regenerated+replaced every frame.
distball:
*default*: regular box. particles are spawned inside an axially aligned box.
for trail effects, mode may be one of:
spiral: particles are given velocities perpendicular to the direction based on the distance moved.
tracer: particles spawn with alternating horizontal velocities (a wake effect).
*default*: particles spawn as a regular trail.
spawnparam1 <value>
obsolete. see spawnmode.
spawnparam2 <value>
obsolete. see spawnmode.
up <value>
the particle's starting origin is moved upwards by this amount (worldspace).
type <mode>
How the particles look.
mode may be:
beam: valid only for trails. Particles form a single textured beam acting as nodes along it.
spark: particles are lines, their length depending upon their speed.
sparkfan: particles are non-planar triangle fans, their length depending upon their speed.
texturedspark: textured particles are aligned along their direction of movement, their length depending upon their speed, width equal to their scale.
cdecal/decal: particles are spawned only upon bsp geometry. They are clipped by it.
udecal: unclipped decal. exact semantics are subject to change.
*default*: Particles are regular, rotating, 2d images.
isbeam
obsolete.
please use 'type beam' instead.
spawntime <value>
spawnchance <value>
emit <effectname>
Specifies the effect to periodically emit.
emitinterval <min>
Particles will not emit additional effects for this duration after emitting one.
emitintervalrand <extra>
FIXME: fold into emitinterval
emitstart <seconds>
Prevents the particle from emitting anything for this duration when it first spawns.
spawnorg <horiz> [vert]
spawnvel <horiz> [vert]
obsolete
viewspace [frac]
Specifies that this particle type should move relative to the camera.
Not compatible with splitscreen.
Should not normally be used in combination with clipping/bouncing.
perframe
apply inverse frametime to count (causes emits to be per frame).
averageout
average trail points from start to end, useful with t_lightning, etc
nostate
Causes the particle system to ignore all state information.
nospreadfirst
don't randomize org/vel for first generated particle
nospreadlast
don't randomize org/vel for last generated particle
sound <soundname> <vol> <attenuation> [pitch] [delay]
When the effect is first spawned, the named sound will play with the given volume and attenuation at the center point. This may not work with all spawn methods, as it will ignore any count scales.
lightradius <radius>
lightradiusfade <radiuspersecond>
lightrgb <r> <g> <b>
lightrgbfade <r/s> <g/s> <b/s>
lighttime <maxage>
Spawns a dlight when the effect is spawned.
dlight is removed when radius drops to 0 or the age is exceeded.
at this time it is not possible to override the corona/specular levels.
lightcubemap <cubemapnum>
value 0 means no cubemap.
otherwise with eg cubemap 5, uses image files cubemaps/5ft.tga, cubemaps/5bk.tga, etc.
fixme: at the current time, the cubemap is world-aligned and cannot rotate.
model <name> <firstframe> <endframe> <framerate> <alpha>
spawns sprites or models that fly away with random angles and run through some frame sequence. handy for simple gib effects.
spawnstain <radius> <r> <g> <b>

233
qs-spike.txt Executable file
View File

@ -0,0 +1,233 @@
this is an attempt to fix some of the deficiencies (as I see them) in quakespasm, while trying not to change how it feels to the user.
changes:
extra builtins -
added lots of misc builtins, including:
string manipulation+buffers,
file access,
bsp introspection,
maths,
tracebox,
botclient stuff,
clientcommands,
etc.
Setting pr_checkextensions 0 will a) prevent extensions from being advertised to the mod. b) display a warning when any extended builtin is used (assuming it would otherwise crash anyway).
stub builtins -
also added stubs for builtins that either don't make sense in quakespasm or would need protocol extensions.
these are merely present to avoid crashes and will display a warning the first time they're called.
multicast -
supports pvs culling. still does not support phs.
precache builtins -
these can now be used mid-map. be warned that this will result in other clients disconnecting (will still display warnings on the server).
misc stuff -
MOVETYPE_FOLLOW and SOLID_CORPSE now work, .movement is supported.
cvar changes -
for mods, autocvars now work. the set+seta command also now works.
I bumped the cbuf size, because not only were well-documented mods were being punished for it, but also stuffcmded aliases were overflowing it.
clc_stringcmd -
rewrote client->server string commands a little.
this security fix blocks clients from being able to execute server-side aliases/cvars with names similar to 'allowed' console commands.
SV_TouchLinks -
rewrote this to avoid potential crashes within recursive touch events (yes, this can happen in rare cases, even in the prior quakespasm version).
may have minor behaviour differences, pr_checkextensions 0 disables this, restoring prior behaviour including instability-from-recursion.
single server socket -
the server now only uses a single socket per protocol family.
this makes NATs/firewalls much easier to deal with as servers no longer need to be DMZoned, only a single port needs to be forwarded..
ipv6 -
this build can natively use ipv6, but will still assume ipv4 first.
uses non-hybrid sockets so this should work on winxp too.
use -noudp4 or -noudp6 to disable one and not the other. by default it'll try to use both.
reduced mtu size to avoid connectivity issues. still higher than vanilla though, which may also cause other issues.
master servers -
it is now possible to automatically list your server globally. hurrah...
unfortunately the server list is still lame and provides no ping info, so have fun with that. :)
thanks to ipv6, servers might get listed twice.
you can turn this on with sv_public, or via the 'new game' menu.
proquake angles -
now uses 16bit angles when connecting to proquake servers, or really most protocol15 servers including qrack+dp+etc.
also advertises protocols, allowing fte servers to serve protocol 666+999 as needed.
bug fixes -
spritegroups will now animate properly - this was a vanilla glquake bug.
model loader -
alpha-tested models now supported
spr32 sprites now supported (still using alpha testing rather than changing all sprites to use blending, so the alpha channel might as well still be 1bit)
many limit-exceeding models will now safely fail to load rather than crashing the engine.
note that some limits were not properly enforced before, and this might mean that a couple of models will newly fail to load (but won't crash the engine any more).
known-but-unsupported model formats will display a more helpful message, without crashing. unknown formats will similarly no longer crash.
bspx coloured lighting is now supported (somewhat common in the qw community). use -bspxlit argument with tyrutils-ericw's light util and discard the lit.
added support for tyrutil-ericw's qbsp's -notex argument that omits textures, reducing file size and avoiding gpl violations. still doesn't do replacements though.
fixed bsps that were compiled with vanilla qbsp's -noclip argument. will retain the correct view height, but will otherwise use the point-hull for everything.
binds menu -
if provided, will parse a bindlist.lst file from the gamedir. this should take the form of lines like:
+thecommand "some desc" "optional much longer description of it"
lines with a command that is just a hyphen are treated as comments/separators.
quakespasm ignores that third part, fte displays it on mouse-over.
if any binds are provided this way, they will completely replace the built-in list.
there is no limit to the binds that can be added, the menu will scroll, but there's no indicator that there are more/less.
particles -
now includes fte's scriptable particle system.
the config parser is a little different from fte (fte uses actual console commands, qs parses directly without the console).
this means that configs are limited to just r_part+r_effect+r_trail. setting cvars from particle configs are not supported here.
also currently missing support for models, embedded shaders (obviously), stains, viewspace particles.
tga images are supported, png+jpg are not.
no effects will be loaded by default, causing the engine to fall back on the existing particle system until a config is loaded.
users can set r_particledesc if they really specific particle effects. mods should include the config name as a namespace/prefix.
example usage (using FTE_SV_POINTPARTICLES with FTE_PART_NAMESPACES):
float myexplosion = particleeffectnum("mypartcfg.myexplosion");
pointparticles(myexplosion, self.origin, trace_plane_normal, 1);
note that DP_SV_POINTPARTICLES and FTE_SV_POINTPARTICLES qc extensions are supposed to be identical, except that *certain* mods see DP_ and assume the entire particle system too, which is NOT the case, hence the need to check both extensions.
note that if FTE_PART_NAMESPACES is supported, then particleeffectnum("mypartcfg.myexplosion") will automatically load the 'mypartcfg' particle set, however the engine deals with that.
note that for dp compat, if FTE_PART_NAMESPACE_EFFECTINFO is supported, then "effectinfo.*" or "effectinfo_*.*" are supported namespaces that will should be compatible with DP's effects (if you ignore the prefix).
Precaching a single particle effect like this will include this config for all particles with no namespace.
So for dp compat, you can use the following:
if (checkextension("FTE_SV_POINTPARTICLES") || checkextension("DP_SV_POINTPARTICLES"))
{
if (checkextension("FTE_PART_NAMESPACE_EFFECTINFO"))
{
particleeffectnum("effectinfo.dummy"); //so clients will attempt to load the effectinfo namespace, if not already available.
particleeffectnum(strcat("effectinfo_", mapname, ".dummy")); //map-specific effects
}
//else we have no idea where we're loading the config from... lets hope its either dp or the user helps us out
//do precaches
myexplosioneffect = particleeffectnum("myexplosioneffect");
}
//later
if (myexplosioneffect)
pointparticles(myexplosioneffect, self.origin, trace_plane_normal, 1);
else
te_explosion(self.origin); //'generic' fallback
(or you can just set 'r_particledesc effectinfo' in your mod's default.cfg and hope the user doesn't change it to something else)
note that particleeffectnum acts as a precache. you don't need to cache the result in a spawn function, but you do need to have called it early enough to avoid warning message.
note that any use of particleeffectnum will cause other clients to disconnect with 'illegible server message'.
it is *ENTIRELY* the modder's/user's responsibility to keep things consistent and not garish. pay close attention to particle sizes, texture resolution, etc.
mappers can add something like the following to their worldspawn entity:
"_texpart_sky1" "weather.tex_rainsky"
surfaces with the "sky1" texture will then act as emitters. this includes _texpart_*teleport or whatever. this doesn't require new gamecode.
r_partredirect can be used to redirect emitters (or effects) dynamically. redirections are normally meant to be a user feature, so mods should probably try to avoid tex_* effects in case the user switches gamedirs.
this command can work recursively, up to 5 itterations.
replacement network protocol -
namely FTE's nack-based PEXT2_REPLACEMENTDELTAS protocol extension. if you're a networking nerd, the archetecture is documented here: https://sourceforge.net/p/fteqw/code/HEAD/tree/trunk/specs/replacementdeltas.txt?format=raw
The extension applies to a base protocol, in this case 15, 666, or 999. There are some differences for QW vs NQ base protocols due to the netchan, these are not applicable to quakespasm.
the protocol is specced for lots of extended entity state, but most of that will be ignored/unused by the rest of the engine, and consequently won't be transmitted either.
automatically disabled per-client if unsupported by that client
all of the below will result in 'illegible server message' in older clients. usage of other extensions will be silently stripped, which may cause weirdness (eg: viewmodelforclient).
use of custom particles (particleeffectnum, trailparticles, pointparticles)
use of late precaches (at least if they were not already precached in a spawn function) [you shouldn't rely on this anyway].
this server implementation has not been applied to everything:
makestatic - static entities will not benefit from extensions like EF_NOSHADOW.
entities are sent as deltas from the previous packet
entity data is split up to not exceed the mtu (without needing netchan changes)
with PEXT2_PREDINFO, stats are also sent semi-unreliably using the same mechanism (avoids reliables-stall issues).
clc_clientdata is redundant.
all stats have full precision.
protocol does not change simply because a mission pack is loaded.
gamedir added to serverinfo, the client responds by displaying a warning if the gamedir does not match
baselines are generated per-client and split over multiple packets, so the signon buffer size is no longer a limiting factor for maps with insane entity counts (for all supported protocols).
MAX_VISEDICTS is gone.
however, try not to have them all visible at once. drawing 5k or 10k entities is a massive performance drain, as can them all getting angry at the player.
this implementation will reduce overall data sizes by avoiding retransmissions, but there is no rate limiting which can result in significant burst.
this may result in extra packetloss bursts if you have saturated tcp/etc connections running at the time.
packetloss is detected serverside and triggers resends
it will recover fully, but new entities appearing may have significant delays to them (especially if static) proportionally to other entities.
interpolation code unchanged, there is no time drifting, which means the engine will not be able to hide packetloss, resulting in slightly jerky player movement.
because of this FTE will still be smoother for internet play.
still playable while simulating 50% packetloss, but not smooth. this tested with no extra ping, so would be worse online.
the protocol does not carry nextthink hints, and quakespasm is too stupid to infer intervals, so assumes interpolation intervals of 0.1.
the protocol supports prediction, but this implementation does not.
To prevent the server from detecting replacement deltas (so they don't appear in demos), use "sv_protocol 666-" (omitting the - will neither enable nor disable fte extensions).
To re-enable extensions, use "sv_protocol FTE+666"
Other than a couple of extended svcs that a mod might use, 666 is effectively obsolete (but still useful as a base protocol for earlier quakespasm, fitzquake, etc).
Using 999 as the base protocol still provides map size increases.
This will also cause custom particle effects to become invisible to all clients.
To prevent the client from advertising support for replacement deltas, use 'cl_nopext 1'.
The server will fall back to only the base protocol, if *either* the client or server has it disabled (or both, obviously).
Ideally, leave it enabled on the server and disable it as desired on the client, but I guess people are not familiar with that yet.
This will allow you to record compatible demos even if you don't have rcon access to the server.
lightstyles -
bumped to 255 (the last index is not valid in bsps, so not relevant).
presense of additional lightmaps won't bug anything out, but actually using them might break other clients if they don't support this raised limit yet.
rcon -
proquake-compatible rcon is implemented server-side.
WARNING: the password is sent as plain text! it is NOT secure! (protocol limitation from )
set rcon_password to enable it, the default blank value will unconditionally treat all attempts as invalid.
no client support. there's no real reason for this, I just didn't bother writing the code (presumably it would be like the 'test' command, but *vomit*).
test mods:
dpmod -
a quick test of this seems to run fine, with the exception of a few non-generic particle effect builtins which are still stubs, mostly modified quad-damage effects.
smc -
this does actually run, but is not exactly enjoyable due to:
the use of pk3s means that everything needs to be extracted first.
use of md3 format (these models will be invisible)
high mdl poly counts making mdls unloadable (resulting in them being .. you guessed it, invisible)
unsupported stereo sounds (won't play, and might spam)
no shader support (hey, at least qs won't obey the 'depthfunc equal' lines)
smc loves late precaches (spammy).
additionally an smc bug causes monsters to be spawned underground (monsters can't move).
there's likely other issues. I've not tested it that extensively.
arcane dimensions -
urm. still works?..
gains DP_QC_GETSURFACE, so broken vanilla skies stop being broken (ad's only use for this extension).
gains EF_NOSHADOW.
ad's engine particle system usage assumes dp internals, and needs a tweak in order to get it to behave more generically.
rogue -
still need to test the ending with its writebyte hacks
issues:
(note that listed critisisms are not always bad things for the average qs user, but are probably bad for _someone_ and do limit potential mods)
(some things can only be good, except for how invasive such a thing would be resulting in too many bugs, or I'm just too lazy/selfish to do them myself)
protocol changes -
late precaches are in, but will still display warnings if depended upon. late precaches can still be an issue if the reliable is delayed while the unreliable is not.
sounds are still sent fully unreliably. this means they are subject to packetloss, and is especially significant on func_plat entities (where the sounds are typically looping, and thus might never be replaced/stopped).
staticsounds still use the sv.signon buffer, and are thus still limited to the underlying protocol and single-signon-buffer size.
pointparticles and trailparticles are new, but there is no support for most extended TE_* values (rain+snow are the only added TEs). Use pointparticles if you need such other effects.
while the protocol supports a range of extra entity state, much of that data is still unused (parsed and not sent). including ef_additive, tags, etc
not added -
voip would be a nice idea for all those first-playthrough map demos
no download mechanism added. I would want to use paks, which would make this far too complex. there's no automatic gamedir switching either.
(there is a gamedir hint, but currently it results in only a warning print because I really cba to rewrite quakespasm's filesystem code)
serious dp protocol issues -
sending 666 or 999 to a dp client results in dp spamming loads of useless gibberish that obscures the actual error, such that few people actually report the actual problem...
the only way I can think of getting DP to be remotely sane is to send it an svc_version for it to crap out on, reducing the spam in the hopes that people will read up a little.
the alternative is to hack in some 'cmd dpcheck $somedponlycvar' and see what the client says that cvar is set to (if $foo is supported).
unfortunately there's no version cvar, or any other kind of useful feature that I'm aware of, so this would fuck up for other clients with seta etc getting saved into $gamedir/config.cfg and then poluting other engines.
(this is not a new issue)
proquake+vanilla sucks:
sv_protocol=666 has higher limits. if this is inadvertantly set, then the server is permitted to send larger serverinfo packets (that exceed the 8192 limit).
proquake then displays 'read error' (due to recvfrom returning EMSGSIZE), which is awkward but not our bug, just a vanilla limitation.
(this is not a new issue)
note that reliables larger than 8192 but with an mtu compatible with vanilla can be used to crash both vanilla and proquake. this is true even if it is a client that sent the packets, crashing the server.
(this is not present in qss)
limited renderer changes -
no md3 support
no iqm support
harsh mdl limits
sprites still alpha-tested
still using render-to-texture instead of glsl for drawing water (which would allow mipmaps and anisotropic filtering, instead of using a random noise texture...)
randomization -
quakespasm likes seeding the random number generator as a handy trick... which can result in timers+particles getting a little too predictable
no filesystem updates -
no pk3s (could potentially decompress to a temp file, and fix up over time, but meh)
still only numbered paks, no pak.lst file or fs enumeration
'game' console command is still stupid (backwards, hardcoded special cases, limited paths)
I did add a 'gamedir' command as an alternative name for 'game', because quakeworld. I'd do show_fps too, but cvars make that messy.
console -
is still annoying
still prints non-ascii/non-unicode chars to stdout, which may include escape codes or other xterm/etc exploits.
con_printf is still horribly slow (at least with vsync), and likely to crash on someone... and not just me. fixing this would make a few poorly designed blocking things not refresh properly.
no markup -
all that ^1funny ^2and ^3annoying ^7text isn't handled, and remains as gibberish.
menu -
still no mouse support
still no way to add mod-specific cvars onto the menu
compiling:
export CFLAGS="-Wall -Werror -Wextra -Wno-missing-field-initializers -Wno-unused-parameter -DBUILD_SPECIAL=-spiked-rFOO"
is the CFLAGS environment setting that I'm compiling with. I only really mention that for branding. Also I'm building in windows with cygwin.
There's no 3rd-party dependancies from the vanilla quakespasm code included. this isn't an issue if you're going to compile for linux, where the dependancies are generally preinstalled as part of your distro, or you can just apt-get them or whatever.
So if you're going to compile for windows or osx, you'll need to do an svn checkout of vanilla quakespasm first, and then overwrite with the extra files (or just apply the patch to your checkout).
running:
win32+win64 binaries are included. linux users will need to build their own. osx users are screwed.
no 3rd-party libraries are included. It is assumed that you'll have previously installed vanilla QuakeSpasm and thus won't need them. In which case grab the exe that matches the archetecture of your existing QS install and just run that as if it were vanilla QuakeSpasm. Delete the other or whatever, it doesn't matter..

441
qsextensions.qc Executable file
View File

@ -0,0 +1,441 @@
/*
Extensions file for QuakeSpasm 0.92.2-spiked-r5
This file is auto-generated by pr_dumpplatform with no args.
You will probably need to use FTEQCC to compile this.
*/
//QuakeSpasm only supports ssqc, so including this file in some other situation is a user error
#if defined(QUAKEWORLD) || defined(CSQC) || defined(MENU)
#error Mixed up module defs
#endif
//List of advertised extensions
//DP_ENT_ALPHA
//DP_CON_SET
//DP_CON_SETA
//DP_EF_NOSHADOW
//DP_QC_AUTOCVARS
//DP_QC_ASINACOSATANATAN2TAN
//DP_QC_COPYENTITY
//DP_QC_CRC16
//DP_QC_CVAR_DEFSTRING
//DP_QC_CVAR_STRING
//DP_QC_CVAR_TYPE
//DP_QC_EDICT_NUM
//DP_QC_ENTITYDATA
//DP_QC_ETOS
//DP_QC_FINDCHAIN
//DP_QC_FINDCHAINFLAGS
//DP_QC_FINDCHAINFLOAT
//DP_QC_FINDFLAGS
//DP_QC_FINDFLOAT
//DP_QC_GETSURFACE
//DP_QC_GETSURFACETRIANGLE
//DP_QC_GETSURFACEPOINTATTRIBUTE
//DP_QC_MINMAXBOUND
//DP_QC_MULTIPLETEMPSTRINGS
//DP_QC_RANDOMVEC
//DP_QC_SINCOSSQRTPOW
//DP_QC_STRFTIME
//DP_QC_STRING_CASE_FUNCTIONS
//DP_QC_STRINGBUFFERS
//DP_QC_STRREPLACE
//DP_QC_TOKENIZEBYSEPARATOR
//DP_QC_TRACEBOX
//DP_QC_TRACETOSS
//DP_QC_TRACE_MOVETYPES
//DP_QC_URI_ESCAPE
//DP_QC_VECTOANGLES_WITH_ROLL
//DP_QC_VECTORVECTORS
//DP_QC_WHICHPACK
//DP_REGISTERCVAR
//DP_SV_BOTCLIENT
//DP_SV_DROPCLIENT
//DP_SV_POINTSOUND
//DP_SV_SETCOLOR
//DP_SV_SPAWNFUNC_PREFIX
//DP_SV_WRITEUNTERMINATEDSTRING
//DP_TE_BLOOD
//DP_TE_STANDARDEFFECTBUILTINS
//EXT_BITSHIFT
//FRIK_FILE
//FTE_PART_SCRIPT
//FTE_PART_NAMESPACES
//FTE_PART_NAMESPACE_EFFECTINFO
//FTE_QC_CHECKCOMMAND
//FTE_QC_INTCONV
//FTE_SV_POINTPARTICLES
//KRIMZON_SV_PARSECLIENTCOMMAND
//ZQ_QC_STRINGS
//Explicitly flag this stuff as probably-not-referenced, meaning fteqcc will shut up about it and silently strip what it can.
#pragma noref 1
//Some custom types (that might be redefined as accessors by fteextensions.qc, although we don't define any methods here)
#ifdef _ACCESSORS
accessor strbuf:float;
accessor searchhandle:float;
accessor hashtable:float;
accessor infostring:string;
accessor filestream:float;
#else
#define strbuf float
#define searchhandle float
#define hashtable float
#define infostring string
#define filestream float
#endif
//Supported Extension fields
.float gravity;
//.float items2; /*if defined, overrides serverflags for displaying runes on the hud*/
.float alpha; /*entity opacity*/
.float traileffectnum; /*can also be set with 'traileffect' from a map editor*/
.float emiteffectnum; /*can also be set with 'traileffect' from a map editor*/
.vector movement; /*describes which forward/right/up keys the player is holidng*/
.entity viewmodelforclient; /*attaches this entity to the specified player's view. invisible to other players*/
//Supported Extension Constants
const float MOVETYPE_FOLLOW = 12;
const float SOLID_CORPSE = 5;
const float FILE_READ = 0;
const float FILE_APPEND = 1;
const float FILE_WRITE = 2;
const float CLIENTTYPE_DISCONNECT = 0;
const float CLIENTTYPE_REAL = 1;
const float CLIENTTYPE_BOT = 2;
const float CLIENTTYPE_NOTCLIENT = 3;
const float EF_NOSHADOW = 0x1000;
const float MSG_MULTICAST = 4;
const float MULTICAST_ALL = MULTICAST_ALL_U;
const float MULTICAST_PVS = MULTICAST_PVS_U;
const float MULTICAST_ONE = MULTICAST_ONE_U;
const float MULTICAST_ALL_R = MULTICAST_ALL_R;
const float MULTICAST_PVS_R = MULTICAST_PVS_R;
const float MULTICAST_ONE_R = MULTICAST_ONE_R;
const float MULTICAST_INIT = MULTICAST_INIT;
//Builtin list
vector(vector fwd, optional vector up) vectoangles2 = #51; /*
Returns the angles (+x=UP) required to orient an entity to look in the given direction. The 'up' argument is required if you wish to set a roll angle, otherwise it will be limited to just monster-style turning. */
float(float angle) sin = #60;
float(float angle) cos = #61;
float(float value) sqrt = #62;
void(entity ent, entity ignore) tracetoss = #64;
string(entity ent) etos = #65;
string(entity e, string key) infokey = #80; /*
If e is world, returns the field 'key' from either the serverinfo or the localinfo. If e is a player, returns the value of 'key' from the player's userinfo string. There are a few special exceptions, like 'ip' which is not technically part of the userinfo. */
float(string) stof = #81;
#define unicast(pl,reli) do{msg_entity = pl; multicast('0 0 0', reli?MULITCAST_ONE_R:MULTICAST_ONE);}while(0)
void(vector where, float set) multicast = #82; /*
Once the MSG_MULTICAST network message buffer has been filled with data, this builtin is used to dispatch it to the given target, filtering by pvs for reduced network bandwidth. */
void(vector start, vector mins, vector maxs, vector end, float nomonsters, entity ent) tracebox = #90; /*
Exactly like traceline, but a box instead of a uselessly thin point. Acceptable sizes are limited by bsp format, q1bsp has strict acceptable size values. */
vector() randomvec = #91; /*
Returns a vector with random values. Each axis is independantly a value between -1 and 1 inclusive. */
vector(vector org) getlight = #92;
float(string cvarname, string defaultvalue) registercvar = #93; /*
Creates a new cvar on the fly. If it does not already exist, it will be given the specified value. If it does exist, this is a no-op.
This builtin has the limitation that it does not apply to configs or commandlines. Such configs will need to use the set or seta command causing this builtin to be a noop.
In engines that support it, you will generally find the autocvar feature easier and more efficient to use. */
float(float a, float b, ...) min = #94; /*
Returns the lowest value of its arguments. */
float(float a, float b, ...) max = #95; /*
Returns the highest value of its arguments. */
float(float minimum, float val, float maximum) bound = #96; /*
Returns val, unless minimum is higher, or maximum is less. */
float(float value, float exp) pow = #97;
#define findentity findfloat
entity(entity start, .__variant fld, __variant match) findfloat = #98; /*
Equivelent to the find builtin, but instead of comparing strings contents, this builtin compares the raw values. This builtin requires multiple calls in order to scan all entities - set start to the previous call's return value.
world is returned when there are no more entities. */
float(string extname) checkextension = #99; /*
Checks for an extension by its name (eg: checkextension("FRIK_FILE") says that its okay to go ahead and use strcat).
Use cvar("pr_checkextension") to see if this builtin exists. */
float(__variant funcref) checkbuiltin = #0; /*
Checks to see if the specified builtin is supported/mapped. This is intended as a way to check for #0 functions, allowing for simple single-builtin functions. */
float(string builtinname) builtin_find = #100; /*
Looks to see if the named builtin is valid, and returns the builtin number it exists at. */
float(float value) anglemod = #102;
filestream(string filename, float mode, optional float mmapminsize) fopen = #110; /*
Opens a file, typically prefixed with "data/", for either read or write access. */
void(filestream fhandle) fclose = #111;
string(filestream fhandle) fgets = #112; /*
Reads a single line out of the file. The new line character is not returned as part of the string. Returns the null string on EOF (use if not(string) to easily test for this, which distinguishes it from the empty string which is returned if the line being read is blank */
void(filestream fhandle, string s, optional string s2, optional string s3, optional string s4, optional string s5, optional string s6, optional string s7) fputs = #113; /*
Writes the given string(s) into the file. For compatibility with fgets, you should ensure that the string is terminated with a \n - this will not otherwise be done for you. It is up to the engine whether dos or unix line endings are actually written. */
#define ftell fseek //c-compat
int(filestream fhandle, optional int newoffset) fseek = #0; /*
Changes the current position of the file, if specified. Returns prior position, in bytes. */
float(string s) strlen = #114;
string(string s1, optional string s2, optional string s3, optional string s4, optional string s5, optional string s6, optional string s7, optional string s8) strcat = #115;
string(string s, float start, float length) substring = #116;
vector(string s) stov = #117;
string(string s, ...) strzone = #118; /*
Create a semi-permanent copy of a string that only becomes invalid once strunzone is called on the string (instead of when the engine assumes your string has left scope). */
void(string s) strunzone = #119; /*
Destroys a string that was allocated by strunzone. Further references to the string MAY crash the game. */
float(float number, float quantity) bitshift = #218;
void(vector org) te_lightningblood = #219;
float(string s1, string sub, optional float startidx) strstrofs = #221; /*
Returns the 0-based offset of sub within the s1 string, or -1 if sub is not in s1.
If startidx is set, this builtin will ignore matches before that 0-based offset. */
float(string str, float index) str2chr = #222; /*
Retrieves the character value at offset 'index'. */
string(float chr, ...) chr2str = #223; /*
The input floats are considered character values, and are concatenated. */
string(float pad, string str1, ...) strpad = #225; /*
Pads the string with spaces, to ensure its a specific length (so long as a fixed-width font is used, anyway). If pad is negative, the spaces are added on the left. If positive the padding is on the right. */
#define strcmp strncmp
float(string s1, string s2, optional float len, optional float s1ofs, optional float s2ofs) strncmp = #228; /*
Compares up to 'len' chars in the two strings. s1ofs allows you to treat s2 as a substring to compare against, or should be 0.
Returns 0 if the two strings are equal, a negative value if s1 appears numerically lower, and positive if s1 appears numerically higher. */
float(string s1, string s2) strcasecmp = #229; /*
Compares the two strings without case sensitivity.
Returns 0 if they are equal. The sign of the return value may be significant, but should not be depended upon. */
float(string s1, string s2, float len, optional float s1ofs, optional float s2ofs) strncasecmp = #230; /*
Compares up to 'len' chars in the two strings without case sensitivity. s1ofs allows you to treat s2 as a substring to compare against, or should be 0.
Returns 0 if they are equal. The sign of the return value may be significant, but should not be depended upon. */
string(string s) strtrim = #0; /*
Trims the whitespace from the start+end of the string. */
void(vector org, float count) te_bloodqw = #239;
float(float a, float n) mod = #245;
int(string) stoi = #259; /*
Converts the given string into a true integer. Base 8, 10, or 16 is determined based upon the format of the string. */
string(int) itos = #260; /*
Converts the passed true integer into a base10 string. */
int(string) stoh = #261; /*
Reads a base-16 string (with or without 0x prefix) as an integer. Bugs out if given a base 8 or base 10 string. :P */
string(int) htos = #262; /*
Formats an integer as a base16 string, with leading 0s and no prefix. Always returns 8 characters. */
int(float) ftoi = #0; /*
Converts the given float into a true integer without depending on extended qcvm instructions. */
float(int) itof = #0; /*
Converts the given true integer into a float without depending on extended qcvm instructions. */
#define dotproduct(v1,v2) ((vector)(v1)*(vector)(v2))
vector(vector v1, vector v2) crossproduct = #0; /*
Small helper function to calculate the crossproduct of two vectors. */
float(float modidx, string framename) frameforname = #276; /*
Looks up a framegroup from a model by name, avoiding the need for hardcoding. Returns -1 on error. */
float(float modidx, float framenum) frameduration = #277; /*
Retrieves the duration (in seconds) of the specified framegroup. */
void(float buf, float fl) WriteFloat = #280;
string(float modidx, float framenum) frametoname = #284;
float(string name) checkcommand = #294; /*
Checks to see if the supplied name is a valid command, cvar, or alias. Returns 0 if it does not exist. */
float(string effectname) particleeffectnum = #335; /*
Precaches the named particle effect. If your effect name is of the form 'foo.bar' then particles/foo.cfg will be loaded by the client if foo.bar was not already defined.
Different engines will have different particle systems, this specifies the QC API only. */
void(float effectnum, entity ent, vector start, vector end) trailparticles = #336; /*
Draws the given effect between the two named points. If ent is not world, distances will be cached in the entity in order to avoid framerate dependancies. The entity is not otherwise used. */
void(float effectnum, vector origin, optional vector dir, optional float count) pointparticles = #337; /*
Spawn a load of particles from the given effect at the given point traveling or aiming along the direction specified. The number of particles are scaled by the count argument. */
void(string s, ...) print = #339; /*
Unconditionally print on the local system's console, even in ssqc (doesn't care about the value of the developer cvar). */
float(entity ent) wasfreed = #353; /*
Quickly check to see if the entity is currently free. This function is only valid during the two-second non-reuse window, after that it may give bad results. Try one second to make it more robust. */
entity(entity from, optional entity to) copyentity = #400; /*
Copies all fields from one entity to another. */
void(entity ent, float colours) setcolors = #401; /*
Changes a player's colours. The bits 0-3 are the lower/trouser colour, bits 4-7 are the upper/shirt colours. */
entity(.string field, string match) findchain = #402;
entity(.float fld, float match) findchainfloat = #403;
void(vector org, vector dir, float count) te_blood = #405;
void(vector mincorner, vector maxcorner, vector vel, float howmany, float color) te_particlerain = #409;
void(vector mincorner, vector maxcorner, vector vel, float howmany, float color) te_particlesnow = #410;
void(vector org, optional float count) te_gunshot = #418;
void(vector org) te_spike = #419;
void(vector org) te_superspike = #420;
void(vector org) te_explosion = #421;
void(vector org) te_tarexplosion = #422;
void(vector org) te_wizspike = #423;
void(vector org) te_knightspike = #424;
void(vector org) te_lavasplash = #425;
void(vector org) te_teleport = #426;
void(vector org, float color, float colorlength) te_explosion2 = #427;
void(entity own, vector start, vector end) te_lightning1 = #428;
void(entity own, vector start, vector end) te_lightning2 = #429;
void(entity own, vector start, vector end) te_lightning3 = #430;
void(entity own, vector start, vector end) te_beam = #431;
void(vector dir) vectorvectors = #432;
float(entity e, float s) getsurfacenumpoints = #434;
vector(entity e, float s, float n) getsurfacepoint = #435;
vector(entity e, float s) getsurfacenormal = #436;
string(entity e, float s) getsurfacetexture = #437;
float(entity e, vector p) getsurfacenearpoint = #438;
vector(entity e, float s, vector p) getsurfaceclippedpoint = #439;
void(entity e, string s) clientcommand = #440;
float(string s) tokenize = #441;
string(float n) argv = #442;
float() argc = #0;
string(string cvarname) cvar_string = #448;
entity(entity start, .float fld, float match) findflags = #449;
entity(.float fld, float match) findchainflags = #450;
void(entity player) dropclient = #453;
entity() spawnclient = #454; /*
Spawns a dummy player entity.
Note that such dummy players will be carried from one map to the next.
Warning: DP_SV_CLIENTCOLORS DP_SV_CLIENTNAME are not implemented in quakespasm, so use KRIMZON_SV_PARSECLIENTCOMMAND's clientcommand builtin to change the bot's name/colours/skin/team/etc, in the same way that clients would ask. */
float(entity client) clienttype = #455;
void(float target, string str) WriteUnterminatedString = #456;
entity(float entnum) edict_num = #459;
strbuf() buf_create = #460;
void(strbuf bufhandle) buf_del = #461;
float(strbuf bufhandle) buf_getsize = #462;
void(strbuf bufhandle_from, strbuf bufhandle_to) buf_copy = #463;
void(strbuf bufhandle, float sortprefixlen, float backward) buf_sort = #464;
string(strbuf bufhandle, string glue) buf_implode = #465;
string(strbuf bufhandle, float string_index) bufstr_get = #466;
void(strbuf bufhandle, float string_index, string str) bufstr_set = #467;
float(strbuf bufhandle, string str, float order) bufstr_add = #468;
void(strbuf bufhandle, float string_index) bufstr_free = #469;
float(float s) asin = #471;
float(float c) acos = #472;
float(float t) atan = #473;
float(float c, float s) atan2 = #474;
float(float a) tan = #475;
string(float uselocaltime, string format, ...) strftime = #478;
float(string s, string separator1, ...) tokenizebyseparator = #479;
string(string s) strtolower = #480;
string(string s) strtoupper = #481;
string(string s) cvar_defstring = #482;
void(vector origin, string sample, float volume, float attenuation) pointsound = #483;
string(string search, string replace, string subject) strreplace = #484;
string(string search, string replace, string subject) strireplace = #485;
vector(entity e, float s, float n, float a) getsurfacepointattribute = #486;
float(float caseinsensitive, string s, ...) crc16 = #494;
float(string name) cvar_type = #495;
float() numentityfields = #496; /*
Gives the number of named entity fields. Note that this is not the size of an entity, but rather just the number of unique names (ie: vectors use 4 names rather than 3). */
float(string fieldname) findentityfield = #0; /*
Find a field index by name. */
typedef .__variant field_t;
field_t(float fieldnum) entityfieldref = #0; /*
Returns a field value that can be directly used to read entity fields. Be sure to validate the type with entityfieldtype before using. */
string(float fieldnum) entityfieldname = #497; /*
Retrieves the name of the given entity field. */
float(float fieldnum) entityfieldtype = #498; /*
Provides information about the type of the field specified by the field num. Returns one of the EV_ values. */
string(float fieldnum, entity ent) getentityfieldstring = #499;
float(float fieldnum, entity ent, string s) putentityfieldstring = #500;
string(string filename, optional float makereferenced) whichpack = #503; /*
Returns the pak file name that contains the file specified. progs/player.mdl will generally return something like 'pak0.pak'. If makereferenced is true, clients will automatically be told that the returned package should be pre-downloaded and used, even if allow_download_refpackages is not set. */
string(string in) uri_escape = #510;
string(string in) uri_unescape = #511;
float(entity ent) num_for_edict = #512;
float(string str) tokenize_console = #514; /*
Tokenize a string exactly as the console's tokenizer would do so. The regular tokenize builtin became bastardized for convienient string parsing, which resulted in a large disparity that can be exploited to bypass checks implemented in a naive SV_ParseClientCommand function, therefore you can use this builtin to make sure it exactly matches. */
float(float idx) argv_start_index = #515; /*
Returns the character index that the tokenized arg started at. */
float(float idx) argv_end_index = #516; /*
Returns the character index that the tokenized arg stopped at. */
string(string cvarname) cvar_description = #518; /*
Retrieves the description of a cvar, which might be useful for tooltips or help files. This may still not be useful. */
float(optional float timetype) gettime = #519;
float(float v, optional float base) log = #532; /*
Determines the logarithm of the input value according to the specified base. This can be used to calculate how much something was shifted by. */
float(string filename, strbuf bufhandle) buf_loadfile = #535; /*
Appends the named file into a string buffer (which must have been created in advance). The return value merely says whether the file was readable. */
float(filestream filehandle, strbuf bufhandle, optional float startpos, optional float numstrings) buf_writefile = #536; /*
Writes the contents of a string buffer onto the end of the supplied filehandle (you must have already used fopen). Additional optional arguments permit you to constrain the writes to a subsection of the stringbuffer. */
void(.../*, string funcname*/) callfunction = #605; /*
Invokes the named function. The function name is always passed as the last parameter and must always be present. The others are passed to the named function as-is */
float(string s) isfunction = #607; /*
Returns true if the named function exists and can be called with the callfunction builtin. */
float(entity e, string s, optional float offset) parseentitydata = #613; /*
Reads a single entity's fields into an already-spawned entity. s should contain field pairs like in a saved game: {"foo1" "bar" "foo2" "5"}. Returns <=0 on failure, otherwise returns the offset in the string that was read to. */
string(string fmt, ...) sprintf = #627;
float(entity e, float s) getsurfacenumtriangles = #628;
vector(entity e, float s, float n) getsurfacetriangle = #629;
//Builtin Stubs List (these are present for simpler compatibility, but not properly supported in QuakeSpasm at this time).
/*
void(vector org, string modelname, float startframe, float endframe, float framerate) effect = #404;
void(vector mincorner, vector maxcorner, float explosionspeed, float howmany) te_bloodshower = #406;
void(vector org, vector color) te_explosionrgb = #407;
void(vector mincorner, vector maxcorner, vector vel, float howmany, float color, float gravityflag, float randomveljitter) te_particlecube = #408;
void(vector org, vector vel, float howmany) te_spark = #411;
void(vector org) te_gunshotquad = #412;
void(vector org) te_spikequad = #413;
void(vector org) te_superspikequad = #414;
void(vector org) te_explosionquad = #415;
void(vector org) te_smallflash = #416;
void(vector org, float radius, float lifetime, vector color) te_customflash = #417;
void(vector org) te_plasmaburn = #433;
void(entity e, entity tagentity, string tagname) setattachment = #443;
float(string s) strlennocol = #476;
string(string s) strdecolorize = #477;
*/
//Reset this back to normal.
#pragma noref 0

View File

@ -67,6 +67,25 @@ entity_t **cl_visedicts;
extern cvar_t r_lerpmodels, r_lerpmove; //johnfitz
void CL_ClearTrailStates(void)
{
int i;
for (i = 0; i < cl.num_statics; i++)
{
PScript_DelinkTrailstate(&(cl_static_entities[i].trailstate));
PScript_DelinkTrailstate(&(cl_static_entities[i].emitstate));
}
for (i = 0; i < cl_max_edicts; i++)
{
PScript_DelinkTrailstate(&(cl_entities[i].trailstate));
PScript_DelinkTrailstate(&(cl_entities[i].emitstate));
}
for (i = 0; i < MAX_BEAMS; i++)
{
PScript_DelinkTrailstate(&(cl_beams[i].trailstate));
}
}
/*
=====================
CL_ClearState
@ -80,6 +99,8 @@ void CL_ClearState (void)
if (!sv.active)
Host_ClearMemory ();
CL_ClearTrailStates();
// wipe the entire cl structure
memset (&cl, 0, sizeof(cl));
@ -438,15 +459,17 @@ void CL_RelinkEntities (void)
float bobjrotate;
vec3_t oldorg;
dlight_t *dl;
float frametime = cl.time - cl.oldtime;
float frametime;
// determine partial update time
frac = CL_LerpPoint ();
frametime = cl.time - cl.oldtime;
if (frametime < 0)
frametime = 0;
if (frametime > 0.1)
frametime = 0.1;
// determine partial update time
frac = CL_LerpPoint ();
if (cl_numvisedicts + 64 > cl_maxvisedicts)
{
cl_maxvisedicts = cl_maxvisedicts+64;
@ -481,7 +504,7 @@ void CL_RelinkEntities (void)
for (i=1,ent=cl_entities+1 ; i<cl.num_entities ; i++,ent++)
{
if (!ent->model)
{ // empty slot
{ // empty slot, ish.
if (ent->forcelink)
R_RemoveEfrags (ent); // just became empty
continue;
@ -559,10 +582,10 @@ void CL_RelinkEntities (void)
//johnfitz -- assume muzzle flash accompanied by muzzle flare, which looks bad when lerped
if (r_lerpmodels.value != 2)
{
if (ent == &cl_entities[cl.viewentity])
cl.viewent.lerpflags |= LERP_RESETANIM|LERP_RESETANIM2; //no lerping for two frames
else
ent->lerpflags |= LERP_RESETANIM|LERP_RESETANIM2; //no lerping for two frames
if (ent == &cl_entities[cl.viewentity])
cl.viewent.lerpflags |= LERP_RESETANIM|LERP_RESETANIM2; //no lerping for two frames
else
ent->lerpflags |= LERP_RESETANIM|LERP_RESETANIM2; //no lerping for two frames
}
//johnfitz
}
@ -583,18 +606,18 @@ void CL_RelinkEntities (void)
}
#ifdef PSET_SCRIPT
if (ent->model->traileffect >= 0)
{
vec3_t axis[3];
AngleVectors(ent->angles, axis[0], axis[1], axis[2]);
PScript_ParticleTrail(oldorg, ent->origin, ent->model->traileffect, i, axis, &ent->trailstate);
}
else if (ent->netstate.traileffectnum > 0 && ent->netstate.traileffectnum < MAX_PARTICLETYPES)
if (ent->netstate.traileffectnum > 0 && ent->netstate.traileffectnum < MAX_PARTICLETYPES)
{
vec3_t axis[3];
AngleVectors(ent->angles, axis[0], axis[1], axis[2]);
PScript_ParticleTrail(oldorg, ent->origin, cl.particle_precache[ent->netstate.traileffectnum].index, i, axis, &ent->trailstate);
}
else if (ent->model->traileffect >= 0)
{
vec3_t axis[3];
AngleVectors(ent->angles, axis[0], axis[1], axis[2]);
PScript_ParticleTrail(oldorg, ent->origin, ent->model->traileffect, i, axis, &ent->trailstate);
}
else
#endif
if (ent->model->flags & EF_GIB)
@ -640,9 +663,26 @@ void CL_RelinkEntities (void)
ent->forcelink = false;
#ifdef PSET_SCRIPT
if (ent->model->emiteffect >= 0)
if (ent->netstate.emiteffectnum > 0)
{
PScript_RunParticleEffectState(ent->origin, NULL, frametime, ent->model->emiteffect, &ent->emitstate);
vec3_t axis[3];
AngleVectors(ent->angles, axis[0], axis[1], axis[2]);
if (ent->model->type == mod_alias)
axis[0][2] *= -1; //stupid vanilla bug
PScript_RunParticleEffectState(ent->origin, axis[0], frametime, cl.particle_precache[ent->netstate.emiteffectnum].index, &ent->emitstate);
}
else if (ent->model->emiteffect >= 0)
{
vec3_t axis[3];
AngleVectors(ent->angles, axis[0], axis[1], axis[2]);
if (ent->model->flags & MOD_EMITFORWARDS)
{
if (ent->model->type == mod_alias)
axis[0][2] *= -1; //stupid vanilla bug
}
else
VectorScale(axis[2], -1, axis[0]);
PScript_RunParticleEffectState(ent->origin, axis[0], frametime, ent->model->emiteffect, &ent->emitstate);
if (ent->model->flags & MOD_EMITREPLACE)
continue;
}

View File

@ -374,6 +374,7 @@ static unsigned int CLFTE_ReadDelta(unsigned int entnum, entity_state_t *news, c
if (bits & UF_SOLID)
{ //knowing the size of an entity is important for prediction
//without prediction, its a bit pointless.
/*if (cl.protocol_pext2 & PEXT2_NEWSIZEENCODING)
{
qbyte enc = MSG_ReadByte();
@ -464,7 +465,17 @@ static unsigned int CLFTE_ReadDelta(unsigned int entnum, entity_state_t *news, c
/*news->lightpflags =*/ MSG_ReadByte();
}
if (bits & UF_TRAILEFFECT)
news->traileffectnum = MSG_ReadShort();
{
unsigned short v = MSG_ReadShort();
news->emiteffectnum = 0;
news->traileffectnum = v & 0x3fff;
if (v & 0x8000)
news->emiteffectnum = MSG_ReadShort() & 0x3fff;
if (news->traileffectnum >= MAX_PARTICLETYPES)
news->traileffectnum = 0;
if (news->emiteffectnum >= MAX_PARTICLETYPES)
news->emiteffectnum = 0;
}
if (bits & UF_COLORMOD)
{
@ -680,13 +691,23 @@ static void CLFTE_ParseEntitiesUpdate(void)
}
if (cl.protocol_pext2 & PEXT2_PREDINFO)
{
{ //stats should normally be sent before the entity data.
VectorCopy (cl.mvelocity[0], cl.mvelocity[1]);
ent = CL_EntityNum(cl.viewentity);
cl.mvelocity[0][0] = ent->netstate.velocity[0]*(1/8.0);
cl.mvelocity[0][1] = ent->netstate.velocity[1]*(1/8.0);
cl.mvelocity[0][2] = ent->netstate.velocity[2]*(1/8.0);
cl.onground = (ent->netstate.eflags & EFLAGS_ONGROUND)?true:false;
cl.punchangle[0] = cl.statsf[STAT_PUNCHANGLE_X];
cl.punchangle[1] = cl.statsf[STAT_PUNCHANGLE_Y];
cl.punchangle[2] = cl.statsf[STAT_PUNCHANGLE_Z];
if (v_punchangles[0][0] != cl.punchangle[0] || v_punchangles[0][1] != cl.punchangle[1] || v_punchangles[0][2] != cl.punchangle[2])
{
VectorCopy (v_punchangles[0], v_punchangles[1]);
VectorCopy (cl.punchangle, v_punchangles[0]);
}
}
if (!cl.requestresend)
@ -1535,6 +1556,10 @@ static void CL_ParseStatic (int version) //johnfitz -- added a parameter
// copy it to the current state
ent->netstate = ent->baseline;
ent->trailstate = NULL;
ent->emitstate = NULL;
ent->model = cl.model_precache[ent->baseline.modelindex];
ent->lerpflags |= LERP_RESETANIM; //johnfitz -- lerping
ent->frame = ent->baseline.frame;
@ -1742,7 +1767,8 @@ void CL_ParseServerMessage (void)
else if (cl_shownet.value == 2)
Con_Printf ("------------------\n");
cl.onground = false; // unless the server says otherwise
if (!(cl.protocol_pext2 & PEXT2_PREDINFO))
cl.onground = false; // unless the server says otherwise
//
// parse the message
//

View File

@ -341,6 +341,7 @@ void CL_ParseTEnt (void);
void CL_UpdateTEnts (void);
void CL_ClearState (void);
void CL_ClearTrailStates(void);
//
// cl_demo.c

View File

@ -24,6 +24,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "quakedef.h"
cvar_t cl_nopext = {"cl_nopext","0",CVAR_NONE}; //Spike -- prevent autodetection of protocol extensions, so that servers fall back to only their base protocol (without needing to reconfigure the server. Requires reconnect.
void Cmd_ForwardToServer (void);
#define MAX_ALIAS_NAME 32
@ -517,6 +518,8 @@ void Cmd_Init (void)
Cmd_AddCommand ("alias",Cmd_Alias_f);
Cmd_AddCommand ("cmd", Cmd_ForwardToServer);
Cmd_AddCommand ("wait", Cmd_Wait_f);
Cvar_RegisterVariable (&cl_nopext);
}
/*
@ -793,7 +796,7 @@ void Cmd_ForwardToServer (void)
SZ_Print (&cls.message, "protocols 15 666 999");
return;
}
if (!strcmp(Cmd_Args(), "pext"))
if (!strcmp(Cmd_Args(), "pext") && !cl_nopext.value)
{ //server asked us for a key+value list of the extensions+attributes we support
SZ_Print (&cls.message, va("pext %#x %#x", PROTOCOL_FTE_PEXT2, PEXT2_SUPPORTED_CLIENT));
return;

View File

@ -192,6 +192,7 @@ void Mod_ClearAll (void)
{
mod->needload = true;
TexMgr_FreeTexturesForOwner (mod); //johnfitz
PScript_ClearSurfaceParticles(mod);
}
}
@ -206,7 +207,10 @@ void Mod_ResetAll (void)
for (i=0 , mod=mod_known ; i<mod_numknown ; i++, mod++)
{
if (!mod->needload) //otherwise Mod_ClearAll() did it already
{
TexMgr_FreeTexturesForOwner (mod);
PScript_ClearSurfaceParticles(mod);
}
memset(mod, 0, sizeof(qmodel_t));
}
mod_numknown = 0;
@ -288,26 +292,28 @@ qmodel_t *Mod_LoadModel (qmodel_t *mod, qboolean crash)
return mod; // not cached at all
}
//
// because the world is so huge, load it one piece at a time
//
if (!crash)
{
}
//
// load the file
//
buf = COM_LoadStackFile (mod->name, stackbuf, sizeof(stackbuf), & mod->path_id);
if (*mod->name == '*')
buf = NULL;
else
buf = COM_LoadStackFile (mod->name, stackbuf, sizeof(stackbuf), & mod->path_id);
if (!buf)
{
if (crash)
Sys_Error ("Mod_LoadModel: %s not found", mod->name); //johnfitz -- was "Mod_NumForName"
else if (mod->name[0] == '*' && (mod->name[1] < '0' || mod->name[1] > '9'))
; //*foo doesn't warn, unless its *NUM. inline models. gah.
else
Con_Warning("Mod_LoadModel: %s not found\n", mod->name);
//avoid crashes
mod->needload = false;
mod->type = mod_ext_invalid;
mod->flags = 0;
Mod_SetExtraFlags (mod); //johnfitz. spike -- moved this to be generic, because most of the flags are anyway.
return mod;
return NULL;

View File

@ -401,6 +401,7 @@ typedef enum {mod_brush, mod_sprite, mod_alias, mod_ext_invalid} modtype_t;
//johnfitz
//spike -- added this for particle stuff
#define MOD_EMITREPLACE 2048 //particle effect completely replaces the model (for flames or whatever).
#define MOD_EMITFORWARDS 4096 //particle effect is emitted forwards, rather than downwards. why down? good question.
typedef struct qmodel_s
{
@ -418,6 +419,9 @@ typedef struct qmodel_s
#ifdef PSET_SCRIPT
int emiteffect; //spike -- this effect is emitted per-frame by entities with this model
int traileffect; //spike -- this effect is used when entities move
struct skytris_s *skytris; //spike -- surface-based particle emission for this model
struct skytriblock_s *skytrimem; //spike -- surface-based particle emission for this model (for better cache performance+less allocs)
double skytime; //doesn't really cope with multiples. oh well...
#endif
//
// volume occupied by the model graphics

View File

@ -238,9 +238,30 @@ void R_StoreEfrags (efrag_t **ppefrag)
pent->visframe = r_framecount;
#ifdef PSET_SCRIPT
if (pent->model->emiteffect >= 0)
if (pent->netstate.emiteffectnum > 0)
{
PScript_RunParticleEffectState(pent->origin, NULL, ((host_frametime>0.1)?0.1:host_frametime), pent->model->emiteffect, &pent->emitstate);
float t = cl.time-cl.oldtime;
vec3_t axis[3];
if (t < 0) t = 0; else if (t > 0.1) t= 0.1;
AngleVectors(pent->angles, axis[0], axis[1], axis[2]);
if (pent->model->type == mod_alias)
axis[0][2] *= -1; //stupid vanilla bug
PScript_RunParticleEffectState(pent->origin, axis[0], t, cl.particle_precache[pent->netstate.emiteffectnum].index, &pent->emitstate);
}
else if (pent->model->emiteffect >= 0)
{
float t = cl.time-cl.oldtime;
vec3_t axis[3];
if (t < 0) t = 0; else if (t > 0.1) t= 0.1;
AngleVectors(pent->angles, axis[0], axis[1], axis[2]);
if (pent->model->flags & MOD_EMITFORWARDS)
{
if (pent->model->type == mod_alias)
axis[0][2] *= -1; //stupid vanilla bug
}
else
VectorScale(axis[2], -1, axis[0]);
PScript_RunParticleEffectState(pent->origin, axis[0], t, pent->model->emiteffect, &pent->emitstate);
if (pent->model->flags & MOD_EMITREPLACE)
continue;
}

View File

@ -112,8 +112,10 @@ int PScript_FindParticleType(const char *fullname);
int PScript_RunParticleEffectTypeString (vec3_t org, vec3_t dir, float count, const char *name);
int PScript_EntParticleTrail(vec3_t oldorg, entity_t *ent, const char *name);
int PScript_RunParticleEffect (vec3_t org, vec3_t dir, int color, int count);
void PScript_DelinkTrailstate(struct trailstate_s **tsk);
void PScript_ClearParticles (void);
void PScript_UpdateModelEffects(qmodel_t *mod);
void PScript_ClearSurfaceParticles(qmodel_t *mod); //model is being unloaded.
#else
#define PScript_RunParticleEffectState(o,d,c,t,s) true
#define PScript_RunParticleEffectTypeString(o,d,c,n) true //just unconditionally returns an error
@ -121,6 +123,8 @@ void PScript_UpdateModelEffects(qmodel_t *mod);
#define PScript_ParticleTrail(o,e,t,d,a,s) true
#define PScript_EntParticleTrail(o,e,n) true
#define PScript_RunParticleEffect(o,d,p,c) true
#define PScript_ClearSurfaceParticles(m)
#define PScript_DelinkTrailstate(tsp)
#endif
//====================================================

View File

@ -556,6 +556,8 @@ void Host_ClearMemory (void)
Hunk_FreeToLowMark (host_hunklevel);
cls.signon = 0;
free(sv.edicts); // ericw -- sv.edicts switched to use malloc()
cl_max_edicts = 0; // Spike -- paranoia
cl_entities = NULL;
memset (&sv, 0, sizeof(sv));
memset (&cl, 0, sizeof(cl));
}

View File

@ -487,7 +487,7 @@ void Host_Status_f (void)
j++;
if (j)
print_fn ( "effects: %i/%i\n", j, MAX_PARTICLETYPES-1);
for (i = 1,j=0; i < sv.max_edicts; i++)
for (i = 1,j=1; i < sv.num_edicts; i++)
if (!sv.edicts[i].free)
j++;
print_fn ( "entities:%i/%i\n", j, sv.max_edicts);

View File

@ -48,7 +48,7 @@ static int droppedDatagrams;
//we additionally look for 'DarkPlaces-Quake' servers too, because we can, but most of those servers will be using dpp7 and will (safely) not respond to our ccreq_server_info requests.
//we are not visible to DarkPlaces users - dp does not support fitz666 so that's not a viable option, at least by default, feel free to switch the order if you also change sv_protocol back to 15.
cvar_t sv_reportheartbeats = {"sv_reportheartbeats", "0"};
cvar_t sv_public = {"sv_public", "0"};
cvar_t sv_public = {"sv_public", NULL};
cvar_t com_protocolname = {"com_protocolname", "FTE-Quake DarkPlaces-Quake"};
cvar_t net_masters[] =
{
@ -1441,7 +1441,7 @@ static void _Datagram_ServerControlPacket (sys_socket_t acceptsock, struct qsock
if (s->disconnected)
continue;
ret = dfunc.AddrCompare(clientaddr, &s->addr);
if (ret >= 0)
if (ret == 0)
{
int i;

View File

@ -186,7 +186,10 @@ const char *NET_QSocketGetMaskedAddressString (const qsocket_t *s)
}
qboolean NET_QSocketGetProQuakeAngleHack(const qsocket_t *s)
{
return s->proquake_angle_hack;
if (s && !s->disconnected)
return s->proquake_angle_hack;
else
return false; //happens with demos
}
void NET_QSocketSetMSS(qsocket_t *s, int mss)
{

View File

@ -536,6 +536,12 @@ int UDP_AddrCompare (struct qsockaddr *addr1, struct qsockaddr *addr2)
((struct sockaddr_in6 *)addr2)->sin6_port)
return 1;
if (((struct sockaddr_in6 *)addr1)->sin6_scope_id &&
((struct sockaddr_in6 *)addr2)->sin6_scope_id &&
((struct sockaddr_in6 *)addr1)->sin6_scope_id !=
((struct sockaddr_in6 *)addr2)->sin6_scope_id) //the ipv6 scope id is for use with link-local addresses, to identify the specific interface.
return 1;
return 0;
}
else

View File

@ -1095,6 +1095,31 @@ static void PF_precache_sound (void)
PR_RunError ("PF_precache_sound: overflow");
}
int SV_Precache_Model(const char *s)
{
size_t i;
for (i = 0; i < MAX_MODELS; i++)
{
if (!sv.model_precache[i])
{
if (sv.state != ss_loading)
{
//let existing clients know about it
MSG_WriteByte(&sv.reliable_datagram, svcdp_precache);
MSG_WriteShort(&sv.reliable_datagram, i|0x8000);
MSG_WriteString(&sv.reliable_datagram, s);
}
sv.model_precache[i] = s;
sv.models[i] = Mod_ForName (s, i==1);
return i;
}
if (!strcmp(sv.model_precache[i], s))
return i;
}
return 0;
}
static void PF_precache_model (void)
{
const char *s;
@ -1553,9 +1578,9 @@ static void PF_WriteEntity (void)
static void PF_makestatic (void)
{
eval_t *val;
entity_state_t *st;
edict_t *ent;
int i;
int bits = 0; //johnfitz -- PROTOCOL_FITZQUAKE
ent = G_EDICT(OFS_PARM0);
@ -1566,56 +1591,29 @@ static void PF_makestatic (void)
}
//johnfitz
//johnfitz -- PROTOCOL_FITZQUAKE
if (sv.protocol == PROTOCOL_NETQUAKE)
{
if (SV_ModelIndex(PR_GetString(ent->v.model)) & 0xFF00 || (int)(ent->v.frame) & 0xFF00)
{
ED_Free (ent);
return; //can't display the correct model & frame, so don't show it at all
}
}
if (sv.num_statics == sv.max_statics)
PR_RunError ("PF_makestatic: Too many static entities");
st = &sv.static_entities[sv.num_statics];
memset(st, 0, sizeof(*st));
VectorCopy(ent->v.origin, st->origin);
VectorCopy(ent->v.angles, st->angles);
st->modelindex = SV_ModelIndex(PR_GetString(ent->v.model)); //o.O Why does QuakeSpasm use model instead of modelindex?
st->frame = ent->v.frame;
st->effects = ent->v.effects;
st->colormap = ent->v.colormap;
st->skin = ent->v.skin;
if ((val = GetEdictFieldValue(ent, pr_extfields.alpha)))
st->alpha = ENTALPHA_ENCODE(val->_float);
else
{
if (SV_ModelIndex(PR_GetString(ent->v.model)) & 0xFF00)
bits |= B_LARGEMODEL;
if ((int)(ent->v.frame) & 0xFF00)
bits |= B_LARGEFRAME;
if (ent->alpha != ENTALPHA_DEFAULT)
bits |= B_ALPHA;
}
if (bits)
{
MSG_WriteByte (&sv.signon, svc_spawnstatic2);
MSG_WriteByte (&sv.signon, bits);
}
else
MSG_WriteByte (&sv.signon, svc_spawnstatic);
if (bits & B_LARGEMODEL)
MSG_WriteShort (&sv.signon, SV_ModelIndex(PR_GetString(ent->v.model)));
else
MSG_WriteByte (&sv.signon, SV_ModelIndex(PR_GetString(ent->v.model)));
if (bits & B_LARGEFRAME)
MSG_WriteShort (&sv.signon, ent->v.frame);
else
MSG_WriteByte (&sv.signon, ent->v.frame);
//johnfitz
MSG_WriteByte (&sv.signon, ent->v.colormap);
MSG_WriteByte (&sv.signon, ent->v.skin);
for (i = 0; i < 3; i++)
{
MSG_WriteCoord(&sv.signon, ent->v.origin[i], sv.protocolflags);
MSG_WriteAngle(&sv.signon, ent->v.angles[i], sv.protocolflags);
}
//johnfitz -- PROTOCOL_FITZQUAKE
if (bits & B_ALPHA)
MSG_WriteByte (&sv.signon, ent->alpha);
//johnfitz
st->alpha = ent->alpha;
// st->pmovetype = ent->v.pmovetype;
if ((val = GetEdictFieldValue(ent, pr_extfields.traileffectnum)))
st->traileffectnum = val->_float;
if ((val = GetEdictFieldValue(ent, pr_extfields.emiteffectnum)))
st->emiteffectnum = val->_float;
// VectorScale(ent->v.velocity, 8, st->velocity);
// st->eflags = ent->v.eflags;
sv.num_statics++;
// throw the entity away now
ED_Free (ent);
@ -1669,6 +1667,25 @@ void PF_Fixme (void);
// PR_RunError ("unimplemented builtin");
//}
void PR_spawnfunc_misc_model(edict_t *self)
{
eval_t *val;
if (!self->v.model && (val = GetEdictFieldValue(self, ED_FindFieldOffset("mdl"))))
self->v.model = val->string;
if (!*PR_GetString(self->v.model)) //must have a model, because otherwise various things will assume its not valid at all.
self->v.model = PR_SetEngineString("*null");
if (self->v.angles[1] < 0) //mimic AD. shame there's no avelocity clientside.
self->v.angles[1] = (rand()*(360.0f/RAND_MAX));
//make sure the model is precached, to avoid errors.
G_INT(OFS_PARM0) = self->v.model;
PF_precache_model();
//and lets just call makestatic instead of worrying if it'll interfere with the rest of the qc.
G_INT(OFS_PARM0) = EDICT_TO_PROG(self);
PF_makestatic();
}
static builtin_t pr_builtin[] =
{

View File

@ -835,6 +835,7 @@ const char *ED_ParseEdict (const char *data, edict_t *ent)
char keyname[256];
qboolean anglehack, init;
int n;
eval_t *val;
init = false;
@ -896,11 +897,26 @@ const char *ED_ParseEdict (const char *data, edict_t *ent)
ent->alpha = ENTALPHA_ENCODE(atof(com_token));
//johnfitz
//spike -- hacks to support func_illusionary with all sorts of mdls, and various particle effects
if (!strcmp(keyname, "model") && sv.state == ss_loading)
/*ent->v.modelindex = */SV_Precache_Model(com_token);
//spike
key = ED_FindField (keyname);
if (!key)
{
if (!strcmp(keyname, "traileffect") && sv.state == ss_loading)
{
if ((val = GetEdictFieldValue(ent, pr_extfields.traileffectnum)))
val->_float = PF_SV_ForceParticlePrecache(com_token);
}
else if (!strcmp(keyname, "emiteffect") && sv.state == ss_loading)
{
if ((val = GetEdictFieldValue(ent, pr_extfields.emiteffectnum)))
val->_float = PF_SV_ForceParticlePrecache(com_token);
}
//johnfitz -- HACK -- suppress error becuase fog/sky/alpha fields might not be mentioned in defs.qc
if (strncmp(keyname, "sky", 3) && strcmp(keyname, "fog") && strcmp(keyname, "alpha"))
else if (strncmp(keyname, "sky", 3) && strcmp(keyname, "fog") && strcmp(keyname, "alpha"))
Con_DPrintf ("\"%s\" is not a field\n", keyname); //johnfitz -- was Con_Printf
continue;
}
@ -1006,9 +1022,15 @@ void ED_LoadFromFile (const char *data)
if (!func)
{
Con_SafePrintf ("No spawn function for:\n"); //johnfitz -- was Con_Printf
ED_Print (ent);
ED_Free (ent);
const char *classname = PR_GetString(ent->v.classname);
if (!strcmp(classname, "misc_model"))
PR_spawnfunc_misc_model(ent);
else
{
Con_SafePrintf ("No spawn function for:\n"); //johnfitz -- was Con_Printf
ED_Print (ent);
ED_Free (ent);
}
continue;
}
@ -1111,18 +1133,25 @@ void PR_LoadProgs (void)
pr_fielddefs[i].s_name = LittleLong (pr_fielddefs[i].s_name);
}
for (i = 0; i < progs->numglobals; i++)
((int *)pr_globals)[i] = LittleLong (((int *)pr_globals)[i]);
//spike: detect extended fields from progs
pr_extfields.items2 = ED_FindFieldOffset("items2");
pr_extfields.gravity = ED_FindFieldOffset("gravity");
pr_extfields.alpha = ED_FindFieldOffset("alpha");
pr_extfields.movement = ED_FindFieldOffset("movement");
pr_extfields.traileffectnum = ED_FindFieldOffset("traileffectnum");
pr_extfields.emiteffectnum = ED_FindFieldOffset("emiteffectnum");
pr_extfields.viewmodelforclient = ED_FindFieldOffset("viewmodelforclient");
for (i = 0; i < progs->numglobals; i++)
((int *)pr_globals)[i] = LittleLong (((int *)pr_globals)[i]);
i = progs->entityfields;
if (pr_extfields.emiteffectnum < 0)
pr_extfields.emiteffectnum = i++;
if (pr_extfields.traileffectnum < 0)
pr_extfields.traileffectnum = i++;
pr_edict_size = progs->entityfields * 4 + sizeof(edict_t) - sizeof(entvars_t);
pr_edict_size = i * 4 + sizeof(edict_t) - sizeof(entvars_t);
// round off to next highest whole word address (esp for Alpha)
// this ensures that pointers in the engine data area are always
// properly aligned

View File

@ -2125,9 +2125,20 @@ static qboolean QC_FixFileName(const char *name, const char **result, const char
return true;
}
static struct
//small note on access modes:
//when reading, we fopen files inside paks, for compat with (crappy non-zip-compatible) filesystem code
//when writing, we directly fopen the file such that it can never be inside a pak.
//this means that we need to take care when reading in order to detect EOF properly.
//writing doesn't need anything like that, so it can just dump stuff out, but we do need to ensure that the modes don't get mixed up, because trying to read from a writable file will not do what you would expect.
//even libc mandates a seek between reading+writing, so no great loss there.
static struct qcfile_s
{
char cache[1024];
int cacheoffset, cachesize;
FILE *file;
int fileoffset;
int filesize;
int filebase; //the offset of the file inside a pak
int mode;
} *qcfiles;
static size_t qcfiles_max;
@ -2140,6 +2151,7 @@ static void PF_fopen(void)
FILE *file;
size_t i;
char name[MAX_OSPATH];
int filesize = 0;
G_FLOAT(OFS_RETURN) = -1; //assume failure
@ -2155,9 +2167,9 @@ static void PF_fopen(void)
switch(fmode)
{
case 0: //read
COM_FOpenFile (fname, &file, NULL);
filesize = COM_FOpenFile (fname, &file, NULL);
if (!file && fallback)
COM_FOpenFile (fallback, &file, NULL);
filesize = COM_FOpenFile (fallback, &file, NULL);
break;
case 1: //append
q_snprintf (name, sizeof(name), "%s/%s", com_gamedir, fname);
@ -2185,8 +2197,13 @@ static void PF_fopen(void)
if (!qcfiles[i].file)
break;
}
qcfiles[i].filebase = ftell(file);
qcfiles[i].file = file;
qcfiles[i].mode = fmode;
//reading needs size info
qcfiles[i].filesize = filesize;
//clear the read cache.
qcfiles[i].fileoffset = qcfiles[i].cacheoffset = qcfiles[i].cachesize = 0;
G_FLOAT(OFS_RETURN) = i+QC_FILE_BASE;
}
@ -2198,20 +2215,45 @@ static void PF_fgets(void)
Con_Warning("PF_fgets: invalid file handle\n");
else if (!qcfiles[fileid].file)
Con_Warning("PF_fgets: file not open\n");
else if (qcfiles[fileid].mode != 0)
Con_Warning("PF_fgets: file not open for reading\n");
else
{
struct qcfile_s *f = &qcfiles[fileid];
char *ret = PR_GetTempString();
char *end;
if (fgets(ret, STRINGTEMP_LENGTH, qcfiles[fileid].file))
char *s = ret;
char *end = ret+STRINGTEMP_LENGTH;
for (;;)
{
//strip any \r\n chars on the end.
end = ret+strlen(ret);
if (end>ret && end[-1] == '\n')
*--end = 0;
if (end>ret && end[-1] == '\r')
*--end = 0;
G_INT(OFS_RETURN) = PR_SetEngineString(ret);
if (!f->cachesize)
{
//figure out how much we can try to cache.
int sz = f->filesize - f->fileoffset;
if (sz < 0 || f->fileoffset < 0) //... maybe we shouldn't have implemented seek support.
sz = 0;
else if ((size_t)sz > sizeof(f->cache))
sz = sizeof(f->cache);
//read a chunk
f->cacheoffset = 0;
f->cachesize = fread(f->cache, 1, sz, f->file);
f->fileoffset += f->cachesize;
if (!f->cachesize)
{
//classic eof...
break;
}
}
*s = f->cache[f->cacheoffset++];
if (*s == '\n') //new line, yay!
break;
s++;
if (s == end)
s--; //rewind if we're overflowing, such that we truncate the string.
}
if (s > ret && s[-1] == '\r')
s--; //terminate it on the \r of a \r\n pair.
*s = 0; //terminate it
G_INT(OFS_RETURN) = PR_SetEngineString(ret);
}
}
static void PF_fputs(void)
@ -2222,6 +2264,8 @@ static void PF_fputs(void)
Con_Warning("PF_fputs: invalid file handle\n");
else if (!qcfiles[fileid].file)
Con_Warning("PF_fputs: file not open\n");
else if (qcfiles[fileid].mode == 0)
Con_Warning("PF_fgets: file not open for writing\n");
else
fputs(str, qcfiles[fileid].file);
}
@ -2248,9 +2292,16 @@ static void PF_fseek(void)
Con_Warning("PF_fread: file not open\n");
else
{
G_INT(OFS_RETURN) = ftell(qcfiles[fileid].file);
if (qcfiles[fileid].mode == 0)
G_INT(OFS_RETURN) = qcfiles[fileid].fileoffset; //when we're reading, use the cached read offset
else
G_INT(OFS_RETURN) = ftell(qcfiles[fileid].file)-qcfiles[fileid].filebase;
if (pr_argc>1)
fseek(qcfiles[fileid].file, G_INT(OFS_PARM1), SEEK_SET);
{
qcfiles[fileid].fileoffset = G_INT(OFS_PARM1);
fseek(qcfiles[fileid].file, qcfiles[fileid].filebase+qcfiles[fileid].fileoffset, SEEK_SET);
qcfiles[fileid].cachesize = qcfiles[fileid].cacheoffset = 0;
}
}
}
#if 0
@ -2301,6 +2352,11 @@ static void PF_fsize(void)
Con_Warning("PF_fread: invalid file handle\n");
else if (!qcfiles[fileid].file)
Con_Warning("PF_fread: file not open\n");
else if (qcfiles[fileid].mode == 0)
{
G_INT(OFS_RETURN) = qcfiles[fileid].filesize;
//can't truncate if we're reading.
}
else
{
long curpos = ftell(qcfiles[fileid].file);
@ -3410,7 +3466,7 @@ static void PF_void_stub(void)
#ifdef PSET_SCRIPT
//for compat with dpp7 protocols, and mods that cba to precache things.
static void COM_Effectinfo_Enumerate(void (*cb)(const char *pname))
static void COM_Effectinfo_Enumerate(int (*cb)(const char *pname))
{
int i;
char *f, *e, *buf;
@ -3472,7 +3528,7 @@ static void COM_Effectinfo_Enumerate(void (*cb)(const char *pname))
}
free(buf);
}
static void PF_SV_ForceParticlePrecache(const char *s)
int PF_SV_ForceParticlePrecache(const char *s)
{
unsigned int i;
for (i = 1; i < MAX_PARTICLETYPES; i++)
@ -3488,11 +3544,12 @@ static void PF_SV_ForceParticlePrecache(const char *s)
}
sv.particle_precache[i] = strcpy(Hunk_Alloc(strlen(s)+1), s); //weirdness to avoid issues with tempstrings
return;
return i;
}
if (!strcmp(sv.particle_precache[i], s))
return;
return i;
}
return 0;
}
static void PF_sv_particleeffectnum(void)
{
@ -3862,7 +3919,7 @@ static const char *extnames[] =
"DP_QC_STRFTIME",
"DP_QC_STRING_CASE_FUNCTIONS",
"DP_QC_STRINGBUFFERS",
// "DP_QC_STRINGCOLORFUNCTIONS", //the functions are implemented... the colour codes are nor
// "DP_QC_STRINGCOLORFUNCTIONS", //the functions are provided only as stubs. the client has absolutely no support.
"DP_QC_STRREPLACE",
"DP_QC_TOKENIZEBYSEPARATOR",
"DP_QC_TRACEBOX",
@ -3883,7 +3940,7 @@ static const char *extnames[] =
"DP_TE_BLOOD",
"DP_TE_STANDARDEFFECTBUILTINS",
"EXT_BITSHIFT",
//"FRIK_FILE", //lacks the file part, but does have the strings part.
"FRIK_FILE", //lacks the file part, but does have the strings part.
#ifdef PSET_SCRIPT
"FTE_PART_SCRIPT",
"FTE_PART_NAMESPACES",
@ -4219,14 +4276,39 @@ void PR_DumpPlatform_f(void)
//extra fields
fprintf(f, "\n\n//Supported Extension fields\n");
fprintf(f, ".float gravity;\n"); //used by hipnotic
fprintf(f, "//.float items2;\n"); //used by both mission packs. *REPLACES* serverflags if defined, so lets try not to define it.
fprintf(f, ".float alpha;\n"); //entity alpha. woot.
fprintf(f, "//.float items2; /*if defined, overrides serverflags for displaying runes on the hud*/\n"); //used by both mission packs. *REPLACES* serverflags if defined, so lets try not to define it.
fprintf(f, ".float alpha; /*entity opacity*/\n"); //entity alpha. woot.
fprintf(f, ".float traileffectnum; /*can also be set with 'traileffect' from a map editor*/\n");
fprintf(f, ".float emiteffectnum; /*can also be set with 'traileffect' from a map editor*/\n");
fprintf(f, ".vector movement; /*describes which forward/right/up keys the player is holidng*/\n");
fprintf(f, ".entity viewmodelforclient; /*attaches this entity to the specified player's view. invisible to other players*/\n");
//extra constants
fprintf(f, "\n\n//Supported Extension Constants\n");
fprintf(f, "const float MOVETYPE_FOLLOW\t= "STRINGIFY(MOVETYPE_EXT_FOLLOW)";\n");
fprintf(f, "const float SOLID_CORPSE\t= "STRINGIFY(SOLID_EXT_CORPSE)";\n");
fprintf(f, "const float MOVETYPE_FOLLOW = "STRINGIFY(MOVETYPE_EXT_FOLLOW)";\n");
fprintf(f, "const float SOLID_CORPSE = "STRINGIFY(SOLID_EXT_CORPSE)";\n");
fprintf(f, "const float FILE_READ = "STRINGIFY(0)";\n");
fprintf(f, "const float FILE_APPEND = "STRINGIFY(1)";\n");
fprintf(f, "const float FILE_WRITE = "STRINGIFY(2)";\n");
fprintf(f, "const float CLIENTTYPE_DISCONNECT = "STRINGIFY(0)";\n");
fprintf(f, "const float CLIENTTYPE_REAL = "STRINGIFY(1)";\n");
fprintf(f, "const float CLIENTTYPE_BOT = "STRINGIFY(2)";\n");
fprintf(f, "const float CLIENTTYPE_NOTCLIENT = "STRINGIFY(3)";\n");
fprintf(f, "const float EF_NOSHADOW = "STRINGIFY(0x1000)";\n");
fprintf(f, "const float MSG_MULTICAST = "STRINGIFY(4)";\n");
fprintf(f, "const float MULTICAST_ALL = "STRINGIFY(MULTICAST_ALL_U)";\n");
// fprintf(f, "const float MULTICAST_PHS = "STRINGIFY(MULTICAST_PHS_U)";\n");
fprintf(f, "const float MULTICAST_PVS = "STRINGIFY(MULTICAST_PVS_U)";\n");
fprintf(f, "const float MULTICAST_ONE = "STRINGIFY(MULTICAST_ONE_U)";\n");
fprintf(f, "const float MULTICAST_ALL_R = "STRINGIFY(MULTICAST_ALL_R)";\n");
// fprintf(f, "const float MULTICAST_PHS_R = "STRINGIFY(MULTICAST_PHS_R)";\n");
fprintf(f, "const float MULTICAST_PVS_R = "STRINGIFY(MULTICAST_PVS_R)";\n");
fprintf(f, "const float MULTICAST_ONE_R = "STRINGIFY(MULTICAST_ONE_R)";\n");
fprintf(f, "const float MULTICAST_INIT = "STRINGIFY(MULTICAST_INIT)";\n");
for (j = 0; j < 2; j++)
{

0
quakespasm/Quake/progdefs.q1 Normal file → Executable file
View File

View File

@ -79,6 +79,10 @@ void PR_EnableExtensions(ddef_t *pr_globaldefs); //adds in the extra builtins et
void PR_AutoCvarChanged(cvar_t *var); //updates the autocvar_ globals when their cvar is changed
void PR_ShutdownExtensions(void); //nooooes!
void PR_DumpPlatform_f(void); //console command: writes out a qsextensions.qc file
//special hacks...
int PF_SV_ForceParticlePrecache(const char *s);
int SV_Precache_Model(const char *s);
void PR_spawnfunc_misc_model(edict_t *self);
//from pr_edict, for pr_ext. reflection is messy.
qboolean ED_ParseEpair (void *base, ddef_t *key, const char *s);
@ -178,6 +182,7 @@ extern struct pr_extfields_s
int movement;
int viewmodelforclient;
int traileffectnum;
int emiteffectnum;
} pr_extfields;
#endif /* _QUAKE_PROGS_H */

View File

@ -362,15 +362,16 @@ typedef struct
vec3_t angles;
unsigned short modelindex; //johnfitz -- was int
unsigned short frame; //johnfitz -- was int
unsigned int effects;
unsigned char colormap; //johnfitz -- was int
unsigned char skin; //johnfitz -- was int
unsigned char alpha; //johnfitz -- added
unsigned char pmovetype; //spike
unsigned short traileffectnum; //spike -- for qc-defined particle trails. typically evilly used for things that are not trails.
unsigned short emiteffectnum; //spike -- for qc-defined particle trails. typically evilly used for things that are not trails.
short velocity[3]; //spike -- the player's velocity.
unsigned short effects;
unsigned char eflags;
unsigned char pad;
// unsigned char pad;
} entity_state_t;
#define EFLAGS_STEP 1
//#define EFLAGS_GLOWTRAIL 2
@ -379,7 +380,7 @@ typedef struct
//#define EFLAGS_ 16
//#define EFLAGS_COLOURMAPPED 32 //.colormap=1024|(top<<4)|bottom), instead of a player number
//#define EFLAGS_ 64
#define EFLAGS_ONGROUND 128
#define EFLAGS_ONGROUND 128 //for bobbing more than anything else. *sigh*.
typedef struct
{

View File

@ -128,7 +128,7 @@ static float psintable[256];
int PScript_RunParticleEffectState (vec3_t org, vec3_t dir, float count, int typenum, trailstate_t **tsk);
int PScript_ParticleTrail (vec3_t startpos, vec3_t end, int type, int dlkey, vec3_t axis[3], trailstate_t **tsk);
static qboolean P_LoadParticleSet(char *name, qboolean implicit);
static qboolean P_LoadParticleSet(char *name, qboolean implicit, qboolean showwarning);
static void R_Particles_KillAllEffects(void);
static void buildsintable(void)
@ -200,7 +200,7 @@ typedef struct skytris_s {
vec3_t x;
vec3_t y;
float area;
float nexttime;
double nexttime;
int ptype;
struct msurface_s *face;
} skytris_t;
@ -430,9 +430,6 @@ static beamseg_t *free_beams;
static beamseg_t *beams;
static int r_numbeams;
static skytriblock_t *skytrimem;
static skytris_t *skytris;
static clippeddecal_t *free_decals;
static clippeddecal_t *decals;
static int r_numdecals;
@ -764,11 +761,11 @@ typedef struct associatedeffect_s
struct associatedeffect_s *next;
char mname[MAX_QPATH];
char pname[MAX_QPATH];
unsigned int flags;
enum
{
AE_TRAIL,
AE_EMIT,
AE_REPLACE
} type;
} associatedeffect_t;
static associatedeffect_t *associatedeffect;
@ -776,16 +773,28 @@ static void PScript_AssociateEffect_f(void)
{
const char *modelname = Cmd_Argv(1);
const char *effectname = Cmd_Argv(2);
int type = atoi(Cmd_Argv(3));
unsigned int flags = 0;
int type;
associatedeffect_t *ae;
int i;
if (!strcmp(Cmd_Argv(0), "r_trail"))
type = AE_TRAIL;
else
{
if (type)
type = AE_REPLACE;
else
type = AE_EMIT;
type = AE_EMIT;
for (i = 3; i < Cmd_Argc(); i++)
{
const char *fn = Cmd_Argv(i);
if (!strcmp(fn, "replace") || !strcmp(fn, "1"))
flags |= MOD_EMITREPLACE;
else if (!strcmp(fn, "forwards") || !strcmp(fn, "forward"))
flags |= MOD_EMITFORWARDS;
else if (!strcmp(fn, "0"))
; //1 or 0 are legacy, meaning replace or not
else
Con_DPrintf("%s %s: unknown flag %s\n", Cmd_Argv(0), modelname, fn);
}
}
if (
@ -796,6 +805,7 @@ static void PScript_AssociateEffect_f(void)
strstr(modelname, ".bsp") ||
strstr(modelname, "turr"))
{
//there is a very real possibility of attaching 'large' effects to models so that they become more visible (eg: a stream of particles passing through walls showing you the entity that they're eminating from)
Con_Printf("Sorry: Not allowed to attach effects to model \"%s\"\n", modelname);
return;
}
@ -808,20 +818,18 @@ static void PScript_AssociateEffect_f(void)
{
if (!strcmp(ae->mname, modelname))
if ((ae->type==AE_TRAIL) == (type==AE_TRAIL))
{
strcpy(ae->pname, effectname);
break;
}
}
if (!ae)
{
ae = Z_Malloc(sizeof(*ae));
ae->type = type;
strcpy(ae->mname, modelname);
strcpy(ae->pname, effectname);
ae->next = associatedeffect;
associatedeffect = ae;
}
strcpy(ae->pname, effectname);
ae->type = type;
ae->flags = flags;
r_plooksdirty = true;
}
@ -888,11 +896,8 @@ void PScript_UpdateModelEffects(qmodel_t *mod)
break;
case AE_EMIT:
mod->emiteffect = PScript_FindParticleType(ae->pname);
mod->flags &= ~MOD_EMITREPLACE;
break;
case AE_REPLACE:
mod->emiteffect = PScript_FindParticleType(ae->pname);
mod->flags |= MOD_EMITREPLACE;
mod->flags &= ~(MOD_EMITREPLACE|MOD_EMITFORWARDS);
mod->flags |= ae->flags;
break;
}
}
@ -1098,7 +1103,7 @@ int PScript_FindParticleType(const char *fullname)
}
}
if (*cfg)
if (P_LoadParticleSet(cfg, true))
if (P_LoadParticleSet(cfg, true, true))
return PScript_FindParticleType(fullname);
/* if (fallback)
@ -1244,6 +1249,13 @@ static void P_LoadTexture(part_type_t *ptype, qboolean warn)
}
ptype->looks.texture = thetex;
}
else if (strstr(ptype->texname, "classicparticle"))
{
extern gltexture_t *particletexture1;
ptype->looks.texture = particletexture1;
ptype->s2 = 0.5;
ptype->t2 = 0.5;
}
else if (strstr(ptype->texname, "glow") || strstr(ptype->texname, "ball") || ptype->looks.type == PT_TEXTUREDSPARK) //sparks and special names get a nice circular texture.
{
static gltexture_t *thetex;
@ -1400,9 +1412,32 @@ static void P_ResetToDefaults(part_type_t *ptype)
ptype->t2 = 1;
}
char *PScript_ReadLine(char *buffer, size_t buffersize, const char *filedata, size_t filesize, size_t *offset)
{
const char *start = filedata + *offset;
const char *f = start;
const char *e = filedata+filesize;
if (f >= e)
return NULL; //eof
while (f < e)
{
if (*f++ == '\n')
break;
}
*offset = f-filedata;
buffersize--;
if (buffersize >= (size_t)(f-start))
buffersize = f-start;
memcpy(buffer, start, buffersize);
buffer[buffersize] = 0; //null terminate it
return buffer;
}
//This is the function that loads the effect descriptions.
void PScript_ParseParticleEffectFile(const char *config, qboolean part_parseweak, FILE *context)
void PScript_ParseParticleEffectFile(const char *config, qboolean part_parseweak, char *context, size_t filesize)
{
const char *var, *value;
char *buf;
@ -1415,14 +1450,15 @@ void PScript_ParseParticleEffectFile(const char *config, qboolean part_parseweak
char line[512];
char part_parsenamespace[MAX_QPATH];
byte *palrgba = (byte *)d_8to24table;
byte *palrgba = (byte *)d_8to24table;
size_t offset = 0;
q_strlcpy(part_parsenamespace, config, sizeof(part_parsenamespace));
config = part_parsenamespace;
nexteffect:
if (!fgets(line, sizeof(line), context))
if (!PScript_ReadLine(line, sizeof(line), context, filesize, &offset))
return; //eof
reparse:
@ -1465,7 +1501,7 @@ reparse:
goto nexteffect;
}
buf = fgets(line, sizeof(line), context);
buf = PScript_ReadLine(line, sizeof(line), context, filesize, &offset);
if (!buf)
return; //eof
while (*buf && *buf <= ' ')
@ -1489,7 +1525,7 @@ reparse:
int depth = 1;
while(1)
{
buf = fgets(line, sizeof(line), context);
buf = PScript_ReadLine(line, sizeof(line), context, filesize, &offset);
if (!buf)
return;
@ -1561,13 +1597,13 @@ reparse:
while(1)
{
buf = fgets(line, sizeof(line), context);
buf = PScript_ReadLine(line, sizeof(line), context, filesize, &offset);
if (!buf)
{
Con_Printf("Unexpected end of buffer with effect %s\n", ptype->name);
return;
}
skipread:
while (*buf && *buf <= ' ')
buf++; //no whitespace please.
if (*buf == '}')
@ -1586,11 +1622,16 @@ reparse:
}
else
#endif
#if UNSUPPORTED
if (!strcmp(var, "shader"))
{
q_strlcpy(ptype->texname, ptype->name, sizeof(ptype->texname));
buf = Cbuf_GetNext(Cmd_ExecLevel, true);
#if UNSUPPORTED
Con_DPrintf("%s.%s: shaders are not supported in this build\n", ptype->config, ptype->name);
#endif
buf = PScript_ReadLine(line, sizeof(line), context, filesize, &offset);
if (!buf)
continue;
while (*buf && *buf <= ' ')
buf++; //no leading whitespace please.
if (*buf == '{')
@ -1603,8 +1644,8 @@ reparse:
str[2] = 0;
while(nest)
{
buf = Cbuf_GetNext(Cmd_ExecLevel, true);
if (!*buf)
buf = PScript_ReadLine(line, sizeof(line), context, filesize, &offset);
if (!buf)
{
Con_Printf("Unexpected end of buffer with effect %s\n", ptype->name);
break;
@ -1621,15 +1662,15 @@ reparse:
str[slen++] = '\n';
}
str[slen] = 0;
#if UNSUPPORTED
R_RegisterShader(ptype->texname, SUF_NONE, str);
#endif
Z_Free(str);
}
else
Cbuf_InsertText(buf, Cmd_ExecLevel, true);
goto skipread;
}
else
#endif
if (!strcmp(var, "texture") || !strcmp(var, "linear_texture") || !strcmp(var, "nearest_texture") || !strcmp(var, "nearesttexture"))
else if (!strcmp(var, "texture") || !strcmp(var, "linear_texture") || !strcmp(var, "nearest_texture") || !strcmp(var, "nearesttexture"))
{
q_strlcpy(ptype->texname, value, sizeof(ptype->texname));
ptype->looks.nearest = !strncmp(var, "nearest", 7);
@ -1802,7 +1843,7 @@ reparse:
#endif
else if (!strcmp(var, "randomvel"))
{
{ //shortcut for velwrand (and velbias for z bias)
ptype->velbias[0] = ptype->velbias[1] = 0;
ptype->velwrand[0] = ptype->velwrand[1] = atof(value);
if (Cmd_Argc()>3)
@ -1845,18 +1886,19 @@ reparse:
ptype->orgbias[1] = atof(Cmd_Argv(2));
ptype->orgbias[2] = atof(Cmd_Argv(3));
}
else if (!strcmp(var, "velbias"))
{
ptype->velbias[0] = atof(value);
ptype->velbias[1] = atof(Cmd_Argv(2));
ptype->velbias[2] = atof(Cmd_Argv(3));
}
else if (!strcmp(var, "orgwrand"))
{
ptype->orgwrand[0] = atof(value);
ptype->orgwrand[1] = atof(Cmd_Argv(2));
ptype->orgwrand[2] = atof(Cmd_Argv(3));
}
else if (!strcmp(var, "velbias"))
{
ptype->velbias[0] = atof(value);
ptype->velbias[1] = atof(Cmd_Argv(2));
ptype->velbias[2] = atof(Cmd_Argv(3));
}
else if (!strcmp(var, "velwrand"))
{
ptype->velwrand[0] = atof(value);
@ -1942,9 +1984,9 @@ parsefluid:
ptype->flags |= PT_TROVERWATER;
goto parsefluid;
}
#if UNSUPPORTED
else if (!strcmp(var, "model"))
{
#if UNSUPPORTED
partmodels_t *mod;
char *e;
@ -2029,8 +2071,10 @@ parsefluid:
else
mod->traileffect = P_INVALID;
}
}
#else
Con_DPrintf("%s.%s: model particles are not supported in this build\n", ptype->config, ptype->name);
#endif
}
else if (!strcmp(var, "sound"))
{
const char *e;
@ -2221,6 +2265,7 @@ parsefluid:
ptype->stainonimpact = atof(value);
else if (!strcmp(var, "blend"))
{
//small note: use premultiplied alpha where possible. this reduces the required state switches.
ptype->looks.premul = false;
if (!strcmp(value, "adda") || !strcmp(value, "add"))
ptype->looks.blendmode = BM_ADDA;
@ -2234,7 +2279,7 @@ parsefluid:
ptype->looks.blendmode = BM_INVMODC;
else if (!strcmp(value, "blendcolour") || !strcmp(value, "blendcolor"))
ptype->looks.blendmode = BM_BLENDCOLOUR;
else if (!strcmp(value, "blendalpha"))
else if (!strcmp(value, "blendalpha") || !strcmp(value, "blend"))
ptype->looks.blendmode = BM_BLEND;
else if (!strcmp(value, "premul_subtract"))
{
@ -2252,7 +2297,10 @@ parsefluid:
ptype->looks.blendmode = BM_PREMUL;
}
else
{
Con_DPrintf("%s.%s: uses unknown blend type '%s', assuming legacy 'blendalpha'\n", ptype->config, ptype->name, value);
ptype->looks.blendmode = BM_BLEND; //fallback
}
}
else if (!strcmp(var, "spawnmode"))
{
@ -2280,8 +2328,13 @@ parsefluid:
}
else if (!strcmp(value, "distball"))
ptype->spawnmode = SM_DISTBALL;
else
else if (!strcmp(value, "box"))
ptype->spawnmode = SM_BOX;
else
{
Con_DPrintf("%s.%s: uses unknown spawn type '%s', assuming 'box'\n", ptype->config, ptype->name, value);
ptype->spawnmode = SM_BOX;
}
if (Cmd_Argc()>2)
{
@ -2304,8 +2357,13 @@ parsefluid:
ptype->looks.type = PT_CDECAL;
else if (!strcmp(value, "udecal"))
ptype->looks.type = PT_UDECAL;
else
else if (!strcmp(value, "normal"))
ptype->looks.type = PT_NORMAL;
else
{
Con_DPrintf("%s.%s: uses unknown render type '%s', assuming 'normal'\n", ptype->config, ptype->name, value);
ptype->looks.type = PT_NORMAL; //fallback
}
settype = true;
}
else if (!strcmp(var, "clippeddecal")) //mask, match
@ -2419,7 +2477,10 @@ parsefluid:
/* else if (!strcmp(var, "spawnparam3"))
ptype->spawnparam3 = atof(value); */
else if (!strcmp(var, "up"))
{
ptype->orgbias[2] = atof(value);
Con_DPrintf("%s.%s: up is deprecated, use orgbias 0 0 Z\n", ptype->config, ptype->name);
}
#endif
else if (!strcmp(var, "rampmode"))
@ -2437,8 +2498,13 @@ parsefluid:
ptype->rampmode = RAMP_NEAREST;
else if (!strcmp(value, "lerp")) //don't use the name 'linear'. ramps are there to avoid linear...
ptype->rampmode = RAMP_LERP;
else //if (!strcmp(value, "delta"))
else if (!strcmp(value, "delta"))
ptype->rampmode = RAMP_DELTA;
else
{
Con_DPrintf("%s.%s: uses unknown ramp mode '%s', assuming 'delta'\n", ptype->config, ptype->name, value);
ptype->rampmode = RAMP_DELTA;
}
}
else if (!strcmp(var, "rampindexlist"))
{ // better not use this with delta ramps...
@ -2523,7 +2589,13 @@ parsefluid:
ptype->rampindexes++;
}
else if (!strcmp(var, "viewspace"))
{
#if UNSUPPORTED
ptype->viewspacefrac = (Cmd_Argc()>1)?atof(value):1;
#else
Con_DPrintf("%s.%s: viewspace particles are not supported in this build\n", ptype->config, ptype->name);
#endif
}
else if (!strcmp(var, "perframe"))
ptype->flags |= PT_INVFRAMETIME;
else if (!strcmp(var, "averageout"))
@ -2573,16 +2645,18 @@ parsefluid:
ptype->dl_scales[1] = atof(Cmd_Argv(2));
ptype->dl_scales[2] = atof(Cmd_Argv(3));
}
#if UNSUPPORTED
else if (!strcmp(var, "spawnstain"))
{
#if UNSUPPORTED
ptype->stain_radius = atof(value);
ptype->stain_rgb[0] = atof(Cmd_Argv(2));
ptype->stain_rgb[1] = atof(Cmd_Argv(3));
ptype->stain_rgb[2] = atof(Cmd_Argv(4));
}
#else
Con_DPrintf("%s.%s: spawnstain is not supported in this build\n", ptype->config, ptype->name);
#endif
else
}
else if (Cmd_Argc())
Con_DPrintf("%s.%s: %s is not a recognised particle type field\n", ptype->config, ptype->name, var);
}
ptype->loaded = part_parseweak?1:2;
@ -2847,7 +2921,7 @@ static void P_ImportEffectInfo(const char *config, char *line, qboolean part_par
{
int i;
FILE *file;
char *file;
const char *line;
char linebuf[1024];
//default assumes 8*8 grid, but we allow more
@ -2859,10 +2933,11 @@ static void P_ImportEffectInfo(const char *config, char *line, qboolean part_par
teximages[i][3] = 1/8.0 * (i>>3);
}
COM_FOpenFile("particles/particlefont.txt", &file, NULL);
file = (char*)COM_LoadMallocFile("particles/particlefont.txt", NULL);
if (file)
{
while (fgets(linebuf, sizeof(linebuf), file))
size_t offset = 0;
while (PScript_ReadLine(linebuf, sizeof(linebuf), file, com_filesize, &offset))
{
float s1,s2,t1,t2;
line = COM_Parse(linebuf);
@ -2883,7 +2958,7 @@ static void P_ImportEffectInfo(const char *config, char *line, qboolean part_par
teximages[i][3] = t1;
}
}
fclose(file);
free(file);
}
}
@ -3318,10 +3393,32 @@ void PScript_InitParticles (void)
//#endif
}
void PScript_ClearSurfaceParticles(qmodel_t *mod)
{
mod->skytime = 0;
mod->skytris = NULL;
while(mod->skytrimem)
{
void *f = mod->skytrimem;
mod->skytrimem = mod->skytrimem->next;
Z_Free(f);
}
}
static void PScript_ClearAllSurfaceParticles(void)
{ //make sure we hit all models, even ones from the previous map. maybe this is overkill
extern qmodel_t mod_known[];
extern int mod_numknown;
int i;
for (i = 0; i < mod_numknown; i++)
PScript_ClearSurfaceParticles(&mod_known[i]);
}
void PScript_Shutdown (void)
{
Cvar_SetCallback(&r_particledesc, NULL);
CL_ClearTrailStates();
// if (fallback)
// fallback->ShutdownParticles();
@ -3364,13 +3461,7 @@ void PScript_Shutdown (void)
free_decals = NULL;
free_beams = NULL;
skytris = NULL;
while(skytrimem)
{
void *f = skytrimem;
skytrimem = skytrimem->next;
Z_Free(f);
}
PScript_ClearAllSurfaceParticles();
r_numparticles = 0;
r_numdecals = 0;
@ -3425,78 +3516,80 @@ qboolean PScript_Startup (void)
void PScript_RecalculateSkyTris (void)
{
skytris = NULL;
while(skytrimem)
{
void *f = skytrimem;
skytrimem = skytrimem->next;
Z_Free(f);
}
qmodel_t *m = cl.worldmodel;
size_t modidx;
if (cl.worldmodel && !cl.worldmodel->needload && cl.worldmodel->type == mod_brush)
{
qmodel_t *m = cl.worldmodel;
int t;
int i;
int ptype;
msurface_t *surf;
char key[128];
const char *data = COM_Parse(cl.worldmodel->entities);
int *remaps;
remaps = malloc(sizeof(*remaps)*m->numtextures);
for (t = 0; t < m->numtextures; t++)
remaps[t] = P_INVALID;
PScript_ClearAllSurfaceParticles();
//parse the worldspawn entity fields for "_texpart_FOO" keys to give texture "FOO" particles from the effect specified by the value
if (data && com_token[0] == '{')
for (modidx = 0; modidx < MAX_MODELS; modidx++)
{
m = cl.model_precache[modidx];
if (m && !m->needload && m->type == mod_brush)
{
while (1)
int t;
int i;
int ptype;
msurface_t *surf;
char key[128];
const char *data = COM_Parse(m->entities);
int *remaps;
remaps = malloc(sizeof(*remaps)*m->numtextures);
for (t = 0; t < m->numtextures; t++)
remaps[t] = P_INVALID;
//parse the worldspawn entity fields for "_texpart_FOO" keys to give texture "FOO" particles from the effect specified by the value
if (data && com_token[0] == '{')
{
data = COM_Parse(data);
if (!data)
break; // error
if (com_token[0] == '}')
break; // end of worldspawn
if (com_token[0] == '_')
strcpy(key, com_token + 1);
else
strcpy(key, com_token);
while (key[strlen(key)-1] == ' ') // remove trailing spaces
key[strlen(key)-1] = 0;
data = COM_Parse(data);
if (!data)
break; // error
if (!q_strncasecmp("texpart_", key, 8))
while (1)
{
for (t = 0; t < m->numtextures; t++)
data = COM_Parse(data);
if (!data)
break; // error
if (com_token[0] == '}')
break; // end of worldspawn
if (com_token[0] == '_')
strcpy(key, com_token + 1);
else
strcpy(key, com_token);
while (key[strlen(key)-1] == ' ') // remove trailing spaces
key[strlen(key)-1] = 0;
data = COM_Parse(data);
if (!data)
break; // error
if (!q_strncasecmp("texpart_", key, 8))
{
if (!q_strcasecmp(key+8, m->textures[t]->name))
remaps[t] = PScript_FindParticleType(com_token);
/*in quakespasm there are always two textures added on the end (rather than pointing to textures outside the model)*/
for (t = 0; t < m->numtextures-2; t++)
{
if (!q_strcasecmp(key+8, m->textures[t]->name))
remaps[t] = PScript_FindParticleType(com_token);
}
}
}
}
}
for (t = 0; t < m->numtextures; t++)
{
ptype = remaps[t];
if (ptype == P_INVALID)
ptype = PScript_FindParticleType(va("tex_%s", m->textures[t]->name));
if (ptype >= 0)
for (t = 0; t < m->numtextures; t++)
{
for (i=0; i<m->nummodelsurfaces; i++)
ptype = remaps[t];
if (ptype == P_INVALID)
ptype = PScript_FindParticleType(va("tex_%s", m->textures[t]->name));
if (ptype >= 0)
{
surf = m->surfaces + i + m->firstmodelsurface;
if (surf->texinfo->texture == m->textures[t])
for (i=0; i<m->nummodelsurfaces; i++)
{
/*FIXME: it would be a good idea to determine the surface's (midpoint) pvs cluster so that we're not spamming for the entire map*/
PScript_EmitSkyEffectTris(m, surf, ptype);
surf = m->surfaces + i + m->firstmodelsurface;
if (surf->texinfo->texture == m->textures[t])
{
/*FIXME: it would be a good idea to determine the surface's (midpoint) pvs cluster so that we're not spamming for the entire map*/
PScript_EmitSkyEffectTris(m, surf, ptype);
}
}
}
}
free(remaps);
}
free(remaps);
}
}
/*
@ -3546,19 +3639,15 @@ void PScript_ClearParticles (void)
part_type[i].beams = NULL;
}
skytris = NULL;
while(skytrimem)
{
void *f = skytrimem;
skytrimem = skytrimem->next;
Z_Free(f);
}
PScript_ClearAllSurfaceParticles();
r_plooksdirty = true;
CL_ClearTrailStates();
}
static qboolean P_LoadParticleSet(char *name, qboolean implicit)
static qboolean P_LoadParticleSet(char *name, qboolean implicit, qboolean showwarning)
{
FILE *file;
char *file;
pcfg_t *cfg;
if (!*name)
@ -3591,13 +3680,13 @@ static qboolean P_LoadParticleSet(char *name, qboolean implicit)
return true;
}
COM_FOpenFile(va("particles/%s.cfg", name), &file, NULL);
file = (char*)COM_LoadMallocFile(va("particles/%s.cfg", name), NULL);
if (!file)
COM_FOpenFile(va("%s.cfg", name), &file, NULL);
file = (char*)COM_LoadMallocFile(va("%s.cfg", name), NULL);
if (file)
{
PScript_ParseParticleEffectFile(name, implicit, file);
fclose(file);
PScript_ParseParticleEffectFile(name, implicit, file, com_filesize);
free(file);
}
else
{
@ -3609,15 +3698,10 @@ static qboolean P_LoadParticleSet(char *name, qboolean implicit)
P_ImportEffectInfo_Name(name);
return true;
}
if (P_LoadParticleSet("high", true))
Con_Printf(CON_WARNING "Couldn't find particle description %s, loading 'high' instead\n", name);
else
#endif
{
if (showwarning)
Con_Printf(CON_WARNING "Couldn't find particle description %s\n", name);
return false;
}
return false;
}
return true;
}
@ -3664,50 +3748,40 @@ static void R_ParticleDesc_Callback(struct cvar_s *var)
for (c = var->string; (c=COM_Parse(c)); )
{
if (*com_token)
P_LoadParticleSet(com_token, false);
P_LoadParticleSet(com_token, false, true);
}
if (cls.state == ca_connected && cl.model_precache[1])
{
//per-map configs. because we can.
memcpy(com_token, "map_", 4);
COM_FileBase(cl.model_precache[1]->name, com_token+4, sizeof(com_token)-4);
P_LoadParticleSet(com_token, false, false);
}
//make sure nothing is stale.
CL_RegisterParticles();
}
static void P_AddRainParticles(void)
static void P_AddRainParticles(qmodel_t *mod, vec3_t axis[3], vec3_t eorg, int visframe, float contribution)
{
float x;
float y;
static float skipped;
static float lastrendered;
part_type_t *type;
vec3_t org, vdist;
vec3_t org, vdist, worg, wnorm;
skytris_t *st;
if (!r_part_rain.value || !r_part_rain_quantity.value)
{
skipped = true;
if (!r_part_rain_quantity.value)
return;
}
if (lastrendered < particletime - 0.5)
skipped = true; //we've gone for half a sec without any new rain. This would cause some strange effects, so reset times.
mod->skytime += contribution;
if (skipped)
for (st = mod->skytris; st; st = st->next)
{
for (st = skytris; st; st = st->next)
if (st->face->visframe != visframe)
{
st->nexttime = particletime;
}
}
skipped = false;
lastrendered = particletime;
for (st = skytris; st; st = st->next)
{
if (st->face->visframe != r_visframecount)
{
st->nexttime = particletime;
st->nexttime = mod->skytime;
continue;
}
@ -3717,46 +3791,46 @@ static void P_AddRainParticles(void)
if (!type->loaded) //woo, batch skipping.
continue;
while (st->nexttime < particletime)
while (st->nexttime < mod->skytime)
{
if (!free_particles)
return;
st->nexttime += 10000/(st->area*r_part_rain_quantity.value*type->rainfrequency);
st->nexttime += 10000.0/(st->area*r_part_rain_quantity.value*type->rainfrequency);
x = frandom()*frandom();
y = frandom() * (1-x);
VectorMA(st->org, x, st->x, org);
VectorMA(org, y, st->y, org);
worg[0] = DotProduct(org, axis[0]) + eorg[0];
worg[1] = -DotProduct(org, axis[1]) + eorg[1];
worg[2] = DotProduct(org, axis[2]) + eorg[2];
VectorSubtract(org, r_refdef.vieworg, vdist);
//ignore it if its too far away
VectorSubtract(worg, r_refdef.vieworg, vdist);
if (VectorLength(vdist) > (1024+512)*frandom())
continue;
if (st->face->flags & SURF_PLANEBACK)
VectorMA(org, -0.5, st->face->plane->normal, org);
VectorScale(st->face->plane->normal, -1, vdist);
else
VectorMA(org, 0.5, st->face->plane->normal, org);
VectorCopy(st->face->plane->normal, vdist);
if (!(CL_PointContentsMask(org) & FTECONTENTS_SOLID)) //should be paranoia
wnorm[0] = DotProduct(vdist, axis[0]);
wnorm[1] = -DotProduct(vdist, axis[1]);
wnorm[2] = DotProduct(vdist, axis[2]);
VectorMA(worg, 0.5, wnorm, worg);
if (!(CL_PointContentsMask(worg) & FTECONTENTS_SOLID)) //should be paranoia, at least for the world.
{
if (st->face->flags & SURF_PLANEBACK)
{
vdist[0] = -st->face->plane->normal[0];
vdist[1] = -st->face->plane->normal[1];
vdist[2] = -st->face->plane->normal[2];
PScript_RunParticleEffectState(org, vdist, 1, st->ptype, NULL);
}
else
PScript_RunParticleEffectState(org, st->face->plane->normal, 1, st->ptype, NULL);
PScript_RunParticleEffectState(worg, wnorm, 1, st->ptype, NULL);
}
}
}
}
static void R_Part_SkyTri(float *v1, float *v2, float *v3, msurface_t *surf, int ptype)
static void R_Part_SkyTri(qmodel_t *mod, float *v1, float *v2, float *v3, msurface_t *surf, int ptype)
{
float dot;
float xm;
@ -3767,13 +3841,13 @@ static void R_Part_SkyTri(float *v1, float *v2, float *v3, msurface_t *surf, int
skytris_t *st;
skytriblock_t *mem = skytrimem;
skytriblock_t *mem = mod->skytrimem;
if (!mem || mem->count == sizeof(mem->tris)/sizeof(mem->tris[0]))
{
skytrimem = Z_Malloc(sizeof(*skytrimem));
skytrimem->next = mem;
skytrimem->count = 0;
mem = skytrimem;
mod->skytrimem = Z_Malloc(sizeof(*mod->skytrimem));
mod->skytrimem->next = mem;
mod->skytrimem->count = 0;
mem = mod->skytrimem;
}
st = &mem->tris[mem->count];
@ -3793,7 +3867,7 @@ static void R_Part_SkyTri(float *v1, float *v2, float *v3, msurface_t *surf, int
dot = DotProduct(xd, yd);
theta = acos(dot/(xm*ym));
st->area = sin(theta)*xm*ym;
st->nexttime = particletime;
st->nexttime = mod->skytime;
st->face = surf;
st->ptype = ptype;
@ -3801,8 +3875,8 @@ static void R_Part_SkyTri(float *v1, float *v2, float *v3, msurface_t *surf, int
return;//bummer.
mem->count++;
st->next = skytris;
skytris = st;
st->next = mod->skytris;
mod->skytris = st;
}
@ -3846,7 +3920,7 @@ void PScript_EmitSkyEffectTris(qmodel_t *mod, msurface_t *fa, int ptype)
v2 = 1;
for (v3 = 2; v3 < numverts; v3++)
{
R_Part_SkyTri(verts[v1], verts[v2], verts[v3], fa, ptype);
R_Part_SkyTri(mod, verts[v1], verts[v2], verts[v3], fa, ptype);
v2 = v3;
}
@ -3866,7 +3940,7 @@ static void P_CleanTrailstate(trailstate_t *ts)
memset(ts, 0, sizeof(trailstate_t));
}
static void PScript_DelinkTrailstate(trailstate_t **tsk)
void PScript_DelinkTrailstate(trailstate_t **tsk)
{
trailstate_t *ts;
trailstate_t *assoc;
@ -5335,8 +5409,17 @@ int PScript_RunParticleEffectState (vec3_t org, vec3_t dir, float count, int typ
// maintain run list
if (!(ptype->state & PS_INRUNLIST) && (ptype->particles || ptype->clippeddecals))
{
ptype->nexttorun = part_run_list;
part_run_list = ptype;
if (part_run_list)
{
//insert after, to try to avoid edge-case weirdness
ptype->nexttorun = part_run_list->nexttorun;
part_run_list->nexttorun = ptype;
}
else
{
ptype->nexttorun = part_run_list;
part_run_list = ptype;
}
ptype->state |= PS_INRUNLIST;
}
@ -6220,7 +6303,6 @@ int PScript_ParticleTrail (vec3_t startpos, vec3_t end, int type, int dlkey, vec
}
static vec3_t pright, pup;
static float pframetime;
static void R_AddFanSparkParticle(scenetris_t *t, particle_t *p, plooks_t *type)
{
@ -6761,7 +6843,7 @@ static void R_AddTexturedParticle(scenetris_t *t, particle_t *p, plooks_t *type)
t->numidx += 6;
}
static void PScript_DrawParticleTypes (void)
static void PScript_DrawParticleTypes (float pframetime)
{
#if UNSUPPORTED
float viewtranslation[16];
@ -6788,7 +6870,6 @@ static void PScript_DrawParticleTypes (void)
int traces=r_particle_tracelimit.value;
int rampind;
static float oldtime;
static float flurrytime;
qboolean doflurry;
int batchflags;
@ -6821,11 +6902,6 @@ static void PScript_DrawParticleTypes (void)
PScript_RecalculateSkyTris();
}
pframetime = cl.time - oldtime;
if (pframetime < 0)
pframetime = 0;
oldtime = cl.time;
VectorScale (vup, 1.5, pup);
VectorScale (vright, 1.5, pright);
@ -7516,7 +7592,7 @@ static void PScript_DrawParticleTypes (void)
{
VectorAdd(stop, oldorg, stop);
VectorScale(stop, 0.5, stop);
#ifdef UNSUPPORTED
#if UNSUPPORTED
RQ_AddDistReorder(bdraw, b, type->slooks, stop);
#endif
}
@ -7548,8 +7624,10 @@ endtype:
{
if (!lastvalidtype)
part_run_list = type->nexttorun;
else
else if (lastvalidtype->nexttorun == type)
lastvalidtype->nexttorun = type->nexttorun;
else
lastvalidtype->nexttorun->nexttorun = type->nexttorun;
type->state &= ~PS_INRUNLIST;
}
else
@ -7641,6 +7719,7 @@ endtype:
glDisable(GL_BLEND);
glShadeModel(GL_FLAT);
glDepthMask(GL_TRUE);
glEnable(GL_CULL_FACE);
glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
GL_PolygonOffset (OFFSET_NONE);
cl_numstris = 0;
@ -7655,9 +7734,38 @@ R_DrawParticles
*/
void PScript_DrawParticles (void)
{
P_AddRainParticles();
int i;
entity_t *ent;
vec3_t axis[3];
float pframetime;
static float oldtime;
pframetime = cl.time - oldtime;
if (pframetime < 0)
pframetime = 0;
if (pframetime > 1)
pframetime = 1;
oldtime = cl.time;
PScript_DrawParticleTypes();
if (r_part_rain.value)
{
for (i = 0; i < cl.num_entities; i++)
{
ent = &cl_entities[i];
if (!ent->model || ent->model->needload)
continue;
if (!ent->model->skytris)
continue;
AngleVectors(ent->angles, axis[0], axis[1], axis[2]);
//this timer, as well as the per-tri timer, are unable to deal with certain rates+sizes. it would be good to fix that...
//it would also be nice to do mdls too...
P_AddRainParticles(ent->model, axis, ent->origin, ((i==0)?r_visframecount:0), pframetime);
}
//FIXME: static entities too!
}
PScript_DrawParticleTypes(pframetime);
// if (fallback)
// fallback->DrawParticles();

View File

@ -80,6 +80,10 @@ typedef struct
byte multicast_buf[MAX_DATAGRAM];
const char *particle_precache[MAX_PARTICLETYPES]; // NULL terminated
entity_state_t *static_entities;
int num_statics;
int max_statics;
} server_t;

View File

@ -148,7 +148,7 @@ static unsigned int SVFTE_DeltaCalcBits(entity_state_t *from, entity_state_t *to
bits |= UF_PREDINFO|UF_MOVETYPE;
/* if (from->weaponframe != to->weaponframe)
bits |= UF_PREDINFO|UF_WEAPONFRAME_OLD;
*/ if (to->pmovetype)
*/// if (to->pmovetype) //we don't support prediction, but we still need to network the player's velocity for bob etc.
{
if (SVFTE_DeltaPredCalcBits(from, to))
bits |= UF_PREDINFO;
@ -224,7 +224,7 @@ static unsigned int SVFTE_DeltaCalcBits(entity_state_t *from, entity_state_t *to
// if (to->light[0] != from->light[0] || to->light[1] != from->light[1] || to->light[2] != from->light[2] || to->light[3] != from->light[3] || to->lightstyle != from->lightstyle || to->lightpflags != from->lightpflags)
// bits |= UF_LIGHT;
if (to->traileffectnum != from->traileffectnum)
if (to->traileffectnum != from->traileffectnum || to->emiteffectnum != from->emiteffectnum)
bits |= UF_TRAILEFFECT;
// if (to->modelindex2 != from->modelindex2)
@ -493,7 +493,15 @@ static void SVFTE_WriteEntityUpdate(unsigned int bits, entity_state_t *state, si
MSG_WriteByte (msg, state->lightpflags);
}
*/ if (bits & UF_TRAILEFFECT)
MSG_WriteShort(msg, state->traileffectnum);
{
if (state->emiteffectnum)
{ //3 spare bits. so that's nice (this is guarenteed to be 14 bits max due to precaches using the upper two bits).
MSG_WriteShort(msg, (state->traileffectnum&0x3fff)|0x8000);
MSG_WriteShort(msg, state->emiteffectnum&0x3fff);
}
else
MSG_WriteShort(msg, state->traileffectnum&0x3fff);
}
/* if (bits & UF_COLORMOD)
{
@ -879,6 +887,7 @@ void SVFTE_BuildSnapshotForClient (client_t *client)
struct entity_num_state_s *ents = snapshot_entstate;
size_t numents = 0;
size_t maxents = snapshot_maxents;
int emiteffect;
// find the client's PVS
VectorAdd (clent->v.origin, clent->v.view_ofs, org);
@ -892,10 +901,11 @@ void SVFTE_BuildSnapshotForClient (client_t *client)
for (e=1 ; e<maxentities ; e++, ent = NEXT_EDICT(ent))
{
eflags = 0;
emiteffect = GetEdictFieldValue(ent, pr_extfields.emiteffectnum)->_float;
if (ent != clent) // clent is ALLWAYS sent
{
// ignore ents without visible models
if (!ent->v.modelindex || !PR_GetString(ent->v.model)[0])
if ((!ent->v.modelindex || !PR_GetString(ent->v.model)[0]) && !emiteffect)
continue;
//johnfitz -- don't send model>255 entities if protocol is 15
@ -907,7 +917,7 @@ void SVFTE_BuildSnapshotForClient (client_t *client)
eflags |= EFLAGS_VIEWMODEL;
else if (val && val->edict)
continue;
else
else if (ent->num_leafs)
{
// ignore if not touching a PV leaf
for (i=0 ; i < ent->num_leafs ; i++)
@ -958,6 +968,7 @@ void SVFTE_BuildSnapshotForClient (client_t *client)
ents[numents].state.traileffectnum = val->_float;
else
ents[numents].state.traileffectnum = 0;
ents[numents].state.emiteffectnum = emiteffect;
ents[numents].state.effects = ent->v.effects;
if (!ent->v.movetype || ent->v.movetype == MOVETYPE_STEP)
eflags |= EFLAGS_STEP;
@ -1090,6 +1101,10 @@ void SV_Init (void)
Cvar_RegisterVariable (&pr_checkextension);
Cvar_RegisterVariable (&sv_altnoclip); //johnfitz
if (isDedicated)
sv_public.string = "1";
else
sv_public.string = "0";
Cvar_RegisterVariable (&sv_public);
Cvar_RegisterVariable (&sv_reportheartbeats);
Cvar_RegisterVariable (&com_protocolname);
@ -2242,7 +2257,7 @@ qboolean SV_SendClientDatagram (client_t *client)
// send the datagram
if (NET_SendUnreliableMessage (client->netconnection, &msg) == -1)
{
SV_DropClient (true);// if the message couldn't send, kick off
SV_DropClient (false);// if the message couldn't send, kick off
return false;
}
@ -2308,7 +2323,7 @@ void SV_SendNop (client_t *client)
MSG_WriteChar (&msg, svc_nop);
if (NET_SendUnreliableMessage (client->netconnection, &msg) == -1)
SV_DropClient (true); // if the message couldn't send, kick off
SV_DropClient (false); // if the message couldn't send, kick off
client->last_message = realtime;
}
@ -2332,6 +2347,85 @@ int SV_SendPrespawnParticlePrecaches(int idx)
return -1;
return idx;
}
int SV_SendPrespawnStatics(int idx)
{
int i;
entity_state_t *svent;
int maxsize = host_client->message.maxsize - 128; //we can go quite large
while (1)
{
if (idx >= sv.num_statics)
return -1;
svent = &sv.static_entities[idx];
if (host_client->message.cursize > maxsize)
break;
idx++;
if (svent->modelindex >= host_client->limit_models)
continue;
if (memcmp(&nullentitystate, svent, sizeof(nullentitystate)))
{
if (host_client->protocol_pext2 & PEXT2_REPLACEMENTDELTAS)
{
MSG_WriteByte(&host_client->message, svcfte_spawnstatic2);
SVFTE_WriteEntityUpdate(SVFTE_DeltaCalcBits(&nullentitystate, svent), svent, &host_client->message, host_client->protocol_pext2);
}
else
{
//johnfitz -- PROTOCOL_FITZQUAKE
int bits = 0;
if (sv.protocol == PROTOCOL_FITZQUAKE) //still want to send baseline in PROTOCOL_NETQUAKE, so reset these values
{
if (svent->modelindex & 0xFF00)
bits |= B_LARGEMODEL;
if (svent->frame & 0xFF00)
bits |= B_LARGEFRAME;
if (svent->alpha != ENTALPHA_DEFAULT)
bits |= B_ALPHA;
}
else if (svent->frame > 0xff)
continue; //can't send these with the vanilla protcol (yay pext).
if (bits)
MSG_WriteByte (&host_client->message, svc_spawnstatic2);
else
MSG_WriteByte (&host_client->message, svc_spawnstatic);
//johnfitz
//johnfitz -- PROTOCOL_FITZQUAKE
if (bits)
MSG_WriteByte (&host_client->message, bits);
if (bits & B_LARGEMODEL)
MSG_WriteShort (&host_client->message, svent->modelindex);
else
MSG_WriteByte (&host_client->message, svent->modelindex);
if (bits & B_LARGEFRAME)
MSG_WriteShort (&host_client->message, svent->frame);
else
MSG_WriteByte (&host_client->message, svent->frame);
//johnfitz
MSG_WriteByte (&host_client->message, svent->colormap);
MSG_WriteByte (&host_client->message, svent->skin);
for (i=0 ; i<3 ; i++)
{
MSG_WriteCoord(&host_client->message, svent->origin[i], sv.protocolflags);
MSG_WriteAngle(&host_client->message, svent->angles[i], sv.protocolflags);
}
//johnfitz -- PROTOCOL_FITZQUAKE
if (bits & B_ALPHA)
MSG_WriteByte (&host_client->message, svent->alpha);
//johnfitz
}
}
}
return idx;
}
int SV_SendPrespawnBaselines(int idx)
{
int i;
@ -2466,6 +2560,15 @@ void SV_SendClientMessages (void)
}
}
if (host_client->sendsignon == 4)
{
host_client->signonidx = SV_SendPrespawnStatics(host_client->signonidx);
if (host_client->signonidx < 0)
{
host_client->signonidx = 0;
host_client->sendsignon++;
}
}
if (host_client->sendsignon == 5)
{
if (host_client->message.cursize+sv.signon.cursize+2 < host_client->message.maxsize)
{
@ -2482,8 +2585,8 @@ void SV_SendClientMessages (void)
// changes level
if (host_client->message.overflowed)
{
SV_DropClient (true);
host_client->message.overflowed = false;
SZ_Clear(&host_client->message);
SV_DropClient (false);
continue;
}
@ -2501,7 +2604,7 @@ void SV_SendClientMessages (void)
{
if (NET_SendMessage (host_client->netconnection
, &host_client->message) == -1)
SV_DropClient (true); // if the message couldn't send, kick off
SV_DropClient (false); // if the message couldn't send, kick off
SZ_Clear (&host_client->message);
host_client->last_message = realtime;
if (host_client->sendsignon == true)
@ -2720,6 +2823,9 @@ void SV_SpawnServer (const char *server)
sv.max_edicts = CLAMP (MIN_EDICTS,(int)max_edicts.value,MAX_EDICTS); //johnfitz -- max_edicts cvar
sv.edicts = (edict_t *) malloc (sv.max_edicts*pr_edict_size); // ericw -- sv.edicts switched to use malloc()
sv.max_statics = MAX_STATIC_ENTITIES;
sv.static_entities = Hunk_Alloc(sv.max_statics * sizeof(*sv.static_entities));
sv.datagram.maxsize = sizeof(sv.datagram_buf);
sv.datagram.cursize = 0;
sv.datagram.data = sv.datagram_buf;

116
weather.cfg Executable file
View File

@ -0,0 +1,116 @@
//lame example weather particles by Spike
//move to id1/particles/weather.cfg
//
//to use from qc, r_particledesc "weather classic" or call particleeffecnum("weather.te_rain") so the engine knows which config to use, then use the particle[rain|snow] builtins as normal.
//without qc changes option 1) use a texture called skyrain or skysnow, in addition to the r_particledesc thing so that this config is actually used (this is more for people to add stuff to existing maps).
//without qc changes option 2) add a worldspawn field called "_texpart_TEXTURENAME" with value "weather.tex_skysnow" or "weather.tex_skyrain"
//without qc changes option 3) an equivelent console command: r_partredirect tex_TEXTURENAME "weather.tex_skysnow"
//note that without qc, you need to restart the map for it to take effect.
//on the other hand, if you wanted to emit particles from models, use r_effect (which can optionally hide those models too).
//to override trails defined by model flags, you can use the r_trail command.
//weather effects that leave decals are probably overkill
//one option is to spawn a particle from qc that emits other particles that leave trails. emitting such trail emitting emitters from surfaces is definitely overkill.
//generic rain for the qc builtin
//create another called te_rain_12 or whatever for alternative versions of rain (going by the specified palette)
r_part te_rain
{
texture ball
scalefactor 1
count 1
alpha 0.4
rgb 255 255 255
die 2
veladd 2
scale 2
stretchfactor -40
type texturedspark
cliptype weather.rainsplash
clipbounce 100
clipcount 5
}
//internal splash effect for rain
r_part weather.rainsplash
{
randomvel 50 50
count 1
texture ball
scalefactor 1
alpha 0.1
rgb 255 255 255
die 0.4
scale 50
stretchfactor -2.5
veladd 1
scale 1
type texturedspark
gravity 800
}
//generic snow for the qc builtin
//naming itself causes it to bounce
r_part te_snow
{
texture ball
scalefactor 1
count 1
alpha 1
rgb 255 255 255
die 2
veladd 1
scale 5
flurry 40
gravity 400
friction 5
//settle
clipbounce 0
}
//rain effect to be emitted from textures
//the base direction points away from the surface, so veladd is the speed to move away from said surface.
//this isn't very useful for rain, because that vector is quite often horizontal (where the sky surrounds the player)
//that said, sitting in a box of such rain surfaces gives an interesting laser-field effect...
//anyway, that's why we can't reuse the qc effects, because qc gives a velocity that is actually meant to be usable.
r_part tex_skyrain
{
texture ball
scalefactor 1
rainfrequency 10
count 1
alpha 0.1
rgb 255 255 255
die 2
veladd 0
velbias -200 -200 -2000 //move sideways slightly in the same direction as the sky
scale 1
stretchfactor -40
type texturedspark
cliptype weather.rainsplash
clipbounce 100
clipcount 5
}
//snow from sky surfaces is often within a box
//this means we don't want the snow to ever settle.
//we also have no volume, so make the snow fall a little further.
r_part tex_skysnow
{
texture ball
scalefactor 1
rainfrequency 2
count 1
alpha 1
rgb 255 255 255
die 8
veladd 0
velbias 0 0 -100
scale 5
flurry 40
gravity 400
friction 5
cliptype tex_skysnow
clipbounce 0
}