Skip to content

Instantly share code, notes, and snippets.

@kkestell
Created September 12, 2025 20:40
Show Gist options
  • Select an option

  • Save kkestell/4b39df32eafdd610304d8770dd83aa41 to your computer and use it in GitHub Desktop.

Select an option

Save kkestell/4b39df32eafdd610304d8770dd83aa41 to your computer and use it in GitHub Desktop.
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <math.h>
#include <setjmp.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <raylib.h>
#define QUAKE_GAME // as opposed to utilities
#define VERSION 1.09
#define GLQUAKE_VERSION 1.00
#define D3DQUAKE_VERSION 0.01
#define WINQUAKE_VERSION 0.996
#define LINUX_VERSION 1.30
#define X11_VERSION 1.10
#define GAMENAME "id1"
#define ALIGN_PTR(p, a) (void *)(((uintptr_t)(p) + ((a) - 1)) & ~((uintptr_t)((a) - 1)))
#define ALIGN_PAD_ELEMS(T,A) (((A) + sizeof(T) - 2) / sizeof(T))
#define UNALIGNED_OK 0
#define CACHE_SIZE 32 // used to align key data structures
#define UNUSED(x) (x = x) // for pesky compiler / lint warnings
#define MINIMUM_MEMORY 0x550000
#define MINIMUM_MEMORY_LEVELPAK (MINIMUM_MEMORY + 0x100000)
#define MAX_NUM_ARGVS 50
#define PITCH 0
#define YAW 1
#define ROLL 2
#define MAX_QPATH 64 // max length of a quake game pathname
#define MAX_OSPATH 128 // max length of a filesystem pathname
#define ON_EPSILON 0.1 // point on plane side epsilon
#define MAX_MSGLEN 8000 // max length of a reliable message
#define MAX_DATAGRAM 1024 // max length of unreliable message
#define MAX_EDICTS 600 // FIXME: ouch! ouch! ouch!
#define MAX_LIGHTSTYLES 64
#define MAX_MODELS 256 // these are sent over the net as bytes
#define MAX_SOUNDS 256 // so they cannot be blindly increased
#define SAVEGAME_COMMENT_LENGTH 39
#define MAX_STYLESTRING 64
#define MAX_CL_STATS 32
#define STAT_HEALTH 0
#define STAT_FRAGS 1
#define STAT_WEAPON 2
#define STAT_AMMO 3
#define STAT_ARMOR 4
#define STAT_WEAPONFRAME 5
#define STAT_SHELLS 6
#define STAT_NAILS 7
#define STAT_ROCKETS 8
#define STAT_CELLS 9
#define STAT_ACTIVEWEAPON 10
#define STAT_TOTALSECRETS 11
#define STAT_TOTALMONSTERS 12
#define STAT_SECRETS 13 // bumped on client side by svc_foundsecret
#define STAT_MONSTERS 14 // bumped by svc_killedmonster
#define IT_SHOTGUN 1
#define IT_SUPER_SHOTGUN 2
#define IT_NAILGUN 4
#define IT_SUPER_NAILGUN 8
#define IT_GRENADE_LAUNCHER 16
#define IT_ROCKET_LAUNCHER 32
#define IT_LIGHTNING 64
#define IT_SUPER_LIGHTNING 128
#define IT_SHELLS 256
#define IT_NAILS 512
#define IT_ROCKETS 1024
#define IT_CELLS 2048
#define IT_AXE 4096
#define IT_ARMOR1 8192
#define IT_ARMOR2 16384
#define IT_ARMOR3 32768
#define IT_SUPERHEALTH 65536
#define IT_KEY1 131072
#define IT_KEY2 262144
#define IT_INVISIBILITY 524288
#define IT_INVULNERABILITY 1048576
#define IT_SUIT 2097152
#define IT_QUAD 4194304
#define IT_SIGIL1 (1 << 28)
#define IT_SIGIL2 (1 << 29)
#define IT_SIGIL3 (1 << 30)
#define IT_SIGIL4 (1 << 31)
#define RIT_SHELLS 128
#define RIT_NAILS 256
#define RIT_ROCKETS 512
#define RIT_CELLS 1024
#define RIT_AXE 2048
#define RIT_LAVA_NAILGUN 4096
#define RIT_LAVA_SUPER_NAILGUN 8192
#define RIT_MULTI_GRENADE 16384
#define RIT_MULTI_ROCKET 32768
#define RIT_PLASMA_GUN 65536
#define RIT_ARMOR1 8388608
#define RIT_ARMOR2 16777216
#define RIT_ARMOR3 33554432
#define RIT_LAVA_NAILS 67108864
#define RIT_PLASMA_AMMO 134217728
#define RIT_MULTI_ROCKETS 268435456
#define RIT_SHIELD 536870912
#define RIT_ANTIGRAV 1073741824
#define RIT_SUPERHEALTH 2147483648
#define HIT_PROXIMITY_GUN_BIT 16
#define HIT_MJOLNIR_BIT 7
#define HIT_LASER_CANNON_BIT 23
#define HIT_PROXIMITY_GUN (1 << HIT_PROXIMITY_GUN_BIT)
#define HIT_MJOLNIR (1 << HIT_MJOLNIR_BIT)
#define HIT_LASER_CANNON (1 << HIT_LASER_CANNON_BIT)
#define HIT_WETSUIT (1 << (23 + 2))
#define HIT_EMPATHY_SHIELDS (1 << (23 + 3))
#define MAX_SCOREBOARD 16
#define MAX_SCOREBOARDNAME 32
#define SOUND_CHANNELS 8
#define STRUCT_FROM_LINK(l, t, m) ((t *)((uint8_t *)l - (int32_t) & (((t *)0)->m)))
#define Q_MAXCHAR ((char)0x7f)
#define Q_MAXSHORT ((int16_t)0x7fff)
#define Q_MAXINT ((int32_t)0x7fffffff)
#define Q_MAXLONG ((int32_t)0x7fffffff)
#define Q_MAXFLOAT ((int32_t)0x7fffffff)
#define Q_MINCHAR ((char)0x80)
#define Q_MINSHORT ((int16_t)0x8000)
#define Q_MININT ((int32_t)0x80000000)
#define Q_MINLONG ((int32_t)0x80000000)
#define Q_MINFLOAT ((int32_t)0x7fffffff)
#define MAX_MAP_HULLS 4
#define MAX_MAP_MODELS 256
#define MAX_MAP_BRUSHES 4096
#define MAX_MAP_ENTITIES 1024
#define MAX_MAP_ENTSTRING 65536
#define MAX_MAP_PLANES 32767
#define MAX_MAP_NODES 32767 // because negative shorts are contents
#define MAX_MAP_CLIPNODES 32767 //
#define MAX_MAP_LEAFS 8192
#define MAX_MAP_VERTS 65535
#define MAX_MAP_FACES 65535
#define MAX_MAP_MARKSURFACES 65535
#define MAX_MAP_TEXINFO 4096
#define MAX_MAP_EDGES 256000
#define MAX_MAP_SURFEDGES 512000
#define MAX_MAP_TEXTURES 512
#define MAX_MAP_MIPTEX 0x200000
#define MAX_MAP_LIGHTING 0x100000
#define MAX_MAP_VISIBILITY 0x100000
#define MAX_MAP_PORTALS 65536
#define MAX_KEY 32
#define MAX_VALUE 1024
#define BSPVERSION 29
#define TOOLVERSION 2
#define LUMP_ENTITIES 0
#define LUMP_PLANES 1
#define LUMP_TEXTURES 2
#define LUMP_VERTEXES 3
#define LUMP_VISIBILITY 4
#define LUMP_NODES 5
#define LUMP_TEXINFO 6
#define LUMP_FACES 7
#define LUMP_LIGHTING 8
#define LUMP_CLIPNODES 9
#define LUMP_LEAFS 10
#define LUMP_MARKSURFACES 11
#define LUMP_EDGES 12
#define LUMP_SURFEDGES 13
#define LUMP_MODELS 14
#define HEADER_LUMPS 15
#define MIPLEVELS 4
#define PLANE_X 0
#define PLANE_Y 1
#define PLANE_Z 2
#define PLANE_ANYX 3
#define PLANE_ANYY 4
#define PLANE_ANYZ 5
#define CONTENTS_EMPTY -1
#define CONTENTS_SOLID -2
#define CONTENTS_WATER -3
#define CONTENTS_SLIME -4
#define CONTENTS_LAVA -5
#define CONTENTS_SKY -6
#define CONTENTS_ORIGIN -7 // removed at csg time
#define CONTENTS_CLIP -8 // changed to contents_solid
#define CONTENTS_CURRENT_0 -9
#define CONTENTS_CURRENT_90 -10
#define CONTENTS_CURRENT_180 -11
#define CONTENTS_CURRENT_270 -12
#define CONTENTS_CURRENT_UP -13
#define CONTENTS_CURRENT_DOWN -14
#define TEX_SPECIAL 1 // sky or slime, no lightmap or 256 subdivision
#define MAXLIGHTMAPS 4
#define AMBIENT_WATER 0
#define AMBIENT_SKY 1
#define AMBIENT_SLIME 2
#define AMBIENT_LAVA 3
#define NUM_AMBIENTS 4 // automatic ambient sounds
#define VID_CBITS 6
#define VID_GRADES (1 << VID_CBITS)
#define IS_NAN(x) (((*(int32_t *)&x) & nanmask) == nanmask)
#define DotProduct(x, y) (x[0] * y[0] + x[1] * y[1] + x[2] * y[2])
#define VectorSubtract(a, b, c) \
{ \
c[0] = a[0] - b[0]; \
c[1] = a[1] - b[1]; \
c[2] = a[2] - b[2]; \
}
#define VectorAdd(a, b, c) \
{ \
c[0] = a[0] + b[0]; \
c[1] = a[1] + b[1]; \
c[2] = a[2] + b[2]; \
}
#define VectorCopy(a, b) \
{ \
b[0] = a[0]; \
b[1] = a[1]; \
b[2] = a[2]; \
}
#define BOX_ON_PLANE_SIDE(emins, emaxs, p) \
(((p)->type < 3) ? (((p)->dist <= (emins)[(p)->type]) ? 1 : (((p)->dist >= (emaxs)[(p)->type]) ? 2 : 3)) \
: BoxOnPlaneSide((emins), (emaxs), (p)))
#define CMP_NONE 0
#define CMP_LZSS 1
#define TYP_NONE 0
#define TYP_LABEL 1
#define TYP_LUMPY 64 // 64 + grab command number
#define TYP_PALETTE 64
#define TYP_QTEX 65
#define TYP_QPIC 66
#define TYP_SOUND 67
#define TYP_MIPTEX 68
#define NET_NAMELEN 64
#define NET_MAXMESSAGE 8192
#define NET_HEADERSIZE (2 * sizeof(uint32_t))
#define NET_DATAGRAMSIZE (MAX_DATAGRAM + NET_HEADERSIZE)
#define NETFLAG_LENGTH_MASK 0x0000ffff
#define NETFLAG_DATA 0x00010000
#define NETFLAG_ACK 0x00020000
#define NETFLAG_NAK 0x00040000
#define NETFLAG_EOM 0x00080000
#define NETFLAG_UNRELIABLE 0x00100000
#define NETFLAG_CTL 0x80000000
#define NET_PROTOCOL_VERSION 3
#define CCREQ_CONNECT 0x01
#define CCREQ_SERVER_INFO 0x02
#define CCREQ_PLAYER_INFO 0x03
#define CCREQ_RULE_INFO 0x04
#define CCREP_ACCEPT 0x81
#define CCREP_REJECT 0x82
#define CCREP_SERVER_INFO 0x83
#define CCREP_PLAYER_INFO 0x84
#define CCREP_RULE_INFO 0x85
#define MAX_NET_DRIVERS 8
#define HOSTCACHESIZE 8
#define PROTOCOL_VERSION 15
#define U_MOREBITS (1 << 0)
#define U_ORIGIN1 (1 << 1)
#define U_ORIGIN2 (1 << 2)
#define U_ORIGIN3 (1 << 3)
#define U_ANGLE2 (1 << 4)
#define U_NOLERP (1 << 5) // don't interpolate movement
#define U_FRAME (1 << 6)
#define U_SIGNAL (1 << 7) // just differentiates from other updates
#define U_ANGLE1 (1 << 8)
#define U_ANGLE3 (1 << 9)
#define U_MODEL (1 << 10)
#define U_COLORMAP (1 << 11)
#define U_SKIN (1 << 12)
#define U_EFFECTS (1 << 13)
#define U_LONGENTITY (1 << 14)
#define SU_VIEWHEIGHT (1 << 0)
#define SU_IDEALPITCH (1 << 1)
#define SU_PUNCH1 (1 << 2)
#define SU_PUNCH2 (1 << 3)
#define SU_PUNCH3 (1 << 4)
#define SU_VELOCITY1 (1 << 5)
#define SU_VELOCITY2 (1 << 6)
#define SU_VELOCITY3 (1 << 7)
#define SU_ITEMS (1 << 9)
#define SU_ONGROUND (1 << 10) // no data follows, the bit is it
#define SU_INWATER (1 << 11) // no data follows, the bit is it
#define SU_WEAPONFRAME (1 << 12)
#define SU_ARMOR (1 << 13)
#define SU_WEAPON (1 << 14)
#define SND_VOLUME (1 << 0) // a byte
#define SND_ATTENUATION (1 << 1) // a byte
#define SND_LOOPING (1 << 2) // a long
#define DEFAULT_VIEWHEIGHT 22
#define GAME_COOP 0
#define GAME_DEATHMATCH 1
#define svc_bad 0
#define svc_nop 1
#define svc_disconnect 2
#define svc_updatestat 3 // [byte] [long]
#define svc_version 4 // [long] server version
#define svc_setview 5 // [short] entity number
#define svc_sound 6 // <see code>
#define svc_time 7 // [float] server time
#define svc_print 8 // [string] null terminated string
#define svc_stufftext \
9 // [string] stuffed into client's console buffer
#define svc_setangle 10 // [angle3] set the view angle to this absolute value
#define svc_serverinfo \
11 // [long] version
#define svc_lightstyle 12 // [byte] [string]
#define svc_updatename 13 // [byte] [string]
#define svc_updatefrags 14 // [byte] [short]
#define svc_clientdata 15 // <shortbits + data>
#define svc_stopsound 16 // <see code>
#define svc_updatecolors 17 // [byte] [byte]
#define svc_particle 18 // [vec3] <variable>
#define svc_damage 19
#define svc_spawnstatic 20
#define svc_spawnbaseline 22
#define svc_temp_entity 23
#define svc_setpause 24 // [byte] on / off
#define svc_signonnum 25 // [byte] used for the signon sequence
#define svc_centerprint 26 // [string] to put in center of the screen
#define svc_killedmonster 27
#define svc_foundsecret 28
#define svc_spawnstaticsound 29 // [coord3] [byte] samp [byte] vol [byte] aten
#define svc_intermission 30 // [string] music
#define svc_finale 31 // [string] music [string] text
#define svc_cdtrack 32 // [byte] track [byte] looptrack
#define svc_sellscreen 33
#define svc_cutscene 34
#define clc_bad 0
#define clc_nop 1
#define clc_disconnect 2
#define clc_move 3 // [usercmd_t]
#define clc_stringcmd 4 // [string] message
#define TE_SPIKE 0
#define TE_SUPERSPIKE 1
#define TE_GUNSHOT 2
#define TE_EXPLOSION 3
#define TE_TAREXPLOSION 4
#define TE_LIGHTNING1 5
#define TE_LIGHTNING2 6
#define TE_WIZSPIKE 7
#define TE_KNIGHTSPIKE 8
#define TE_LIGHTNING3 9
#define TE_LAVASPLASH 10
#define TE_TELEPORT 11
#define TE_EXPLOSION2 12
#define TE_BEAM 13
#define SBAR_HEIGHT 24
#define __SOUND__
#define DEFAULT_SOUND_PACKET_VOLUME 255
#define DEFAULT_SOUND_PACKET_ATTENUATION 1.0
#define MAX_CHANNELS 128
#define MAX_DYNAMIC_CHANNELS 8
#define MAXCLIPPLANES 11
#define TOP_RANGE 16 // soldier uniform colors
#define BOTTOM_RANGE 96
#define CSHIFT_CONTENTS 0
#define CSHIFT_DAMAGE 1
#define CSHIFT_BONUS 2
#define CSHIFT_POWERUP 3
#define NUM_CSHIFTS 4
#define NAME_LENGTH 64
#define SIGNONS 4 // signon messages to receive before connected
#define MAX_DLIGHTS 32
#define MAX_BEAMS 24
#define MAX_EFRAGS 640
#define MAX_MAPSTRING 2048
#define MAX_DEMOS 8
#define MAX_DEMONAME 16
#define MAX_TEMP_ENTITIES 64 // lightning bolts, etc
#define MAX_STATIC_ENTITIES 128 // torches, etc
#define MAX_VISEDICTS 256
#define OFS_NULL 0
#define OFS_RETURN 1
#define OFS_PARM0 4 // leave 3 ofs for each parm to hold vectors
#define OFS_PARM1 7
#define OFS_PARM2 10
#define OFS_PARM3 13
#define OFS_PARM4 16
#define OFS_PARM5 19
#define OFS_PARM6 22
#define OFS_PARM7 25
#define RESERVED_OFS 28
#define DEF_SAVEGLOBAL (1 << 15)
#define MAX_PARMS 8
#define PROG_VERSION 6
#define PROGHEADER_CRC 5927
#define MAX_ENT_LEAFS 16
#define EDICT_FROM_AREA(l) STRUCT_FROM_LINK(l, edict_t, area)
#define NEXT_EDICT(e) ((edict_t *)((uint8_t *)e + pr_edict_size))
#define EDICT_TO_PROG(e) ((uint8_t *)e - (uint8_t *)sv.edicts)
#define PROG_TO_EDICT(e) ((edict_t *)((uint8_t *)sv.edicts + e))
#define G_FLOAT(o) (pr_globals[o])
#define G_INT(o) (*(int32_t *)&pr_globals[o])
#define G_EDICT(o) ((edict_t *)((uint8_t *)sv.edicts + *(int32_t *)&pr_globals[o]))
#define G_EDICTNUM(o) NUM_FOR_EDICT(G_EDICT(o))
#define G_VECTOR(o) (&pr_globals[o])
#define G_STRING(o) (pr_strings + *(string_t *)&pr_globals[o])
#define G_FUNCTION(o) (*(func_t *)&pr_globals[o])
#define E_FLOAT(e, o) (((float *)&e->v)[o])
#define E_INT(e, o) (*(int32_t *)&((float *)&e->v)[o])
#define E_VECTOR(e, o) (&((float *)&e->v)[o])
#define E_STRING(e, o) (pr_strings + *(string_t *)&((float *)&e->v)[o])
#define NUM_PING_TIMES 16
#define NUM_SPAWN_PARMS 16
#define MOVETYPE_NONE 0 // never moves
#define MOVETYPE_ANGLENOCLIP 1
#define MOVETYPE_ANGLECLIP 2
#define MOVETYPE_WALK 3 // gravity
#define MOVETYPE_STEP 4 // gravity, special edge handling
#define MOVETYPE_FLY 5
#define MOVETYPE_TOSS 6 // gravity
#define MOVETYPE_PUSH 7 // no clip to world, push and crush
#define MOVETYPE_NOCLIP 8
#define MOVETYPE_FLYMISSILE 9 // extra size to monsters
#define MOVETYPE_BOUNCE 10
#define MOVETYPE_BOUNCEMISSILE 11 // bounce w/o gravity
#define MOVETYPE_FOLLOW 12 // track movement of aiment
#define SOLID_NOT 0 // no interaction with other objects
#define SOLID_TRIGGER 1 // touch on edge, but not blocking
#define SOLID_BBOX 2 // touch on edge, block
#define SOLID_SLIDEBOX 3 // touch on edge, but not an onground
#define SOLID_BSP 4 // bsp clip, touch on edge, block
#define DEAD_NO 0
#define DEAD_DYING 1
#define DEAD_DEAD 2
#define DAMAGE_NO 0
#define DAMAGE_YES 1
#define DAMAGE_AIM 2
#define FL_FLY 1
#define FL_SWIM 2
#define FL_CONVEYOR 4
#define FL_CLIENT 8
#define FL_INWATER 16
#define FL_MONSTER 32
#define FL_GODMODE 64
#define FL_NOTARGET 128
#define FL_ITEM 256
#define FL_ONGROUND 512
#define FL_PARTIALGROUND 1024 // not all corners are valid
#define FL_WATERJUMP 2048 // player jumping out of water
#define FL_JUMPRELEASED 4096 // for jump debouncing
#define EF_BRIGHTFIELD 1
#define EF_MUZZLEFLASH 2
#define EF_BRIGHTLIGHT 4
#define EF_DIMLIGHT 8
#define SPAWNFLAG_NOT_EASY 256
#define SPAWNFLAG_NOT_MEDIUM 512
#define SPAWNFLAG_NOT_HARD 1024
#define SPAWNFLAG_NOT_DEATHMATCH 2048
#define __MODEL__
#define ALIAS_VERSION 6
#define ALIAS_ONSEAM 0x0020
#define SYNCTYPE_T
#define DT_FACES_FRONT 0x0010
#define IDPOLYHEADER (('O' << 24) + ('P' << 16) + ('D' << 8) + 'I')
#define SPRITE_VERSION 1
#define SPR_VP_PARALLEL_UPRIGHT 0
#define SPR_FACING_UPRIGHT 1
#define SPR_VP_PARALLEL 2
#define SPR_ORIENTED 3
#define SPR_VP_PARALLEL_ORIENTED 4
#define IDSPRITEHEADER (('P' << 24) + ('S' << 16) + ('D' << 8) + 'I')
#define SIDE_FRONT 0
#define SIDE_BACK 1
#define SIDE_ON 2
#define SURF_PLANEBACK 2
#define SURF_DRAWSKY 4
#define SURF_DRAWSPRITE 8
#define SURF_DRAWTURB 0x10
#define SURF_DRAWTILED 0x20
#define SURF_DRAWBACKGROUND 0x40
#define EF_ROCKET 1 // leave a trail
#define EF_GRENADE 2 // leave a trail
#define EF_GIB 4 // leave a trail
#define EF_ROTATE 8 // rotate (bonus items)
#define EF_TRACER 16 // green split trail
#define EF_ZOMGIB 32 // small blood trail
#define EF_TRACER2 64 // orange split trail + rotate
#define EF_TRACER3 128 // purple trail
#define WARP_WIDTH 320
#define WARP_HEIGHT 200
#define MAX_LBM_HEIGHT 480
#define PARTICLE_Z_CLIP 8.0
#define DR_SOLID 0
#define DR_TRANSPARENT 1
#define TRANSPARENT_COLOR 0xFF
#define TURB_TEX_SIZE 64 // base turbulent texture size
#define CYCLE 128 // turbulent cycle size
#define TILE_SIZE 128 // size of textures generated by R_GenTiledSurf
#define SKYSHIFT 7
#define SKYSIZE (1 << SKYSHIFT)
#define SKYMASK (SKYSIZE - 1)
#define MOVE_NORMAL 0
#define MOVE_NOMONSTERS 1
#define MOVE_MISSILE 2
#define K_TAB 9
#define K_ENTER 13
#define K_ESCAPE 27
#define K_SPACE 32
#define K_BACKSPACE 127
#define K_UPARROW 128
#define K_DOWNARROW 129
#define K_LEFTARROW 130
#define K_RIGHTARROW 131
#define K_ALT 132
#define K_CTRL 133
#define K_SHIFT 134
#define K_F1 135
#define K_F2 136
#define K_F3 137
#define K_F4 138
#define K_F5 139
#define K_F6 140
#define K_F7 141
#define K_F8 142
#define K_F9 143
#define K_F10 144
#define K_F11 145
#define K_F12 146
#define K_INS 147
#define K_DEL 148
#define K_PGDN 149
#define K_PGUP 150
#define K_HOME 151
#define K_END 152
#define K_PAUSE 255
#define K_MOUSE1 200
#define K_MOUSE2 201
#define K_MOUSE3 202
#define K_JOY1 203
#define K_JOY2 204
#define K_JOY3 205
#define K_JOY4 206
#define K_AUX1 207
#define K_AUX2 208
#define K_AUX3 209
#define K_AUX4 210
#define K_AUX5 211
#define K_AUX6 212
#define K_AUX7 213
#define K_AUX8 214
#define K_AUX9 215
#define K_AUX10 216
#define K_AUX11 217
#define K_AUX12 218
#define K_AUX13 219
#define K_AUX14 220
#define K_AUX15 221
#define K_AUX16 222
#define K_AUX17 223
#define K_AUX18 224
#define K_AUX19 225
#define K_AUX20 226
#define K_AUX21 227
#define K_AUX22 228
#define K_AUX23 229
#define K_AUX24 230
#define K_AUX25 231
#define K_AUX26 232
#define K_AUX27 233
#define K_AUX28 234
#define K_AUX29 235
#define K_AUX30 236
#define K_AUX31 237
#define K_AUX32 238
#define K_MWHEELUP 239
#define K_MWHEELDOWN 240
#define MNET_IPX 1
#define MNET_TCP 2
#define SHOWNET(x) \
if (cl_shownet.value == 2) \
Con_Printf("%3i:%s\n", msg_readcount - 1, x);
#define MAX_ALIAS_NAME 32
#define MAX_ARGS 80
#define NUM_SAFE_ARGVS 7
#define PAK0_COUNT 339
#define PAK0_CRC 32981
#define CMDLINE_LENGTH 256
#define MAX_FILES_IN_PACK 2048
#define CON_TEXTSIZE 16384
#define NUM_CON_TIMES 4
#define MAXCMDLINE 256
#define MAXGAMEDIRLEN 1000
#define MAXPRINTMSG 4096
#define CRC_INIT_VALUE 0xffff
#define CRC_XOR_VALUE 0x0000
#define _R_SHARED_H_
#define MAXVERTS 16 // max points in a surface polygon
#define MAXWORKINGVERTS \
(MAXVERTS + 4) // max points in an intermediate
#define MAXHEIGHT 1024
#define MAXWIDTH 1280
#define MAXDIMENSION ((MAXHEIGHT > MAXWIDTH) ? MAXHEIGHT : MAXWIDTH)
#define SIN_BUFFER_SIZE (MAXDIMENSION + CYCLE)
#define INFINITE_DISTANCE \
0x10000 // distance that's always guaranteed to
#define NUMSTACKEDGES 2400
#define MINEDGES NUMSTACKEDGES
#define NUMSTACKSURFACES 800
#define MINSURFACES NUMSTACKSURFACES
#define MAXSPANS 3000
#define ALIAS_LEFT_CLIP 0x0001
#define ALIAS_TOP_CLIP 0x0002
#define ALIAS_RIGHT_CLIP 0x0004
#define ALIAS_BOTTOM_CLIP 0x0008
#define ALIAS_Z_CLIP 0x0010
#define ALIAS_XY_CLIP_MASK 0x000F
#define SCANBUFFERPAD 0x1000
#define R_SKY_SMASK 0x007F0000
#define R_SKY_TMASK 0x007F0000
#define DS_SPAN_LIST_END -128
#define SURFCACHE_SIZE_AT_320X200 600 * 1024
#define NUM_MIPS 4
#define ALIAS_BASE_SIZE_RATIO (1.0 / 11.0)
#define BMODEL_FULLY_CLIPPED \
0x10 // value returned by R_BmodelCheckBBox ()
#define XCENTERING (1.0 / 2.0)
#define YCENTERING (1.0 / 2.0)
#define CLIP_EPSILON 0.001
#define BACKFACE_EPSILON 0.01
#define DIST_NOT_SET 98765
#define NEAR_CLIP 0.01
#define MAXBVERTINDEXES \
1000 // new clipped vertices when clipping bmodels
#define MAX_BTOFPOLYS 5000 // FIXME: tune this
#define MAXALIASVERTS 2000 // TODO: tune this
#define ALIAS_Z_CLIP_PLANE 5
#define AMP 8 * 0x10000
#define AMP2 3
#define SPEED 20
#define DPS_MAXSPANS MAXHEIGHT + 1
#define SKY_SPAN_SHIFT 5
#define SKY_SPAN_MAX (1 << SKY_SPAN_SHIFT)
#define GUARDSIZE 4
#define MAX_CACHED_PICS 128
#define VCR_SIGNATURE 0x56435231
#define SAVEGAME_VERSION 5
#define DEG2RAD(a) (a * M_PI) / 180.0F
#define StartingGame (m_multiplayer_cursor == 1)
#define JoiningGame (m_multiplayer_cursor == 0)
#define SerialConfig (m_net_cursor == 0)
#define DirectConfig (m_net_cursor == 1)
#define IPXConfig (m_net_cursor == 2)
#define TCPIPConfig (m_net_cursor == 3)
#define MAIN_ITEMS 5
#define SINGLEPLAYER_ITEMS 3
#define MAX_SAVEGAMES 12
#define MULTIPLAYER_ITEMS 3
#define NUM_SETUP_CMDS 5
#define OPTIONS_ITEMS 13
#define SLIDER_RANGE 10
#define NUMCOMMANDS (sizeof(bindnames) / sizeof(bindnames[0]))
#define NUM_HELP_PAGES 6
#define NUM_SERIALCONFIG_CMDS 6
#define NUM_MODEMCONFIG_CMDS 5
#define NUM_LANCONFIG_CMDS 3
#define NUM_GAMEOPTIONS 9
#define MAX_MOD_KNOWN 256
#define NL_PRESENT 0
#define NL_NEEDS_LOADED 1
#define NL_UNREFERENCED 2
#define ANIM_CYCLE 2
#define sfunc net_landrivers[sock->landriver]
#define dfunc net_landrivers[net_landriverlevel]
#define VCR_OP_CONNECT 1
#define VCR_OP_GETMESSAGE 2
#define VCR_OP_SENDMESSAGE 3
#define VCR_OP_CANSENDMESSAGE 4
#define VCR_MAX_MESSAGE 4
#define RETURN_EDICT(e) (((int32_t *)pr_globals)[OFS_RETURN] = EDICT_TO_PROG(e))
#define MAX_CHECK 16
#define MSG_BROADCAST 0 // unreliable to all
#define MSG_ONE 1 // reliable to one (msg_entity)
#define MSG_ALL 2 // reliable to all
#define MSG_INIT 3 // write to the init string
#define MAX_FIELD_LEN 64
#define GEFV_CACHESIZE 2
#define MAX_STACK_DEPTH 32
#define LOCALSTACK_SIZE 2048
#define LIGHT_MIN \
5 // lowest light value we'll allow, to avoid the
#define NUMVERTEXNORMALS 162
#define MAX_BMODEL_VERTS 500 // 6K
#define MAX_BMODEL_EDGES 1000 // 12K
#define MAXLEFTCLIPEDGES 100
#define FULLY_CLIPPED_CACHED 0x80000000
#define FRAMECOUNT_MASK 0x7FFFFFFF
#define MAX_TIMINGS 100
#define MAX_PARTICLES \
2048 // default max # of particles at one
#define ABSOLUTE_MIN_PARTICLES \
512 // no fewer than this no matter what's
#define STAT_MINUS 10 // num frame for '-' stats digit
#define MAX_SFX 512
#define PAINTBUFFER_SIZE 512
#define STEPSIZE 18
#define DI_NODIR -1
#define MOVE_EPSILON 0.01
#define STOP_EPSILON 0.1
#define MAX_CLIP_PLANES 5
#define MAX_FORWARD 6
#define MAX_HANDLES 10
#define BASEWIDTH (640)
#define BASEHEIGHT (400)
#define AREA_DEPTH 4
#define AREA_NODES 32
#define DIST_EPSILON (0.03125)
#define DYNAMIC_SIZE 0xc000
#define ZONEID 0x1d4a11
#define MINFRAGMENT 64
#define HUNK_SENTINAL 0x1df001ed
typedef struct sizebuf_s
{
bool allowoverflow;
bool overflowed;
uint8_t *data;
int32_t maxsize;
int32_t cursize;
} sizebuf_t;
typedef struct link_s
{
struct link_s *prev;
struct link_s *next;
} link_t;
struct cache_user_s;
extern struct cvar_s registered;
typedef struct
{
int32_t fileofs;
int32_t filelen;
} lump_t;
typedef struct
{
float mins[3];
float maxs[3];
float origin[3];
int32_t headnode[MAX_MAP_HULLS];
int32_t visleafs;
int32_t firstface;
int32_t numfaces;
} dmodel_t;
typedef struct
{
int32_t version;
lump_t lumps[HEADER_LUMPS];
} dheader_t;
typedef struct
{
int32_t nummiptex;
int32_t dataofs[4];
} dmiptexlump_t;
typedef struct miptex_s
{
int8_t name[16];
uint32_t width;
uint32_t height;
uint32_t offsets[MIPLEVELS];
} miptex_t;
typedef struct
{
float point[3];
} dvertex_t;
typedef struct
{
float normal[3];
float dist;
int32_t type;
} dplane_t;
typedef struct
{
int32_t planenum;
int16_t children[2];
int16_t mins[3];
int16_t maxs[3];
uint16_t firstface;
uint16_t numfaces;
} dnode_t;
typedef struct
{
int32_t planenum;
int16_t children[2];
} dclipnode_t;
typedef struct texinfo_s
{
float vecs[2][4];
int32_t miptex;
int32_t flags;
} texinfo_t;
typedef struct
{
uint16_t v[2];
} dedge_t;
typedef struct
{
int16_t planenum;
int16_t side;
int32_t firstedge;
int16_t numedges;
int16_t texinfo;
uint8_t styles[MAXLIGHTMAPS];
int32_t lightofs;
} dface_t;
typedef struct
{
int32_t contents;
int32_t visofs;
int16_t mins[3];
int16_t maxs[3];
uint16_t firstmarksurface;
uint16_t nummarksurfaces;
uint8_t ambient_level[NUM_AMBIENTS];
} dleaf_t;
typedef uint8_t pixel_t;
typedef struct vrect_s
{
int32_t x;
int32_t y;
int32_t width;
int32_t height;
struct vrect_s *pnext;
} vrect_t;
typedef struct
{
pixel_t *buffer;
pixel_t *colormap;
uint16_t *colormap16;
int32_t fullbright;
uint32_t rowbytes;
uint32_t width;
uint32_t height;
float aspect;
int32_t numpages;
int32_t recalc_refdef;
pixel_t *conbuffer;
int32_t conrowbytes;
uint32_t conwidth;
uint32_t conheight;
int32_t maxwarpwidth;
int32_t maxwarpheight;
pixel_t *direct;
} viddef_t;
typedef struct cache_user_s
{
void *data;
} cache_user_t;
typedef float vec_t;
typedef vec_t vec3_t[3];
typedef vec_t vec5_t[5];
typedef int32_t fixed4_t;
typedef int32_t fixed8_t;
typedef int32_t fixed16_t;
struct mplane_s;
typedef struct
{
vec3_t origin;
vec3_t angles;
int32_t modelindex;
int32_t frame;
int32_t colormap;
int32_t skin;
int32_t effects;
} entity_state_t;
typedef struct
{
int32_t width;
int32_t height;
uint8_t data[4];
} qpic_t;
typedef struct
{
char identification[4];
int32_t numlumps;
int32_t infotableofs;
} wadinfo_t;
typedef struct
{
int32_t filepos;
int32_t disksize;
int32_t size;
char type;
char compression;
char pad1;
char pad2;
char name[16];
} lumpinfo_t;
typedef struct cvar_s
{
char *name;
char *string;
bool archive;
bool server;
float value;
struct cvar_s *next;
} cvar_t;
struct qsockaddr
{
int16_t sa_family;
unsigned char sa_data[14];
};
typedef struct qsocket_s
{
struct qsocket_s *next;
double connecttime;
double lastMessageTime;
double lastSendTime;
bool disconnected;
bool canSend;
bool sendNext;
int32_t driver;
int32_t landriver;
int32_t socket;
void *driverdata;
uint32_t ackSequence;
uint32_t sendSequence;
uint32_t unreliableSendSequence;
int32_t sendMessageLength;
uint8_t sendMessage[NET_MAXMESSAGE];
uint32_t receiveSequence;
uint32_t unreliableReceiveSequence;
int32_t receiveMessageLength;
uint8_t receiveMessage[NET_MAXMESSAGE];
struct qsockaddr addr;
char address[NET_NAMELEN];
} qsocket_t;
typedef struct
{
char *name;
bool initialized;
int32_t controlSock;
int32_t (*Init)(void);
void (*Shutdown)(void);
void (*Listen)(bool state);
int32_t (*OpenSocket)(int32_t port);
int32_t (*CloseSocket)(int32_t socket);
int32_t (*Connect)(int32_t socket, struct qsockaddr *addr);
int32_t (*CheckNewConnections)(void);
int32_t (*Read)(int32_t socket, uint8_t *buf, int32_t len, struct qsockaddr *addr);
int32_t (*Write)(int32_t socket, uint8_t *buf, int32_t len, struct qsockaddr *addr);
int32_t (*Broadcast)(int32_t socket, uint8_t *buf, int32_t len);
char *(*AddrToString)(struct qsockaddr *addr);
int32_t (*StringToAddr)(char *string, struct qsockaddr *addr);
int32_t (*GetSocketAddr)(int32_t socket, struct qsockaddr *addr);
int32_t (*GetNameFromAddr)(struct qsockaddr *addr, char *name);
int32_t (*GetAddrFromName)(char *name, struct qsockaddr *addr);
int32_t (*AddrCompare)(struct qsockaddr *addr1, struct qsockaddr *addr2);
int32_t (*GetSocketPort)(struct qsockaddr *addr);
int32_t (*SetSocketPort)(struct qsockaddr *addr, int32_t port);
} net_landriver_t;
typedef struct
{
char *name;
bool initialized;
int32_t (*Init)(void);
void (*Listen)(bool state);
void (*SearchForHosts)(bool xmit);
qsocket_t *(*Connect)(char *host);
qsocket_t *(*CheckNewConnections)(void);
int32_t (*QGetMessage)(qsocket_t *sock);
int32_t (*QSendMessage)(qsocket_t *sock, sizebuf_t *data);
int32_t (*SendUnreliableMessage)(qsocket_t *sock, sizebuf_t *data);
bool (*CanSendMessage)(qsocket_t *sock);
bool (*CanSendUnreliableMessage)(qsocket_t *sock);
void (*Close)(qsocket_t *sock);
void (*Shutdown)(void);
int32_t controlSock;
} net_driver_t;
typedef struct
{
char name[16];
char map[16];
char cname[32];
int32_t users;
int32_t maxusers;
int32_t driver;
int32_t ldriver;
struct qsockaddr addr;
} hostcache_t;
typedef struct _PollProcedure
{
struct _PollProcedure *next;
double nextTime;
void (*procedure)();
void *arg;
} PollProcedure;
typedef void (*xcommand_t)(void);
typedef enum
{
src_client,
src_command
} cmd_source_t;
typedef struct
{
int32_t left;
int32_t right;
} portable_samplepair_t;
typedef struct sfx_s
{
char name[MAX_QPATH];
cache_user_t cache;
} sfx_t;
typedef struct
{
int32_t length;
int32_t loopstart;
int32_t speed;
int32_t width;
int32_t stereo;
uint8_t data[1];
} sfxcache_t;
typedef struct
{
bool gamealive;
bool soundalive;
bool splitbuffer;
int32_t channels;
int32_t samples;
int32_t submission_chunk;
int32_t samplepos;
int32_t samplebits;
int32_t speed;
unsigned char *buffer;
} dma_t;
typedef struct
{
sfx_t *sfx;
int32_t leftvol;
int32_t rightvol;
int32_t end;
int32_t pos;
int32_t looping;
int32_t entnum;
int32_t entchannel;
vec3_t origin;
vec_t dist_mult;
int32_t master_vol;
} channel_t;
typedef struct
{
int32_t rate;
int32_t width;
int32_t channels;
int32_t loopstart;
int32_t samples;
int32_t dataofs;
} wavinfo_t;
typedef struct efrag_s
{
struct mleaf_s *leaf;
struct efrag_s *leafnext;
struct entity_s *entity;
struct efrag_s *entnext;
} efrag_t;
typedef struct entity_s
{
bool forcelink;
int32_t update_type;
entity_state_t baseline;
double msgtime;
vec3_t msg_origins[2];
vec3_t origin;
vec3_t msg_angles[2];
vec3_t angles;
struct model_s *model;
struct efrag_s *efrag;
int32_t frame;
float syncbase;
uint8_t *colormap;
int32_t effects;
int32_t skinnum;
int32_t visframe;
int32_t dlightframe;
int32_t dlightbits;
int32_t trivial_accept;
struct mnode_s *topnode;
} entity_t;
typedef struct
{
vrect_t vrect;
vrect_t aliasvrect;
int32_t vrectright;
int32_t vrectbottom;
int32_t aliasvrectright;
int32_t aliasvrectbottom;
float vrectrightedge;
float fvrectx;
float fvrecty;
float fvrectx_adj;
float fvrecty_adj;
int32_t vrect_x_adj_shift20;
int32_t vrectright_adj_shift20;
float fvrectright_adj;
float fvrectbottom_adj;
float fvrectright;
float fvrectbottom;
float horizontalFieldOfView;
float xOrigin;
float yOrigin;
vec3_t vieworg;
vec3_t viewangles;
float fov_x;
float fov_y;
int32_t ambientlight;
} refdef_t;
extern struct texture_s *r_notexture_mip;
typedef struct
{
vec3_t viewangles;
float forwardmove;
float sidemove;
float upmove;
} usercmd_t;
typedef struct
{
int32_t length;
char map[MAX_STYLESTRING];
} lightstyle_t;
typedef struct
{
char name[MAX_SCOREBOARDNAME];
float entertime;
int32_t frags;
int32_t colors;
uint8_t translations[VID_GRADES * 256];
} scoreboard_t;
typedef struct
{
int32_t destcolor[3];
int32_t percent;
} cshift_t;
typedef struct
{
vec3_t origin;
float radius;
float die;
float decay;
float minlight;
int32_t key;
} dlight_t;
typedef struct
{
int32_t entity;
struct model_s *model;
float endtime;
vec3_t start;
vec3_t end;
} beam_t;
typedef enum
{
ca_dedicated,
ca_disconnected,
ca_connected
} cactive_t;
typedef struct
{
cactive_t state;
char mapstring[MAX_QPATH];
char spawnparms[MAX_MAPSTRING];
int32_t demonum;
char demos[MAX_DEMOS][MAX_DEMONAME];
bool demorecording;
bool demoplayback;
bool timedemo;
int32_t forcetrack;
FILE *demofile;
int32_t td_lastframe;
int32_t td_startframe;
float td_starttime;
int32_t signon;
struct qsocket_s *netcon;
sizebuf_t message;
} client_static_t;
typedef struct
{
int32_t movemessages;
usercmd_t cmd;
int32_t stats[MAX_CL_STATS];
int32_t items;
float item_gettime[32];
float faceanimtime;
cshift_t cshifts[NUM_CSHIFTS];
cshift_t prev_cshifts[NUM_CSHIFTS];
vec3_t mviewangles[2];
vec3_t viewangles;
vec3_t mvelocity[2];
vec3_t velocity;
vec3_t punchangle;
float idealpitch;
float pitchvel;
bool nodrift;
float driftmove;
double laststop;
float viewheight;
float crouch;
bool paused;
bool onground;
bool inwater;
int32_t intermission;
int32_t completed_time;
double mtime[2];
double time;
double oldtime;
float last_received_message;
struct model_s *model_precache[MAX_MODELS];
struct sfx_s *sound_precache[MAX_SOUNDS];
char levelname[40];
int32_t viewentity;
int32_t maxclients;
int32_t gametype;
struct model_s *worldmodel;
struct efrag_s *free_efrags;
int32_t num_entities;
int32_t num_statics;
entity_t viewent;
int32_t cdtrack;
int32_t looptrack;
scoreboard_t *scores;
} client_state_t;
typedef struct
{
int32_t down[2];
int32_t state;
} kbutton_t;
typedef int32_t func_t;
typedef int32_t string_t;
typedef enum
{
ev_void,
ev_string,
ev_float,
ev_vector,
ev_entity,
ev_field,
ev_function,
ev_pointer
} etype_t;
enum
{
OP_DONE,
OP_MUL_F,
OP_MUL_V,
OP_MUL_FV,
OP_MUL_VF,
OP_DIV_F,
OP_ADD_F,
OP_ADD_V,
OP_SUB_F,
OP_SUB_V,
OP_EQ_F,
OP_EQ_V,
OP_EQ_S,
OP_EQ_E,
OP_EQ_FNC,
OP_NE_F,
OP_NE_V,
OP_NE_S,
OP_NE_E,
OP_NE_FNC,
OP_LE,
OP_GE,
OP_LT,
OP_GT,
OP_LOAD_F,
OP_LOAD_V,
OP_LOAD_S,
OP_LOAD_ENT,
OP_LOAD_FLD,
OP_LOAD_FNC,
OP_ADDRESS,
OP_STORE_F,
OP_STORE_V,
OP_STORE_S,
OP_STORE_ENT,
OP_STORE_FLD,
OP_STORE_FNC,
OP_STOREP_F,
OP_STOREP_V,
OP_STOREP_S,
OP_STOREP_ENT,
OP_STOREP_FLD,
OP_STOREP_FNC,
OP_RETURN,
OP_NOT_F,
OP_NOT_V,
OP_NOT_S,
OP_NOT_ENT,
OP_NOT_FNC,
OP_IF,
OP_IFNOT,
OP_CALL0,
OP_CALL1,
OP_CALL2,
OP_CALL3,
OP_CALL4,
OP_CALL5,
OP_CALL6,
OP_CALL7,
OP_CALL8,
OP_STATE,
OP_GOTO,
OP_AND,
OP_OR,
OP_BITAND,
OP_BITOR
};
typedef struct statement_s
{
uint16_t op;
int16_t a;
int16_t b;
int16_t c;
} dstatement_t;
typedef struct
{
uint16_t type;
uint16_t ofs;
int32_t s_name;
} ddef_t;
typedef struct
{
int32_t first_statement;
int32_t parm_start;
int32_t locals;
int32_t profile;
int32_t s_name;
int32_t s_file;
int32_t numparms;
uint8_t parm_size[MAX_PARMS];
} dfunction_t;
typedef struct
{
int32_t version;
int32_t crc;
int32_t ofs_statements;
int32_t numstatements;
int32_t ofs_globaldefs;
int32_t numglobaldefs;
int32_t ofs_fielddefs;
int32_t numfielddefs;
int32_t ofs_functions;
int32_t numfunctions;
int32_t ofs_strings;
int32_t numstrings;
int32_t ofs_globals;
int32_t numglobals;
int32_t entityfields;
} dprograms_t;
typedef struct
{
int pad[28];
int self;
int other;
int world;
float time;
float frametime;
float force_retouch;
string_t mapname;
float deathmatch;
float coop;
float teamplay;
float serverflags;
float total_secrets;
float total_monsters;
float found_secrets;
float killed_monsters;
float parm1;
float parm2;
float parm3;
float parm4;
float parm5;
float parm6;
float parm7;
float parm8;
float parm9;
float parm10;
float parm11;
float parm12;
float parm13;
float parm14;
float parm15;
float parm16;
vec3_t v_forward;
vec3_t v_up;
vec3_t v_right;
float trace_allsolid;
float trace_startsolid;
float trace_fraction;
vec3_t trace_endpos;
vec3_t trace_plane_normal;
float trace_plane_dist;
int trace_ent;
float trace_inopen;
float trace_inwater;
int msg_entity;
func_t main;
func_t StartFrame;
func_t PlayerPreThink;
func_t PlayerPostThink;
func_t ClientKill;
func_t ClientConnect;
func_t PutClientInServer;
func_t ClientDisconnect;
func_t SetNewParms;
func_t SetChangeParms;
} globalvars_t;
typedef struct
{
float modelindex;
vec3_t absmin;
vec3_t absmax;
float ltime;
float movetype;
float solid;
vec3_t origin;
vec3_t oldorigin;
vec3_t velocity;
vec3_t angles;
vec3_t avelocity;
vec3_t punchangle;
string_t classname;
string_t model;
float frame;
float skin;
float effects;
vec3_t mins;
vec3_t maxs;
vec3_t size;
func_t touch;
func_t use;
func_t think;
func_t blocked;
float nextthink;
int groundentity;
float health;
float frags;
float weapon;
string_t weaponmodel;
float weaponframe;
float currentammo;
float ammo_shells;
float ammo_nails;
float ammo_rockets;
float ammo_cells;
float items;
float takedamage;
int chain;
float deadflag;
vec3_t view_ofs;
float button0;
float button1;
float button2;
float impulse;
float fixangle;
vec3_t v_angle;
float idealpitch;
string_t netname;
int enemy;
float flags;
float colormap;
float team;
float max_health;
float teleport_time;
float armortype;
float armorvalue;
float waterlevel;
float watertype;
float ideal_yaw;
float yaw_speed;
int aiment;
int goalentity;
float spawnflags;
string_t target;
string_t targetname;
float dmg_take;
float dmg_save;
int dmg_inflictor;
int owner;
vec3_t movedir;
string_t message;
float sounds;
string_t noise;
string_t noise1;
string_t noise2;
string_t noise3;
} entvars_t;
typedef union eval_s
{
string_t string;
float _float;
float vector[3];
func_t function;
int32_t _int;
int32_t edict;
} eval_t;
typedef struct edict_s
{
bool free;
link_t area;
int32_t num_leafs;
int16_t leafnums[MAX_ENT_LEAFS];
entity_state_t baseline;
float freetime;
entvars_t v;
} edict_t;
typedef void (*builtin_t)(void);
typedef struct
{
int32_t maxclients;
int32_t maxclientslimit;
struct client_s *clients;
int32_t serverflags;
bool changelevel_issued;
} server_static_t;
typedef enum
{
ss_loading,
ss_active
} server_state_t;
typedef struct
{
bool active;
bool paused;
bool loadgame;
double time;
int32_t lastcheck;
double lastchecktime;
char name[64];
char startspot[64];
char modelname[64];
struct model_s *worldmodel;
char *model_precache[MAX_MODELS];
struct model_s *models[MAX_MODELS];
char *sound_precache[MAX_SOUNDS];
char *lightstyles[MAX_LIGHTSTYLES];
int32_t num_edicts;
int32_t max_edicts;
edict_t *edicts;
server_state_t state;
sizebuf_t datagram;
uint8_t datagram_buf[MAX_DATAGRAM];
sizebuf_t reliable_datagram;
uint8_t reliable_datagram_buf[MAX_DATAGRAM];
sizebuf_t signon;
uint8_t signon_buf[8192];
} server_t;
typedef struct client_s
{
bool active;
bool spawned;
bool dropasap;
bool privileged;
bool sendsignon;
double last_message;
struct qsocket_s *netconnection;
usercmd_t cmd;
vec3_t wishdir;
sizebuf_t message;
uint8_t msgbuf[MAX_MSGLEN];
edict_t *edict;
char name[32];
int32_t colors;
float ping_times[NUM_PING_TIMES];
int32_t num_pings;
float spawn_parms[NUM_SPAWN_PARMS];
int32_t old_frags;
} client_t;
typedef enum
{
ST_SYNC = 0,
ST_RAND
} synctype_t;
typedef enum
{
ALIAS_SINGLE = 0,
ALIAS_GROUP
} aliasframetype_t;
typedef enum
{
ALIAS_SKIN_SINGLE = 0,
ALIAS_SKIN_GROUP
} aliasskintype_t;
typedef struct
{
int32_t ident;
int32_t version;
vec3_t scale;
vec3_t scale_origin;
float boundingradius;
vec3_t eyeposition;
int32_t numskins;
int32_t skinwidth;
int32_t skinheight;
int32_t numverts;
int32_t numtris;
int32_t numframes;
synctype_t synctype;
int32_t flags;
float size;
} mdl_t;
typedef struct
{
int32_t onseam;
int32_t s;
int32_t t;
} stvert_t;
typedef struct dtriangle_s
{
int32_t facesfront;
int32_t vertindex[3];
} dtriangle_t;
typedef struct
{
uint8_t v[3];
uint8_t lightnormalindex;
} trivertx_t;
typedef struct
{
trivertx_t bboxmin;
trivertx_t bboxmax;
char name[16];
} daliasframe_t;
typedef struct
{
int32_t numframes;
trivertx_t bboxmin;
trivertx_t bboxmax;
} daliasgroup_t;
typedef struct
{
int32_t numskins;
} daliasskingroup_t;
typedef struct
{
float interval;
} daliasinterval_t;
typedef struct
{
float interval;
} daliasskininterval_t;
typedef struct
{
aliasframetype_t type;
} daliasframetype_t;
typedef struct
{
aliasskintype_t type;
} daliasskintype_t;
typedef struct
{
int32_t ident;
int32_t version;
int32_t type;
float boundingradius;
int32_t width;
int32_t height;
int32_t numframes;
float beamlength;
synctype_t synctype;
} dsprite_t;
typedef struct
{
int32_t origin[2];
int32_t width;
int32_t height;
} dspriteframe_t;
typedef struct
{
int32_t numframes;
} dspritegroup_t;
typedef struct
{
float interval;
} dspriteinterval_t;
typedef enum
{
SPR_SINGLE = 0,
SPR_GROUP
} spriteframetype_t;
typedef struct
{
spriteframetype_t type;
} dspriteframetype_t;
typedef struct
{
vec3_t position;
} mvertex_t;
typedef struct mplane_s
{
vec3_t normal;
float dist;
uint8_t type;
uint8_t signbits;
uint8_t pad[2];
} mplane_t;
typedef struct texture_s
{
char name[16];
uint32_t width;
uint32_t height;
int32_t anim_total;
int32_t anim_min;
int32_t anim_max;
struct texture_s *anim_next;
struct texture_s *alternate_anims;
uint32_t offsets[MIPLEVELS];
} texture_t;
typedef struct
{
uint16_t v[2];
uint32_t cachededgeoffset;
} medge_t;
typedef struct
{
float vecs[2][4];
float mipadjust;
texture_t *texture;
int32_t flags;
} mtexinfo_t;
typedef struct msurface_s
{
int32_t visframe;
int32_t dlightframe;
int32_t dlightbits;
mplane_t *plane;
int32_t flags;
int32_t firstedge;
int32_t numedges;
struct surfcache_s *cachespots[MIPLEVELS];
int16_t texturemins[2];
int16_t extents[2];
mtexinfo_t *texinfo;
uint8_t styles[MAXLIGHTMAPS];
uint8_t *samples;
} msurface_t;
typedef struct mnode_s
{
int32_t contents;
int32_t visframe;
int16_t minmaxs[6];
struct mnode_s *parent;
mplane_t *plane;
struct mnode_s *children[2];
uint16_t firstsurface;
uint16_t numsurfaces;
} mnode_t;
typedef struct mleaf_s
{
int32_t contents;
int32_t visframe;
int16_t minmaxs[6];
struct mnode_s *parent;
uint8_t *compressed_vis;
efrag_t *efrags;
msurface_t **firstmarksurface;
int32_t nummarksurfaces;
int32_t key;
uint8_t ambient_sound_level[NUM_AMBIENTS];
} mleaf_t;
typedef struct
{
dclipnode_t *clipnodes;
mplane_t *planes;
int32_t firstclipnode;
int32_t lastclipnode;
vec3_t clip_mins;
vec3_t clip_maxs;
} hull_t;
typedef struct mspriteframe_s
{
int32_t width;
int32_t height;
void *pcachespot;
float up;
float down;
float left;
float right;
uint8_t pixels[4];
} mspriteframe_t;
typedef struct
{
int32_t numframes;
float *intervals;
mspriteframe_t *frames[1];
} mspritegroup_t;
typedef struct
{
spriteframetype_t type;
mspriteframe_t *frameptr;
} mspriteframedesc_t;
typedef struct
{
int32_t type;
int32_t maxwidth;
int32_t maxheight;
int32_t numframes;
float beamlength;
void *cachespot;
mspriteframedesc_t frames[1];
} msprite_t;
typedef struct
{
aliasframetype_t type;
trivertx_t bboxmin;
trivertx_t bboxmax;
int32_t frame;
char name[16];
} maliasframedesc_t;
typedef struct
{
aliasskintype_t type;
void *pcachespot;
int32_t skin;
} maliasskindesc_t;
typedef struct
{
trivertx_t bboxmin;
trivertx_t bboxmax;
int32_t frame;
} maliasgroupframedesc_t;
typedef struct
{
int32_t numframes;
int32_t intervals;
maliasgroupframedesc_t frames[1];
} maliasgroup_t;
typedef struct
{
int32_t numskins;
int32_t intervals;
maliasskindesc_t skindescs[1];
} maliasskingroup_t;
typedef struct mtriangle_s
{
int32_t facesfront;
int32_t vertindex[3];
} mtriangle_t;
typedef struct
{
int32_t model;
int32_t stverts;
int32_t skindesc;
int32_t triangles;
maliasframedesc_t frames[1];
} aliashdr_t;
typedef enum
{
mod_brush,
mod_sprite,
mod_alias
} modtype_t;
typedef struct model_s
{
char name[MAX_QPATH];
bool needload;
modtype_t type;
int32_t numframes;
synctype_t synctype;
int32_t flags;
vec3_t mins;
vec3_t maxs;
float radius;
int32_t firstmodelsurface;
int32_t nummodelsurfaces;
int32_t numsubmodels;
dmodel_t *submodels;
int32_t numplanes;
mplane_t *planes;
int32_t numleafs;
mleaf_t *leafs;
int32_t numvertexes;
mvertex_t *vertexes;
int32_t numedges;
medge_t *edges;
int32_t numnodes;
mnode_t *nodes;
int32_t numtexinfo;
mtexinfo_t *texinfo;
int32_t numsurfaces;
msurface_t *surfaces;
int32_t numsurfedges;
int32_t *surfedges;
int32_t numclipnodes;
dclipnode_t *clipnodes;
int32_t nummarksurfaces;
msurface_t **marksurfaces;
hull_t hulls[MAX_MAP_HULLS];
int32_t numtextures;
texture_t **textures;
uint8_t *visdata;
uint8_t *lightdata;
char *entities;
cache_user_t cache;
} model_t;
typedef struct
{
float u;
float v;
float s;
float t;
float zi;
} emitpoint_t;
typedef enum
{
pt_static,
pt_grav,
pt_slowgrav,
pt_fire,
pt_explode,
pt_explode2,
pt_blob,
pt_blob2
} ptype_t;
typedef struct particle_s
{
vec3_t org;
float color;
struct particle_s *next;
vec3_t vel;
float ramp;
float die;
ptype_t type;
} particle_t;
typedef struct polyvert_s
{
float u;
float v;
float zi;
float s;
float t;
} polyvert_t;
typedef struct polydesc_s
{
int32_t numverts;
float nearzi;
msurface_t *pcurrentface;
polyvert_t *pverts;
} polydesc_t;
typedef struct finalvert_s
{
int32_t v[6];
int32_t flags;
float reserved;
} finalvert_t;
typedef struct
{
void *pskin;
maliasskindesc_t *pskindesc;
int32_t skinwidth;
int32_t skinheight;
mtriangle_t *ptriangles;
finalvert_t *pfinalverts;
int32_t numtriangles;
int32_t drawtype;
int32_t seamfixupX16;
} affinetridesc_t;
typedef struct
{
float u;
float v;
float zi;
float color;
} screenpart_t;
typedef struct
{
int32_t nump;
emitpoint_t *pverts;
mspriteframe_t *pspriteframe;
vec3_t vup;
vec3_t vright;
vec3_t vpn;
float nearzi;
} spritedesc_t;
typedef struct
{
int32_t u;
int32_t v;
float zi;
int32_t color;
} zpointdesc_t;
typedef struct
{
pixel_t *surfdat;
int32_t rowbytes;
msurface_t *surf;
fixed8_t lightadj[MAXLIGHTMAPS];
texture_t *texture;
int32_t surfmip;
int32_t surfwidth;
int32_t surfheight;
} drawsurf_t;
typedef struct
{
vec3_t normal;
float dist;
} plane_t;
typedef struct
{
bool allsolid;
bool startsolid;
bool inopen;
bool inwater;
float fraction;
vec3_t endpos;
plane_t plane;
edict_t *ent;
} trace_t;
typedef enum
{
key_game,
key_console,
key_message,
key_menu
} keydest_t;
typedef struct
{
int8_t *basedir;
int8_t *cachedir;
int32_t argc;
int8_t **argv;
void *membase;
int32_t memsize;
} quakeparms_t;
typedef struct cmdalias_s
{
struct cmdalias_s *next;
char name[MAX_ALIAS_NAME];
char *value;
} cmdalias_t;
typedef struct cmd_function_s
{
struct cmd_function_s *next;
char *name;
xcommand_t function;
} cmd_function_t;
typedef struct
{
char name[MAX_QPATH];
int32_t filepos;
int32_t filelen;
} packfile_t;
typedef struct pack_s
{
char filename[MAX_OSPATH];
int32_t handle;
int32_t numfiles;
packfile_t *files;
} pack_t;
typedef struct
{
char name[56];
int32_t filepos;
int32_t filelen;
} dpackfile_t;
typedef struct
{
char id[4];
int32_t dirofs;
int32_t dirlen;
} dpackheader_t;
typedef struct searchpath_s
{
char filename[MAX_OSPATH];
pack_t *pack;
struct searchpath_s *next;
} searchpath_t;
typedef struct espan_s
{
int32_t u;
int32_t v;
int32_t count;
struct espan_s *pnext;
} espan_t;
typedef struct surf_s
{
struct surf_s *next;
struct surf_s *prev;
struct espan_s *spans;
int32_t key;
int32_t last_u;
int32_t spanstate;
int32_t flags;
void *data;
entity_t *entity;
float nearzi;
bool insubmodel;
float d_ziorigin;
float d_zistepu;
float d_zistepv;
int32_t pad[2];
} surf_t;
typedef struct edge_s
{
fixed16_t u;
fixed16_t u_step;
struct edge_s *prev;
struct edge_s *next;
uint16_t surfs[2];
struct edge_s *nextremove;
float nearzi;
medge_t *owner;
} edge_t;
typedef struct surfcache_s
{
struct surfcache_s *next;
struct surfcache_s **owner;
int32_t lightadj[MAXLIGHTMAPS];
int32_t dlight;
int32_t size;
uint32_t width;
uint32_t height;
float mipscale;
struct texture_s *texture;
uint8_t data[4];
} surfcache_t;
typedef struct sspan_s
{
int32_t u;
int32_t v;
int32_t count;
} sspan_t;
typedef struct
{
int32_t ambientlight;
int32_t shadelight;
float *plightvec;
} alight_t;
typedef struct bedge_s
{
mvertex_t *v[2];
struct bedge_s *pnext;
} bedge_t;
typedef struct
{
float fv[3];
} auxvert_t;
typedef struct clipplane_s
{
vec3_t normal;
float dist;
struct clipplane_s *next;
uint8_t leftedge;
uint8_t rightedge;
uint8_t reserved[2];
} clipplane_t;
typedef struct btofpoly_s
{
int32_t clipflags;
msurface_t *psurf;
} btofpoly_t;
typedef struct
{
void *pdest;
int16_t *pz;
int32_t count;
uint8_t *ptex;
int32_t sfrac;
int32_t tfrac;
int32_t light;
int32_t zi;
} spanpackage_t;
typedef struct
{
int32_t isflattop;
int32_t numleftedges;
int32_t *pleftedgevert0;
int32_t *pleftedgevert1;
int32_t *pleftedgevert2;
int32_t numrightedges;
int32_t *prightedgevert0;
int32_t *prightedgevert1;
int32_t *prightedgevert2;
} edgetable;
typedef struct
{
int32_t quotient;
int32_t remainder;
} adivtab_t;
typedef struct
{
vrect_t rect;
int32_t width;
int32_t height;
uint8_t *ptexbytes;
int32_t rowbytes;
} rectdesc_t;
typedef struct cachepic_s
{
char name[MAX_QPATH];
cache_user_t cache;
} cachepic_t;
typedef struct
{
char *name;
int32_t keynum;
} keyname_t;
enum
{
m_none,
m_main,
m_singleplayer,
m_load,
m_save,
m_multiplayer,
m_setup,
m_net,
m_options,
m_video,
m_keys,
m_help,
m_quit,
m_serialconfig,
m_modemconfig,
m_lanconfig,
m_gameoptions,
m_search,
m_slist
} m_state;
typedef struct
{
char *name;
char *description;
} level_t;
typedef struct
{
char *description;
int32_t firstLevel;
int32_t levels;
} episode_t;
static struct
{
uint32_t length;
uint32_t sequence;
uint8_t data[MAX_DATAGRAM];
} packetBuffer;
static struct
{
double time;
int32_t op;
int32_t session;
} vcrConnect;
static struct
{
double time;
int32_t op;
int32_t session;
int32_t ret;
int32_t len;
} vcrGetMessage;
static struct
{
double time;
int32_t op;
int32_t session;
int32_t r;
} vcrSendMessage;
static struct
{
double time;
int32_t op;
int32_t session;
} next;
typedef struct
{
ddef_t *pcache;
char field[MAX_FIELD_LEN];
} gefv_cache;
typedef struct
{
int32_t s;
dfunction_t *f;
} prstack_t;
typedef struct
{
int32_t index0;
int32_t index1;
} aedge_t;
typedef enum
{
touchessolid,
drawnode,
nodrawnode
} solidstate_t;
typedef struct
{
float u;
float v;
int32_t ceilv;
} evert_t;
typedef struct
{
char manufacturer;
char version;
char encoding;
char bits_per_pixel;
uint16_t xmin;
uint16_t ymin;
uint16_t xmax;
uint16_t ymax;
uint16_t hres;
uint16_t vres;
unsigned char palette[48];
char reserved;
char color_planes;
uint16_t bytes_per_line;
uint16_t palette_type;
char filler[58];
unsigned char data;
} pcx_t;
typedef struct
{
vec3_t boxmins;
vec3_t boxmaxs;
float *mins;
float *maxs;
vec3_t mins2;
vec3_t maxs2;
float *start;
float *end;
trace_t trace;
int32_t type;
edict_t *passedict;
} moveclip_t;
typedef struct areanode_s
{
int32_t axis;
float dist;
struct areanode_s *children[2];
link_t trigger_edicts;
link_t solid_edicts;
} areanode_t;
typedef struct memblock_s
{
int32_t size;
int32_t tag;
int32_t id;
struct memblock_s *next;
struct memblock_s *prev;
int32_t pad;
} memblock_t;
typedef struct
{
int32_t size;
memblock_t blocklist;
memblock_t *rover;
} memzone_t;
typedef struct
{
int32_t sentinal;
int32_t size;
char name[8];
} hunk_t;
typedef struct cache_system_s
{
int32_t size;
cache_user_t *user;
char name[16];
struct cache_system_s *prev;
struct cache_system_s *next;
struct cache_system_s *lru_prev;
struct cache_system_s *lru_next;
} cache_system_t;
void VID_LockBuffer(void);
void VID_UnlockBuffer(void);
void SZ_Alloc(sizebuf_t *buf, int32_t startsize);
void SZ_Free(sizebuf_t *buf);
void SZ_Clear(sizebuf_t *buf);
void *SZ_GetSpace(sizebuf_t *buf, int32_t length);
void SZ_Write(sizebuf_t *buf, void *data, int32_t length);
void SZ_Print(sizebuf_t *buf, char *data);
void ClearLink(link_t *l);
void RemoveLink(link_t *l);
void InsertLinkBefore(link_t *l, link_t *before);
void InsertLinkAfter(link_t *l, link_t *after);
void MSG_WriteChar(sizebuf_t *sb, int32_t c);
void MSG_WriteByte(sizebuf_t *sb, int32_t c);
void MSG_WriteShort(sizebuf_t *sb, int32_t c);
void MSG_WriteLong(sizebuf_t *sb, int32_t c);
void MSG_WriteFloat(sizebuf_t *sb, float f);
void MSG_WriteString(sizebuf_t *sb, char *s);
void MSG_WriteCoord(sizebuf_t *sb, float f);
void MSG_WriteAngle(sizebuf_t *sb, float f);
void MSG_BeginReading(void);
int32_t MSG_ReadChar(void);
int32_t MSG_ReadByte(void);
int32_t MSG_ReadShort(void);
int32_t MSG_ReadLong(void);
float MSG_ReadFloat(void);
char *MSG_ReadString(void);
float MSG_ReadCoord(void);
float MSG_ReadAngle(void);
char *COM_Parse(char *data);
int32_t COM_CheckParm(char *parm);
void COM_Init(char *path);
void COM_InitArgv(int32_t argc, char **argv);
char *COM_SkipPath(char *pathname);
void COM_StripExtension(char *in, char *out);
void COM_FileBase(char *in, char *out);
void COM_DefaultExtension(char *path, char *extension);
char *va(char *format, ...);
void COM_WriteFile(char *filename, void *data, int32_t len);
int32_t COM_OpenFile(char *filename, int32_t *hndl);
int32_t COM_FOpenFile(char *filename, FILE **file);
void COM_CloseFile(int32_t h);
uint8_t *COM_LoadStackFile(char *path, void *buffer, int32_t bufsize);
uint8_t *COM_LoadTempFile(char *path);
uint8_t *COM_LoadHunkFile(char *path);
void COM_LoadCacheFile(char *path, struct cache_user_s *cu);
void VID_SetPalette(unsigned char *palette);
void VID_ShiftPalette(unsigned char *palette);
void VID_Init(unsigned char *palette);
void VID_Shutdown(void);
void VID_Update(vrect_t *rects);
int32_t VID_SetMode(int32_t modenum, unsigned char *palette);
void VID_HandlePause(bool pause);
int32_t Sys_FileOpenRead(char *path, int32_t *hndl);
int32_t Sys_FileOpenWrite(char *path);
void Sys_FileClose(int32_t handle);
void Sys_FileSeek(int32_t handle, int32_t position);
int32_t Sys_FileRead(int32_t handle, void *dest, int32_t count);
int32_t Sys_FileWrite(int32_t handle, void *data, int32_t count);
int32_t Sys_FileTime(char *path);
void Sys_mkdir(char *path);
void Sys_DebugLog(char *file, char *fmt, ...);
void Sys_Error(char *error, ...);
void Sys_Printf(char *fmt, ...);
void Sys_Quit(void);
double Sys_FloatTime(void);
char *Sys_ConsoleInput(void);
void Sys_SendKeyEvents(void);
void Sys_LowFPPrecision(void);
void Sys_HighFPPrecision(void);
void Sys_SetFPCW(void);
void Memory_Init(void *buf, int32_t size);
void Z_Free(void *ptr);
void *Z_Malloc(int32_t size);
void *Z_TagMalloc(int32_t size, int32_t tag);
void Z_DumpHeap(void);
void Z_CheckHeap(void);
int32_t Z_FreeMemory(void);
void *Hunk_Alloc(int32_t size);
void *Hunk_AllocName(int32_t size, char *name);
void *Hunk_HighAllocName(int32_t size, char *name);
int32_t Hunk_LowMark(void);
void Hunk_FreeToLowMark(int32_t mark);
int32_t Hunk_HighMark(void);
void Hunk_FreeToHighMark(int32_t mark);
void *Hunk_TempAlloc(int32_t size);
void Hunk_Check(void);
void Cache_Flush(void);
void *Cache_Check(cache_user_t *c);
void Cache_Free(cache_user_t *c);
void *Cache_Alloc(cache_user_t *c, int32_t size, char *name);
void Cache_Report(void);
void VectorMA(vec3_t veca, float scale, vec3_t vecb, vec3_t vecc);
vec_t _DotProduct(vec3_t v1, vec3_t v2);
void _VectorSubtract(vec3_t veca, vec3_t vecb, vec3_t out);
void _VectorAdd(vec3_t veca, vec3_t vecb, vec3_t out);
void _VectorCopy(vec3_t in, vec3_t out);
int32_t VectorCompare(vec3_t v1, vec3_t v2);
vec_t Length(vec3_t v);
void CrossProduct(vec3_t v1, vec3_t v2, vec3_t cross);
float VectorNormalize(vec3_t v);
void VectorInverse(vec3_t v);
void VectorScale(vec3_t in, vec_t scale, vec3_t out);
int32_t Q_log2(int32_t val);
void R_ConcatRotations(float in1[3][3], float in2[3][3], float out[3][3]);
void R_ConcatTransforms(float in1[3][4], float in2[3][4], float out[3][4]);
void FloorDivMod(double numer, double denom, int32_t *quotient, int32_t *rem);
fixed16_t Invert24To16(fixed16_t val);
int32_t GreatestCommonDivisor(int32_t i1, int32_t i2);
void AngleVectors(vec3_t angles, vec3_t forward, vec3_t right, vec3_t up);
int32_t BoxOnPlaneSide(vec3_t emins, vec3_t emaxs, struct mplane_s *plane);
float anglemod(float a);
void W_LoadWadFile(char *filename);
void W_CleanupName(char *in, char *out);
lumpinfo_t *W_GetLumpinfo(char *name);
void *W_GetLumpName(char *name);
void *W_GetLumpNum(int32_t num);
void SwapPic(qpic_t *pic);
void Draw_Init(void);
void Draw_Character(int32_t x, int32_t y, int32_t num);
void Draw_DebugChar(char num);
void Draw_Pic(int32_t x, int32_t y, qpic_t *pic);
void Draw_TransPic(int32_t x, int32_t y, qpic_t *pic);
void Draw_TransPicTranslate(int32_t x, int32_t y, qpic_t *pic, uint8_t *translation);
void Draw_ConsoleBackground(int32_t lines);
void Draw_BeginDisc(void);
void Draw_EndDisc(void);
void Draw_TileClear(int32_t x, int32_t y, int32_t w, int32_t h);
void Draw_Fill(int32_t x, int32_t y, int32_t w, int32_t h, int32_t c);
void Draw_FadeScreen(void);
void Draw_String(int32_t x, int32_t y, char *str);
qpic_t *Draw_PicFromWad(char *name);
qpic_t *Draw_CachePic(char *path);
void Cvar_RegisterVariable(cvar_t *variable);
void Cvar_Set(char *var_name, char *value);
void Cvar_SetValue(char *var_name, float value);
float Cvar_VariableValue(char *var_name);
char *Cvar_VariableString(char *var_name);
char *Cvar_CompleteVariable(char *partial);
bool Cvar_Command(void);
void Cvar_WriteVariables(FILE *f);
cvar_t *Cvar_FindVar(char *var_name);
void SCR_Init(void);
void SCR_UpdateScreen(void);
void SCR_SizeUp(void);
void SCR_SizeDown(void);
void SCR_BringDownConsole(void);
void SCR_CenterPrint(char *str);
void SCR_BeginLoadingPlaque(void);
void SCR_EndLoadingPlaque(void);
int32_t SCR_ModalMessage(char *text);
void SCR_UpdateWholeScreen(void);
qsocket_t *NET_NewQSocket(void);
void NET_FreeQSocket(qsocket_t *);
double SetNetTime(void);
void NET_Init(void);
void NET_Shutdown(void);
struct qsocket_s *NET_CheckNewConnections(void);
struct qsocket_s *NET_Connect(char *host);
bool NET_CanSendMessage(qsocket_t *sock);
int32_t NET_GetMessage(struct qsocket_s *sock);
int32_t NET_SendMessage(struct qsocket_s *sock, sizebuf_t *data);
int32_t NET_SendUnreliableMessage(struct qsocket_s *sock, sizebuf_t *data);
int32_t NET_SendToAll(sizebuf_t *data, int32_t blocktime);
void NET_Close(struct qsocket_s *sock);
void NET_Poll(void);
void SchedulePollProcedure(PollProcedure *pp, double timeOffset);
void NET_Slist_f(void);
void Cbuf_Init(void);
void Cbuf_AddText(char *text);
void Cbuf_InsertText(char *text);
void Cbuf_Execute(void);
void Cmd_Init(void);
void Cmd_AddCommand(char *cmd_name, xcommand_t function);
bool Cmd_Exists(char *cmd_name);
char *Cmd_CompleteCommand(char *partial);
int32_t Cmd_Argc(void);
char *Cmd_Argv(int32_t arg);
char *Cmd_Args(void);
int32_t Cmd_CheckParm(char *parm);
void Cmd_TokenizeString(char *text);
void Cmd_ExecuteString(char *text, cmd_source_t src);
void Cmd_ForwardToServer(void);
void Cmd_Print(char *text);
void Sbar_Init(void);
void Sbar_Changed(void);
void Sbar_Draw(void);
void Sbar_IntermissionOverlay(void);
void Sbar_FinaleOverlay(void);
void S_Init(void);
void S_Startup(void);
void S_Shutdown(void);
void S_StartSound(int32_t entnum, int32_t entchannel, sfx_t *sfx, vec3_t origin, float fvol, float attenuation);
void S_StaticSound(sfx_t *sfx, vec3_t origin, float vol, float attenuation);
void S_StopSound(int32_t entnum, int32_t entchannel);
void S_StopAllSounds(bool clear);
void S_ClearBuffer(void);
void S_Update(vec3_t origin, vec3_t v_forward, vec3_t v_right, vec3_t v_up);
void S_ExtraUpdate(void);
sfx_t *S_PrecacheSound(char *sample);
void S_TouchSound(char *sample);
void S_ClearPrecache(void);
void S_BeginPrecaching(void);
void S_EndPrecaching(void);
void S_PaintChannels(int32_t endtime);
void S_InitPaintChannels(void);
channel_t *SND_PickChannel(int32_t entnum, int32_t entchannel);
void SND_Spatialize(channel_t *ch);
bool SNDDMA_Init(void);
int32_t SNDDMA_GetDMAPos(void);
void SNDDMA_Shutdown(void);
void S_LocalSound(char *s);
sfxcache_t *S_LoadSound(sfx_t *s);
wavinfo_t GetWavinfo(char *name, uint8_t *wav, int32_t wavlength);
void SND_InitScaletable(void);
void SNDDMA_Submit(void);
void S_AmbientOff(void);
void S_AmbientOn(void);
void R_Init(void);
void R_InitTextures(void);
void R_InitEfrags(void);
void R_RenderView(void);
void R_ViewChanged(vrect_t *pvrect, int32_t lineadj, float aspect);
void R_InitSky(struct texture_s *mt);
void R_AddEfrags(entity_t *ent);
void R_RemoveEfrags(entity_t *ent);
void R_NewMap(void);
void R_ParseParticleEffect(void);
void R_RunParticleEffect(vec3_t org, vec3_t dir, int32_t color, int32_t count);
void R_RocketTrail(vec3_t start, vec3_t end, int32_t type);
void R_EntityParticles(entity_t *ent);
void R_BlobExplosion(vec3_t org);
void R_ParticleExplosion(vec3_t org);
void R_ParticleExplosion2(vec3_t org, int32_t colorStart, int32_t colorLength);
void R_LavaSplash(vec3_t org);
void R_TeleportSplash(vec3_t org);
void R_PushDlights(void);
int32_t D_SurfaceCacheForRes(int32_t width, int32_t height);
void D_FlushCaches(void);
void D_DeleteSurfaceCache(void);
void D_InitCaches(void *buffer, int32_t size);
void R_SetVrect(vrect_t *pvrect, vrect_t *pvrectin, int32_t lineadj);
dlight_t *CL_AllocDlight(int32_t key);
void CL_DecayLights(void);
void CL_Init(void);
void CL_EstablishConnection(char *host);
void CL_Signon1(void);
void CL_Signon2(void);
void CL_Signon3(void);
void CL_Signon4(void);
void CL_Disconnect(void);
void CL_Disconnect_f(void);
void CL_NextDemo(void);
void CL_InitInput(void);
void CL_SendCmd(void);
void CL_SendMove(usercmd_t *cmd);
void CL_ParseTEnt(void);
void CL_UpdateTEnts(void);
void CL_ClearState(void);
int32_t CL_ReadFromServer(void);
void CL_WriteToServer(usercmd_t *cmd);
void CL_BaseMove(usercmd_t *cmd);
float CL_KeyState(kbutton_t *key);
char *Key_KeynumToString(int32_t keynum);
void CL_StopPlayback(void);
int32_t CL_GetMessage(void);
void CL_Stop_f(void);
void CL_Record_f(void);
void CL_PlayDemo_f(void);
void CL_TimeDemo_f(void);
void CL_ParseServerMessage(void);
void CL_NewTranslation(int32_t slot);
void V_StartPitchDrift(void);
void V_StopPitchDrift(void);
void V_RenderView(void);
void V_UpdatePalette(void);
void V_Register(void);
void V_ParseDamage(void);
void V_SetContentsColor(int32_t contents);
void CL_InitTEnts(void);
void CL_SignonReply(void);
void PR_Init(void);
void PR_ExecuteProgram(func_t fnum);
void PR_LoadProgs(void);
void PR_Profile_f(void);
edict_t *ED_Alloc(void);
void ED_Free(edict_t *ed);
char *ED_NewString(char *string);
void ED_Print(edict_t *ed);
void ED_Write(FILE *f, edict_t *ed);
char *ED_ParseEdict(char *data, edict_t *ent);
void ED_WriteGlobals(FILE *f);
void ED_ParseGlobals(char *data);
void ED_LoadFromFile(char *data);
edict_t *EDICT_NUM(int32_t n);
int32_t NUM_FOR_EDICT(edict_t *e);
void PR_RunError(char *error, ...);
void ED_PrintEdicts(void);
void ED_PrintNum(int32_t ent);
eval_t *GetEdictFieldValue(edict_t *ed, char *field);
void SV_Init(void);
void SV_StartParticle(vec3_t org, vec3_t dir, int32_t color, int32_t count);
void SV_StartSound(edict_t *entity, int32_t channel, char *sample, int32_t volume, float attenuation);
void SV_DropClient(bool crash);
void SV_SendClientMessages(void);
void SV_ClearDatagram(void);
int32_t SV_ModelIndex(char *name);
void SV_SetIdealPitch(void);
void SV_AddUpdates(void);
void SV_ClientThink(void);
void SV_AddClientToServer(struct qsocket_s *ret);
void SV_ClientPrintf(char *fmt, ...);
void SV_BroadcastPrintf(char *fmt, ...);
void SV_Physics(void);
bool SV_CheckBottom(edict_t *ent);
bool SV_movestep(edict_t *ent, vec3_t move, bool relink);
void SV_WriteClientdataToMessage(edict_t *ent, sizebuf_t *msg);
void SV_MoveToGoal(void);
void SV_CheckForNewClients(void);
void SV_RunClients(void);
void SV_SaveSpawnparms();
void SV_SpawnServer(char *server);
void Mod_Init(void);
void Mod_ClearAll(void);
model_t *Mod_ForName(char *name, bool crash);
void *Mod_Extradata(model_t *mod);
void Mod_TouchModel(char *name);
mleaf_t *Mod_PointInLeaf(float *p, model_t *model);
uint8_t *Mod_LeafPVS(mleaf_t *leaf, model_t *model);
void D_Aff8Patch(void *pcolormap);
void D_BeginDirectRect(int32_t x, int32_t y, uint8_t *pbitmap, int32_t width, int32_t height);
void D_DisableBackBufferAccess(void);
void D_EndDirectRect(int32_t x, int32_t y, int32_t width, int32_t height);
void D_PolysetDraw(void);
void D_PolysetDrawFinalVerts(finalvert_t *fv, int32_t numverts);
void D_DrawParticle(particle_t *pparticle);
void D_DrawPoly(void);
void D_DrawSprite(void);
void D_DrawSurfaces(void);
void D_DrawZPoint(void);
void D_EnableBackBufferAccess(void);
void D_EndParticles(void);
void D_Init(void);
void D_ViewChanged(void);
void D_SetupFrame(void);
void D_StartParticles(void);
void D_TurnZOn(void);
void D_WarpScreen(void);
void D_FillRect(const vrect_t *vrect, int32_t color);
void D_DrawRect(void);
void D_UpdateRects(vrect_t *prect);
void D_PolysetUpdateTables(void);
void R_DrawSurface(void);
void R_GenTile(msurface_t *psurf, void *pdest);
void IN_Init(void);
void IN_Shutdown(void);
void IN_Commands(void);
void IN_Move(usercmd_t *cmd);
void IN_ClearStates(void);
void SV_ClearWorld(void);
void SV_UnlinkEdict(edict_t *ent);
void SV_LinkEdict(edict_t *ent, bool touch_triggers);
int32_t SV_PointContents(vec3_t p);
int32_t SV_TruePointContents(vec3_t p);
edict_t *SV_TestEntityPosition(edict_t *ent);
trace_t SV_Move(vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int32_t type, edict_t *passedict);
void Key_Event(int32_t key, bool down);
void Key_Init(void);
void Key_WriteBindings(FILE *f);
void Key_SetBinding(int32_t keynum, char *binding);
void Key_ClearStates(void);
void Con_DrawCharacter(int32_t cx, int32_t line, int32_t num);
void Con_CheckResize(void);
void Con_Init(void);
void Con_DrawConsole(int32_t lines, bool drawinput);
void Con_Print(char *txt);
void Con_Printf(char *fmt, ...);
void Con_DPrintf(char *fmt, ...);
void Con_SafePrintf(char *fmt, ...);
void Con_Clear_f(void);
void Con_DrawNotify(void);
void Con_ClearNotify(void);
void Con_ToggleConsole_f(void);
void Con_NotifyBox(char *text);
void V_Init(void);
float V_CalcRoll(vec3_t angles, vec3_t velocity);
void M_Init(void);
void M_Keydown(int32_t key);
void M_Draw(void);
void M_ToggleMenu_f(void);
void CRC_Init(uint16_t *crcvalue);
void CRC_ProcessByte(uint16_t *crcvalue, uint8_t data);
uint16_t CRC_Value(uint16_t crcvalue);
int32_t CDAudio_Init(void);
void CDAudio_Play(uint8_t track, bool looping);
void CDAudio_Stop(void);
void CDAudio_Pause(void);
void CDAudio_Resume(void);
void CDAudio_Shutdown(void);
void CDAudio_Update(void);
void Host_ClearMemory(void);
void Host_ServerFrame(void);
void Host_InitCommands(void);
void Host_Init(quakeparms_t *parms);
void Host_Shutdown(void);
void Host_Error(char *error, ...);
void Host_EndGame(char *message, ...);
void Host_Frame(float time);
void Host_Quit_f(void);
void Host_ClientCommands(char *fmt, ...);
void Host_ShutdownServer(bool crash);
void Chase_Init(void);
void Chase_Reset(void);
void Chase_Update(void);
static void TraceLine(vec3_t start, vec3_t end, vec3_t impact);
static void CL_FinishTimeDemo(void);
static void CL_WriteDemoMessage(void);
static void KeyDown(kbutton_t *b);
static void KeyUp(kbutton_t *b);
static void IN_KLookDown(void);
static void IN_KLookUp(void);
static void IN_MLookDown(void);
static void IN_MLookUp(void);
static void IN_UpDown(void);
static void IN_UpUp(void);
static void IN_DownDown(void);
static void IN_DownUp(void);
static void IN_LeftDown(void);
static void IN_LeftUp(void);
static void IN_RightDown(void);
static void IN_RightUp(void);
static void IN_ForwardDown(void);
static void IN_ForwardUp(void);
static void IN_BackDown(void);
static void IN_BackUp(void);
static void IN_LookupDown(void);
static void IN_LookupUp(void);
static void IN_LookdownDown(void);
static void IN_LookdownUp(void);
static void IN_MoveleftDown(void);
static void IN_MoveleftUp(void);
static void IN_MoverightDown(void);
static void IN_MoverightUp(void);
static void IN_SpeedDown(void);
static void IN_SpeedUp(void);
static void IN_StrafeDown(void);
static void IN_StrafeUp(void);
static void IN_AttackDown(void);
static void IN_AttackUp(void);
static void IN_UseDown(void);
static void IN_UseUp(void);
static void IN_JumpDown(void);
static void IN_JumpUp(void);
static void IN_Impulse(void);
static void CL_AdjustAngles(void);
static void CL_PrintEntities_f(void);
static void SetPal(int32_t i);
static float CL_LerpPoint(void);
static void CL_RelinkEntities(void);
static entity_t *CL_EntityNum(int32_t num);
static void CL_ParseStartSoundPacket(void);
static void CL_KeepaliveMessage(void);
static void CL_ParseServerInfo(void);
static void CL_ParseUpdate(int32_t bits);
static void CL_ParseBaseline(entity_t *ent);
static void CL_ParseClientdata(int32_t bits);
static void CL_ParseStatic(void);
static void CL_ParseStaticSound(void);
static void CL_ParseBeam(model_t *m);
static entity_t *CL_NewTempEntity(void);
static void Cmd_Wait_f(void);
static void Cmd_StuffCmds_f(void);
static void Cmd_Exec_f(void);
static void Cmd_Echo_f(void);
static char *CopyString(char *in);
static void Cmd_Alias_f(void);
static void COM_InitFilesystem(void);
static char *COM_FileExtension(char *in);
static void COM_Path_f(void);
void COM_Init(char *basedir);
static void COM_CreatePath(char *path);
static void COM_CopyFile(char *netpath, char *cachepath);
static int32_t COM_FindFile(char *filename, int32_t *handle, FILE **file);
int32_t COM_OpenFile(char *filename, int32_t *handle);
static uint8_t *COM_LoadFile(char *path, int32_t usehunk);
static pack_t *COM_LoadPackFile(char *packfile);
static void COM_AddGameDirectory(char *dir);
extern void M_Menu_Main_f(void);
static void Con_MessageMode_f(void);
static void Con_MessageMode2_f(void);
static void Con_Linefeed(void);
static void Con_DebugLog(char *file, char *fmt, ...);
static void Con_DrawInput(void);
extern void R_DrawLine(polyvert_t *polyvert0, polyvert_t *polyvert1);
extern void TransformVector(vec3_t in, vec3_t out);
extern void SetUpForLineScan(fixed8_t startvertu, fixed8_t startvertv, fixed8_t endvertu, fixed8_t endvertv);
extern void R_MakeSky(void);
void D_DrawSpans8(espan_t *pspans);
void D_DrawSpans16(espan_t *pspans);
void D_DrawZSpans(espan_t *pspans);
void Turbulent8(espan_t *pspan);
void D_SpriteDrawSpans(sspan_t *pspan);
void D_DrawSkyScans8(espan_t *pspan);
void D_DrawSkyScans16(espan_t *pspan);
void R_ShowSubDiv(void);
surfcache_t *D_CacheSurface(msurface_t *surface, int32_t miplevel);
extern int32_t D_MipLevelForScale(float scale);
extern void R_RotateBmodel(void);
extern void R_TransformFrustum(void);
static void D_DrawSolidSurface(surf_t *surf, int32_t color);
static void D_CalcGradients(msurface_t *pface);
void D_FillRect(const vrect_t *vrect, const int32_t color);
void R_RenderWorld(void);
void R_ClearPolyList(void);
void R_DrawPolyList(void);
void R_DrawSprite(void);
void R_RenderFace(msurface_t *fa, int32_t clipflags);
void R_RenderPoly(msurface_t *fa, int32_t clipflags);
void R_RenderBmodelFace(bedge_t *pedges, msurface_t *psurf);
void R_TransformPlane(mplane_t *p, float *normal, float *dist);
void R_SetSkyFrame(void);
void R_DrawSurfaceBlock16(void);
void R_DrawSurfaceBlock8(void);
texture_t *R_TextureAnimation(texture_t *base);
void R_GenSkyTile(void *pdest);
void R_GenSkyTile16(void *pdest);
void R_Surf8Patch(void);
void R_Surf16Patch(void);
void R_DrawSubmodelPolygons(model_t *pmodel, int32_t clipflags);
void R_DrawSolidClippedSubmodelPolygons(model_t *pmodel);
void R_AddPolygonEdges(emitpoint_t *pverts, int32_t numverts, int32_t miplevel);
surf_t *R_GetSurf(void);
void R_AliasDrawModel(alight_t *plighting);
void R_BeginEdgeFrame(void);
void R_ScanEdges(void);
void R_InsertNewEdges(edge_t *edgestoadd, edge_t *edgelist);
void R_StepActiveU(edge_t *pedge);
void R_RemoveEdges(edge_t *pedge);
extern void R_Surf8Start(void);
extern void R_Surf8End(void);
extern void R_Surf16Start(void);
extern void R_Surf16End(void);
extern void R_EdgeCodeStart(void);
extern void R_EdgeCodeEnd(void);
void R_InitTurb(void);
void R_ZDrawSubmodelPolys(model_t *clmodel);
bool R_AliasCheckBBox(void);
void R_DrawParticles(void);
void R_InitParticles(void);
void R_ClearParticles(void);
void R_ReadPointFile_f(void);
void R_SurfacePatch(void);
void R_AliasClipTriangle(mtriangle_t *ptri);
void R_StoreEfrags(efrag_t **ppefrag);
void R_TimeRefresh_f(void);
void R_TimeGraph(void);
void R_PrintAliasStats(void);
void R_PrintTimes(void);
void R_PrintDSpeeds(void);
void R_AnimateLight(void);
int32_t R_LightPoint(vec3_t p);
void R_SetupFrame(void);
void R_cshift_f(void);
void R_EmitEdge(mvertex_t *pv0, mvertex_t *pv1);
void R_ClipEdge(mvertex_t *pv0, mvertex_t *pv1, clipplane_t *clip);
void R_SplitEntityOnNode2(mnode_t *node);
void R_MarkLights(dlight_t *light, int32_t bit, mnode_t *node);
static void D_PolysetDrawSpans8(spanpackage_t *pspanpackage);
static void D_PolysetCalcGradients(int32_t skinwidth);
static void D_DrawSubdiv(void);
static void D_DrawNonSubdiv(void);
static void D_PolysetRecursiveTriangle(int32_t *p1, int32_t *p2, int32_t *p3);
static void D_PolysetSetEdgeTable(void);
static void D_RasterizeAliasPolySmooth(void);
static void D_PolysetScanLeftEdge(int32_t height);
static void D_PolysetRecursiveTriangle(int32_t *lp1, int32_t *lp2, int32_t *lp3);
static void D_PolysetSetUpForLineScan(fixed8_t startvertu, fixed8_t startvertv, fixed8_t endvertu, fixed8_t endvertv);
static void D_PolysetFillSpans8(spanpackage_t *pspanpackage);
static void D_DrawTurbulent8Span(void);
void D_DrawSpans8(espan_t *pspan);
void D_DrawZSpans(espan_t *pspan);
static void D_Sky_uv_To_st(int32_t u, int32_t v, fixed16_t *s, fixed16_t *t);
static void D_SpriteScanLeftEdge(void);
static void D_SpriteScanRightEdge(void);
static void D_SpriteCalculateGradients(void);
void D_CheckCacheGuard(void);
void D_ClearCacheGuard(void);
surfcache_t *D_SCAlloc(int32_t width, int32_t size);
void D_SCDump(void);
int32_t MaskForNum(int32_t num);
int32_t D_log2(int32_t num);
void Draw_CharToConback(int32_t num, uint8_t *dest);
void R_DrawRect8(vrect_t *prect, int32_t rowbytes, uint8_t *psrc, int32_t transparent);
void R_DrawRect16(vrect_t *prect, int32_t rowbytes, uint8_t *psrc, int32_t transparent);
void Host_FindMaxClients(void);
void Host_InitLocal(void);
void Host_WriteConfiguration(void);
bool Host_FilterTime(float time);
void Host_GetConsoleCommands(void);
void _Host_Frame(float time);
void Host_InitVCR(quakeparms_t *parms);
void Mod_Print(void);
extern void M_Menu_Quit_f(void);
void Host_Status_f(void);
void Host_God_f(void);
void Host_Notarget_f(void);
void Host_Noclip_f(void);
void Host_Fly_f(void);
void Host_Ping_f(void);
void Host_Map_f(void);
void Host_Changelevel_f(void);
void Host_Restart_f(void);
void Host_Reconnect_f(void);
void Host_Connect_f(void);
void Host_SavegameComment(char *text);
void Host_Savegame_f(void);
void Host_Loadgame_f(void);
void Host_Name_f(void);
void Host_Version_f(void);
void Host_Say(bool teamonly);
void Host_Say_f(void);
void Host_Say_Team_f(void);
void Host_Tell_f(void);
void Host_Color_f(void);
void Host_Kill_f(void);
void Host_Pause_f(void);
void Host_PreSpawn_f(void);
void Host_Spawn_f(void);
void Host_Begin_f(void);
void Host_Kick_f(void);
void Host_Give_f(void);
edict_t *FindViewthing(void);
void Host_Viewmodel_f(void);
void Host_Viewframe_f(void);
void PrintFrameName(model_t *m, int32_t frame);
void Host_Viewnext_f(void);
void Host_Viewprev_f(void);
void Host_Startdemos_f(void);
void Host_Demos_f(void);
void Host_Stopdemo_f(void);
void Key_Console(int32_t key);
void Key_Message(int32_t key);
int32_t Key_StringToKeynum(char *str);
void Key_Unbind_f(void);
void Key_Unbindall_f(void);
void Key_Bind_f(void);
void ProjectPointOnPlane(vec3_t dst, const vec3_t p, const vec3_t normal);
void PerpendicularVector(vec3_t dst, const vec3_t src);
void RotatePointAroundVector(vec3_t dst, const vec3_t dir, const vec3_t point, float degrees);
void BOPS_Error(void);
int32_t BoxOnPlaneSide(vec3_t emins, vec3_t emaxs, mplane_t *p);
double sqrt(double x);
void M_Menu_SinglePlayer_f(void);
void M_Menu_Load_f(void);
void M_Menu_Save_f(void);
void M_Menu_MultiPlayer_f(void);
void M_Menu_Setup_f(void);
void M_Menu_Net_f(void);
void M_Menu_Options_f(void);
void M_Menu_Keys_f(void);
void M_Menu_Video_f(void);
void M_Menu_Help_f(void);
void M_Menu_SerialConfig_f(void);
void M_Menu_ModemConfig_f(void);
void M_Menu_LanConfig_f(void);
void M_Menu_GameOptions_f(void);
void M_Menu_Search_f(void);
void M_Menu_ServerList_f(void);
void M_Main_Draw(void);
void M_SinglePlayer_Draw(void);
void M_Load_Draw(void);
void M_Save_Draw(void);
void M_MultiPlayer_Draw(void);
void M_Setup_Draw(void);
void M_Net_Draw(void);
void M_Options_Draw(void);
void M_Keys_Draw(void);
void M_Video_Draw(void);
void M_Help_Draw(void);
void M_Quit_Draw(void);
void M_SerialConfig_Draw(void);
void M_ModemConfig_Draw(void);
void M_LanConfig_Draw(void);
void M_GameOptions_Draw(void);
void M_Search_Draw(void);
void M_ServerList_Draw(void);
void M_Main_Key(int32_t key);
void M_SinglePlayer_Key(int32_t key);
void M_Load_Key(int32_t key);
void M_Save_Key(int32_t key);
void M_MultiPlayer_Key(int32_t key);
void M_Setup_Key(int32_t key);
void M_Net_Key(int32_t key);
void M_Options_Key(int32_t key);
void M_Keys_Key(int32_t key);
void M_Video_Key(int32_t key);
void M_Help_Key(int32_t key);
void M_Quit_Key(int32_t key);
void M_SerialConfig_Key(int32_t key);
void M_ModemConfig_Key(int32_t key);
void M_LanConfig_Key(int32_t key);
void M_GameOptions_Key(int32_t key);
void M_Search_Key(int32_t key);
void M_ServerList_Key(int32_t key);
void M_ConfigureNetSubsystem(void);
void M_DrawCharacter(int32_t cx, int32_t line, int32_t num);
void M_Print(int32_t cx, int32_t cy, char *str);
void M_PrintWhite(int32_t cx, int32_t cy, char *str);
void M_DrawTransPic(int32_t x, int32_t y, qpic_t *pic);
void M_DrawPic(int32_t x, int32_t y, qpic_t *pic);
void M_BuildTranslationTable(int32_t top, int32_t bottom);
void M_DrawTransPicTranslate(int32_t x, int32_t y, qpic_t *pic);
void M_DrawTextBox(int32_t x, int32_t y, int32_t width, int32_t lines);
void M_ScanSaves(void);
void M_Load_Key(int32_t k);
void M_Save_Key(int32_t k);
void M_Setup_Key(int32_t k);
void M_Net_Key(int32_t k);
void M_AdjustSliders(int32_t dir);
void M_DrawSlider(int32_t x, int32_t y, float range);
void M_DrawCheckbox(int32_t x, int32_t y, int32_t on);
void M_Options_Key(int32_t k);
void M_FindKeysForCommand(char *command, int32_t *twokeys);
void M_UnbindCommand(char *command);
void M_Keys_Key(int32_t k);
void M_NetStart_Change(int32_t dir);
void M_ServerList_Key(int32_t k);
void Mod_LoadSpriteModel(model_t *mod, void *buffer);
void Mod_LoadBrushModel(model_t *mod, void *buffer);
void Mod_LoadAliasModel(model_t *mod, void *buffer);
model_t *Mod_LoadModel(model_t *mod, bool crash);
mleaf_t *Mod_PointInLeaf(vec3_t p, model_t *model);
uint8_t *Mod_DecompressVis(uint8_t *in, model_t *model);
model_t *Mod_FindName(char *name);
void Mod_LoadTextures(lump_t *l);
void Mod_LoadLighting(lump_t *l);
void Mod_LoadVisibility(lump_t *l);
void Mod_LoadEntities(lump_t *l);
void Mod_LoadVertexes(lump_t *l);
void Mod_LoadSubmodels(lump_t *l);
void Mod_LoadEdges(lump_t *l);
void Mod_LoadTexinfo(lump_t *l);
void CalcSurfaceExtents(msurface_t *s);
void Mod_LoadFaces(lump_t *l);
void Mod_SetParent(mnode_t *node, mnode_t *parent);
void Mod_LoadNodes(lump_t *l);
void Mod_LoadLeafs(lump_t *l);
void Mod_LoadClipnodes(lump_t *l);
void Mod_MakeHull0(void);
void Mod_LoadMarksurfaces(lump_t *l);
void Mod_LoadSurfedges(lump_t *l);
void Mod_LoadPlanes(lump_t *l);
float RadiusFromBounds(vec3_t mins, vec3_t maxs);
void *Mod_LoadAliasFrame(void *pin, int32_t *pframeindex, int32_t numv, trivertx_t *pbboxmin, trivertx_t *pbboxmax, aliashdr_t *pheader, char *name);
void *Mod_LoadAliasGroup(void *pin, int32_t *pframeindex, int32_t numv, trivertx_t *pbboxmin, trivertx_t *pbboxmax, aliashdr_t *pheader, char *name);
void *Mod_LoadAliasSkin(void *pin, int32_t *pskinindex, int32_t skinsize, aliashdr_t *pheader);
void *Mod_LoadAliasSkinGroup(void *pin, int32_t *pskinindex, int32_t skinsize, aliashdr_t *pheader);
void *Mod_LoadSpriteFrame(void *pin, mspriteframe_t **ppframe);
void *Mod_LoadSpriteGroup(void *pin, mspriteframe_t **ppframe);
int32_t Datagram_Init(void);
void Datagram_Listen(bool state);
void Datagram_SearchForHosts(bool xmit);
qsocket_t *Datagram_Connect(char *host);
qsocket_t *Datagram_CheckNewConnections(void);
int32_t Datagram_GetMessage(qsocket_t *sock);
int32_t Datagram_SendMessage(qsocket_t *sock, sizebuf_t *data);
int32_t Datagram_SendUnreliableMessage(qsocket_t *sock, sizebuf_t *data);
bool Datagram_CanSendMessage(qsocket_t *sock);
bool Datagram_CanSendUnreliableMessage(qsocket_t *sock);
void Datagram_Close(qsocket_t *sock);
void Datagram_Shutdown(void);
int32_t SendMessageNext(qsocket_t *sock);
int32_t ReSendMessage(qsocket_t *sock);
void PrintStats(qsocket_t *s);
void NET_Stats_f(void);
static void Test_Poll(void);
static void Test_f(void);
static void Test2_Poll(void);
static void Test2_f(void);
static qsocket_t *_Datagram_CheckNewConnections(void);
static void _Datagram_SearchForHosts(bool xmit);
static qsocket_t *_Datagram_Connect(char *host);
int32_t Loop_Init(void);
void Loop_Listen(bool state);
void Loop_SearchForHosts(bool xmit);
qsocket_t *Loop_Connect(char *host);
qsocket_t *Loop_CheckNewConnections(void);
int32_t Loop_GetMessage(qsocket_t *sock);
int32_t Loop_SendMessage(qsocket_t *sock, sizebuf_t *data);
int32_t Loop_SendUnreliableMessage(qsocket_t *sock, sizebuf_t *data);
bool Loop_CanSendMessage(qsocket_t *sock);
bool Loop_CanSendUnreliableMessage(qsocket_t *sock);
void Loop_Close(qsocket_t *sock);
void Loop_Shutdown(void);
static int32_t IntAlign(int32_t value);
int32_t VCR_Init(void);
void VCR_Listen(bool state);
void VCR_SearchForHosts(bool xmit);
qsocket_t *VCR_Connect(char *host);
qsocket_t *VCR_CheckNewConnections(void);
int32_t VCR_GetMessage(qsocket_t *sock);
int32_t VCR_SendMessage(qsocket_t *sock, sizebuf_t *data);
bool VCR_CanSendMessage(qsocket_t *sock);
void VCR_Close(qsocket_t *sock);
void VCR_Shutdown(void);
static void Slist_Send(void);
static void Slist_Poll(void);
void NET_FreeQSocket(qsocket_t *sock);
static void NET_Listen_f(void);
static void MaxPlayers_f(void);
static void NET_Port_f(void);
static void PrintSlistHeader(void);
static void PrintSlist(void);
static void PrintSlistTrailer(void);
qsocket_t *NET_Connect(char *host);
qsocket_t *NET_CheckNewConnections(void);
void NET_Close(qsocket_t *sock);
int32_t NET_GetMessage(qsocket_t *sock);
int32_t NET_SendMessage(qsocket_t *sock, sizebuf_t *data);
int32_t NET_SendUnreliableMessage(qsocket_t *sock, sizebuf_t *data);
void SchedulePollProcedure(PollProcedure *proc, double timeOffset);
void VCR_ReadNext(void);
void R_Surf8Patch();
void R_Surf16Patch();
char *PF_VarString(int32_t first);
void PF_error(void);
void PF_objerror(void);
void PF_makevectors(void);
void PF_setorigin(void);
void SetMinMaxSize(edict_t *e, float *min, float *max, bool rotate);
void PF_setsize(void);
void PF_setmodel(void);
void PF_bprint(void);
void PF_sprint(void);
void PF_centerprint(void);
void PF_normalize(void);
void PF_vlen(void);
void PF_vectoyaw(void);
void PF_vectoangles(void);
void PF_random(void);
void PF_particle(void);
void PF_ambientsound(void);
void PF_sound(void);
void PF_break(void);
void PF_traceline(void);
void PF_checkpos(void);
int32_t PF_newcheckclient(int32_t check);
void PF_checkclient(void);
void PF_stuffcmd(void);
void PF_localcmd(void);
void PF_cvar(void);
void PF_cvar_set(void);
void PF_findradius(void);
void PF_dprint(void);
void PF_ftos(void);
void PF_fabs(void);
void PF_vtos(void);
void PF_Spawn(void);
void PF_Remove(void);
void PF_Find(void);
void PR_CheckEmptyString(char *s);
void PF_precache_file(void);
void PF_precache_sound(void);
void PF_precache_model(void);
void PF_coredump(void);
void PF_traceon(void);
void PF_traceoff(void);
void PF_eprint(void);
void PF_walkmove(void);
void PF_droptofloor(void);
void PF_lightstyle(void);
void PF_rint(void);
void PF_floor(void);
void PF_ceil(void);
void PF_checkbottom(void);
void PF_pointcontents(void);
void PF_nextent(void);
void PF_aim(void);
void PF_changeyaw(void);
sizebuf_t *WriteDest(void);
void PF_WriteByte(void);
void PF_WriteChar(void);
void PF_WriteShort(void);
void PF_WriteLong(void);
void PF_WriteAngle(void);
void PF_WriteCoord(void);
void PF_WriteString(void);
void PF_WriteEntity(void);
void PF_makestatic(void);
void PF_setspawnparms(void);
void PF_changelevel(void);
void PF_Fixme(void);
ddef_t *ED_FieldAtOfs(int32_t ofs);
bool ED_ParseEpair(void *base, ddef_t *key, char *s);
void ED_ClearEdict(edict_t *e);
ddef_t *ED_GlobalAtOfs(int32_t ofs);
ddef_t *ED_FindField(char *name);
ddef_t *ED_FindGlobal(char *name);
dfunction_t *ED_FindFunction(char *name);
char *PR_ValueString(etype_t type, eval_t *val);
char *PR_UglyValueString(etype_t type, eval_t *val);
char *PR_GlobalString(int32_t ofs);
char *PR_GlobalStringNoContents(int32_t ofs);
void ED_PrintEdict_f(void);
void ED_Count(void);
void PR_PrintStatement(dstatement_t *s);
void PR_StackTrace(void);
int32_t PR_EnterFunction(dfunction_t *f);
int32_t PR_LeaveFunction(void);
void R_AliasProjectFinalVert(finalvert_t *fv, auxvert_t *av);
void R_Alias_clip_top(finalvert_t *pfv0, finalvert_t *pfv1, finalvert_t *out);
void R_Alias_clip_bottom(finalvert_t *pfv0, finalvert_t *pfv1, finalvert_t *out);
void R_Alias_clip_left(finalvert_t *pfv0, finalvert_t *pfv1, finalvert_t *out);
void R_Alias_clip_right(finalvert_t *pfv0, finalvert_t *pfv1, finalvert_t *out);
void R_Alias_clip_z(finalvert_t *pfv0, finalvert_t *pfv1, finalvert_t *out);
int32_t R_AliasClip(finalvert_t *in, finalvert_t *out, int32_t flag, int32_t count, void (*clip)(finalvert_t *pfv0, finalvert_t *pfv1, finalvert_t *out));
void R_AliasTransformAndProjectFinalVerts(finalvert_t *fv, stvert_t *pstverts);
void R_AliasSetUpTransform(int32_t trivial_accept);
void R_AliasTransformVector(vec3_t in, vec3_t out);
void R_AliasTransformFinalVert(finalvert_t *fv, auxvert_t *av, trivertx_t *pverts, stvert_t *pstverts);
void R_AliasPreparePoints(void);
void R_AliasPrepareUnclippedPoints(void);
void R_AliasSetupSkin(void);
void R_AliasSetupLighting(alight_t *plighting);
void R_AliasSetupFrame(void);
void R_EntityRotate(vec3_t vec);
void R_RecursiveClipBPoly(bedge_t *pedges, mnode_t *pnode, msurface_t *psurf);
void R_RecursiveWorldNode(mnode_t *node, int32_t clipflags);
void R_EmitCachedEdge(void);
void R_ZDrawSubmodelPolys(model_t *pmodel);
static void R_GenerateSpans(void);
static void R_GenerateSpansBackward(void);
static void R_LeadingEdge(edge_t *edge);
static void R_LeadingEdgeBackwards(edge_t *edge);
static void R_TrailingEdge(surf_t *surf, edge_t *edge);
void R_DrawCulledPolys(void);
static void R_CleanupSpan();
static void R_SplitEntityOnNode(mnode_t *node);
static int32_t RecursiveLightPoint(mnode_t *node, vec3_t start, vec3_t end);
void R_MarkLeaves(void);
void CreatePassages(void);
void SetVisibilityByPassages(void);
void R_SetVrect(vrect_t *pvrectin, vrect_t *pvrect, int32_t lineadj);
void R_DrawEntitiesOnList(void);
void R_DrawViewModel(void);
int32_t R_BmodelCheckBBox(model_t *clmodel, float *minmaxs);
void R_DrawBEntitiesOnList(void);
void R_EdgeDrawing(void);
void R_RenderView_(void);
void R_CheckVariables(void);
void Show(void);
void R_LineGraph(int32_t x, int32_t y, int32_t h);
void WarpPalette(void);
void R_SetUpFrustumIndexes(void);
void R_InitSky(texture_t *mt);
void R_RotateSprite(float beamlength);
int32_t R_ClipSpriteFace(int32_t nump, clipplane_t *pclipplane);
void R_SetupAndDrawSprite();
mspriteframe_t *R_GetSpriteframe(msprite_t *psprite);
void R_DrawSurfaceBlock8_mip0(void);
void R_DrawSurfaceBlock8_mip1(void);
void R_DrawSurfaceBlock8_mip2(void);
void R_DrawSurfaceBlock8_mip3(void);
void R_AddDynamicLights(void);
void R_BuildLightMap(void);
void R_GenTurbTile(pixel_t *pbasetex, void *pdest);
void R_GenTurbTile16(pixel_t *pbasetex, void *pdest);
void Sbar_MiniDeathmatchOverlay(void);
void Sbar_DeathmatchOverlay(void);
void Sbar_ShowScores(void);
void Sbar_DontShowScores(void);
void Sbar_DrawPic(int32_t x, int32_t y, qpic_t *pic);
void Sbar_DrawTransPic(int32_t x, int32_t y, qpic_t *pic);
void Sbar_DrawCharacter(int32_t x, int32_t y, int32_t num);
void Sbar_DrawString(int32_t x, int32_t y, char *str);
int32_t Sbar_itoa(int32_t num, char *buf);
void Sbar_DrawNum(int32_t x, int32_t y, int32_t num, int32_t digits, int32_t color);
void Sbar_SortFrags(void);
int32_t Sbar_ColorForMap(int32_t m);
void Sbar_UpdateScoreboard(void);
void Sbar_SoloScoreboard(void);
void Sbar_DrawScoreboard(void);
void Sbar_DrawInventory(void);
void Sbar_DrawFrags(void);
void Sbar_DrawFace(void);
void Sbar_IntermissionNumber(int32_t x, int32_t y, int32_t num, int32_t digits, int32_t color);
void SCR_ScreenShot_f(void);
void SCR_EraseCenterString(void);
void SCR_DrawCenterString(void);
void SCR_CheckDrawCenterString(void);
float CalcFov(float fov_x, float width, float height);
static void SCR_CalcRefdef(void);
void SCR_SizeUp_f(void);
void SCR_SizeDown_f(void);
void SCR_DrawRam(void);
void SCR_DrawTurtle(void);
void SCR_DrawNet(void);
void SCR_DrawPause(void);
void SCR_DrawLoading(void);
void SCR_SetUpToDrawConsole(void);
void SCR_DrawConsole(void);
void WritePCXfile(char *filename, uint8_t *data, int32_t width, int32_t height, int32_t rowbytes, uint8_t *palette);
void SCR_DrawNotifyString(void);
void S_Play(void);
void S_PlayVol(void);
void S_SoundList(void);
void S_Update_();
void S_StopAllSoundsC(void);
void S_SoundInfo_f(void);
sfx_t *S_FindName(char *name);
void S_TouchSound(char *name);
sfx_t *S_PrecacheSound(char *name);
void S_UpdateAmbientSounds(void);
void S_Update(vec3_t origin, vec3_t forward, vec3_t right, vec3_t up);
void GetSoundtime(void);
void S_Update_(void);
void S_LocalSound(char *sound);
uint8_t *S_Alloc(int32_t size);
void ResampleSfx(sfx_t *sfx, int32_t inrate, int32_t inwidth, uint8_t *data);
int16_t GetLittleShort(void);
int32_t GetLittleLong(void);
void FindNextChunk(char *name);
void FindChunk(char *name);
void DumpChunks(void);
void Snd_WriteLinearBlastStereo16(void);
void S_TransferStereo16(int32_t endtime);
void S_TransferPaintBuffer(int32_t endtime);
void SND_PaintChannelFrom8(channel_t *ch, sfxcache_t *sc, int32_t endtime);
void SND_PaintChannelFrom16(channel_t *ch, sfxcache_t *sc, int32_t endtime);
void SND_PaintChannelFrom8(channel_t *ch, sfxcache_t *sc, int32_t count);
void SND_PaintChannelFrom16(channel_t *ch, sfxcache_t *sc, int32_t count);
void SV_SendServerinfo(client_t *client);
void SV_ConnectClient(int32_t clientnum);
void SV_AddToFatPVS(vec3_t org, mnode_t *node);
uint8_t *SV_FatPVS(vec3_t org);
void SV_WriteEntitiesToClient(edict_t *clent, sizebuf_t *msg);
void SV_CleanupEnts(void);
bool SV_SendClientDatagram(client_t *client);
void SV_UpdateToReliableMessages(void);
void SV_SendNop(client_t *client);
void SV_CreateBaseline(void);
void SV_SendReconnect(void);
void SV_SaveSpawnparms(void);
bool SV_StepDirection(edict_t *ent, float yaw, float dist);
void SV_FixCheckBottom(edict_t *ent);
void SV_NewChaseDir(edict_t *actor, edict_t *enemy, float dist);
bool SV_CloseEnough(edict_t *ent, edict_t *goal, float dist);
void SV_Physics_Toss(edict_t *ent);
void SV_CheckAllEnts(void);
void SV_CheckVelocity(edict_t *ent);
bool SV_RunThink(edict_t *ent);
void SV_Impact(edict_t *e1, edict_t *e2);
int32_t ClipVelocity(vec3_t in, vec3_t normal, vec3_t out, float overbounce);
int32_t SV_FlyMove(edict_t *ent, float time, trace_t *steptrace);
void SV_AddGravity(edict_t *ent);
trace_t SV_PushEntity(edict_t *ent, vec3_t push);
void SV_PushMove(edict_t *pusher, float movetime);
void SV_Physics_Pusher(edict_t *ent);
void SV_CheckStuck(edict_t *ent);
bool SV_CheckWater(edict_t *ent);
void SV_WallFriction(edict_t *ent, trace_t *trace);
int32_t SV_TryUnstick(edict_t *ent, vec3_t oldvel);
void SV_WalkMove(edict_t *ent);
void SV_Physics_Client(edict_t *ent, int32_t num);
void SV_Physics_None(edict_t *ent);
void SV_Physics_Noclip(edict_t *ent);
void SV_CheckWaterTransition(edict_t *ent);
void SV_Physics_Step(edict_t *ent);
void SV_UserFriction(void);
void SV_Accelerate(void);
void SV_AirAccelerate(vec3_t wishveloc);
void DropPunchAngle(void);
void SV_WaterMove(void);
void SV_WaterJump(void);
void SV_AirMove(void);
void SV_ReadClientMove(usercmd_t *move);
bool SV_ReadClientMessage(void);
void Sys_DebugNumber(int32_t y, int32_t val);
void Sys_Init(void);
void Sys_Warn(char *warning, ...);
int32_t findhandle(void);
static int32_t Qfilelength(FILE *f);
int32_t Sys_FileRead(int32_t handle, void *dst, int32_t count);
int32_t Sys_FileWrite(int32_t handle, void *src, int32_t count);
int main(int argc, char *argv[]);
void VID_SetPalette(unsigned char *palette_data);
static int TranslateKey(int key);
void VID_LockBuffer();
void VID_UnlockBuffer();
float V_CalcBob(void);
void V_DriftPitch(void);
void BuildGammaTable(float g);
bool V_CheckGamma(void);
void V_cshift_f(void);
void V_BonusFlash_f(void);
void V_CalcPowerupCshift(void);
float angledelta(float a);
void CalcGunAngle(void);
void V_BoundOffsets(void);
void V_AddIdle(void);
void V_CalcViewRoll(void);
void V_CalcIntermissionRefdef(void);
void V_CalcRefdef(void);
int32_t SV_HullPointContents(hull_t *hull, int32_t num, vec3_t p);
void SV_InitBoxHull(void);
hull_t *SV_HullForBox(vec3_t mins, vec3_t maxs);
hull_t *SV_HullForEntity(edict_t *ent, vec3_t mins, vec3_t maxs, vec3_t offset);
areanode_t *SV_CreateAreaNode(int32_t depth, vec3_t mins, vec3_t maxs);
void SV_TouchLinks(edict_t *ent, areanode_t *node);
void SV_FindTouchedLeafs(edict_t *ent, mnode_t *node);
bool SV_RecursiveHullCheck(hull_t *hull, int32_t num, float p1f, float p2f, vec3_t p1, vec3_t p2, trace_t *trace);
trace_t SV_ClipMoveToEntity(edict_t *ent, vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end);
void SV_ClipToLinks(areanode_t *node, moveclip_t *clip);
void SV_MoveBounds(vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, vec3_t boxmins, vec3_t boxmaxs);
void Cache_FreeLow(int32_t new_low_hunk);
void Cache_FreeHigh(int32_t new_high_hunk);
void Z_ClearZone(memzone_t *zone, int32_t size);
void Z_Print(memzone_t *zone);
void R_FreeTextures(void);
void Hunk_Print(bool all);
cache_system_t *Cache_TryAlloc(int32_t size, bool nobottom);
void Cache_Move(cache_system_t *c);
void Cache_UnlinkLRU(cache_system_t *cs);
void Cache_MakeLRU(cache_system_t *cs);
void Cache_Print(void);
void Cache_Compact(void);
void Cache_Init(void);
Music music = (Music){0};
cvar_t chase_back = {"chase_back", "100"};
cvar_t chase_up = {"chase_up", "16"};
cvar_t chase_right = {"chase_right", "0"};
cvar_t chase_active = {"chase_active", "0"};
vec3_t chase_pos;
vec3_t chase_angles;
vec3_t chase_dest;
vec3_t chase_dest_angles;
kbutton_t in_mlook;
kbutton_t in_klook;
kbutton_t in_left;
kbutton_t in_right;
kbutton_t in_forward;
kbutton_t in_back;
kbutton_t in_lookup;
kbutton_t in_lookdown;
kbutton_t in_moveleft;
kbutton_t in_moveright;
kbutton_t in_strafe;
kbutton_t in_speed;
kbutton_t in_use;
kbutton_t in_jump;
kbutton_t in_attack;
kbutton_t in_up;
kbutton_t in_down;
int32_t in_impulse;
cvar_t cl_upspeed = {"cl_upspeed", "200"};
cvar_t cl_forwardspeed = {"cl_forwardspeed", "200", 1};
cvar_t cl_backspeed = {"cl_backspeed", "200", 1};
cvar_t cl_sidespeed = {"cl_sidespeed", "350"};
cvar_t cl_movespeedkey = {"cl_movespeedkey", "2.0"};
cvar_t cl_yawspeed = {"cl_yawspeed", "140"};
cvar_t cl_pitchspeed = {"cl_pitchspeed", "150"};
cvar_t cl_anglespeedkey = {"cl_anglespeedkey", "1.5"};
cvar_t cl_name = {"_cl_name", "player", 1};
cvar_t cl_color = {"_cl_color", "0", 1};
cvar_t cl_shownet = {"cl_shownet", "0"};
cvar_t cl_nolerp = {"cl_nolerp", "0"};
cvar_t lookspring = {"lookspring", "0", 1};
cvar_t lookstrafe = {"lookstrafe", "0", 1};
cvar_t sensitivity = {"sensitivity", "3", 1};
cvar_t m_pitch = {"m_pitch", "0.022", 1};
cvar_t m_yaw = {"m_yaw", "0.022", 1};
cvar_t m_forward = {"m_forward", "1", 1};
cvar_t m_side = {"m_side", "0.8", 1};
client_static_t cls;
client_state_t cl;
efrag_t cl_efrags[MAX_EFRAGS];
entity_t cl_entities[MAX_EDICTS];
entity_t cl_static_entities[MAX_STATIC_ENTITIES];
lightstyle_t cl_lightstyle[MAX_LIGHTSTYLES];
dlight_t cl_dlights[MAX_DLIGHTS];
int32_t cl_numvisedicts;
entity_t *cl_visedicts[MAX_VISEDICTS];
char *svc_strings[] = {"svc_bad", "svc_nop", "svc_disconnect", "svc_updatestat", "svc_version", "svc_setview", "svc_sound", "svc_time", "svc_print", "svc_stufftext", "svc_setangle", "svc_serverinfo", "svc_lightstyle", "svc_updatename", "svc_updatefrags", "svc_clientdata", "svc_stopsound", "svc_updatecolors", "svc_particle", "svc_damage", "svc_spawnstatic", "OBSOLETE svc_spawnbinary", "svc_spawnbaseline", "svc_temp_entity", "svc_setpause", "svc_signonnum", "svc_centerprint", "svc_killedmonster", "svc_foundsecret", "svc_spawnstaticsound", "svc_intermission", "svc_finale", "svc_cdtrack", "svc_sellscreen", "svc_cutscene"};
int32_t bitcounts[16];
int32_t num_temp_entities;
entity_t cl_temp_entities[MAX_TEMP_ENTITIES];
beam_t cl_beams[MAX_BEAMS];
sfx_t *cl_sfx_wizhit;
sfx_t *cl_sfx_knighthit;
sfx_t *cl_sfx_tink1;
sfx_t *cl_sfx_ric1;
sfx_t *cl_sfx_ric2;
sfx_t *cl_sfx_ric3;
sfx_t *cl_sfx_r_exp3;
cmdalias_t *cmd_alias;
bool cmd_wait;
sizebuf_t cmd_text;
int32_t cmd_argc;
char *cmd_argv[MAX_ARGS];
char *cmd_null_string = "";
char *cmd_args = 0;
cmd_source_t cmd_source;
cmd_function_t *cmd_functions;
char *largv[(MAX_NUM_ARGVS + NUM_SAFE_ARGVS) + 1];
char *argvdummy = " ";
char *safeargvs[NUM_SAFE_ARGVS] = {"-stdvid", "-nolan", "-nosound", "-nocdaudio", "-nojoy", "-nomouse", "-dibonly"};
cvar_t registered = {"registered", "1"};
cvar_t cmdline = {"cmdline", "0", 0, 1};
bool com_modified;
bool proghack;
int32_t static_registered = 1;
bool msg_suppress_1 = 0;
char com_token[1024];
int32_t com_argc;
char **com_argv;
char com_cmdline[CMDLINE_LENGTH];
bool standard_quake = 1;
bool rogue;
bool hipnotic;
uint16_t pop[] = {0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x6600, 0x0000, 0x0000, 0x0000, 0x6600, 0x0000, 0x0000, 0x0066, 0x0000, 0x0000, 0x0000, 0x0000, 0x0067, 0x0000, 0x0000, 0x6665, 0x0000, 0x0000, 0x0000, 0x0000, 0x0065, 0x6600, 0x0063, 0x6561, 0x0000, 0x0000, 0x0000, 0x0000, 0x0061, 0x6563, 0x0064, 0x6561, 0x0000, 0x0000, 0x0000, 0x0000, 0x0061, 0x6564, 0x0064, 0x6564, 0x0000, 0x6469, 0x6969, 0x6400, 0x0064, 0x6564, 0x0063, 0x6568, 0x6200, 0x0064, 0x6864, 0x0000, 0x6268, 0x6563, 0x0000, 0x6567, 0x6963, 0x0064, 0x6764, 0x0063, 0x6967, 0x6500, 0x0000, 0x6266, 0x6769, 0x6a68, 0x6768, 0x6a69, 0x6766, 0x6200, 0x0000, 0x0062, 0x6566, 0x6666, 0x6666, 0x6666, 0x6562, 0x0000, 0x0000, 0x0000, 0x0062, 0x6364, 0x6664, 0x6362, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0062, 0x6662, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0061, 0x6661, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x6500, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x6400, 0x0000, 0x0000, 0x0000};
int32_t msg_readcount;
bool msg_badread;
int32_t com_filesize;
char com_cachedir[MAX_OSPATH];
char com_gamedir[MAX_OSPATH];
searchpath_t *com_searchpaths;
cache_user_t *loadcache;
uint8_t *loadbuf;
int32_t loadsize;
int32_t con_linewidth;
float con_cursorspeed = 4;
bool con_forcedup;
int32_t con_totallines;
int32_t con_backscroll;
int32_t con_current;
int32_t con_x;
char *con_text = 0;
cvar_t con_notifytime = {"con_notifytime", "3"};
float con_times[NUM_CON_TIMES];
int32_t con_vislines;
bool con_debuglog;
bool con_initialized;
int32_t con_notifylines;
uint16_t crctable[256] = {0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de, 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485, 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d, 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4, 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc, 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823, 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12, 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41, 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49, 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70, 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78, 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f, 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067, 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256, 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d, 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c, 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634, 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab, 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3, 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92, 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1, 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0};
cvar_t *cvar_vars;
char *cvar_null_string = "";
fixed16_t sadjust;
fixed16_t tadjust;
fixed16_t bbextents;
fixed16_t bbextentt;
void (*prealspandrawer)(void);
int32_t miplevel;
float scale_for_mip;
int32_t screenwidth;
int32_t ubasestep;
int32_t errorterm;
int32_t erroradjustup;
int32_t erroradjustdown;
int32_t vstartscan;
vec3_t transformed_modelorg;
cvar_t d_subdiv16 = {"d_subdiv16", "1"};
cvar_t d_mipcap = {"d_mipcap", "0"};
cvar_t d_mipscale = {"d_mipscale", "1"};
surfcache_t *d_initial_rover;
bool d_roverwrapped;
int32_t d_minmip;
float d_scalemip[NUM_MIPS - 1];
float basemip[NUM_MIPS - 1] = {1.0, 0.5 * 0.8, 0.25 * 0.8};
void (*d_drawspans)(espan_t *pspan);
int32_t d_vrectx;
int32_t d_vrecty;
int32_t d_vrectright_particle;
int32_t d_vrectbottom_particle;
int32_t d_y_aspect_shift;
int32_t d_pix_min;
int32_t d_pix_max;
int32_t d_pix_shift;
int32_t d_scantable[MAXHEIGHT];
int16_t *zspantable[MAXHEIGHT];
int32_t r_p0[6];
int32_t r_p1[6];
int32_t r_p2[6];
uint8_t *d_pcolormap;
int32_t d_aflatcolor;
int32_t d_xdenom;
edgetable *pedgetable;
edgetable edgetables[12] = {{0, 1, r_p0, r_p2, 0, 2, r_p0, r_p1, r_p2}, {0, 2, r_p1, r_p0, r_p2, 1, r_p1, r_p2, 0}, {1, 1, r_p0, r_p2, 0, 1, r_p1, r_p2, 0}, {0, 1, r_p1, r_p0, 0, 2, r_p1, r_p2, r_p0}, {0, 2, r_p0, r_p2, r_p1, 1, r_p0, r_p1, 0}, {0, 1, r_p2, r_p1, 0, 1, r_p2, r_p0, 0}, {0, 1, r_p2, r_p1, 0, 2, r_p2, r_p0, r_p1}, {0, 2, r_p2, r_p1, r_p0, 1, r_p2, r_p0, 0}, {0, 1, r_p1, r_p0, 0, 1, r_p1, r_p2, 0}, {1, 1, r_p2, r_p1, 0, 1, r_p0, r_p1, 0}, {1, 1, r_p1, r_p0, 0, 1, r_p2, r_p0, 0}, {0, 1, r_p0, r_p2, 0, 1, r_p0, r_p1, 0}};
int32_t a_sstepxfrac;
int32_t a_tstepxfrac;
int32_t r_lstepx;
int32_t a_ststepxwhole;
int32_t r_sstepx;
int32_t r_tstepx;
int32_t r_lstepy;
int32_t r_sstepy;
int32_t r_tstepy;
int32_t r_zistepx;
int32_t r_zistepy;
int32_t d_aspancount;
int32_t d_countextrastep;
spanpackage_t *a_spans;
spanpackage_t *d_pedgespanpackage;
int32_t ystart;
uint8_t *d_pdest;
uint8_t *d_ptex;
int16_t *d_pz;
int32_t d_sfrac;
int32_t d_tfrac;
int32_t d_light;
int32_t d_zi;
int32_t d_ptexextrastep;
int32_t d_sfracextrastep;
int32_t d_tfracextrastep;
int32_t d_lightextrastep;
int32_t d_pdestextrastep;
int32_t d_lightbasestep;
int32_t d_pdestbasestep;
int32_t d_ptexbasestep;
int32_t d_sfracbasestep;
int32_t d_tfracbasestep;
int32_t d_ziextrastep;
int32_t d_zibasestep;
int32_t d_pzextrastep;
int32_t d_pzbasestep;
adivtab_t adivtab[32 * 32] = {{1, 0}, {1, -1}, {1, -2}, {1, -3}, {1, -4}, {1, -5}, {1, -6}, {1, -7}, {2, -1}, {2, -3}, {3, 0}, {3, -3}, {5, 0}, {7, -1}, {15, 0}, {0, 0}, {-15, 0}, {-8, 1}, {-5, 0}, {-4, 1}, {-3, 0}, {-3, 3}, {-3, 6}, {-2, 1}, {-2, 3}, {-2, 5}, {-2, 7}, {-2, 9}, {-2, 11}, {-2, 13}, {-1, 0}, {-1, 1}, {0, -14}, {1, 0}, {1, -1}, {1, -2}, {1, -3}, {1, -4}, {1, -5}, {1, -6}, {2, 0}, {2, -2}, {2, -4}, {3, -2}, {4, -2}, {7, 0}, {14, 0}, {0, 0}, {-14, 0}, {-7, 0}, {-5, 1}, {-4, 2}, {-3, 1}, {-3, 4}, {-2, 0}, {-2, 2}, {-2, 4}, {-2, 6}, {-2, 8}, {-2, 10}, {-2, 12}, {-1, 0}, {-1, 1}, {-1, 2}, {0, -13}, {0, -13}, {1, 0}, {1, -1}, {1, -2}, {1, -3}, {1, -4}, {1, -5}, {1, -6}, {2, -1}, {2, -3}, {3, -1}, {4, -1}, {6, -1}, {13, 0}, {0, 0}, {-13, 0}, {-7, 1}, {-5, 2}, {-4, 3}, {-3, 2}, {-3, 5}, {-2, 1}, {-2, 3}, {-2, 5}, {-2, 7}, {-2, 9}, {-2, 11}, {-1, 0}, {-1, 1}, {-1, 2}, {-1, 3}, {0, -12}, {0, -12}, {0, -12}, {1, 0}, {1, -1}, {1, -2}, {1, -3}, {1, -4}, {1, -5}, {2, 0}, {2, -2}, {3, 0}, {4, 0}, {6, 0}, {12, 0}, {0, 0}, {-12, 0}, {-6, 0}, {-4, 0}, {-3, 0}, {-3, 3}, {-2, 0}, {-2, 2}, {-2, 4}, {-2, 6}, {-2, 8}, {-2, 10}, {-1, 0}, {-1, 1}, {-1, 2}, {-1, 3}, {-1, 4}, {0, -11}, {0, -11}, {0, -11}, {0, -11}, {1, 0}, {1, -1}, {1, -2}, {1, -3}, {1, -4}, {1, -5}, {2, -1}, {2, -3}, {3, -2}, {5, -1}, {11, 0}, {0, 0}, {-11, 0}, {-6, 1}, {-4, 1}, {-3, 1}, {-3, 4}, {-2, 1}, {-2, 3}, {-2, 5}, {-2, 7}, {-2, 9}, {-1, 0}, {-1, 1}, {-1, 2}, {-1, 3}, {-1, 4}, {-1, 5}, {0, -10}, {0, -10}, {0, -10}, {0, -10}, {0, -10}, {1, 0}, {1, -1}, {1, -2}, {1, -3}, {1, -4}, {2, 0}, {2, -2}, {3, -1}, {5, 0}, {10, 0}, {0, 0}, {-10, 0}, {-5, 0}, {-4, 2}, {-3, 2}, {-2, 0}, {-2, 2}, {-2, 4}, {-2, 6}, {-2, 8}, {-1, 0}, {-1, 1}, {-1, 2}, {-1, 3}, {-1, 4}, {-1, 5}, {-1, 6}, {0, -9}, {0, -9}, {0, -9}, {0, -9}, {0, -9}, {0, -9}, {1, 0}, {1, -1}, {1, -2}, {1, -3}, {1, -4}, {2, -1}, {3, 0}, {4, -1}, {9, 0}, {0, 0}, {-9, 0}, {-5, 1}, {-3, 0}, {-3, 3}, {-2, 1}, {-2, 3}, {-2, 5}, {-2, 7}, {-1, 0}, {-1, 1}, {-1, 2}, {-1, 3}, {-1, 4}, {-1, 5}, {-1, 6}, {-1, 7}, {0, -8}, {0, -8}, {0, -8}, {0, -8}, {0, -8}, {0, -8}, {0, -8}, {1, 0}, {1, -1}, {1, -2}, {1, -3}, {2, 0}, {2, -2}, {4, 0}, {8, 0}, {0, 0}, {-8, 0}, {-4, 0}, {-3, 1}, {-2, 0}, {-2, 2}, {-2, 4}, {-2, 6}, {-1, 0}, {-1, 1}, {-1, 2}, {-1, 3}, {-1, 4}, {-1, 5}, {-1, 6}, {-1, 7}, {-1, 8}, {0, -7}, {0, -7}, {0, -7}, {0, -7}, {0, -7}, {0, -7}, {0, -7}, {0, -7}, {1, 0}, {1, -1}, {1, -2}, {1, -3}, {2, -1}, {3, -1}, {7, 0}, {0, 0}, {-7, 0}, {-4, 1}, {-3, 2}, {-2, 1}, {-2, 3}, {-2, 5}, {-1, 0}, {-1, 1}, {-1, 2}, {-1, 3}, {-1, 4}, {-1, 5}, {-1, 6}, {-1, 7}, {-1, 8}, {-1, 9}, {0, -6}, {0, -6}, {0, -6}, {0, -6}, {0, -6}, {0, -6}, {0, -6}, {0, -6}, {0, -6}, {1, 0}, {1, -1}, {1, -2}, {2, 0}, {3, 0}, {6, 0}, {0, 0}, {-6, 0}, {-3, 0}, {-2, 0}, {-2, 2}, {-2, 4}, {-1, 0}, {-1, 1}, {-1, 2}, {-1, 3}, {-1, 4}, {-1, 5}, {-1, 6}, {-1, 7}, {-1, 8}, {-1, 9}, {-1, 10}, {0, -5}, {0, -5}, {0, -5}, {0, -5}, {0, -5}, {0, -5}, {0, -5}, {0, -5}, {0, -5}, {0, -5}, {1, 0}, {1, -1}, {1, -2}, {2, -1}, {5, 0}, {0, 0}, {-5, 0}, {-3, 1}, {-2, 1}, {-2, 3}, {-1, 0}, {-1, 1}, {-1, 2}, {-1, 3}, {-1, 4}, {-1, 5}, {-1, 6}, {-1, 7}, {-1, 8}, {-1, 9}, {-1, 10}, {-1, 11}, {0, -4}, {0, -4}, {0, -4}, {0, -4}, {0, -4}, {0, -4}, {0, -4}, {0, -4}, {0, -4}, {0, -4}, {0, -4}, {1, 0}, {1, -1}, {2, 0}, {4, 0}, {0, 0}, {-4, 0}, {-2, 0}, {-2, 2}, {-1, 0}, {-1, 1}, {-1, 2}, {-1, 3}, {-1, 4}, {-1, 5}, {-1, 6}, {-1, 7}, {-1, 8}, {-1, 9}, {-1, 10}, {-1, 11}, {-1, 12}, {0, -3}, {0, -3}, {0, -3}, {0, -3}, {0, -3}, {0, -3}, {0, -3}, {0, -3}, {0, -3}, {0, -3}, {0, -3}, {0, -3}, {1, 0}, {1, -1}, {3, 0}, {0, 0}, {-3, 0}, {-2, 1}, {-1, 0}, {-1, 1}, {-1, 2}, {-1, 3}, {-1, 4}, {-1, 5}, {-1, 6}, {-1, 7}, {-1, 8}, {-1, 9}, {-1, 10}, {-1, 11}, {-1, 12}, {-1, 13}, {0, -2}, {0, -2}, {0, -2}, {0, -2}, {0, -2}, {0, -2}, {0, -2}, {0, -2}, {0, -2}, {0, -2}, {0, -2}, {0, -2}, {0, -2}, {1, 0}, {2, 0}, {0, 0}, {-2, 0}, {-1, 0}, {-1, 1}, {-1, 2}, {-1, 3}, {-1, 4}, {-1, 5}, {-1, 6}, {-1, 7}, {-1, 8}, {-1, 9}, {-1, 10}, {-1, 11}, {-1, 12}, {-1, 13}, {-1, 14}, {0, -1}, {0, -1}, {0, -1}, {0, -1}, {0, -1}, {0, -1}, {0, -1}, {0, -1}, {0, -1}, {0, -1}, {0, -1}, {0, -1}, {0, -1}, {0, -1}, {1, 0}, {0, 0}, {-1, 0}, {-1, 1}, {-1, 2}, {-1, 3}, {-1, 4}, {-1, 5}, {-1, 6}, {-1, 7}, {-1, 8}, {-1, 9}, {-1, 10}, {-1, 11}, {-1, 12}, {-1, 13}, {-1, 14}, {-1, 15}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {-1, -14}, {-1, -13}, {-1, -12}, {-1, -11}, {-1, -10}, {-1, -9}, {-1, -8}, {-1, -7}, {-1, -6}, {-1, -5}, {-1, -4}, {-1, -3}, {-1, -2}, {-1, -1}, {-1, 0}, {0, 0}, {1, 0}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {-1, -13}, {-1, -12}, {-1, -11}, {-1, -10}, {-1, -9}, {-1, -8}, {-1, -7}, {-1, -6}, {-1, -5}, {-1, -4}, {-1, -3}, {-1, -2}, {-1, -1}, {-1, 0}, {-2, 0}, {0, 0}, {2, 0}, {1, 0}, {0, 2}, {0, 2}, {0, 2}, {0, 2}, {0, 2}, {0, 2}, {0, 2}, {0, 2}, {0, 2}, {0, 2}, {0, 2}, {0, 2}, {0, 2}, {0, 2}, {-1, -12}, {-1, -11}, {-1, -10}, {-1, -9}, {-1, -8}, {-1, -7}, {-1, -6}, {-1, -5}, {-1, -4}, {-1, -3}, {-1, -2}, {-1, -1}, {-1, 0}, {-2, -1}, {-3, 0}, {0, 0}, {3, 0}, {1, 1}, {1, 0}, {0, 3}, {0, 3}, {0, 3}, {0, 3}, {0, 3}, {0, 3}, {0, 3}, {0, 3}, {0, 3}, {0, 3}, {0, 3}, {0, 3}, {0, 3}, {-1, -11}, {-1, -10}, {-1, -9}, {-1, -8}, {-1, -7}, {-1, -6}, {-1, -5}, {-1, -4}, {-1, -3}, {-1, -2}, {-1, -1}, {-1, 0}, {-2, -2}, {-2, 0}, {-4, 0}, {0, 0}, {4, 0}, {2, 0}, {1, 1}, {1, 0}, {0, 4}, {0, 4}, {0, 4}, {0, 4}, {0, 4}, {0, 4}, {0, 4}, {0, 4}, {0, 4}, {0, 4}, {0, 4}, {0, 4}, {-1, -10}, {-1, -9}, {-1, -8}, {-1, -7}, {-1, -6}, {-1, -5}, {-1, -4}, {-1, -3}, {-1, -2}, {-1, -1}, {-1, 0}, {-2, -3}, {-2, -1}, {-3, -1}, {-5, 0}, {0, 0}, {5, 0}, {2, 1}, {1, 2}, {1, 1}, {1, 0}, {0, 5}, {0, 5}, {0, 5}, {0, 5}, {0, 5}, {0, 5}, {0, 5}, {0, 5}, {0, 5}, {0, 5}, {0, 5}, {-1, -9}, {-1, -8}, {-1, -7}, {-1, -6}, {-1, -5}, {-1, -4}, {-1, -3}, {-1, -2}, {-1, -1}, {-1, 0}, {-2, -4}, {-2, -2}, {-2, 0}, {-3, 0}, {-6, 0}, {0, 0}, {6, 0}, {3, 0}, {2, 0}, {1, 2}, {1, 1}, {1, 0}, {0, 6}, {0, 6}, {0, 6}, {0, 6}, {0, 6}, {0, 6}, {0, 6}, {0, 6}, {0, 6}, {0, 6}, {-1, -8}, {-1, -7}, {-1, -6}, {-1, -5}, {-1, -4}, {-1, -3}, {-1, -2}, {-1, -1}, {-1, 0}, {-2, -5}, {-2, -3}, {-2, -1}, {-3, -2}, {-4, -1}, {-7, 0}, {0, 0}, {7, 0}, {3, 1}, {2, 1}, {1, 3}, {1, 2}, {1, 1}, {1, 0}, {0, 7}, {0, 7}, {0, 7}, {0, 7}, {0, 7}, {0, 7}, {0, 7}, {0, 7}, {0, 7}, {-1, -7}, {-1, -6}, {-1, -5}, {-1, -4}, {-1, -3}, {-1, -2}, {-1, -1}, {-1, 0}, {-2, -6}, {-2, -4}, {-2, -2}, {-2, 0}, {-3, -1}, {-4, 0}, {-8, 0}, {0, 0}, {8, 0}, {4, 0}, {2, 2}, {2, 0}, {1, 3}, {1, 2}, {1, 1}, {1, 0}, {0, 8}, {0, 8}, {0, 8}, {0, 8}, {0, 8}, {0, 8}, {0, 8}, {0, 8}, {-1, -6}, {-1, -5}, {-1, -4}, {-1, -3}, {-1, -2}, {-1, -1}, {-1, 0}, {-2, -7}, {-2, -5}, {-2, -3}, {-2, -1}, {-3, -3}, {-3, 0}, {-5, -1}, {-9, 0}, {0, 0}, {9, 0}, {4, 1}, {3, 0}, {2, 1}, {1, 4}, {1, 3}, {1, 2}, {1, 1}, {1, 0}, {0, 9}, {0, 9}, {0, 9}, {0, 9}, {0, 9}, {0, 9}, {0, 9}, {-1, -5}, {-1, -4}, {-1, -3}, {-1, -2}, {-1, -1}, {-1, 0}, {-2, -8}, {-2, -6}, {-2, -4}, {-2, -2}, {-2, 0}, {-3, -2}, {-4, -2}, {-5, 0}, {-10, 0}, {0, 0}, {10, 0}, {5, 0}, {3, 1}, {2, 2}, {2, 0}, {1, 4}, {1, 3}, {1, 2}, {1, 1}, {1, 0}, {0, 10}, {0, 10}, {0, 10}, {0, 10}, {0, 10}, {0, 10}, {-1, -4}, {-1, -3}, {-1, -2}, {-1, -1}, {-1, 0}, {-2, -9}, {-2, -7}, {-2, -5}, {-2, -3}, {-2, -1}, {-3, -4}, {-3, -1}, {-4, -1}, {-6, -1}, {-11, 0}, {0, 0}, {11, 0}, {5, 1}, {3, 2}, {2, 3}, {2, 1}, {1, 5}, {1, 4}, {1, 3}, {1, 2}, {1, 1}, {1, 0}, {0, 11}, {0, 11}, {0, 11}, {0, 11}, {0, 11}, {-1, -3}, {-1, -2}, {-1, -1}, {-1, 0}, {-2, -10}, {-2, -8}, {-2, -6}, {-2, -4}, {-2, -2}, {-2, 0}, {-3, -3}, {-3, 0}, {-4, 0}, {-6, 0}, {-12, 0}, {0, 0}, {12, 0}, {6, 0}, {4, 0}, {3, 0}, {2, 2}, {2, 0}, {1, 5}, {1, 4}, {1, 3}, {1, 2}, {1, 1}, {1, 0}, {0, 12}, {0, 12}, {0, 12}, {0, 12}, {-1, -2}, {-1, -1}, {-1, 0}, {-2, -11}, {-2, -9}, {-2, -7}, {-2, -5}, {-2, -3}, {-2, -1}, {-3, -5}, {-3, -2}, {-4, -3}, {-5, -2}, {-7, -1}, {-13, 0}, {0, 0}, {13, 0}, {6, 1}, {4, 1}, {3, 1}, {2, 3}, {2, 1}, {1, 6}, {1, 5}, {1, 4}, {1, 3}, {1, 2}, {1, 1}, {1, 0}, {0, 13}, {0, 13}, {0, 13}, {-1, -1}, {-1, 0}, {-2, -12}, {-2, -10}, {-2, -8}, {-2, -6}, {-2, -4}, {-2, -2}, {-2, 0}, {-3, -4}, {-3, -1}, {-4, -2}, {-5, -1}, {-7, 0}, {-14, 0}, {0, 0}, {14, 0}, {7, 0}, {4, 2}, {3, 2}, {2, 4}, {2, 2}, {2, 0}, {1, 6}, {1, 5}, {1, 4}, {1, 3}, {1, 2}, {1, 1}, {1, 0}, {0, 14}, {0, 14}, {-1, 0}, {-2, -13}, {-2, -11}, {-2, -9}, {-2, -7}, {-2, -5}, {-2, -3}, {-2, -1}, {-3, -6}, {-3, -3}, {-3, 0}, {-4, -1}, {-5, 0}, {-8, -1}, {-15, 0}, {0, 0}, {15, 0}, {7, 1}, {5, 0}, {3, 3}, {3, 0}, {2, 3}, {2, 1}, {1, 7}, {1, 6}, {1, 5}, {1, 4}, {1, 3}, {1, 2}, {1, 1}, {1, 0}, {0, 15}, {-2, -14}, {-2, -12}, {-2, -10}, {-2, -8}, {-2, -6}, {-2, -4}, {-2, -2}, {-2, 0}, {-3, -5}, {-3, -2}, {-4, -4}, {-4, 0}, {-6, -2}, {-8, 0}, {-16, 0}, {0, 0}, {16, 0}, {8, 0}, {5, 1}, {4, 0}, {3, 1}, {2, 4}, {2, 2}, {2, 0}, {1, 7}, {1, 6}, {1, 5}, {1, 4}, {1, 3}, {1, 2}, {1, 1}, {1, 0}};
uint8_t *skintable[MAX_LBM_HEIGHT];
int32_t skinwidth;
uint8_t *skinstart;
unsigned char *r_turb_pbase;
unsigned char *r_turb_pdest;
fixed16_t r_turb_s;
fixed16_t r_turb_t;
fixed16_t r_turb_sstep;
fixed16_t r_turb_tstep;
int32_t *r_turb_turb;
int32_t r_turb_spancount;
int32_t sprite_height;
int32_t minindex;
int32_t maxindex;
sspan_t *sprite_spans;
float surfscale;
bool r_cache_thrash;
int32_t sc_size;
surfcache_t *sc_rover;
surfcache_t *sc_base;
float d_sdivzstepu;
float d_tdivzstepu;
float d_zistepu;
float d_sdivzstepv;
float d_tdivzstepv;
float d_zistepv;
float d_sdivzorigin;
float d_tdivzorigin;
float d_ziorigin;
pixel_t *cacheblock;
int32_t cachewidth;
pixel_t *d_viewbuffer;
int16_t *d_pzbuffer;
uint32_t d_zrowbytes;
uint32_t d_zwidth;
rectdesc_t r_rectdesc;
uint8_t *draw_chars;
qpic_t *draw_disc;
qpic_t *draw_backtile;
cachepic_t menu_cachepics[MAX_CACHED_PICS];
int32_t menu_numcachepics;
quakeparms_t host_parms;
bool host_initialized;
double host_frametime;
double host_time;
double realtime;
double oldrealtime;
int32_t host_framecount;
int32_t host_hunklevel;
int32_t minimum_memory;
client_t *host_client;
jmp_buf host_abortserver;
uint8_t *host_basepal;
uint8_t *host_colormap;
cvar_t host_framerate = {"host_framerate", "0"};
cvar_t host_speeds = {"host_speeds", "0"};
cvar_t sys_ticrate = {"sys_ticrate", "0.05"};
cvar_t serverprofile = {"serverprofile", "0"};
cvar_t fraglimit = {"fraglimit", "0", 0, 1};
cvar_t timelimit = {"timelimit", "0", 0, 1};
cvar_t teamplay = {"teamplay", "0", 0, 1};
cvar_t samelevel = {"samelevel", "0"};
cvar_t noexit = {"noexit", "0", 0, 1};
cvar_t developer = {"developer", "0"};
cvar_t skill = {"skill", "1"};
cvar_t deathmatch = {"deathmatch", "0"};
cvar_t coop = {"coop", "0"};
cvar_t pausable = {"pausable", "1"};
cvar_t temp1 = {"temp1", "0"};
int32_t current_skill;
bool noclip_anglehack;
char key_lines[32][MAXCMDLINE];
int32_t key_linepos;
int32_t shift_down = 0;
int32_t key_lastpress;
int32_t edit_line = 0;
int32_t history_line = 0;
keydest_t key_dest;
int32_t key_count;
char *keybindings[256];
bool consolekeys[256];
bool menubound[256];
int32_t keyshift[256];
int32_t key_repeats[256];
bool keydown[256];
keyname_t keynames[] = {{"TAB", K_TAB}, {"ENTER", K_ENTER}, {"ESCAPE", K_ESCAPE}, {"SPACE", K_SPACE}, {"BACKSPACE", K_BACKSPACE}, {"UPARROW", K_UPARROW}, {"DOWNARROW", K_DOWNARROW}, {"LEFTARROW", K_LEFTARROW}, {"RIGHTARROW", K_RIGHTARROW}, {"ALT", K_ALT}, {"CTRL", K_CTRL}, {"SHIFT", K_SHIFT}, {"F1", K_F1}, {"F2", K_F2}, {"F3", K_F3}, {"F4", K_F4}, {"F5", K_F5}, {"F6", K_F6}, {"F7", K_F7}, {"F8", K_F8}, {"F9", K_F9}, {"F10", K_F10}, {"F11", K_F11}, {"F12", K_F12}, {"INS", K_INS}, {"DEL", K_DEL}, {"PGDN", K_PGDN}, {"PGUP", K_PGUP}, {"HOME", K_HOME}, {"END", K_END}, {"MOUSE1", K_MOUSE1}, {"MOUSE2", K_MOUSE2}, {"MOUSE3", K_MOUSE3}, {"JOY1", K_JOY1}, {"JOY2", K_JOY2}, {"JOY3", K_JOY3}, {"JOY4", K_JOY4}, {"AUX1", K_AUX1}, {"AUX2", K_AUX2}, {"AUX3", K_AUX3}, {"AUX4", K_AUX4}, {"AUX5", K_AUX5}, {"AUX6", K_AUX6}, {"AUX7", K_AUX7}, {"AUX8", K_AUX8}, {"AUX9", K_AUX9}, {"AUX10", K_AUX10}, {"AUX11", K_AUX11}, {"AUX12", K_AUX12}, {"AUX13", K_AUX13}, {"AUX14", K_AUX14}, {"AUX15", K_AUX15}, {"AUX16", K_AUX16}, {"AUX17", K_AUX17}, {"AUX18", K_AUX18}, {"AUX19", K_AUX19}, {"AUX20", K_AUX20}, {"AUX21", K_AUX21}, {"AUX22", K_AUX22}, {"AUX23", K_AUX23}, {"AUX24", K_AUX24}, {"AUX25", K_AUX25}, {"AUX26", K_AUX26}, {"AUX27", K_AUX27}, {"AUX28", K_AUX28}, {"AUX29", K_AUX29}, {"AUX30", K_AUX30}, {"AUX31", K_AUX31}, {"AUX32", K_AUX32}, {"PAUSE", K_PAUSE}, {"MWHEELUP", K_MWHEELUP}, {"MWHEELDOWN", K_MWHEELDOWN}, {"SEMICOLON", ';'}, {0, 0}};
char chat_buffer[32];
bool team_message = 0;
vec3_t vec3_origin = {0, 0, 0};
int32_t nanmask = 255 << 23;
void (*vid_menudrawfn)(void);
void (*vid_menukeyfn)(int32_t key);
bool m_entersound;
bool m_recursiveDraw;
int32_t m_return_state;
bool m_return_onerror;
char m_return_reason[32];
uint8_t identityTable[256];
uint8_t translationTable[256];
int32_t m_save_demonum;
int32_t m_main_cursor;
int32_t m_singleplayer_cursor;
int32_t load_cursor;
char m_filenames[MAX_SAVEGAMES][SAVEGAME_COMMENT_LENGTH + 1];
int32_t loadable[MAX_SAVEGAMES];
int32_t m_multiplayer_cursor;
int32_t setup_cursor = 4;
int32_t setup_cursor_table[] = {40, 56, 80, 104, 140};
char setup_hostname[16];
char setup_myname[16];
int32_t setup_oldtop;
int32_t setup_oldbottom;
int32_t setup_top;
int32_t setup_bottom;
int32_t m_net_cursor;
int32_t m_net_items;
int32_t m_net_saveHeight;
char *net_helpMessage[] = {" ", " Two computers connected", " through two modems. ", " ", " ", " Two computers connected", " by a null-modem cable. ", " ", " Novell network LANs ", " or Windows 95 DOS-box. ", " ", "(LAN=Local Area Network)", " Commonly used to play ", " over the Internet, but ", " also used on a Local ", " Area Network. "};
int32_t options_cursor;
char *bindnames[][2] = {{"+attack", "attack"}, {"impulse 10", "change weapon"}, {"+jump", "jump / swim up"}, {"+forward", "walk forward"}, {"+back", "backpedal"}, {"+left", "turn left"}, {"+right", "turn right"}, {"+speed", "run"}, {"+moveleft", "step left"}, {"+moveright", "step right"}, {"+strafe", "sidestep"}, {"+lookup", "look up"}, {"+lookdown", "look down"}, {"centerview", "center view"}, {"+mlook", "mouse look"}, {"+klook", "keyboard look"}, {"+moveup", "swim up"}, {"+movedown", "swim down"}};
int32_t keys_cursor;
int32_t bind_grab;
int32_t help_page;
int32_t msgNumber;
int32_t m_quit_prevstate;
bool wasInMenus;
char *quitMessage[] = {" Are you gonna quit ", " this game just like ", " everything else? ", " ", " Milord, methinks that ", " thou art a lowly ", " quitter. Is this true? ", " ", " Do I need to bust your ", " face open for trying ", " to quit? ", " ", " Man, I oughta smack you", " for trying to quit! ", " Press Y to get ", " smacked out. ", " Press Y to quit like a ", " big loser in life. ", " Press N to stay proud ", " and successful! ", " If you press Y to ", " quit, I will summon ", " Satan all over your ", " hard drive! ", " Um, Asmodeus dislikes ", " his children trying to ", " quit. Press Y to return", " to your Tinkertoys. ", " If you quit now, I'll ", " throw a blanket-party ", " for you next time! ", " "};
int32_t serialConfig_cursor;
int32_t serialConfig_cursor_table[] = {48, 64, 80, 96, 112, 132};
int32_t ISA_uarts[] = {0x3f8, 0x2f8, 0x3e8, 0x2e8};
int32_t ISA_IRQs[] = {4, 3, 4, 3};
int32_t serialConfig_baudrate[] = {9600, 14400, 19200, 28800, 38400, 57600};
int32_t serialConfig_comport;
int32_t serialConfig_irq;
int32_t serialConfig_baud;
char serialConfig_phone[16];
int32_t modemConfig_cursor;
int32_t modemConfig_cursor_table[] = {40, 56, 88, 120, 156};
char modemConfig_dialing;
char modemConfig_clear[16];
char modemConfig_init[32];
char modemConfig_hangup[16];
int32_t lanConfig_cursor = -1;
int32_t lanConfig_cursor_table[] = {72, 92, 124};
int32_t lanConfig_port;
char lanConfig_portname[6];
char lanConfig_joinname[22];
level_t levels[] = {{"start", "Entrance"}, {"e1m1", "Slipgate Complex"}, {"e1m2", "Castle of the Damned"}, {"e1m3", "The Necropolis"}, {"e1m4", "The Grisly Grotto"}, {"e1m5", "Gloom Keep"}, {"e1m6", "The Door To Chthon"}, {"e1m7", "The House of Chthon"}, {"e1m8", "Ziggurat Vertigo"}, {"e2m1", "The Installation"}, {"e2m2", "Ogre Citadel"}, {"e2m3", "Crypt of Decay"}, {"e2m4", "The Ebon Fortress"}, {"e2m5", "The Wizard's Manse"}, {"e2m6", "The Dismal Oubliette"}, {"e2m7", "Underearth"}, {"e3m1", "Termination Central"}, {"e3m2", "The Vaults of Zin"}, {"e3m3", "The Tomb of Terror"}, {"e3m4", "Satan's Dark Delight"}, {"e3m5", "Wind Tunnels"}, {"e3m6", "Chambers of Torment"}, {"e3m7", "The Haunted Halls"}, {"e4m1", "The Sewage System"}, {"e4m2", "The Tower of Despair"}, {"e4m3", "The Elder God Shrine"}, {"e4m4", "The Palace of Hate"}, {"e4m5", "Hell's Atrium"}, {"e4m6", "The Pain Maze"}, {"e4m7", "Azure Agony"}, {"e4m8", "The Nameless City"}, {"end", "Shub-Niggurath's Pit"}, {"dm1", "Place of Two Deaths"}, {"dm2", "Claustrophobopolis"}, {"dm3", "The Abandoned Base"}, {"dm4", "The Bad Place"}, {"dm5", "The Cistern"}, {"dm6", "The Dark Zone"}};
level_t hipnoticlevels[] = {{"start", "Command HQ"}, {"hip1m1", "The Pumping Station"}, {"hip1m2", "Storage Facility"}, {"hip1m3", "The Lost Mine"}, {"hip1m4", "Research Facility"}, {"hip1m5", "Military Complex"}, {"hip2m1", "Ancient Realms"}, {"hip2m2", "The Black Cathedral"}, {"hip2m3", "The Catacombs"}, {"hip2m4", "The Crypt"}, {"hip2m5", "Mortum's Keep"}, {"hip2m6", "The Gremlin's Domain"}, {"hip3m1", "Tur Torment"}, {"hip3m2", "Pandemonium"}, {"hip3m3", "Limbo"}, {"hip3m4", "The Gauntlet"}, {"hipend", "Armagon's Lair"}, {"hipdm1", "The Edge of Oblivion"}};
level_t roguelevels[] = {{"start", "Split Decision"}, {"r1m1", "Deviant's Domain"}, {"r1m2", "Dread Portal"}, {"r1m3", "Judgement Call"}, {"r1m4", "Cave of Death"}, {"r1m5", "Towers of Wrath"}, {"r1m6", "Temple of Pain"}, {"r1m7", "Tomb of the Overlord"}, {"r2m1", "Tempus Fugit"}, {"r2m2", "Elemental Fury I"}, {"r2m3", "Elemental Fury II"}, {"r2m4", "Curse of Osiris"}, {"r2m5", "Wizard's Keep"}, {"r2m6", "Blood Sacrifice"}, {"r2m7", "Last Bastion"}, {"r2m8", "Source of Evil"}, {"ctf1", "Division of Change"}};
episode_t episodes[] = {{"Welcome to Quake", 0, 1}, {"Doomed Dimension", 1, 8}, {"Realm of Black Magic", 9, 7}, {"Netherworld", 16, 7}, {"The Elder World", 23, 8}, {"Final Level", 31, 1}, {"Deathmatch Arena", 32, 6}};
episode_t hipnoticepisodes[] = {{"Scourge of Armagon", 0, 1}, {"Fortress of the Dead", 1, 5}, {"Dominion of Darkness", 6, 6}, {"The Rift", 12, 4}, {"Final Level", 16, 1}, {"Deathmatch Arena", 17, 1}};
episode_t rogueepisodes[] = {{"Introduction", 0, 1}, {"Hell's Fortress", 1, 7}, {"Corridors of Time", 8, 8}, {"Deathmatch Arena", 16, 1}};
int32_t startepisode;
int32_t startlevel;
int32_t maxplayers;
bool m_serverInfoMessage = 0;
double m_serverInfoMessageTime;
int32_t gameoptions_cursor_table[] = {40, 56, 64, 72, 80, 88, 96, 112, 120};
int32_t gameoptions_cursor;
bool searchComplete = 0;
double searchCompleteTime;
int32_t slist_cursor;
bool slist_sorted;
model_t *loadmodel;
char loadname[32];
uint8_t mod_novis[MAX_MAP_LEAFS / 8];
model_t mod_known[MAX_MOD_KNOWN];
int32_t mod_numknown;
uint8_t *mod_base;
int32_t net_landriverlevel;
int32_t packetsSent = 0;
int32_t packetsReSent = 0;
int32_t packetsReceived = 0;
int32_t receivedDuplicateCount = 0;
int32_t shortPacketCount = 0;
int32_t droppedDatagrams;
int32_t myDriverLevel;
bool testInProgress = 0;
int32_t testPollCount;
int32_t testDriver;
int32_t testSocket;
PollProcedure testPollProcedure = {0, 0.0, Test_Poll};
bool test2InProgress = 0;
int32_t test2Driver;
int32_t test2Socket;
PollProcedure test2PollProcedure = {0, 0.0, Test2_Poll};
bool localconnectpending = 0;
qsocket_t *loop_client = 0;
qsocket_t *loop_server = 0;
qsocket_t *net_activeSockets = 0;
qsocket_t *net_freeSockets = 0;
int32_t net_numsockets = 0;
bool serialAvailable = 0;
bool ipxAvailable = 0;
bool tcpipAvailable = 0;
int32_t net_hostport;
int32_t DEFAULTnet_hostport = 26000;
char my_ipx_address[NET_NAMELEN];
char my_tcpip_address[NET_NAMELEN];
void (*GetComPortConfig)(int32_t portNumber, int32_t *port, int32_t *irq, int32_t *baud, bool *useModem);
void (*SetComPortConfig)(int32_t portNumber, int32_t port, int32_t irq, int32_t baud, bool useModem);
void (*GetModemConfig)(int32_t portNumber, char *dialType, char *clear, char *init, char *hangup);
void (*SetModemConfig)(int32_t portNumber, char *dialType, char *clear, char *init, char *hangup);
bool listening = 0;
bool slistInProgress = 0;
bool slistSilent = 0;
bool slistLocal = 1;
double slistStartTime;
int32_t slistLastShown;
PollProcedure slistSendProcedure = {0, 0.0, Slist_Send};
PollProcedure slistPollProcedure = {0, 0.0, Slist_Poll};
sizebuf_t net_message;
int32_t net_activeconnections = 0;
int32_t messagesSent = 0;
int32_t messagesReceived = 0;
int32_t unreliableMessagesSent = 0;
int32_t unreliableMessagesReceived = 0;
cvar_t net_messagetimeout = {"net_messagetimeout", "300"};
cvar_t hostname = {"hostname", "UNNAMED"};
bool configRestored = 0;
cvar_t config_com_port = {"_config_com_port", "0x3f8", 1};
cvar_t config_com_irq = {"_config_com_irq", "4", 1};
cvar_t config_com_baud = {"_config_com_baud", "57600", 1};
cvar_t config_com_modem = {"_config_com_modem", "1", 1};
cvar_t config_modem_dialtype = {"_config_modem_dialtype", "T", 1};
cvar_t config_modem_clear = {"_config_modem_clear", "ATZ", 1};
cvar_t config_modem_init = {"_config_modem_init", "", 1};
cvar_t config_modem_hangup = {"_config_modem_hangup", "AT H", 1};
int32_t vcrFile = -1;
bool recording = 0;
int32_t net_driverlevel;
double net_time;
int32_t hostCacheCount = 0;
hostcache_t hostcache[HOSTCACHESIZE];
PollProcedure *pollProcedureList = 0;
net_driver_t net_drivers[MAX_NET_DRIVERS] = {{"Loopback", 0, Loop_Init, Loop_Listen, Loop_SearchForHosts, Loop_Connect, Loop_CheckNewConnections, Loop_GetMessage, Loop_SendMessage, Loop_SendUnreliableMessage, Loop_CanSendMessage, Loop_CanSendUnreliableMessage, Loop_Close, Loop_Shutdown}};
int32_t net_numdrivers = 1;
net_landriver_t net_landrivers[MAX_NET_DRIVERS];
int32_t net_numlandrivers = 0;
uint8_t checkpvs[MAX_MAP_LEAFS / 8];
int32_t c_invis;
int32_t c_notvis;
char pr_string_temp[128];
cvar_t sv_aim = {"sv_aim", "0.93"};
builtin_t pr_builtin[] = {PF_Fixme, PF_makevectors, PF_setorigin, PF_setmodel, PF_setsize, PF_Fixme, PF_break, PF_random, PF_sound, PF_normalize, PF_error, PF_objerror, PF_vlen, PF_vectoyaw, PF_Spawn, PF_Remove, PF_traceline, PF_checkclient, PF_Find, PF_precache_sound, PF_precache_model, PF_stuffcmd, PF_findradius, PF_bprint, PF_sprint, PF_dprint, PF_ftos, PF_vtos, PF_coredump, PF_traceon, PF_traceoff, PF_eprint, PF_walkmove, PF_Fixme, PF_droptofloor, PF_lightstyle, PF_rint, PF_floor, PF_ceil, PF_Fixme, PF_checkbottom, PF_pointcontents, PF_Fixme, PF_fabs, PF_aim, PF_cvar, PF_localcmd, PF_nextent, PF_particle, PF_changeyaw, PF_Fixme, PF_vectoangles, PF_WriteByte, PF_WriteChar, PF_WriteShort, PF_WriteLong, PF_WriteCoord, PF_WriteAngle, PF_WriteString, PF_WriteEntity, PF_Fixme, PF_Fixme, PF_Fixme, PF_Fixme, PF_Fixme, PF_Fixme, PF_Fixme, SV_MoveToGoal, PF_precache_file, PF_makestatic, PF_changelevel, PF_Fixme, PF_cvar_set, PF_centerprint, PF_ambientsound, PF_precache_model, PF_precache_sound, PF_precache_file, PF_setspawnparms};
builtin_t *pr_builtins = pr_builtin;
int32_t pr_numbuiltins = (sizeof(pr_builtin)) / (sizeof(pr_builtin[0]));
dprograms_t *progs;
dfunction_t *pr_functions;
char *pr_strings;
ddef_t *pr_fielddefs;
ddef_t *pr_globaldefs;
dstatement_t *pr_statements;
globalvars_t *pr_global_struct;
float *pr_globals;
int32_t pr_edict_size;
uint16_t pr_crc;
int32_t type_size[8] = {1, (sizeof(string_t)) / 4, 1, 3, 1, 1, (sizeof(func_t)) / 4, (sizeof(void *)) / 4};
cvar_t nomonsters = {"nomonsters", "0"};
cvar_t gamecfg = {"gamecfg", "0"};
cvar_t scratch1 = {"scratch1", "0"};
cvar_t scratch2 = {"scratch2", "0"};
cvar_t scratch3 = {"scratch3", "0"};
cvar_t scratch4 = {"scratch4", "0"};
cvar_t savedgamecfg = {"savedgamecfg", "0", 1};
cvar_t saved1 = {"saved1", "0", 1};
cvar_t saved2 = {"saved2", "0", 1};
cvar_t saved3 = {"saved3", "0", 1};
cvar_t saved4 = {"saved4", "0", 1};
gefv_cache gefvCache[GEFV_CACHESIZE] = {{0, ""}, {0, ""}};
prstack_t pr_stack[MAX_STACK_DEPTH];
int32_t pr_depth;
int32_t localstack[LOCALSTACK_SIZE];
int32_t localstack_used;
bool pr_trace;
dfunction_t *pr_xfunction;
int32_t pr_xstatement;
int32_t pr_argc;
char *pr_opnames[] = {"DONE", "MUL_F", "MUL_V", "MUL_FV", "MUL_VF", "DIV", "ADD_F", "ADD_V", "SUB_F", "SUB_V", "EQ_F", "EQ_V", "EQ_S", "EQ_E", "EQ_FNC", "NE_F", "NE_V", "NE_S", "NE_E", "NE_FNC", "LE", "GE", "LT", "GT", "INDIRECT", "INDIRECT", "INDIRECT", "INDIRECT", "INDIRECT", "INDIRECT", "ADDRESS", "STORE_F", "STORE_V", "STORE_S", "STORE_ENT", "STORE_FLD", "STORE_FNC", "STOREP_F", "STOREP_V", "STOREP_S", "STOREP_ENT", "STOREP_FLD", "STOREP_FNC", "RETURN", "NOT_F", "NOT_V", "NOT_S", "NOT_ENT", "NOT_FNC", "IF", "IFNOT", "CALL0", "CALL1", "CALL2", "CALL3", "CALL4", "CALL5", "CALL6", "CALL7", "CALL8", "STATE", "GOTO", "AND", "OR", "BITAND", "BITOR"};
finalvert_t _fv[2][8];
auxvert_t av[8];
mtriangle_t *ptriangles;
affinetridesc_t r_affinetridesc;
void *acolormap;
trivertx_t *r_apverts;
mdl_t *pmdl;
vec3_t r_plightvec;
int32_t r_ambientlight;
float r_shadelight;
aliashdr_t *paliashdr;
finalvert_t *pfinalverts;
auxvert_t *pauxverts;
float ziscale;
model_t *pmodel;
vec3_t alias_forward;
vec3_t alias_right;
vec3_t alias_up;
maliasskindesc_t *pskindesc;
int32_t r_amodels_drawn;
int32_t a_skinwidth;
int32_t r_anumverts;
float aliastransform[3][4];
aedge_t aedges[12] = {{0, 1}, {1, 2}, {2, 3}, {3, 0}, {4, 5}, {5, 6}, {6, 7}, {7, 4}, {0, 5}, {1, 4}, {2, 7}, {3, 6}};
float r_avertexnormals[NUMVERTEXNORMALS][3] = {{-0.525731, 0.000000, 0.850651}, {-0.442863, 0.238856, 0.864188}, {-0.295242, 0.000000, 0.955423}, {-0.309017, 0.500000, 0.809017}, {-0.162460, 0.262866, 0.951056}, {0.000000, 0.000000, 1.000000}, {0.000000, 0.850651, 0.525731}, {-0.147621, 0.716567, 0.681718}, {0.147621, 0.716567, 0.681718}, {0.000000, 0.525731, 0.850651}, {0.309017, 0.500000, 0.809017}, {0.525731, 0.000000, 0.850651}, {0.295242, 0.000000, 0.955423}, {0.442863, 0.238856, 0.864188}, {0.162460, 0.262866, 0.951056}, {-0.681718, 0.147621, 0.716567}, {-0.809017, 0.309017, 0.500000}, {-0.587785, 0.425325, 0.688191}, {-0.850651, 0.525731, 0.000000}, {-0.864188, 0.442863, 0.238856}, {-0.716567, 0.681718, 0.147621}, {-0.688191, 0.587785, 0.425325}, {-0.500000, 0.809017, 0.309017}, {-0.238856, 0.864188, 0.442863}, {-0.425325, 0.688191, 0.587785}, {-0.716567, 0.681718, -0.147621}, {-0.500000, 0.809017, -0.309017}, {-0.525731, 0.850651, 0.000000}, {0.000000, 0.850651, -0.525731}, {-0.238856, 0.864188, -0.442863}, {0.000000, 0.955423, -0.295242}, {-0.262866, 0.951056, -0.162460}, {0.000000, 1.000000, 0.000000}, {0.000000, 0.955423, 0.295242}, {-0.262866, 0.951056, 0.162460}, {0.238856, 0.864188, 0.442863}, {0.262866, 0.951056, 0.162460}, {0.500000, 0.809017, 0.309017}, {0.238856, 0.864188, -0.442863}, {0.262866, 0.951056, -0.162460}, {0.500000, 0.809017, -0.309017}, {0.850651, 0.525731, 0.000000}, {0.716567, 0.681718, 0.147621}, {0.716567, 0.681718, -0.147621}, {0.525731, 0.850651, 0.000000}, {0.425325, 0.688191, 0.587785}, {0.864188, 0.442863, 0.238856}, {0.688191, 0.587785, 0.425325}, {0.809017, 0.309017, 0.500000}, {0.681718, 0.147621, 0.716567}, {0.587785, 0.425325, 0.688191}, {0.955423, 0.295242, 0.000000}, {1.000000, 0.000000, 0.000000}, {0.951056, 0.162460, 0.262866}, {0.850651, -0.525731, 0.000000}, {0.955423, -0.295242, 0.000000}, {0.864188, -0.442863, 0.238856}, {0.951056, -0.162460, 0.262866}, {0.809017, -0.309017, 0.500000}, {0.681718, -0.147621, 0.716567}, {0.850651, 0.000000, 0.525731}, {0.864188, 0.442863, -0.238856}, {0.809017, 0.309017, -0.500000}, {0.951056, 0.162460, -0.262866}, {0.525731, 0.000000, -0.850651}, {0.681718, 0.147621, -0.716567}, {0.681718, -0.147621, -0.716567}, {0.850651, 0.000000, -0.525731}, {0.809017, -0.309017, -0.500000}, {0.864188, -0.442863, -0.238856}, {0.951056, -0.162460, -0.262866}, {0.147621, 0.716567, -0.681718}, {0.309017, 0.500000, -0.809017}, {0.425325, 0.688191, -0.587785}, {0.442863, 0.238856, -0.864188}, {0.587785, 0.425325, -0.688191}, {0.688191, 0.587785, -0.425325}, {-0.147621, 0.716567, -0.681718}, {-0.309017, 0.500000, -0.809017}, {0.000000, 0.525731, -0.850651}, {-0.525731, 0.000000, -0.850651}, {-0.442863, 0.238856, -0.864188}, {-0.295242, 0.000000, -0.955423}, {-0.162460, 0.262866, -0.951056}, {0.000000, 0.000000, -1.000000}, {0.295242, 0.000000, -0.955423}, {0.162460, 0.262866, -0.951056}, {-0.442863, -0.238856, -0.864188}, {-0.309017, -0.500000, -0.809017}, {-0.162460, -0.262866, -0.951056}, {0.000000, -0.850651, -0.525731}, {-0.147621, -0.716567, -0.681718}, {0.147621, -0.716567, -0.681718}, {0.000000, -0.525731, -0.850651}, {0.309017, -0.500000, -0.809017}, {0.442863, -0.238856, -0.864188}, {0.162460, -0.262866, -0.951056}, {0.238856, -0.864188, -0.442863}, {0.500000, -0.809017, -0.309017}, {0.425325, -0.688191, -0.587785}, {0.716567, -0.681718, -0.147621}, {0.688191, -0.587785, -0.425325}, {0.587785, -0.425325, -0.688191}, {0.000000, -0.955423, -0.295242}, {0.000000, -1.000000, 0.000000}, {0.262866, -0.951056, -0.162460}, {0.000000, -0.850651, 0.525731}, {0.000000, -0.955423, 0.295242}, {0.238856, -0.864188, 0.442863}, {0.262866, -0.951056, 0.162460}, {0.500000, -0.809017, 0.309017}, {0.716567, -0.681718, 0.147621}, {0.525731, -0.850651, 0.000000}, {-0.238856, -0.864188, -0.442863}, {-0.500000, -0.809017, -0.309017}, {-0.262866, -0.951056, -0.162460}, {-0.850651, -0.525731, 0.000000}, {-0.716567, -0.681718, -0.147621}, {-0.716567, -0.681718, 0.147621}, {-0.525731, -0.850651, 0.000000}, {-0.500000, -0.809017, 0.309017}, {-0.238856, -0.864188, 0.442863}, {-0.262866, -0.951056, 0.162460}, {-0.864188, -0.442863, 0.238856}, {-0.809017, -0.309017, 0.500000}, {-0.688191, -0.587785, 0.425325}, {-0.681718, -0.147621, 0.716567}, {-0.442863, -0.238856, 0.864188}, {-0.587785, -0.425325, 0.688191}, {-0.309017, -0.500000, 0.809017}, {-0.147621, -0.716567, 0.681718}, {-0.425325, -0.688191, 0.587785}, {-0.162460, -0.262866, 0.951056}, {0.442863, -0.238856, 0.864188}, {0.162460, -0.262866, 0.951056}, {0.309017, -0.500000, 0.809017}, {0.147621, -0.716567, 0.681718}, {0.000000, -0.525731, 0.850651}, {0.425325, -0.688191, 0.587785}, {0.587785, -0.425325, 0.688191}, {0.688191, -0.587785, 0.425325}, {-0.955423, 0.295242, 0.000000}, {-0.951056, 0.162460, 0.262866}, {-1.000000, 0.000000, 0.000000}, {-0.850651, 0.000000, 0.525731}, {-0.955423, -0.295242, 0.000000}, {-0.951056, -0.162460, 0.262866}, {-0.864188, 0.442863, -0.238856}, {-0.951056, 0.162460, -0.262866}, {-0.809017, 0.309017, -0.500000}, {-0.864188, -0.442863, -0.238856}, {-0.951056, -0.162460, -0.262866}, {-0.809017, -0.309017, -0.500000}, {-0.681718, 0.147621, -0.716567}, {-0.681718, -0.147621, -0.716567}, {-0.850651, 0.000000, -0.525731}, {-0.688191, 0.587785, -0.425325}, {-0.587785, 0.425325, -0.688191}, {-0.425325, 0.688191, -0.587785}, {-0.425325, -0.688191, -0.587785}, {-0.587785, -0.425325, -0.688191}, {-0.688191, -0.587785, -0.425325}};
bool insubmodel;
entity_t *currententity;
vec3_t modelorg;
vec3_t base_modelorg;
vec3_t r_entorigin;
float entity_rotation[3][3];
vec3_t r_worldmodelorg;
int32_t r_currentbkey;
mvertex_t *pbverts;
bedge_t *pbedges;
int32_t numbverts;
int32_t numbedges;
mvertex_t *pfrontenter;
mvertex_t *pfrontexit;
bool makeclippededge;
uint32_t cacheoffset;
int32_t c_faceclip;
zpointdesc_t r_zpointdesc;
polydesc_t r_polydesc;
clipplane_t *entity_clipplanes;
clipplane_t view_clipplanes[4];
clipplane_t world_clipplanes[16];
medge_t *r_pedge;
bool r_leftclipped;
bool r_rightclipped;
bool makeleftedge;
bool makerightedge;
bool r_nearzionly;
int32_t sintable[SIN_BUFFER_SIZE];
int32_t intsintable[SIN_BUFFER_SIZE];
mvertex_t r_leftenter;
mvertex_t r_leftexit;
mvertex_t r_rightenter;
mvertex_t r_rightexit;
int32_t r_emitted;
float r_nearzi;
float r_u1;
float r_v1;
float r_lzi1;
int32_t r_ceilv1;
bool r_lastvertvalid;
edge_t *auxedges;
edge_t *r_edges;
edge_t *edge_p;
edge_t *edge_max;
surf_t *surfaces;
surf_t *surface_p;
surf_t *surf_max;
edge_t *newedges[MAXHEIGHT];
edge_t *removeedges[MAXHEIGHT];
espan_t *span_p;
espan_t *max_span_p;
int32_t r_currentkey;
int32_t current_iv;
int32_t edge_head_u_shift20;
int32_t edge_tail_u_shift20;
void (*pdrawfunc)(void);
edge_t edge_head;
edge_t edge_tail;
edge_t edge_aftertail;
edge_t edge_sentinel;
float fv;
mnode_t *r_pefragtopnode;
efrag_t **lastlink;
vec3_t r_emins;
vec3_t r_emaxs;
entity_t *r_addent;
int32_t r_dlightframecount;
void *colormap;
vec3_t viewlightvec;
alight_t r_viewlighting = {128, 192, viewlightvec};
float r_time1;
int32_t r_numallocatededges;
bool r_drawpolys;
bool r_drawculledpolys;
bool r_worldpolysbacktofront;
bool r_recursiveaffinetriangles = 1;
int32_t r_pixbytes = 1;
float r_aliasuvscale = 1.0;
int32_t r_outofsurfaces;
int32_t r_outofedges;
bool r_dowarp;
bool r_dowarpold;
bool r_viewchanged;
int32_t numbtofpolys;
btofpoly_t *pbtofpolys;
mvertex_t *r_pcurrentvertbase;
int32_t c_surf;
int32_t r_maxsurfsseen;
int32_t r_maxedgesseen;
int32_t r_cnumsurfs;
bool r_surfsonstack;
int32_t r_clipflags;
uint8_t *r_warpbuffer;
uint8_t *r_stack_start;
bool r_fov_greater_than_90;
vec3_t vup;
vec3_t base_vup;
vec3_t vpn;
vec3_t base_vpn;
vec3_t vright;
vec3_t base_vright;
vec3_t r_origin;
refdef_t r_refdef;
float xcenter;
float ycenter;
float xscale;
float yscale;
float xscaleinv;
float yscaleinv;
float xscaleshrink;
float yscaleshrink;
float aliasxscale;
float aliasyscale;
float aliasxcenter;
float aliasycenter;
float pixelAspect;
float screenAspect;
float verticalFieldOfView;
float xOrigin;
float yOrigin;
mplane_t screenedge[4];
int32_t r_framecount = 1;
int32_t r_visframecount;
int32_t d_spanpixcount;
int32_t r_polycount;
int32_t r_drawnpolycount;
int32_t r_wholepolycount;
int32_t *pfrustum_indexes[4];
int32_t r_frustum_indexes[4 * 6];
int32_t reinit_surfcache = 1;
mleaf_t *r_viewleaf;
mleaf_t *r_oldviewleaf;
texture_t *r_notexture_mip;
float r_aliastransition;
float r_resfudge;
int32_t d_lightstylevalue[256];
float dp_time1;
float dp_time2;
float db_time1;
float db_time2;
float rw_time1;
float rw_time2;
float se_time1;
float se_time2;
float de_time1;
float de_time2;
float dv_time1;
float dv_time2;
cvar_t r_draworder = {"r_draworder", "0"};
cvar_t r_speeds = {"r_speeds", "0"};
cvar_t r_timegraph = {"r_timegraph", "0"};
cvar_t r_graphheight = {"r_graphheight", "10"};
cvar_t r_clearcolor = {"r_clearcolor", "2"};
cvar_t r_waterwarp = {"r_waterwarp", "1"};
cvar_t r_fullbright = {"r_fullbright", "0"};
cvar_t r_drawentities = {"r_drawentities", "1"};
cvar_t r_drawviewmodel = {"r_drawviewmodel", "1"};
cvar_t r_aliasstats = {"r_polymodelstats", "0"};
cvar_t r_dspeeds = {"r_dspeeds", "0"};
cvar_t r_drawflat = {"r_drawflat", "0"};
cvar_t r_ambient = {"r_ambient", "0"};
cvar_t r_reportsurfout = {"r_reportsurfout", "0"};
cvar_t r_maxsurfs = {"r_maxsurfs", "0"};
cvar_t r_numsurfs = {"r_numsurfs", "0"};
cvar_t r_reportedgeout = {"r_reportedgeout", "0"};
cvar_t r_maxedges = {"r_maxedges", "0"};
cvar_t r_numedges = {"r_numedges", "0"};
cvar_t r_aliastransbase = {"r_aliastransbase", "200"};
cvar_t r_aliastransadj = {"r_aliastransadj", "100"};
int32_t ramp1[8] = {0x6f, 0x6d, 0x6b, 0x69, 0x67, 0x65, 0x63, 0x61};
int32_t ramp2[8] = {0x6f, 0x6e, 0x6d, 0x6c, 0x6b, 0x6a, 0x68, 0x66};
int32_t ramp3[8] = {0x6d, 0x6b, 6, 5, 4, 3};
particle_t *active_particles;
particle_t *free_particles;
particle_t *particles;
int32_t r_numparticles;
vec3_t r_pright;
vec3_t r_pup;
vec3_t r_ppn;
vec3_t avelocities[NUMVERTEXNORMALS];
float beamlength = 16;
vec3_t avelocity = {23, 7, 3};
float partstep = 0.01;
float timescale = 0.01;
int32_t iskyspeed = 8;
int32_t iskyspeed2 = 2;
float skyspeed;
float skyspeed2;
float skytime;
uint8_t *r_skysource;
int32_t r_skymade;
int32_t r_skydirect;
uint8_t bottomsky[128 * 131];
uint8_t bottommask[128 * 131];
uint8_t newsky[128 * 256];
int32_t clip_current;
vec5_t clip_verts[2][MAXWORKINGVERTS];
int32_t sprite_width;
spritedesc_t r_spritedesc;
drawsurf_t r_drawsurf;
int32_t lightleft;
int32_t sourcesstep;
int32_t blocksize;
int32_t sourcetstep;
int32_t lightdelta;
int32_t lightdeltastep;
int32_t lightright;
int32_t lightleftstep;
int32_t lightrightstep;
int32_t blockdivshift;
uint32_t blockdivmask;
void *prowdestbase;
unsigned char *pbasesource;
int32_t surfrowbytes;
uint32_t *r_lightptr;
int32_t r_stepback;
int32_t r_lightwidth;
int32_t r_numhblocks;
int32_t r_numvblocks;
unsigned char *r_source;
unsigned char *r_sourcemax;
void (*surfmiptable[4])(void) = {R_DrawSurfaceBlock8_mip0, R_DrawSurfaceBlock8_mip1, R_DrawSurfaceBlock8_mip2, R_DrawSurfaceBlock8_mip3};
uint32_t blocklights[18 * 18];
int32_t r_bmodelactive;
int32_t sb_updates;
qpic_t *sb_nums[2][11];
qpic_t *sb_colon;
qpic_t *sb_slash;
qpic_t *sb_ibar;
qpic_t *sb_sbar;
qpic_t *sb_scorebar;
qpic_t *sb_weapons[7][8];
qpic_t *sb_ammo[4];
qpic_t *sb_sigil[4];
qpic_t *sb_armor[3];
qpic_t *sb_items[32];
qpic_t *sb_faces[7][2];
qpic_t *sb_face_invis;
qpic_t *sb_face_quad;
qpic_t *sb_face_invuln;
qpic_t *sb_face_invis_invuln;
bool sb_showscores;
int32_t sb_lines;
qpic_t *rsb_invbar[2];
qpic_t *rsb_weapons[5];
qpic_t *rsb_items[2];
qpic_t *rsb_ammo[3];
qpic_t *rsb_teambord;
qpic_t *hsb_weapons[7][5];
int32_t hipweapons[4] = {HIT_LASER_CANNON_BIT, HIT_MJOLNIR_BIT, 4, HIT_PROXIMITY_GUN_BIT};
qpic_t *hsb_items[2];
int32_t fragsort[MAX_SCOREBOARD];
char scoreboardtext[MAX_SCOREBOARD][20];
int32_t scoreboardtop[MAX_SCOREBOARD];
int32_t scoreboardbottom[MAX_SCOREBOARD];
int32_t scoreboardcount[MAX_SCOREBOARD];
int32_t scoreboardlines;
int32_t scr_copytop;
int32_t scr_copyeverything;
float scr_con_current;
float scr_conlines;
float oldscreensize;
float oldfov;
cvar_t scr_viewsize = {"viewsize", "100", 1};
cvar_t scr_fov = {"fov", "90"};
cvar_t scr_conspeed = {"scr_conspeed", "300"};
cvar_t scr_centertime = {"scr_centertime", "2"};
cvar_t scr_showram = {"showram", "1"};
cvar_t scr_showturtle = {"showturtle", "0"};
cvar_t scr_showpause = {"showpause", "1"};
cvar_t scr_printspeed = {"scr_printspeed", "8"};
bool scr_initialized;
qpic_t *scr_ram;
qpic_t *scr_net;
qpic_t *scr_turtle;
int32_t scr_fullupdate;
int32_t clearconsole;
int32_t clearnotify;
viddef_t vid;
vrect_t *pconupdate;
vrect_t scr_vrect;
bool scr_disabled_for_loading;
bool scr_drawloading;
float scr_disabled_time;
bool scr_skipupdate;
bool block_drawing;
char scr_centerstring[1024];
float scr_centertime_start;
float scr_centertime_off;
int32_t scr_center_lines;
int32_t scr_erase_lines;
int32_t scr_erase_center;
char *scr_notifystring;
bool scr_drawdialog;
channel_t channels[MAX_CHANNELS];
int32_t total_channels;
int32_t snd_blocked = 0;
bool snd_ambient = 1;
bool snd_initialized = 0;
volatile dma_t *shm = 0;
volatile dma_t sn;
vec3_t listener_origin;
vec3_t listener_forward;
vec3_t listener_right;
vec3_t listener_up;
vec_t sound_nominal_clip_dist = 1000.0;
int32_t soundtime;
int32_t paintedtime;
sfx_t *known_sfx;
int32_t num_sfx;
sfx_t *ambient_sfx[NUM_AMBIENTS];
int32_t desired_speed = 44100;
int32_t desired_bits = 16;
int32_t sound_started = 0;
cvar_t bgmvolume = {"bgmvolume", "1", 1};
cvar_t volume = {"volume", "0.7", 1};
cvar_t nosound = {"nosound", "0"};
cvar_t precache = {"precache", "1"};
cvar_t loadas8bit = {"loadas8bit", "0"};
cvar_t bgmbuffer = {"bgmbuffer", "4096"};
cvar_t ambient_level = {"ambient_level", "0.3"};
cvar_t ambient_fade = {"ambient_fade", "100"};
cvar_t snd_noextraupdate = {"snd_noextraupdate", "0"};
cvar_t snd_show = {"snd_show", "0"};
cvar_t _snd_mixahead = {"_snd_mixahead", "0.1", 1};
bool fakedma = 0;
int32_t fakedma_updates = 15;
int32_t cache_full_cycle;
uint8_t *data_p;
uint8_t *iff_end;
uint8_t *last_chunk;
uint8_t *iff_data;
int32_t iff_chunk_len;
portable_samplepair_t paintbuffer[PAINTBUFFER_SIZE];
int32_t snd_scaletable[32][256];
int32_t *snd_p;
int32_t snd_linear_count;
int32_t snd_vol;
int16_t *snd_out;
dma_t the_shm;
int32_t snd_inited;
AudioStream stream;
void *mixbuffer = 0;
server_t sv;
server_static_t svs;
char localmodels[MAX_MODELS][5];
int32_t fatbytes;
uint8_t fatpvs[MAX_MAP_LEAFS / 8];
int32_t c_yes;
int32_t c_no;
cvar_t sv_friction = {"sv_friction", "4", 0, 1};
cvar_t sv_stopspeed = {"sv_stopspeed", "100"};
cvar_t sv_gravity = {"sv_gravity", "800", 0, 1};
cvar_t sv_maxvelocity = {"sv_maxvelocity", "2000"};
cvar_t sv_nostep = {"sv_nostep", "0"};
edict_t *sv_player;
cvar_t sv_edgefriction = {"edgefriction", "2"};
vec3_t forward;
vec3_t right;
vec3_t up;
vec3_t wishdir;
float wishspeed;
float *angles;
float *origin;
float *velocity;
bool onground;
usercmd_t cmd;
cvar_t sv_idealpitchscale = {"sv_idealpitchscale", "0.8"};
cvar_t sv_maxspeed = {"sv_maxspeed", "320", 0, 1};
cvar_t sv_accelerate = {"sv_accelerate", "10"};
bool isDedicated;
char *basedir = ".";
cvar_t sys_nostdout = {"sys_nostdout", "0"};
FILE *sys_handles[MAX_HANDLES];
uint16_t d_8to16table[256];
Image image8bpp = {0};
Image image32bpp = {0};
Texture2D screenTexture = {0};
Color palette[256];
bool mouse_avail;
float mouse_x;
float mouse_y;
int32_t mouse_oldbuttonstate = 0;
void (*vid_menudrawfn)(void) = 0;
void (*vid_menukeyfn)(int32_t key) = 0;
cvar_t lcd_x = {"lcd_x", "0"};
cvar_t lcd_yaw = {"lcd_yaw", "0"};
cvar_t scr_ofsx = {"scr_ofsx", "0", 0};
cvar_t scr_ofsy = {"scr_ofsy", "0", 0};
cvar_t scr_ofsz = {"scr_ofsz", "0", 0};
cvar_t cl_rollspeed = {"cl_rollspeed", "200"};
cvar_t cl_rollangle = {"cl_rollangle", "2.0"};
cvar_t cl_bob = {"cl_bob", "0.02", 0};
cvar_t cl_bobcycle = {"cl_bobcycle", "0.6", 0};
cvar_t cl_bobup = {"cl_bobup", "0.5", 0};
cvar_t v_kicktime = {"v_kicktime", "0.5", 0};
cvar_t v_kickroll = {"v_kickroll", "0.6", 0};
cvar_t v_kickpitch = {"v_kickpitch", "0.6", 0};
cvar_t v_iyaw_cycle = {"v_iyaw_cycle", "2", 0};
cvar_t v_iroll_cycle = {"v_iroll_cycle", "0.5", 0};
cvar_t v_ipitch_cycle = {"v_ipitch_cycle", "1", 0};
cvar_t v_iyaw_level = {"v_iyaw_level", "0.3", 0};
cvar_t v_iroll_level = {"v_iroll_level", "0.1", 0};
cvar_t v_ipitch_level = {"v_ipitch_level", "0.3", 0};
cvar_t v_idlescale = {"v_idlescale", "0", 0};
cvar_t crosshair = {"crosshair", "0", 1};
cvar_t cl_crossx = {"cl_crossx", "0", 0};
cvar_t cl_crossy = {"cl_crossy", "0", 0};
cvar_t gl_cshiftpercent = {"gl_cshiftpercent", "100", 0};
float v_dmg_time;
float v_dmg_roll;
float v_dmg_pitch;
cvar_t v_centermove = {"v_centermove", "0.15", 0};
cvar_t v_centerspeed = {"v_centerspeed", "500"};
cshift_t cshift_empty = {{130, 80, 50}, 0};
cshift_t cshift_water = {{130, 80, 50}, 128};
cshift_t cshift_slime = {{0, 25, 5}, 150};
cshift_t cshift_lava = {{255, 80, 0}, 150};
cvar_t v_gamma = {"gamma", "1", 1};
uint8_t gammatable[256];
int32_t wad_numlumps;
lumpinfo_t *wad_lumps;
uint8_t *wad_base;
hull_t box_hull;
dclipnode_t box_clipnodes[6];
mplane_t box_planes[6];
areanode_t sv_areanodes[AREA_NODES];
int32_t sv_numareanodes;
memzone_t *mainzone;
uint8_t *hunk_base;
int32_t hunk_size;
int32_t hunk_low_used;
int32_t hunk_high_used;
bool hunk_tempactive;
int32_t hunk_tempmark;
cache_system_t cache_head;
int32_t CDAudio_Init(void)
{
return 0;
}
void CDAudio_Play(uint8_t track, bool looping)
{
if (IsMusicValid(music))
{
UnloadMusicStream(music);
}
char filename[256];
snprintf(filename, sizeof(filename), "id1/music/%d.mp3", track);
music = LoadMusicStream(filename);
if (!IsMusicValid(music))
{
Con_Printf("Could not load music file: %s\n", filename);
return;
}
music.looping = looping;
PlayMusicStream(music);
}
void CDAudio_Stop(void)
{
if (IsMusicValid(music))
{
StopMusicStream(music);
UnloadMusicStream(music);
music = (Music){0};
}
}
void CDAudio_Pause(void)
{
if (IsMusicValid(music))
{
PauseMusicStream(music);
}
}
void CDAudio_Resume(void)
{
if (IsMusicValid(music))
{
ResumeMusicStream(music);
}
}
void CDAudio_Shutdown(void)
{
CDAudio_Stop();
}
void CDAudio_Update(void)
{
if (IsMusicValid(music))
{
UpdateMusicStream(music);
SetMusicVolume(music, bgmvolume.value);
}
}
void Chase_Init(void)
{
Cvar_RegisterVariable(&chase_back);
Cvar_RegisterVariable(&chase_up);
Cvar_RegisterVariable(&chase_right);
Cvar_RegisterVariable(&chase_active);
}
void Chase_Reset(void)
{
}
static void TraceLine(vec3_t start, vec3_t end, vec3_t impact)
{
trace_t trace;
memset(&trace, 0, sizeof(trace));
SV_RecursiveHullCheck(cl.worldmodel->hulls, 0, 0, 1, start, end, &trace);
VectorCopy(trace.endpos, impact);
}
void Chase_Update(void)
{
int32_t i;
float dist;
vec3_t forward;
vec3_t up;
vec3_t right;
vec3_t dest;
vec3_t stop;
AngleVectors(cl.viewangles, forward, right, up);
for (i = 0; i < 3; i++)
chase_dest[i] = (r_refdef.vieworg[i] - (forward[i] * chase_back.value)) - (right[i] * chase_right.value);
chase_dest[2] = r_refdef.vieworg[2] + chase_up.value;
VectorMA(r_refdef.vieworg, 4096, forward, dest);
TraceLine(r_refdef.vieworg, dest, stop);
VectorSubtract(stop, r_refdef.vieworg, stop);
dist = DotProduct(stop, forward);
if (dist < 1)
dist = 1;
r_refdef.viewangles[PITCH] = ((-atan(stop[2] / dist)) / M_PI) * 180;
VectorCopy(chase_dest, r_refdef.vieworg);
}
void CL_StopPlayback(void)
{
if (!cls.demoplayback)
return;
fclose(cls.demofile);
cls.demoplayback = 0;
cls.demofile = 0;
cls.state = ca_disconnected;
if (cls.timedemo)
CL_FinishTimeDemo();
}
static void CL_WriteDemoMessage(void)
{
int32_t len;
int32_t i;
float f;
len = net_message.cursize;
fwrite(&len, 4, 1, cls.demofile);
for (i = 0; i < 3; i++)
{
f = cl.viewangles[i];
fwrite(&f, 4, 1, cls.demofile);
}
fwrite(net_message.data, net_message.cursize, 1, cls.demofile);
fflush(cls.demofile);
}
int32_t CL_GetMessage(void)
{
int32_t r;
int32_t i;
float f;
if (cls.demoplayback)
{
if (cls.signon == SIGNONS)
{
if (cls.timedemo)
{
if (host_framecount == cls.td_lastframe)
return 0;
cls.td_lastframe = host_framecount;
if (host_framecount == (cls.td_startframe + 1))
cls.td_starttime = realtime;
}
else
if (cl.time <= cl.mtime[0])
{
return 0;
}
}
fread(&net_message.cursize, 4, 1, cls.demofile);
VectorCopy(cl.mviewangles[0], cl.mviewangles[1]);
for (i = 0; i < 3; i++)
{
r = fread(&f, 4, 1, cls.demofile);
cl.mviewangles[0][i] = f;
}
net_message.cursize = net_message.cursize;
if (net_message.cursize > MAX_MSGLEN)
Sys_Error("Demo message > MAX_MSGLEN");
r = fread(net_message.data, net_message.cursize, 1, cls.demofile);
if (r != 1)
{
CL_StopPlayback();
return 0;
}
return 1;
}
while (1)
{
r = NET_GetMessage(cls.netcon);
if ((r != 1) && (r != 2))
return r;
if ((net_message.cursize == 1) && (net_message.data[0] == svc_nop))
Con_Printf("<-- server to client keepalive\n");
else
break;
}
if (cls.demorecording)
CL_WriteDemoMessage();
return r;
}
void CL_Stop_f(void)
{
if (cmd_source != src_command)
return;
if (!cls.demorecording)
{
Con_Printf("Not recording a demo.\n");
return;
}
SZ_Clear(&net_message);
MSG_WriteByte(&net_message, svc_disconnect);
CL_WriteDemoMessage();
fclose(cls.demofile);
cls.demofile = 0;
cls.demorecording = 0;
Con_Printf("Completed demo\n");
}
void CL_Record_f(void)
{
int32_t c;
char name[MAX_OSPATH];
int32_t track;
if (cmd_source != src_command)
return;
c = Cmd_Argc();
if (((c != 2) && (c != 3)) && (c != 4))
{
Con_Printf("record <demoname> [<map> [cd track]]\n");
return;
}
if (strstr(Cmd_Argv(1), ".."))
{
Con_Printf("Relative pathnames are not allowed.\n");
return;
}
if ((c == 2) && (cls.state == ca_connected))
{
Con_Printf("Can not record - already connected to server\nClient demo recording must be started before connecting\n");
return;
}
if (c == 4)
{
track = atoi(Cmd_Argv(3));
Con_Printf("Forcing CD track to %i\n", cls.forcetrack);
}
else
track = -1;
sprintf(name, "%s/%s", com_gamedir, Cmd_Argv(1));
if (c > 2)
Cmd_ExecuteString(va("map %s", Cmd_Argv(2)), src_command);
COM_DefaultExtension(name, ".dem");
Con_Printf("recording to %s.\n", name);
cls.demofile = fopen(name, "wb");
if (!cls.demofile)
{
Con_Printf("ERROR: couldn't open.\n");
return;
}
cls.forcetrack = track;
fprintf(cls.demofile, "%i\n", cls.forcetrack);
cls.demorecording = 1;
}
void CL_PlayDemo_f(void)
{
char name[256];
int32_t c;
bool neg = 0;
if (cmd_source != src_command)
return;
if (Cmd_Argc() != 2)
{
Con_Printf("play <demoname> : plays a demo\n");
return;
}
CL_Disconnect();
strcpy(name, Cmd_Argv(1));
COM_DefaultExtension(name, ".dem");
Con_Printf("Playing demo from %s.\n", name);
COM_FOpenFile(name, &cls.demofile);
if (!cls.demofile)
{
Con_Printf("ERROR: couldn't open.\n");
cls.demonum = -1;
return;
}
cls.demoplayback = 1;
cls.state = ca_connected;
cls.forcetrack = 0;
while ((c = getc(cls.demofile)) != '\n')
if (c == '-')
neg = 1;
else
cls.forcetrack = (cls.forcetrack * 10) + (c - '0');
if (neg)
cls.forcetrack = -cls.forcetrack;
}
static void CL_FinishTimeDemo(void)
{
int32_t frames;
float time;
cls.timedemo = 0;
frames = (host_framecount - cls.td_startframe) - 1;
time = realtime - cls.td_starttime;
if (!time)
time = 1;
Con_Printf("%i frames %5.1f seconds %5.1f fps\n", frames, time, frames / time);
Sys_Quit();
}
void CL_TimeDemo_f(void)
{
if (cmd_source != src_command)
return;
if (Cmd_Argc() != 2)
{
Con_Printf("timedemo <demoname> : gets demo speeds\n");
return;
}
CL_PlayDemo_f();
cls.timedemo = 1;
cls.td_startframe = host_framecount;
cls.td_lastframe = -1;
}
static void KeyDown(kbutton_t *b)
{
int32_t k;
char *c;
c = Cmd_Argv(1);
if (c[0])
k = atoi(c);
else
k = -1;
if ((k == b->down[0]) || (k == b->down[1]))
return;
if (!b->down[0])
b->down[0] = k;
else
if (!b->down[1])
b->down[1] = k;
else
{
Con_Printf("Three keys down for a button!\n");
return;
}
if (b->state & 1)
return;
b->state |= 1 + 2;
}
static void KeyUp(kbutton_t *b)
{
int32_t k;
char *c;
c = Cmd_Argv(1);
if (c[0])
k = atoi(c);
else
{
b->down[0] = (b->down[1] = 0);
b->state = 4;
return;
}
if (b->down[0] == k)
b->down[0] = 0;
else
if (b->down[1] == k)
b->down[1] = 0;
else
return;
if (b->down[0] || b->down[1])
return;
if (!(b->state & 1))
return;
b->state &= ~1;
b->state |= 4;
}
static void IN_KLookDown(void)
{
KeyDown(&in_klook);
}
static void IN_KLookUp(void)
{
KeyUp(&in_klook);
}
static void IN_MLookDown(void)
{
KeyDown(&in_mlook);
}
static void IN_MLookUp(void)
{
KeyUp(&in_mlook);
if ((!(in_mlook.state & 1)) && lookspring.value)
V_StartPitchDrift();
}
static void IN_UpDown(void)
{
KeyDown(&in_up);
}
static void IN_UpUp(void)
{
KeyUp(&in_up);
}
static void IN_DownDown(void)
{
KeyDown(&in_down);
}
static void IN_DownUp(void)
{
KeyUp(&in_down);
}
static void IN_LeftDown(void)
{
KeyDown(&in_left);
}
static void IN_LeftUp(void)
{
KeyUp(&in_left);
}
static void IN_RightDown(void)
{
KeyDown(&in_right);
}
static void IN_RightUp(void)
{
KeyUp(&in_right);
}
static void IN_ForwardDown(void)
{
KeyDown(&in_forward);
}
static void IN_ForwardUp(void)
{
KeyUp(&in_forward);
}
static void IN_BackDown(void)
{
KeyDown(&in_back);
}
static void IN_BackUp(void)
{
KeyUp(&in_back);
}
static void IN_LookupDown(void)
{
KeyDown(&in_lookup);
}
static void IN_LookupUp(void)
{
KeyUp(&in_lookup);
}
static void IN_LookdownDown(void)
{
KeyDown(&in_lookdown);
}
static void IN_LookdownUp(void)
{
KeyUp(&in_lookdown);
}
static void IN_MoveleftDown(void)
{
KeyDown(&in_moveleft);
}
static void IN_MoveleftUp(void)
{
KeyUp(&in_moveleft);
}
static void IN_MoverightDown(void)
{
KeyDown(&in_moveright);
}
static void IN_MoverightUp(void)
{
KeyUp(&in_moveright);
}
static void IN_SpeedDown(void)
{
KeyDown(&in_speed);
}
static void IN_SpeedUp(void)
{
KeyUp(&in_speed);
}
static void IN_StrafeDown(void)
{
KeyDown(&in_strafe);
}
static void IN_StrafeUp(void)
{
KeyUp(&in_strafe);
}
static void IN_AttackDown(void)
{
KeyDown(&in_attack);
}
static void IN_AttackUp(void)
{
KeyUp(&in_attack);
}
static void IN_UseDown(void)
{
KeyDown(&in_use);
}
static void IN_UseUp(void)
{
KeyUp(&in_use);
}
static void IN_JumpDown(void)
{
KeyDown(&in_jump);
}
static void IN_JumpUp(void)
{
KeyUp(&in_jump);
}
static void IN_Impulse(void)
{
in_impulse = (int32_t) strtol(Cmd_Argv(1), 0, 0);
}
float CL_KeyState(kbutton_t *key)
{
float val;
bool impulsedown;
bool impulseup;
bool down;
impulsedown = key->state & 2;
impulseup = key->state & 4;
down = key->state & 1;
val = 0;
if (impulsedown && (!impulseup))
if (down)
val = 0.5;
else
val = 0;
if (impulseup && (!impulsedown))
if (down)
val = 0;
else
val = 0;
if ((!impulsedown) && (!impulseup))
if (down)
val = 1.0;
else
val = 0;
if (impulsedown && impulseup)
if (down)
val = 0.75;
else
val = 0.25;
key->state &= 1;
return val;
}
static void CL_AdjustAngles(void)
{
float speed;
float up;
float down;
if (in_speed.state & 1)
speed = host_frametime * cl_anglespeedkey.value;
else
speed = host_frametime;
if (!(in_strafe.state & 1))
{
cl.viewangles[YAW] -= (speed * cl_yawspeed.value) * CL_KeyState(&in_right);
cl.viewangles[YAW] += (speed * cl_yawspeed.value) * CL_KeyState(&in_left);
cl.viewangles[YAW] = anglemod(cl.viewangles[YAW]);
}
if (in_klook.state & 1)
{
V_StopPitchDrift();
cl.viewangles[PITCH] -= (speed * cl_pitchspeed.value) * CL_KeyState(&in_forward);
cl.viewangles[PITCH] += (speed * cl_pitchspeed.value) * CL_KeyState(&in_back);
}
up = CL_KeyState(&in_lookup);
down = CL_KeyState(&in_lookdown);
cl.viewangles[PITCH] -= (speed * cl_pitchspeed.value) * up;
cl.viewangles[PITCH] += (speed * cl_pitchspeed.value) * down;
if (up || down)
V_StopPitchDrift();
if (cl.viewangles[PITCH] > 80)
cl.viewangles[PITCH] = 80;
if (cl.viewangles[PITCH] < (-70))
cl.viewangles[PITCH] = -70;
if (cl.viewangles[ROLL] > 50)
cl.viewangles[ROLL] = 50;
if (cl.viewangles[ROLL] < (-50))
cl.viewangles[ROLL] = -50;
}
void CL_BaseMove(usercmd_t *cmd)
{
if (cls.signon != SIGNONS)
return;
CL_AdjustAngles();
memset(cmd, 0, sizeof(*cmd));
if (in_strafe.state & 1)
{
cmd->sidemove += cl_sidespeed.value * CL_KeyState(&in_right);
cmd->sidemove -= cl_sidespeed.value * CL_KeyState(&in_left);
}
cmd->sidemove += cl_sidespeed.value * CL_KeyState(&in_moveright);
cmd->sidemove -= cl_sidespeed.value * CL_KeyState(&in_moveleft);
cmd->upmove += cl_upspeed.value * CL_KeyState(&in_up);
cmd->upmove -= cl_upspeed.value * CL_KeyState(&in_down);
if (!(in_klook.state & 1))
{
cmd->forwardmove += cl_forwardspeed.value * CL_KeyState(&in_forward);
cmd->forwardmove -= cl_backspeed.value * CL_KeyState(&in_back);
}
if (in_speed.state & 1)
{
cmd->forwardmove *= cl_movespeedkey.value;
cmd->sidemove *= cl_movespeedkey.value;
cmd->upmove *= cl_movespeedkey.value;
}
}
void CL_SendMove(usercmd_t *cmd)
{
int32_t i;
int32_t bits;
sizebuf_t buf;
uint8_t data[128];
buf.maxsize = 128;
buf.cursize = 0;
buf.data = data;
cl.cmd = *cmd;
MSG_WriteByte(&buf, clc_move);
MSG_WriteFloat(&buf, cl.mtime[0]);
for (i = 0; i < 3; i++)
MSG_WriteAngle(&buf, cl.viewangles[i]);
MSG_WriteShort(&buf, cmd->forwardmove);
MSG_WriteShort(&buf, cmd->sidemove);
MSG_WriteShort(&buf, cmd->upmove);
bits = 0;
if (in_attack.state & 3)
bits |= 1;
in_attack.state &= ~2;
if (in_jump.state & 3)
bits |= 2;
in_jump.state &= ~2;
MSG_WriteByte(&buf, bits);
MSG_WriteByte(&buf, in_impulse);
in_impulse = 0;
if (cls.demoplayback)
return;
if ((++cl.movemessages) <= 2)
return;
if (NET_SendUnreliableMessage(cls.netcon, &buf) == (-1))
{
Con_Printf("CL_SendMove: lost server connection\n");
CL_Disconnect();
}
}
void CL_InitInput(void)
{
Cmd_AddCommand("+moveup", IN_UpDown);
Cmd_AddCommand("-moveup", IN_UpUp);
Cmd_AddCommand("+movedown", IN_DownDown);
Cmd_AddCommand("-movedown", IN_DownUp);
Cmd_AddCommand("+left", IN_LeftDown);
Cmd_AddCommand("-left", IN_LeftUp);
Cmd_AddCommand("+right", IN_RightDown);
Cmd_AddCommand("-right", IN_RightUp);
Cmd_AddCommand("+forward", IN_ForwardDown);
Cmd_AddCommand("-forward", IN_ForwardUp);
Cmd_AddCommand("+back", IN_BackDown);
Cmd_AddCommand("-back", IN_BackUp);
Cmd_AddCommand("+lookup", IN_LookupDown);
Cmd_AddCommand("-lookup", IN_LookupUp);
Cmd_AddCommand("+lookdown", IN_LookdownDown);
Cmd_AddCommand("-lookdown", IN_LookdownUp);
Cmd_AddCommand("+strafe", IN_StrafeDown);
Cmd_AddCommand("-strafe", IN_StrafeUp);
Cmd_AddCommand("+moveleft", IN_MoveleftDown);
Cmd_AddCommand("-moveleft", IN_MoveleftUp);
Cmd_AddCommand("+moveright", IN_MoverightDown);
Cmd_AddCommand("-moveright", IN_MoverightUp);
Cmd_AddCommand("+speed", IN_SpeedDown);
Cmd_AddCommand("-speed", IN_SpeedUp);
Cmd_AddCommand("+attack", IN_AttackDown);
Cmd_AddCommand("-attack", IN_AttackUp);
Cmd_AddCommand("+use", IN_UseDown);
Cmd_AddCommand("-use", IN_UseUp);
Cmd_AddCommand("+jump", IN_JumpDown);
Cmd_AddCommand("-jump", IN_JumpUp);
Cmd_AddCommand("impulse", IN_Impulse);
Cmd_AddCommand("+klook", IN_KLookDown);
Cmd_AddCommand("-klook", IN_KLookUp);
Cmd_AddCommand("+mlook", IN_MLookDown);
Cmd_AddCommand("-mlook", IN_MLookUp);
}
void CL_ClearState(void)
{
int32_t i;
if (!sv.active)
Host_ClearMemory();
memset(&cl, 0, sizeof(cl));
SZ_Clear(&cls.message);
memset(cl_efrags, 0, sizeof(cl_efrags));
memset(cl_entities, 0, sizeof(cl_entities));
memset(cl_dlights, 0, sizeof(cl_dlights));
memset(cl_lightstyle, 0, sizeof(cl_lightstyle));
memset(cl_temp_entities, 0, sizeof(cl_temp_entities));
memset(cl_beams, 0, sizeof(cl_beams));
cl.free_efrags = cl_efrags;
for (i = 0; i < (MAX_EFRAGS - 1); i++)
cl.free_efrags[i].entnext = &cl.free_efrags[i + 1];
cl.free_efrags[i].entnext = 0;
}
void CL_Disconnect(void)
{
S_StopAllSounds(1);
if (cls.demoplayback)
CL_StopPlayback();
else
if (cls.state == ca_connected)
{
if (cls.demorecording)
CL_Stop_f();
Con_DPrintf("Sending clc_disconnect\n");
SZ_Clear(&cls.message);
MSG_WriteByte(&cls.message, clc_disconnect);
NET_SendUnreliableMessage(cls.netcon, &cls.message);
SZ_Clear(&cls.message);
NET_Close(cls.netcon);
cls.state = ca_disconnected;
if (sv.active)
Host_ShutdownServer(0);
}
cls.demoplayback = (cls.timedemo = 0);
cls.signon = 0;
}
void CL_Disconnect_f(void)
{
CL_Disconnect();
if (sv.active)
Host_ShutdownServer(0);
}
void CL_EstablishConnection(char *host)
{
if (cls.state == ca_dedicated)
return;
if (cls.demoplayback)
return;
CL_Disconnect();
cls.netcon = NET_Connect(host);
if (!cls.netcon)
Host_Error("CL_Connect: connect failed\n");
Con_DPrintf("CL_EstablishConnection: connected to %s\n", host);
cls.demonum = -1;
cls.state = ca_connected;
cls.signon = 0;
}
void CL_SignonReply(void)
{
char str[8192];
Con_DPrintf("CL_SignonReply: %i\n", cls.signon);
switch (cls.signon)
{
case 1:
MSG_WriteByte(&cls.message, clc_stringcmd);
MSG_WriteString(&cls.message, "prespawn");
break;
case 2:
MSG_WriteByte(&cls.message, clc_stringcmd);
MSG_WriteString(&cls.message, va("name \"%s\"\n", cl_name.string));
MSG_WriteByte(&cls.message, clc_stringcmd);
MSG_WriteString(&cls.message, va("color %i %i\n", ((int32_t) cl_color.value) >> 4, ((int32_t) cl_color.value) & 15));
MSG_WriteByte(&cls.message, clc_stringcmd);
sprintf(str, "spawn %s", cls.spawnparms);
MSG_WriteString(&cls.message, str);
break;
case 3:
MSG_WriteByte(&cls.message, clc_stringcmd);
MSG_WriteString(&cls.message, "begin");
Cache_Report();
break;
case 4:
SCR_EndLoadingPlaque();
break;
}
}
void CL_NextDemo(void)
{
char str[1024];
if (cls.demonum == (-1))
return;
SCR_BeginLoadingPlaque();
if ((!cls.demos[cls.demonum][0]) || (cls.demonum == MAX_DEMOS))
{
cls.demonum = 0;
if (!cls.demos[cls.demonum][0])
{
Con_Printf("No demos listed with startdemos\n");
cls.demonum = -1;
return;
}
}
sprintf(str, "playdemo %s\n", cls.demos[cls.demonum]);
Cbuf_InsertText(str);
cls.demonum++;
}
static void CL_PrintEntities_f(void)
{
entity_t *ent;
int32_t i;
for (i = 0, ent = cl_entities; i < cl.num_entities; i++, ent++)
{
Con_Printf("%3i:", i);
if (!ent->model)
{
Con_Printf("EMPTY\n");
continue;
}
Con_Printf("%s:%2i (%5.1f,%5.1f,%5.1f) [%5.1f %5.1f %5.1f]\n", ent->model->name, ent->frame, ent->origin[0], ent->origin[1], ent->origin[2], ent->angles[0], ent->angles[1], ent->angles[2]);
}
}
static void SetPal(int32_t i)
{
}
dlight_t *CL_AllocDlight(int32_t key)
{
int32_t i;
dlight_t *dl;
if (key)
{
dl = cl_dlights;
for (i = 0; i < MAX_DLIGHTS; i++, dl++)
{
if (dl->key == key)
{
memset(dl, 0, sizeof(*dl));
dl->key = key;
return dl;
}
}
}
dl = cl_dlights;
for (i = 0; i < MAX_DLIGHTS; i++, dl++)
{
if (dl->die < cl.time)
{
memset(dl, 0, sizeof(*dl));
dl->key = key;
return dl;
}
}
dl = &cl_dlights[0];
memset(dl, 0, sizeof(*dl));
dl->key = key;
return dl;
}
void CL_DecayLights(void)
{
int32_t i;
dlight_t *dl;
float time;
time = cl.time - cl.oldtime;
dl = cl_dlights;
for (i = 0; i < MAX_DLIGHTS; i++, dl++)
{
if ((dl->die < cl.time) || (!dl->radius))
continue;
dl->radius -= time * dl->decay;
if (dl->radius < 0)
dl->radius = 0;
}
}
static float CL_LerpPoint(void)
{
float f;
float frac;
f = cl.mtime[0] - cl.mtime[1];
if ((((!f) || cl_nolerp.value) || cls.timedemo) || sv.active)
{
cl.time = cl.mtime[0];
return 1;
}
if (f > 0.1)
{
cl.mtime[1] = cl.mtime[0] - 0.1;
f = 0.1;
}
frac = (cl.time - cl.mtime[1]) / f;
if (frac < 0)
{
if (frac < (-0.01))
{
SetPal(1);
cl.time = cl.mtime[1];
}
frac = 0;
}
else
if (frac > 1)
{
if (frac > 1.01)
{
SetPal(2);
cl.time = cl.mtime[0];
}
frac = 1;
}
else
SetPal(0);
return frac;
}
static void CL_RelinkEntities(void)
{
entity_t *ent;
int32_t i;
int32_t j;
float frac;
float f;
float d;
vec3_t delta;
float bobjrotate;
vec3_t oldorg;
dlight_t *dl;
frac = CL_LerpPoint();
cl_numvisedicts = 0;
for (i = 0; i < 3; i++)
cl.velocity[i] = cl.mvelocity[1][i] + (frac * (cl.mvelocity[0][i] - cl.mvelocity[1][i]));
if (cls.demoplayback)
{
for (j = 0; j < 3; j++)
{
d = cl.mviewangles[0][j] - cl.mviewangles[1][j];
if (d > 180)
d -= 360;
else
if (d < (-180))
d += 360;
cl.viewangles[j] = cl.mviewangles[1][j] + (frac * d);
}
}
bobjrotate = anglemod(100 * cl.time);
for (i = 1, ent = cl_entities + 1; i < cl.num_entities; i++, ent++)
{
if (!ent->model)
{
if (ent->forcelink)
R_RemoveEfrags(ent);
continue;
}
if (ent->msgtime != cl.mtime[0])
{
ent->model = 0;
continue;
}
VectorCopy(ent->origin, oldorg);
if (ent->forcelink)
{
VectorCopy(ent->msg_origins[0], ent->origin);
VectorCopy(ent->msg_angles[0], ent->angles);
}
else
{
f = frac;
for (j = 0; j < 3; j++)
{
delta[j] = ent->msg_origins[0][j] - ent->msg_origins[1][j];
if ((delta[j] > 100) || (delta[j] < (-100)))
f = 1;
}
for (j = 0; j < 3; j++)
{
ent->origin[j] = ent->msg_origins[1][j] + (f * delta[j]);
d = ent->msg_angles[0][j] - ent->msg_angles[1][j];
if (d > 180)
d -= 360;
else
if (d < (-180))
d += 360;
ent->angles[j] = ent->msg_angles[1][j] + (f * d);
}
}
if (ent->model->flags & EF_ROTATE)
ent->angles[1] = bobjrotate;
if (ent->effects & EF_BRIGHTFIELD)
R_EntityParticles(ent);
if (ent->effects & EF_MUZZLEFLASH)
{
vec3_t fv;
vec3_t rv;
vec3_t uv;
dl = CL_AllocDlight(i);
VectorCopy(ent->origin, dl->origin);
dl->origin[2] += 16;
AngleVectors(ent->angles, fv, rv, uv);
VectorMA(dl->origin, 18, fv, dl->origin);
dl->radius = 200 + (rand() & 31);
dl->minlight = 32;
dl->die = cl.time + 0.1;
}
if (ent->effects & EF_BRIGHTLIGHT)
{
dl = CL_AllocDlight(i);
VectorCopy(ent->origin, dl->origin);
dl->origin[2] += 16;
dl->radius = 400 + (rand() & 31);
dl->die = cl.time + 0.001;
}
if (ent->effects & EF_DIMLIGHT)
{
dl = CL_AllocDlight(i);
VectorCopy(ent->origin, dl->origin);
dl->radius = 200 + (rand() & 31);
dl->die = cl.time + 0.001;
}
if (ent->model->flags & EF_GIB)
R_RocketTrail(oldorg, ent->origin, 2);
else
if (ent->model->flags & EF_ZOMGIB)
R_RocketTrail(oldorg, ent->origin, 4);
else
if (ent->model->flags & EF_TRACER)
R_RocketTrail(oldorg, ent->origin, 3);
else
if (ent->model->flags & EF_TRACER2)
R_RocketTrail(oldorg, ent->origin, 5);
else
if (ent->model->flags & EF_ROCKET)
{
R_RocketTrail(oldorg, ent->origin, 0);
dl = CL_AllocDlight(i);
VectorCopy(ent->origin, dl->origin);
dl->radius = 200;
dl->die = cl.time + 0.01;
}
else
if (ent->model->flags & EF_GRENADE)
R_RocketTrail(oldorg, ent->origin, 1);
else
if (ent->model->flags & EF_TRACER3)
R_RocketTrail(oldorg, ent->origin, 6);
ent->forcelink = 0;
if ((i == cl.viewentity) && (!chase_active.value))
continue;
if (cl_numvisedicts < MAX_VISEDICTS)
{
cl_visedicts[cl_numvisedicts] = ent;
cl_numvisedicts++;
}
}
}
int32_t CL_ReadFromServer(void)
{
int32_t ret;
cl.oldtime = cl.time;
cl.time += host_frametime;
do
{
ret = CL_GetMessage();
if (ret == (-1))
Host_Error("CL_ReadFromServer: lost server connection");
if (!ret)
break;
cl.last_received_message = realtime;
CL_ParseServerMessage();
}
while (ret && (cls.state == ca_connected));
if (cl_shownet.value)
Con_Printf("\n");
CL_RelinkEntities();
CL_UpdateTEnts();
return 0;
}
void CL_SendCmd(void)
{
usercmd_t cmd;
if (cls.state != ca_connected)
return;
if (cls.signon == SIGNONS)
{
CL_BaseMove(&cmd);
IN_Move(&cmd);
CL_SendMove(&cmd);
}
if (cls.demoplayback)
{
SZ_Clear(&cls.message);
return;
}
if (!cls.message.cursize)
return;
if (!NET_CanSendMessage(cls.netcon))
{
Con_DPrintf("CL_WriteToServer: can't send\n");
return;
}
if (NET_SendMessage(cls.netcon, &cls.message) == (-1))
Host_Error("CL_WriteToServer: lost server connection");
SZ_Clear(&cls.message);
}
void CL_Init(void)
{
SZ_Alloc(&cls.message, 1024);
CL_InitInput();
CL_InitTEnts();
Cvar_RegisterVariable(&cl_name);
Cvar_RegisterVariable(&cl_color);
Cvar_RegisterVariable(&cl_upspeed);
Cvar_RegisterVariable(&cl_forwardspeed);
Cvar_RegisterVariable(&cl_backspeed);
Cvar_RegisterVariable(&cl_sidespeed);
Cvar_RegisterVariable(&cl_movespeedkey);
Cvar_RegisterVariable(&cl_yawspeed);
Cvar_RegisterVariable(&cl_pitchspeed);
Cvar_RegisterVariable(&cl_anglespeedkey);
Cvar_RegisterVariable(&cl_shownet);
Cvar_RegisterVariable(&cl_nolerp);
Cvar_RegisterVariable(&lookspring);
Cvar_RegisterVariable(&lookstrafe);
Cvar_RegisterVariable(&sensitivity);
Cvar_RegisterVariable(&m_pitch);
Cvar_RegisterVariable(&m_yaw);
Cvar_RegisterVariable(&m_forward);
Cvar_RegisterVariable(&m_side);
Cmd_AddCommand("entities", CL_PrintEntities_f);
Cmd_AddCommand("disconnect", CL_Disconnect_f);
Cmd_AddCommand("record", CL_Record_f);
Cmd_AddCommand("stop", CL_Stop_f);
Cmd_AddCommand("playdemo", CL_PlayDemo_f);
Cmd_AddCommand("timedemo", CL_TimeDemo_f);
}
static entity_t *CL_EntityNum(int32_t num)
{
if (num >= cl.num_entities)
{
if (num >= MAX_EDICTS)
Host_Error("CL_EntityNum: %i is an invalid number", num);
while (cl.num_entities <= num)
{
cl_entities[cl.num_entities].colormap = vid.colormap;
cl.num_entities++;
}
}
return &cl_entities[num];
}
static void CL_ParseStartSoundPacket(void)
{
vec3_t pos;
int32_t channel;
int32_t ent;
int32_t sound_num;
int32_t volume;
int32_t field_mask;
float attenuation;
int32_t i;
field_mask = MSG_ReadByte();
if (field_mask & SND_VOLUME)
volume = MSG_ReadByte();
else
volume = DEFAULT_SOUND_PACKET_VOLUME;
if (field_mask & SND_ATTENUATION)
attenuation = MSG_ReadByte() / 64.0;
else
attenuation = DEFAULT_SOUND_PACKET_ATTENUATION;
channel = MSG_ReadShort();
sound_num = MSG_ReadByte();
ent = channel >> 3;
channel &= 7;
if (ent > MAX_EDICTS)
Host_Error("CL_ParseStartSoundPacket: ent = %i", ent);
for (i = 0; i < 3; i++)
pos[i] = MSG_ReadCoord();
S_StartSound(ent, channel, cl.sound_precache[sound_num], pos, volume / 255.0, attenuation);
}
static void CL_KeepaliveMessage(void)
{
float time;
static float lastmsg;
int32_t ret;
sizebuf_t old;
uint8_t olddata[8192];
if (sv.active)
return;
if (cls.demoplayback)
return;
old = net_message;
memcpy(olddata, net_message.data, net_message.cursize);
do
{
ret = CL_GetMessage();
switch (ret)
{
default:
Host_Error("CL_KeepaliveMessage: CL_GetMessage failed");
case 0:
break;
case 1:
Host_Error("CL_KeepaliveMessage: received a message");
break;
case 2:
if (MSG_ReadByte() != svc_nop)
Host_Error("CL_KeepaliveMessage: datagram wasn't a nop");
break;
}
}
while (ret);
net_message = old;
memcpy(net_message.data, olddata, net_message.cursize);
time = Sys_FloatTime();
if ((time - lastmsg) < 5)
return;
lastmsg = time;
Con_Printf("--> client to server keepalive\n");
MSG_WriteByte(&cls.message, clc_nop);
NET_SendMessage(cls.netcon, &cls.message);
SZ_Clear(&cls.message);
}
static void CL_ParseServerInfo(void)
{
char *str;
int32_t i;
int32_t nummodels;
int32_t numsounds;
char model_precache[MAX_MODELS][MAX_QPATH];
char sound_precache[MAX_SOUNDS][MAX_QPATH];
Con_DPrintf("Serverinfo packet received.\n");
CL_ClearState();
i = MSG_ReadLong();
if (i != PROTOCOL_VERSION)
{
Con_Printf("Server returned version %i, not %i", i, PROTOCOL_VERSION);
return;
}
cl.maxclients = MSG_ReadByte();
if ((cl.maxclients < 1) || (cl.maxclients > MAX_SCOREBOARD))
{
Con_Printf("Bad maxclients (%u) from server\n", cl.maxclients);
return;
}
cl.scores = Hunk_AllocName(cl.maxclients * (sizeof(*cl.scores)), "scores");
cl.gametype = MSG_ReadByte();
str = MSG_ReadString();
strncpy(cl.levelname, str, (sizeof(cl.levelname)) - 1);
Con_Printf("\n\n\35\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\37\n\n");
Con_Printf("%c%s\n", 2, str);
memset(cl.model_precache, 0, sizeof(cl.model_precache));
for (nummodels = 1;; nummodels++)
{
str = MSG_ReadString();
if (!str[0])
break;
if (nummodels == MAX_MODELS)
{
Con_Printf("Server sent too many model precaches\n");
return;
}
strcpy(model_precache[nummodels], str);
Mod_TouchModel(str);
}
memset(cl.sound_precache, 0, sizeof(cl.sound_precache));
for (numsounds = 1;; numsounds++)
{
str = MSG_ReadString();
if (!str[0])
break;
if (numsounds == MAX_SOUNDS)
{
Con_Printf("Server sent too many sound precaches\n");
return;
}
strcpy(sound_precache[numsounds], str);
S_TouchSound(str);
}
for (i = 1; i < nummodels; i++)
{
cl.model_precache[i] = Mod_ForName(model_precache[i], 0);
if (cl.model_precache[i] == 0)
{
Con_Printf("Model %s not found\n", model_precache[i]);
return;
}
CL_KeepaliveMessage();
}
S_BeginPrecaching();
for (i = 1; i < numsounds; i++)
{
cl.sound_precache[i] = S_PrecacheSound(sound_precache[i]);
CL_KeepaliveMessage();
}
S_EndPrecaching();
cl_entities[0].model = (cl.worldmodel = cl.model_precache[1]);
R_NewMap();
Hunk_Check();
noclip_anglehack = 0;
}
static void CL_ParseUpdate(int32_t bits)
{
int32_t i;
model_t *model;
int32_t modnum;
bool forcelink;
entity_t *ent;
int32_t num;
int32_t skin;
if (cls.signon == (SIGNONS - 1))
{
cls.signon = SIGNONS;
CL_SignonReply();
}
if (bits & U_MOREBITS)
{
i = MSG_ReadByte();
bits |= i << 8;
}
if (bits & U_LONGENTITY)
num = MSG_ReadShort();
else
num = MSG_ReadByte();
ent = CL_EntityNum(num);
for (i = 0; i < 16; i++)
if (bits & (1 << i))
bitcounts[i]++;
if (ent->msgtime != cl.mtime[1])
forcelink = 1;
else
forcelink = 0;
ent->msgtime = cl.mtime[0];
if (bits & U_MODEL)
{
modnum = MSG_ReadByte();
if (modnum >= MAX_MODELS)
Host_Error("CL_ParseModel: bad modnum");
}
else
modnum = ent->baseline.modelindex;
model = cl.model_precache[modnum];
if (model != ent->model)
{
ent->model = model;
if (model)
{
if (model->synctype == ST_RAND)
ent->syncbase = ((float) (rand() & 0x7fff)) / 0x7fff;
else
ent->syncbase = 0.0;
}
else
forcelink = 1;
}
if (bits & U_FRAME)
ent->frame = MSG_ReadByte();
else
ent->frame = ent->baseline.frame;
if (bits & U_COLORMAP)
i = MSG_ReadByte();
else
i = ent->baseline.colormap;
if (!i)
ent->colormap = vid.colormap;
else
{
if (i > cl.maxclients)
Sys_Error("i >= cl.maxclients");
ent->colormap = cl.scores[i - 1].translations;
}
if (bits & U_SKIN)
ent->skinnum = MSG_ReadByte();
else
ent->skinnum = ent->baseline.skin;
if (bits & U_EFFECTS)
ent->effects = MSG_ReadByte();
else
ent->effects = ent->baseline.effects;
VectorCopy(ent->msg_origins[0], ent->msg_origins[1]);
VectorCopy(ent->msg_angles[0], ent->msg_angles[1]);
if (bits & U_ORIGIN1)
ent->msg_origins[0][0] = MSG_ReadCoord();
else
ent->msg_origins[0][0] = ent->baseline.origin[0];
if (bits & U_ANGLE1)
ent->msg_angles[0][0] = MSG_ReadAngle();
else
ent->msg_angles[0][0] = ent->baseline.angles[0];
if (bits & U_ORIGIN2)
ent->msg_origins[0][1] = MSG_ReadCoord();
else
ent->msg_origins[0][1] = ent->baseline.origin[1];
if (bits & U_ANGLE2)
ent->msg_angles[0][1] = MSG_ReadAngle();
else
ent->msg_angles[0][1] = ent->baseline.angles[1];
if (bits & U_ORIGIN3)
ent->msg_origins[0][2] = MSG_ReadCoord();
else
ent->msg_origins[0][2] = ent->baseline.origin[2];
if (bits & U_ANGLE3)
ent->msg_angles[0][2] = MSG_ReadAngle();
else
ent->msg_angles[0][2] = ent->baseline.angles[2];
if (bits & U_NOLERP)
ent->forcelink = 1;
if (forcelink)
{
VectorCopy(ent->msg_origins[0], ent->msg_origins[1]);
VectorCopy(ent->msg_origins[0], ent->origin);
VectorCopy(ent->msg_angles[0], ent->msg_angles[1]);
VectorCopy(ent->msg_angles[0], ent->angles);
ent->forcelink = 1;
}
}
static void CL_ParseBaseline(entity_t *ent)
{
int32_t i;
ent->baseline.modelindex = MSG_ReadByte();
ent->baseline.frame = MSG_ReadByte();
ent->baseline.colormap = MSG_ReadByte();
ent->baseline.skin = MSG_ReadByte();
for (i = 0; i < 3; i++)
{
ent->baseline.origin[i] = MSG_ReadCoord();
ent->baseline.angles[i] = MSG_ReadAngle();
}
}
static void CL_ParseClientdata(int32_t bits)
{
int32_t i;
int32_t j;
if (bits & SU_VIEWHEIGHT)
cl.viewheight = MSG_ReadChar();
else
cl.viewheight = DEFAULT_VIEWHEIGHT;
if (bits & SU_IDEALPITCH)
cl.idealpitch = MSG_ReadChar();
else
cl.idealpitch = 0;
VectorCopy(cl.mvelocity[0], cl.mvelocity[1]);
for (i = 0; i < 3; i++)
{
if (bits & (SU_PUNCH1 << i))
cl.punchangle[i] = MSG_ReadChar();
else
cl.punchangle[i] = 0;
if (bits & (SU_VELOCITY1 << i))
cl.mvelocity[0][i] = MSG_ReadChar() * 16;
else
cl.mvelocity[0][i] = 0;
}
i = MSG_ReadLong();
if (cl.items != i)
{
Sbar_Changed();
for (j = 0; j < 32; j++)
if ((i & (1 << j)) && (!(cl.items & (1 << j))))
cl.item_gettime[j] = cl.time;
cl.items = i;
}
cl.onground = (bits & SU_ONGROUND) != 0;
cl.inwater = (bits & SU_INWATER) != 0;
if (bits & SU_WEAPONFRAME)
cl.stats[STAT_WEAPONFRAME] = MSG_ReadByte();
else
cl.stats[STAT_WEAPONFRAME] = 0;
if (bits & SU_ARMOR)
i = MSG_ReadByte();
else
i = 0;
if (cl.stats[STAT_ARMOR] != i)
{
cl.stats[STAT_ARMOR] = i;
Sbar_Changed();
}
if (bits & SU_WEAPON)
i = MSG_ReadByte();
else
i = 0;
if (cl.stats[STAT_WEAPON] != i)
{
cl.stats[STAT_WEAPON] = i;
Sbar_Changed();
}
i = MSG_ReadShort();
if (cl.stats[STAT_HEALTH] != i)
{
cl.stats[STAT_HEALTH] = i;
Sbar_Changed();
}
i = MSG_ReadByte();
if (cl.stats[STAT_AMMO] != i)
{
cl.stats[STAT_AMMO] = i;
Sbar_Changed();
}
for (i = 0; i < 4; i++)
{
j = MSG_ReadByte();
if (cl.stats[STAT_SHELLS + i] != j)
{
cl.stats[STAT_SHELLS + i] = j;
Sbar_Changed();
}
}
i = MSG_ReadByte();
if (standard_quake)
{
if (cl.stats[STAT_ACTIVEWEAPON] != i)
{
cl.stats[STAT_ACTIVEWEAPON] = i;
Sbar_Changed();
}
}
else
{
if (cl.stats[STAT_ACTIVEWEAPON] != (1 << i))
{
cl.stats[STAT_ACTIVEWEAPON] = 1 << i;
Sbar_Changed();
}
}
}
void CL_NewTranslation(int32_t slot)
{
int32_t i;
int32_t j;
int32_t top;
int32_t bottom;
uint8_t *dest;
uint8_t *source;
if (slot > cl.maxclients)
Sys_Error("CL_NewTranslation: slot > cl.maxclients");
dest = cl.scores[slot].translations;
source = vid.colormap;
memcpy(dest, vid.colormap, sizeof(cl.scores[slot].translations));
top = cl.scores[slot].colors & 0xf0;
bottom = (cl.scores[slot].colors & 15) << 4;
for (i = 0; i < VID_GRADES; i++, dest += 256, source += 256)
{
if (top < 128)
memcpy(dest + TOP_RANGE, source + top, 16);
else
for (j = 0; j < 16; j++)
dest[TOP_RANGE + j] = source[(top + 15) - j];
if (bottom < 128)
memcpy(dest + BOTTOM_RANGE, source + bottom, 16);
else
for (j = 0; j < 16; j++)
dest[BOTTOM_RANGE + j] = source[(bottom + 15) - j];
}
}
static void CL_ParseStatic(void)
{
entity_t *ent;
int32_t i;
i = cl.num_statics;
if (i >= MAX_STATIC_ENTITIES)
Host_Error("Too many static entities");
ent = &cl_static_entities[i];
cl.num_statics++;
CL_ParseBaseline(ent);
ent->model = cl.model_precache[ent->baseline.modelindex];
ent->frame = ent->baseline.frame;
ent->colormap = vid.colormap;
ent->skinnum = ent->baseline.skin;
ent->effects = ent->baseline.effects;
VectorCopy(ent->baseline.origin, ent->origin);
VectorCopy(ent->baseline.angles, ent->angles);
R_AddEfrags(ent);
}
static void CL_ParseStaticSound(void)
{
vec3_t org;
int32_t sound_num;
int32_t vol;
int32_t atten;
int32_t i;
for (i = 0; i < 3; i++)
org[i] = MSG_ReadCoord();
sound_num = MSG_ReadByte();
vol = MSG_ReadByte();
atten = MSG_ReadByte();
S_StaticSound(cl.sound_precache[sound_num], org, vol, atten);
}
void CL_ParseServerMessage(void)
{
int32_t cmd;
int32_t i;
if (cl_shownet.value == 1)
Con_Printf("%i ", net_message.cursize);
else
if (cl_shownet.value == 2)
Con_Printf("------------------\n");
cl.onground = 0;
MSG_BeginReading();
while (1)
{
if (msg_badread)
Host_Error("CL_ParseServerMessage: Bad server message");
cmd = MSG_ReadByte();
if (cmd == (-1))
{
SHOWNET("END OF MESSAGE");
return;
}
if (cmd & 128)
{
SHOWNET("fast update");
CL_ParseUpdate(cmd & 127);
continue;
}
SHOWNET(svc_strings[cmd]);
switch (cmd)
{
default:
Host_Error("CL_ParseServerMessage: Illegible server message\n");
break;
case svc_nop:
break;
case svc_time:
cl.mtime[1] = cl.mtime[0];
cl.mtime[0] = MSG_ReadFloat();
break;
case svc_clientdata:
i = MSG_ReadShort();
CL_ParseClientdata(i);
break;
case svc_version:
i = MSG_ReadLong();
if (i != PROTOCOL_VERSION)
Host_Error("CL_ParseServerMessage: Server is protocol %i instead of %i\n", i, PROTOCOL_VERSION);
break;
case svc_disconnect:
Host_EndGame("Server disconnected\n");
case svc_print:
Con_Printf("%s", MSG_ReadString());
break;
case svc_centerprint:
SCR_CenterPrint(MSG_ReadString());
break;
case svc_stufftext:
Cbuf_AddText(MSG_ReadString());
break;
case svc_damage:
V_ParseDamage();
break;
case svc_serverinfo:
CL_ParseServerInfo();
vid.recalc_refdef = 1;
break;
case svc_setangle:
for (i = 0; i < 3; i++)
cl.viewangles[i] = MSG_ReadAngle();
break;
case svc_setview:
cl.viewentity = MSG_ReadShort();
break;
case svc_lightstyle:
i = MSG_ReadByte();
if (i >= MAX_LIGHTSTYLES)
Sys_Error("svc_lightstyle > MAX_LIGHTSTYLES");
strcpy(cl_lightstyle[i].map, MSG_ReadString());
cl_lightstyle[i].length = strlen(cl_lightstyle[i].map);
break;
case svc_sound:
CL_ParseStartSoundPacket();
break;
case svc_stopsound:
i = MSG_ReadShort();
S_StopSound(i >> 3, i & 7);
break;
case svc_updatename:
Sbar_Changed();
i = MSG_ReadByte();
if (i >= cl.maxclients)
Host_Error("CL_ParseServerMessage: svc_updatename > MAX_SCOREBOARD");
strcpy(cl.scores[i].name, MSG_ReadString());
break;
case svc_updatefrags:
Sbar_Changed();
i = MSG_ReadByte();
if (i >= cl.maxclients)
Host_Error("CL_ParseServerMessage: svc_updatefrags > MAX_SCOREBOARD");
cl.scores[i].frags = MSG_ReadShort();
break;
case svc_updatecolors:
Sbar_Changed();
i = MSG_ReadByte();
if (i >= cl.maxclients)
Host_Error("CL_ParseServerMessage: svc_updatecolors > MAX_SCOREBOARD");
cl.scores[i].colors = MSG_ReadByte();
CL_NewTranslation(i);
break;
case svc_particle:
R_ParseParticleEffect();
break;
case svc_spawnbaseline:
i = MSG_ReadShort();
CL_ParseBaseline(CL_EntityNum(i));
break;
case svc_spawnstatic:
CL_ParseStatic();
break;
case svc_temp_entity:
CL_ParseTEnt();
break;
case svc_setpause:
{
cl.paused = MSG_ReadByte();
if (cl.paused)
{
CDAudio_Pause();
}
else
{
CDAudio_Resume();
}
}
break;
case svc_signonnum:
i = MSG_ReadByte();
if (i <= cls.signon)
Host_Error("Received signon %i when at %i", i, cls.signon);
cls.signon = i;
CL_SignonReply();
break;
case svc_killedmonster:
cl.stats[STAT_MONSTERS]++;
break;
case svc_foundsecret:
cl.stats[STAT_SECRETS]++;
break;
case svc_updatestat:
i = MSG_ReadByte();
if ((i < 0) || (i >= MAX_CL_STATS))
Sys_Error("svc_updatestat: %i is invalid", i);
cl.stats[i] = MSG_ReadLong();
;
break;
case svc_spawnstaticsound:
CL_ParseStaticSound();
break;
case svc_cdtrack:
cl.cdtrack = MSG_ReadByte();
cl.looptrack = MSG_ReadByte();
if ((cls.demoplayback || cls.demorecording) && (cls.forcetrack != (-1)))
CDAudio_Play((uint8_t) cls.forcetrack, 1);
else
CDAudio_Play((uint8_t) cl.cdtrack, 1);
break;
case svc_intermission:
cl.intermission = 1;
cl.completed_time = cl.time;
vid.recalc_refdef = 1;
break;
case svc_finale:
cl.intermission = 2;
cl.completed_time = cl.time;
vid.recalc_refdef = 1;
SCR_CenterPrint(MSG_ReadString());
break;
case svc_cutscene:
cl.intermission = 3;
cl.completed_time = cl.time;
vid.recalc_refdef = 1;
SCR_CenterPrint(MSG_ReadString());
break;
case svc_sellscreen:
Cmd_ExecuteString("help", src_command);
break;
}
}
}
void CL_InitTEnts(void)
{
cl_sfx_wizhit = S_PrecacheSound("wizard/hit.wav");
cl_sfx_knighthit = S_PrecacheSound("hknight/hit.wav");
cl_sfx_tink1 = S_PrecacheSound("weapons/tink1.wav");
cl_sfx_ric1 = S_PrecacheSound("weapons/ric1.wav");
cl_sfx_ric2 = S_PrecacheSound("weapons/ric2.wav");
cl_sfx_ric3 = S_PrecacheSound("weapons/ric3.wav");
cl_sfx_r_exp3 = S_PrecacheSound("weapons/r_exp3.wav");
}
static void CL_ParseBeam(model_t *m)
{
int32_t ent;
vec3_t start;
vec3_t end;
beam_t *b;
int32_t i;
ent = MSG_ReadShort();
start[0] = MSG_ReadCoord();
start[1] = MSG_ReadCoord();
start[2] = MSG_ReadCoord();
end[0] = MSG_ReadCoord();
end[1] = MSG_ReadCoord();
end[2] = MSG_ReadCoord();
for (i = 0, b = cl_beams; i < MAX_BEAMS; i++, b++)
if (b->entity == ent)
{
b->entity = ent;
b->model = m;
b->endtime = cl.time + 0.2;
VectorCopy(start, b->start);
VectorCopy(end, b->end);
return;
}
for (i = 0, b = cl_beams; i < MAX_BEAMS; i++, b++)
{
if ((!b->model) || (b->endtime < cl.time))
{
b->entity = ent;
b->model = m;
b->endtime = cl.time + 0.2;
VectorCopy(start, b->start);
VectorCopy(end, b->end);
return;
}
}
Con_Printf("beam list overflow!\n");
}
void CL_ParseTEnt(void)
{
int32_t type;
vec3_t pos;
dlight_t *dl;
int32_t rnd;
int32_t colorStart;
int32_t colorLength;
type = MSG_ReadByte();
switch (type)
{
case TE_WIZSPIKE:
pos[0] = MSG_ReadCoord();
pos[1] = MSG_ReadCoord();
pos[2] = MSG_ReadCoord();
R_RunParticleEffect(pos, vec3_origin, 20, 30);
S_StartSound(-1, 0, cl_sfx_wizhit, pos, 1, 1);
break;
case TE_KNIGHTSPIKE:
pos[0] = MSG_ReadCoord();
pos[1] = MSG_ReadCoord();
pos[2] = MSG_ReadCoord();
R_RunParticleEffect(pos, vec3_origin, 226, 20);
S_StartSound(-1, 0, cl_sfx_knighthit, pos, 1, 1);
break;
case TE_SPIKE:
pos[0] = MSG_ReadCoord();
pos[1] = MSG_ReadCoord();
pos[2] = MSG_ReadCoord();
R_RunParticleEffect(pos, vec3_origin, 0, 10);
if (rand() % 5)
S_StartSound(-1, 0, cl_sfx_tink1, pos, 1, 1);
else
{
rnd = rand() & 3;
if (rnd == 1)
S_StartSound(-1, 0, cl_sfx_ric1, pos, 1, 1);
else
if (rnd == 2)
S_StartSound(-1, 0, cl_sfx_ric2, pos, 1, 1);
else
S_StartSound(-1, 0, cl_sfx_ric3, pos, 1, 1);
}
break;
case TE_SUPERSPIKE:
pos[0] = MSG_ReadCoord();
pos[1] = MSG_ReadCoord();
pos[2] = MSG_ReadCoord();
R_RunParticleEffect(pos, vec3_origin, 0, 20);
if (rand() % 5)
S_StartSound(-1, 0, cl_sfx_tink1, pos, 1, 1);
else
{
rnd = rand() & 3;
if (rnd == 1)
S_StartSound(-1, 0, cl_sfx_ric1, pos, 1, 1);
else
if (rnd == 2)
S_StartSound(-1, 0, cl_sfx_ric2, pos, 1, 1);
else
S_StartSound(-1, 0, cl_sfx_ric3, pos, 1, 1);
}
break;
case TE_GUNSHOT:
pos[0] = MSG_ReadCoord();
pos[1] = MSG_ReadCoord();
pos[2] = MSG_ReadCoord();
R_RunParticleEffect(pos, vec3_origin, 0, 20);
break;
case TE_EXPLOSION:
pos[0] = MSG_ReadCoord();
pos[1] = MSG_ReadCoord();
pos[2] = MSG_ReadCoord();
R_ParticleExplosion(pos);
dl = CL_AllocDlight(0);
VectorCopy(pos, dl->origin);
dl->radius = 350;
dl->die = cl.time + 0.5;
dl->decay = 300;
S_StartSound(-1, 0, cl_sfx_r_exp3, pos, 1, 1);
break;
case TE_TAREXPLOSION:
pos[0] = MSG_ReadCoord();
pos[1] = MSG_ReadCoord();
pos[2] = MSG_ReadCoord();
R_BlobExplosion(pos);
S_StartSound(-1, 0, cl_sfx_r_exp3, pos, 1, 1);
break;
case TE_LIGHTNING1:
CL_ParseBeam(Mod_ForName("progs/bolt.mdl", 1));
break;
case TE_LIGHTNING2:
CL_ParseBeam(Mod_ForName("progs/bolt2.mdl", 1));
break;
case TE_LIGHTNING3:
CL_ParseBeam(Mod_ForName("progs/bolt3.mdl", 1));
break;
case TE_BEAM:
CL_ParseBeam(Mod_ForName("progs/beam.mdl", 1));
break;
case TE_LAVASPLASH:
pos[0] = MSG_ReadCoord();
pos[1] = MSG_ReadCoord();
pos[2] = MSG_ReadCoord();
R_LavaSplash(pos);
break;
case TE_TELEPORT:
pos[0] = MSG_ReadCoord();
pos[1] = MSG_ReadCoord();
pos[2] = MSG_ReadCoord();
R_TeleportSplash(pos);
break;
case TE_EXPLOSION2:
pos[0] = MSG_ReadCoord();
pos[1] = MSG_ReadCoord();
pos[2] = MSG_ReadCoord();
colorStart = MSG_ReadByte();
colorLength = MSG_ReadByte();
R_ParticleExplosion2(pos, colorStart, colorLength);
dl = CL_AllocDlight(0);
VectorCopy(pos, dl->origin);
dl->radius = 350;
dl->die = cl.time + 0.5;
dl->decay = 300;
S_StartSound(-1, 0, cl_sfx_r_exp3, pos, 1, 1);
break;
default:
Sys_Error("CL_ParseTEnt: bad type");
}
}
static entity_t *CL_NewTempEntity(void)
{
entity_t *ent;
if (cl_numvisedicts == MAX_VISEDICTS)
return 0;
if (num_temp_entities == MAX_TEMP_ENTITIES)
return 0;
ent = &cl_temp_entities[num_temp_entities];
memset(ent, 0, sizeof(*ent));
num_temp_entities++;
cl_visedicts[cl_numvisedicts] = ent;
cl_numvisedicts++;
ent->colormap = vid.colormap;
return ent;
}
void CL_UpdateTEnts(void)
{
int32_t i;
beam_t *b;
vec3_t dist;
vec3_t org;
float d;
entity_t *ent;
float yaw;
float pitch;
float forward;
num_temp_entities = 0;
for (i = 0, b = cl_beams; i < MAX_BEAMS; i++, b++)
{
if ((!b->model) || (b->endtime < cl.time))
continue;
if (b->entity == cl.viewentity)
{
VectorCopy(cl_entities[cl.viewentity].origin, b->start);
}
VectorSubtract(b->end, b->start, dist);
if ((dist[1] == 0) && (dist[0] == 0))
{
yaw = 0;
if (dist[2] > 0)
pitch = 90;
else
pitch = 270;
}
else
{
yaw = (int32_t) ((atan2(dist[1], dist[0]) * 180) / M_PI);
if (yaw < 0)
yaw += 360;
forward = sqrt((dist[0] * dist[0]) + (dist[1] * dist[1]));
pitch = (int32_t) ((atan2(dist[2], forward) * 180) / M_PI);
if (pitch < 0)
pitch += 360;
}
VectorCopy(b->start, org);
d = VectorNormalize(dist);
while (d > 0)
{
ent = CL_NewTempEntity();
if (!ent)
return;
VectorCopy(org, ent->origin);
ent->model = b->model;
ent->angles[0] = pitch;
ent->angles[1] = yaw;
ent->angles[2] = rand() % 360;
for (i = 0; i < 3; i++)
org[i] += dist[i] * 30;
d -= 30;
}
}
}
static void Cmd_Wait_f(void)
{
cmd_wait = 1;
}
void Cbuf_Init(void)
{
SZ_Alloc(&cmd_text, 8192);
}
void Cbuf_AddText(char *text)
{
int32_t l;
l = strlen(text);
if ((cmd_text.cursize + l) >= cmd_text.maxsize)
{
Con_Printf("Cbuf_AddText: overflow\n");
return;
}
SZ_Write(&cmd_text, text, strlen(text));
}
void Cbuf_InsertText(char *text)
{
char *temp;
int32_t templen;
templen = cmd_text.cursize;
if (templen)
{
temp = Z_Malloc(templen);
memcpy(temp, cmd_text.data, templen);
SZ_Clear(&cmd_text);
}
else
temp = 0;
Cbuf_AddText(text);
if (templen)
{
SZ_Write(&cmd_text, temp, templen);
Z_Free(temp);
}
}
void Cbuf_Execute(void)
{
int32_t i;
char *text;
char line[1024];
int32_t quotes;
while (cmd_text.cursize)
{
text = (char *) cmd_text.data;
quotes = 0;
for (i = 0; i < cmd_text.cursize; i++)
{
if (text[i] == '"')
quotes++;
if ((!(quotes & 1)) && (text[i] == ';'))
break;
if (text[i] == '\n')
break;
}
memcpy(line, text, i);
line[i] = 0;
if (i == cmd_text.cursize)
cmd_text.cursize = 0;
else
{
i++;
cmd_text.cursize -= i;
memcpy(text, text + i, cmd_text.cursize);
}
Cmd_ExecuteString(line, src_command);
if (cmd_wait)
{
cmd_wait = 0;
break;
}
}
}
static void Cmd_StuffCmds_f(void)
{
int32_t i;
int32_t j;
int32_t s;
char *text;
char *build;
char c;
if (Cmd_Argc() != 1)
{
Con_Printf("stuffcmds : execute command line parameters\n");
return;
}
s = 0;
for (i = 1; i < com_argc; i++)
{
if (!com_argv[i])
continue;
s += strlen(com_argv[i]) + 1;
}
if (!s)
return;
text = Z_Malloc(s + 1);
text[0] = 0;
for (i = 1; i < com_argc; i++)
{
if (!com_argv[i])
continue;
strcat(text, com_argv[i]);
if (i != (com_argc - 1))
strcat(text, " ");
}
build = Z_Malloc(s + 1);
build[0] = 0;
for (i = 0; i < (s - 1); i++)
{
if (text[i] == '+')
{
i++;
for (j = i; ((text[j] != '+') && (text[j] != '-')) && (text[j] != 0); j++)
;
c = text[j];
text[j] = 0;
strcat(build, text + i);
strcat(build, "\n");
text[j] = c;
i = j - 1;
}
}
if (build[0])
Cbuf_InsertText(build);
Z_Free(text);
Z_Free(build);
}
static void Cmd_Exec_f(void)
{
char *f;
int32_t mark;
if (Cmd_Argc() != 2)
{
Con_Printf("exec <filename> : execute a script file\n");
return;
}
mark = Hunk_LowMark();
f = (char *) COM_LoadHunkFile(Cmd_Argv(1));
if (!f)
{
Con_Printf("couldn't exec %s\n", Cmd_Argv(1));
return;
}
Con_Printf("execing %s\n", Cmd_Argv(1));
Cbuf_InsertText(f);
Hunk_FreeToLowMark(mark);
}
static void Cmd_Echo_f(void)
{
int32_t i;
for (i = 1; i < Cmd_Argc(); i++)
Con_Printf("%s ", Cmd_Argv(i));
Con_Printf("\n");
}
static char *CopyString(char *in)
{
char *out;
out = Z_Malloc(strlen(in) + 1);
strcpy(out, in);
return out;
}
static void Cmd_Alias_f(void)
{
cmdalias_t *a;
char cmd[1024];
int32_t i;
int32_t c;
char *s;
if (Cmd_Argc() == 1)
{
Con_Printf("Current alias commands:\n");
for (a = cmd_alias; a; a = a->next)
Con_Printf("%s : %s\n", a->name, a->value);
return;
}
s = Cmd_Argv(1);
if (strlen(s) >= MAX_ALIAS_NAME)
{
Con_Printf("Alias name is too int32_t\n");
return;
}
for (a = cmd_alias; a; a = a->next)
{
if (!strcmp(s, a->name))
{
Z_Free(a->value);
break;
}
}
if (!a)
{
a = Z_Malloc(sizeof(cmdalias_t));
a->next = cmd_alias;
cmd_alias = a;
}
strcpy(a->name, s);
cmd[0] = 0;
c = Cmd_Argc();
for (i = 2; i < c; i++)
{
strcat(cmd, Cmd_Argv(i));
if (i != c)
strcat(cmd, " ");
}
strcat(cmd, "\n");
a->value = CopyString(cmd);
}
void Cmd_Init(void)
{
Cmd_AddCommand("stuffcmds", Cmd_StuffCmds_f);
Cmd_AddCommand("exec", Cmd_Exec_f);
Cmd_AddCommand("echo", Cmd_Echo_f);
Cmd_AddCommand("alias", Cmd_Alias_f);
Cmd_AddCommand("cmd", Cmd_ForwardToServer);
Cmd_AddCommand("wait", Cmd_Wait_f);
}
int32_t Cmd_Argc(void)
{
return cmd_argc;
}
char *Cmd_Argv(int32_t arg)
{
if (((uint32_t) arg) >= cmd_argc)
return cmd_null_string;
return cmd_argv[arg];
}
char *Cmd_Args(void)
{
return cmd_args;
}
void Cmd_TokenizeString(char *text)
{
int32_t i;
for (i = 0; i < cmd_argc; i++)
Z_Free(cmd_argv[i]);
cmd_argc = 0;
cmd_args = 0;
while (1)
{
while (((*text) && ((*text) <= ' ')) && ((*text) != '\n'))
{
text++;
}
if ((*text) == '\n')
{
text++;
break;
}
if (!(*text))
return;
if (cmd_argc == 1)
cmd_args = text;
text = COM_Parse(text);
if (!text)
return;
if (cmd_argc < MAX_ARGS)
{
cmd_argv[cmd_argc] = Z_Malloc(strlen(com_token) + 1);
strcpy(cmd_argv[cmd_argc], com_token);
cmd_argc++;
}
}
}
void Cmd_AddCommand(char *cmd_name, xcommand_t function)
{
cmd_function_t *cmd;
if (host_initialized)
Sys_Error("Cmd_AddCommand after host_initialized");
if (Cvar_VariableString(cmd_name)[0])
{
Con_Printf("Cmd_AddCommand: %s already defined as a var\n", cmd_name);
return;
}
for (cmd = cmd_functions; cmd; cmd = cmd->next)
{
if (!strcmp(cmd_name, cmd->name))
{
Con_Printf("Cmd_AddCommand: %s already defined\n", cmd_name);
return;
}
}
cmd = Hunk_Alloc(sizeof(cmd_function_t));
cmd->name = cmd_name;
cmd->function = function;
cmd->next = cmd_functions;
cmd_functions = cmd;
}
bool Cmd_Exists(char *cmd_name)
{
cmd_function_t *cmd;
for (cmd = cmd_functions; cmd; cmd = cmd->next)
{
if (!strcmp(cmd_name, cmd->name))
return 1;
}
return 0;
}
char *Cmd_CompleteCommand(char *partial)
{
cmd_function_t *cmd;
int32_t len;
len = strlen(partial);
if (!len)
return 0;
for (cmd = cmd_functions; cmd; cmd = cmd->next)
if (!strncmp(partial, cmd->name, len))
return cmd->name;
return 0;
}
void Cmd_ExecuteString(char *text, cmd_source_t src)
{
cmd_function_t *cmd;
cmdalias_t *a;
cmd_source = src;
Cmd_TokenizeString(text);
if (!Cmd_Argc())
return;
for (cmd = cmd_functions; cmd; cmd = cmd->next)
{
if (!strcasecmp(cmd_argv[0], cmd->name))
{
cmd->function();
return;
}
}
for (a = cmd_alias; a; a = a->next)
{
if (!strcasecmp(cmd_argv[0], a->name))
{
Cbuf_InsertText(a->value);
return;
}
}
if (!Cvar_Command())
Con_Printf("Unknown command \"%s\"\n", Cmd_Argv(0));
}
void Cmd_ForwardToServer(void)
{
if (cls.state != ca_connected)
{
Con_Printf("Can't \"%s\", not connected\n", Cmd_Argv(0));
return;
}
if (cls.demoplayback)
return;
MSG_WriteByte(&cls.message, clc_stringcmd);
if (strcasecmp(Cmd_Argv(0), "cmd") != 0)
{
SZ_Print(&cls.message, Cmd_Argv(0));
SZ_Print(&cls.message, " ");
}
if (Cmd_Argc() > 1)
SZ_Print(&cls.message, Cmd_Args());
else
SZ_Print(&cls.message, "\n");
}
int32_t Cmd_CheckParm(char *parm)
{
int32_t i;
if (!parm)
Sys_Error("Cmd_CheckParm: NULL");
for (i = 1; i < Cmd_Argc(); i++)
if (!strcasecmp(parm, Cmd_Argv(i)))
return i;
return 0;
}
void ClearLink(link_t *l)
{
l->prev = (l->next = l);
}
void RemoveLink(link_t *l)
{
l->next->prev = l->prev;
l->prev->next = l->next;
}
void InsertLinkBefore(link_t *l, link_t *before)
{
l->next = before;
l->prev = before->prev;
l->prev->next = l;
l->next->prev = l;
}
void InsertLinkAfter(link_t *l, link_t *after)
{
l->next = after->next;
l->prev = after;
l->prev->next = l;
l->next->prev = l;
}
void MSG_WriteChar(sizebuf_t *sb, int32_t c)
{
uint8_t *buf;
buf = SZ_GetSpace(sb, 1);
buf[0] = c;
}
void MSG_WriteByte(sizebuf_t *sb, int32_t c)
{
uint8_t *buf;
buf = SZ_GetSpace(sb, 1);
buf[0] = c;
}
void MSG_WriteShort(sizebuf_t *sb, int32_t c)
{
uint8_t *buf;
buf = SZ_GetSpace(sb, 2);
buf[0] = c & 0xff;
buf[1] = c >> 8;
}
void MSG_WriteLong(sizebuf_t *sb, int32_t c)
{
uint8_t *buf;
buf = SZ_GetSpace(sb, 4);
buf[0] = c & 0xff;
buf[1] = (c >> 8) & 0xff;
buf[2] = (c >> 16) & 0xff;
buf[3] = c >> 24;
}
void MSG_WriteFloat(sizebuf_t *sb, float f)
{
union
{
float f;
int32_t l;
} dat;
dat.f = f;
dat.l = dat.l;
SZ_Write(sb, &dat.l, 4);
}
void MSG_WriteString(sizebuf_t *sb, char *s)
{
if (!s)
SZ_Write(sb, "", 1);
else
SZ_Write(sb, s, strlen(s) + 1);
}
void MSG_WriteCoord(sizebuf_t *sb, float f)
{
MSG_WriteShort(sb, (int32_t) (f * 8));
}
void MSG_WriteAngle(sizebuf_t *sb, float f)
{
MSG_WriteByte(sb, ((((int32_t) f) * 256) / 360) & 255);
}
void MSG_BeginReading(void)
{
msg_readcount = 0;
msg_badread = 0;
}
int32_t MSG_ReadChar(void)
{
int32_t c;
if ((msg_readcount + 1) > net_message.cursize)
{
msg_badread = 1;
return -1;
}
c = (signed char) net_message.data[msg_readcount];
msg_readcount++;
return c;
}
int32_t MSG_ReadByte(void)
{
int32_t c;
if ((msg_readcount + 1) > net_message.cursize)
{
msg_badread = 1;
return -1;
}
c = (unsigned char) net_message.data[msg_readcount];
msg_readcount++;
return c;
}
int32_t MSG_ReadShort(void)
{
int32_t c;
if ((msg_readcount + 2) > net_message.cursize)
{
msg_badread = 1;
return -1;
}
c = (int16_t) (net_message.data[msg_readcount] + (net_message.data[msg_readcount + 1] << 8));
msg_readcount += 2;
return c;
}
int32_t MSG_ReadLong(void)
{
int32_t c;
if ((msg_readcount + 4) > net_message.cursize)
{
msg_badread = 1;
return -1;
}
c = ((net_message.data[msg_readcount] + (net_message.data[msg_readcount + 1] << 8)) + (net_message.data[msg_readcount + 2] << 16)) + (net_message.data[msg_readcount + 3] << 24);
msg_readcount += 4;
return c;
}
float MSG_ReadFloat(void)
{
union
{
uint8_t b[4];
float f;
int32_t l;
} dat;
dat.b[0] = net_message.data[msg_readcount];
dat.b[1] = net_message.data[msg_readcount + 1];
dat.b[2] = net_message.data[msg_readcount + 2];
dat.b[3] = net_message.data[msg_readcount + 3];
msg_readcount += 4;
dat.l = dat.l;
return dat.f;
}
char *MSG_ReadString(void)
{
static char string[2048];
int32_t l;
int32_t c;
l = 0;
do
{
c = MSG_ReadChar();
if ((c == (-1)) || (c == 0))
break;
string[l] = c;
l++;
}
while (l < ((sizeof(string)) - 1));
string[l] = 0;
return string;
}
float MSG_ReadCoord(void)
{
return MSG_ReadShort() * (1.0 / 8);
}
float MSG_ReadAngle(void)
{
return MSG_ReadChar() * (360.0 / 256);
}
void SZ_Alloc(sizebuf_t *buf, int32_t startsize)
{
if (startsize < 256)
startsize = 256;
buf->data = Hunk_AllocName(startsize, "sizebuf");
buf->maxsize = startsize;
buf->cursize = 0;
}
void SZ_Free(sizebuf_t *buf)
{
buf->cursize = 0;
}
void SZ_Clear(sizebuf_t *buf)
{
buf->cursize = 0;
}
void *SZ_GetSpace(sizebuf_t *buf, int32_t length)
{
void *data;
if ((buf->cursize + length) > buf->maxsize)
{
if (!buf->allowoverflow)
Sys_Error("SZ_GetSpace: overflow without allowoverflow set");
if (length > buf->maxsize)
Sys_Error("SZ_GetSpace: %i is > full buffer size", length);
buf->overflowed = 1;
Con_Printf("SZ_GetSpace: overflow");
SZ_Clear(buf);
}
data = buf->data + buf->cursize;
buf->cursize += length;
return data;
}
void SZ_Write(sizebuf_t *buf, void *data, int32_t length)
{
memcpy(SZ_GetSpace(buf, length), data, length);
}
void SZ_Print(sizebuf_t *buf, char *data)
{
int32_t len;
len = strlen(data) + 1;
if (buf->data[buf->cursize - 1])
memcpy((uint8_t *) SZ_GetSpace(buf, len), data, len);
else
memcpy(((uint8_t *) SZ_GetSpace(buf, len - 1)) - 1, data, len);
}
char *COM_SkipPath(char *pathname)
{
char *p = strrchr(pathname, '/');
return (p) ? (p + 1) : (pathname);
}
void COM_StripExtension(char *in, char *out)
{
size_t n = strcspn(in, ".");
memcpy(out, in, n);
out[n] = '\0';
}
static char *COM_FileExtension(char *in)
{
static char exten[8];
const char *dot = strchr(in, '.');
if ((!dot) || (!(*(dot + 1))))
return "";
dot++;
size_t i = 0;
while ((i < ((sizeof(exten)) - 1)) && (dot[i] != '\0'))
{
exten[i] = dot[i];
i++;
}
exten[i] = '\0';
return exten;
}
void COM_FileBase(char *in, char *out)
{
const char *dot = strrchr(in, '.');
if (!dot)
{
strcpy(out, "?model?");
return;
}
const char *slash = dot;
while ((slash > in) && ((*slash) != '/'))
slash--;
if ((*slash) == '/')
slash++;
else
slash = in;
if (dot <= slash)
{
strcpy(out, "?model?");
return;
}
size_t len = (size_t) (dot - slash);
memcpy(out, slash, len);
out[len] = '\0';
}
void COM_DefaultExtension(char *path, char *extension)
{
char *base = strrchr(path, '/');
base = (base) ? (base + 1) : (path);
if (!strchr(base, '.'))
strcat(path, extension);
}
char *COM_Parse(char *data)
{
int32_t c;
int32_t len = 0;
com_token[0] = 0;
if (!data)
return 0;
skipwhite:
while ((c = *data) <= ' ')
{
if (c == 0)
return 0;
data++;
}
if ((c == '/') && (data[1] == '/'))
{
data += strcspn(data, "\n");
if ((*data) == '\n')
data++;
goto skipwhite;
}
if (c == '\"')
{
data++;
for (;;)
{
c = *(data++);
if ((c == '\"') || (!c))
{
com_token[len] = 0;
return data;
}
com_token[len++] = (char) c;
}
}
if ((((((c == '{') || (c == '}')) || (c == ')')) || (c == '(')) || (c == '\'')) || (c == ':'))
{
com_token[len++] = (char) c;
com_token[len] = 0;
return data + 1;
}
do
{
com_token[len++] = (char) c;
data++;
c = *data;
if ((((((c == '{') || (c == '}')) || (c == ')')) || (c == '(')) || (c == '\'')) || (c == ':'))
break;
}
while (c > 32);
com_token[len] = 0;
return data;
}
int32_t COM_CheckParm(char *parm)
{
for (int32_t i = 1; i < com_argc; i++)
{
if (!com_argv[i])
continue;
if (strcmp(parm, com_argv[i]) == 0)
return i;
}
return 0;
}
void COM_InitArgv(int32_t argc, char **argv)
{
bool safe;
int32_t i;
int32_t j;
int32_t n;
n = 0;
for (j = 0; (j < MAX_NUM_ARGVS) && (j < argc); j++)
{
i = 0;
while ((n < (CMDLINE_LENGTH - 1)) && argv[j][i])
{
com_cmdline[n++] = argv[j][i++];
}
if (n < (CMDLINE_LENGTH - 1))
com_cmdline[n++] = ' ';
else
break;
}
com_cmdline[n] = 0;
safe = 0;
for (com_argc = 0; (com_argc < MAX_NUM_ARGVS) && (com_argc < argc); com_argc++)
{
largv[com_argc] = argv[com_argc];
if (!strcmp("-safe", argv[com_argc]))
safe = 1;
}
if (safe)
{
for (i = 0; i < NUM_SAFE_ARGVS; i++)
{
largv[com_argc] = safeargvs[i];
com_argc++;
}
}
largv[com_argc] = argvdummy;
com_argv = largv;
if (COM_CheckParm("-rogue"))
{
rogue = 1;
standard_quake = 0;
}
if (COM_CheckParm("-hipnotic"))
{
hipnotic = 1;
standard_quake = 0;
}
}
void COM_Init(char *basedir)
{
Cvar_RegisterVariable(&registered);
Cvar_RegisterVariable(&cmdline);
Cmd_AddCommand("path", COM_Path_f);
COM_InitFilesystem();
}
char *va(char *format, ...)
{
va_list argptr;
static char string[1024];
va_start(argptr, format);
vsprintf(string, format, argptr);
va_end(argptr);
return string;
}
static void COM_Path_f(void)
{
searchpath_t *s;
Con_Printf("Current search path:\n");
for (s = com_searchpaths; s; s = s->next)
{
if (s->pack)
{
Con_Printf("%s (%i files)\n", s->pack->filename, s->pack->numfiles);
}
else
Con_Printf("%s\n", s->filename);
}
}
void COM_WriteFile(char *filename, void *data, int32_t len)
{
int32_t handle;
char name[MAX_OSPATH];
sprintf(name, "%s/%s", com_gamedir, filename);
handle = Sys_FileOpenWrite(name);
if (handle == (-1))
{
Sys_Printf("COM_WriteFile: failed on %s\n", name);
return;
}
Sys_Printf("COM_WriteFile: %s\n", name);
Sys_FileWrite(handle, data, len);
Sys_FileClose(handle);
}
static void COM_CreatePath(char *path)
{
char *ofs;
for (ofs = path + 1; *ofs; ofs++)
{
if ((*ofs) == '/')
{
*ofs = 0;
Sys_mkdir(path);
*ofs = '/';
}
}
}
static void COM_CopyFile(char *netpath, char *cachepath)
{
int32_t in;
int32_t out;
int32_t remaining;
int32_t count;
char buf[4096];
remaining = Sys_FileOpenRead(netpath, &in);
COM_CreatePath(cachepath);
out = Sys_FileOpenWrite(cachepath);
while (remaining)
{
if (remaining < (sizeof(buf)))
count = remaining;
else
count = sizeof(buf);
Sys_FileRead(in, buf, count);
Sys_FileWrite(out, buf, count);
remaining -= count;
}
Sys_FileClose(in);
Sys_FileClose(out);
}
static int32_t COM_FindFile(char *filename, int32_t *handle, FILE **file)
{
searchpath_t *search;
char netpath[MAX_OSPATH];
char cachepath[MAX_OSPATH];
pack_t *pak;
int32_t i;
int32_t findtime;
int32_t cachetime;
if (file && handle)
Sys_Error("COM_FindFile: both handle and file set");
if ((!file) && (!handle))
Sys_Error("COM_FindFile: neither handle or file set");
search = com_searchpaths;
if (proghack)
{
if (!strcmp(filename, "progs.dat"))
search = search->next;
}
for (; search; search = search->next)
{
if (search->pack)
{
pak = search->pack;
for (i = 0; i < pak->numfiles; i++)
if (!strcmp(pak->files[i].name, filename))
{
Sys_Printf("PackFile: %s : %s\n", pak->filename, filename);
if (handle)
{
*handle = pak->handle;
Sys_FileSeek(pak->handle, pak->files[i].filepos);
}
else
{
*file = fopen(pak->filename, "rb");
if (*file)
fseek(*file, pak->files[i].filepos, 0);
}
com_filesize = pak->files[i].filelen;
return com_filesize;
}
}
else
{
if (!static_registered)
{
if (strchr(filename, '/') || strchr(filename, '\\'))
continue;
}
sprintf(netpath, "%s/%s", search->filename, filename);
findtime = Sys_FileTime(netpath);
if (findtime == (-1))
continue;
if (!com_cachedir[0])
strcpy(cachepath, netpath);
else
{
sprintf(cachepath, "%s%s", com_cachedir, netpath);
cachetime = Sys_FileTime(cachepath);
if (cachetime < findtime)
COM_CopyFile(netpath, cachepath);
strcpy(netpath, cachepath);
}
Sys_Printf("FindFile: %s\n", netpath);
com_filesize = Sys_FileOpenRead(netpath, &i);
if (handle)
*handle = i;
else
{
Sys_FileClose(i);
*file = fopen(netpath, "rb");
}
return com_filesize;
}
}
Sys_Printf("FindFile: can't find %s\n", filename);
if (handle)
*handle = -1;
else
*file = 0;
com_filesize = -1;
return -1;
}
int32_t COM_OpenFile(char *filename, int32_t *handle)
{
return COM_FindFile(filename, handle, 0);
}
int32_t COM_FOpenFile(char *filename, FILE **file)
{
return COM_FindFile(filename, 0, file);
}
void COM_CloseFile(int32_t h)
{
searchpath_t *s;
for (s = com_searchpaths; s; s = s->next)
if (s->pack && (s->pack->handle == h))
return;
Sys_FileClose(h);
}
static uint8_t *COM_LoadFile(char *path, int32_t usehunk)
{
int32_t h;
uint8_t *buf;
char base[32];
int32_t len;
buf = 0;
len = COM_OpenFile(path, &h);
if (h == (-1))
return 0;
COM_FileBase(path, base);
if (usehunk == 1)
buf = Hunk_AllocName(len + 1, base);
else
if (usehunk == 2)
buf = Hunk_TempAlloc(len + 1);
else
if (usehunk == 0)
buf = Z_Malloc(len + 1);
else
if (usehunk == 3)
buf = Cache_Alloc(loadcache, len + 1, base);
else
if (usehunk == 4)
{
if ((len + 1) > loadsize)
buf = Hunk_TempAlloc(len + 1);
else
buf = loadbuf;
}
else
Sys_Error("COM_LoadFile: bad usehunk");
if (!buf)
Sys_Error("COM_LoadFile: not enough space for %s", path);
((uint8_t *) buf)[len] = 0;
Draw_BeginDisc();
Sys_FileRead(h, buf, len);
COM_CloseFile(h);
Draw_EndDisc();
return buf;
}
uint8_t *COM_LoadHunkFile(char *path)
{
return COM_LoadFile(path, 1);
}
uint8_t *COM_LoadTempFile(char *path)
{
return COM_LoadFile(path, 2);
}
void COM_LoadCacheFile(char *path, struct cache_user_s *cu)
{
loadcache = cu;
COM_LoadFile(path, 3);
}
uint8_t *COM_LoadStackFile(char *path, void *buffer, int32_t bufsize)
{
uint8_t *buf;
loadbuf = (uint8_t *) buffer;
loadsize = bufsize;
buf = COM_LoadFile(path, 4);
return buf;
}
static pack_t *COM_LoadPackFile(char *packfile)
{
dpackheader_t header;
int32_t i;
packfile_t *newfiles;
int32_t numpackfiles;
pack_t *pack;
int32_t packhandle;
dpackfile_t info[MAX_FILES_IN_PACK];
uint16_t crc;
if (Sys_FileOpenRead(packfile, &packhandle) == (-1))
{
return 0;
}
Sys_FileRead(packhandle, (void *) (&header), sizeof(header));
if ((((header.id[0] != 'P') || (header.id[1] != 'A')) || (header.id[2] != 'C')) || (header.id[3] != 'K'))
Sys_Error("%s is not a packfile", packfile);
header.dirofs = header.dirofs;
header.dirlen = header.dirlen;
numpackfiles = header.dirlen / (sizeof(dpackfile_t));
if (numpackfiles > MAX_FILES_IN_PACK)
Sys_Error("%s has %i files", packfile, numpackfiles);
if (numpackfiles != PAK0_COUNT)
com_modified = 1;
newfiles = Hunk_AllocName(numpackfiles * (sizeof(packfile_t)), "packfile");
Sys_FileSeek(packhandle, header.dirofs);
Sys_FileRead(packhandle, (void *) info, header.dirlen);
CRC_Init(&crc);
for (i = 0; i < header.dirlen; i++)
CRC_ProcessByte(&crc, ((uint8_t *) info)[i]);
if (crc != PAK0_CRC)
com_modified = 1;
for (i = 0; i < numpackfiles; i++)
{
strcpy(newfiles[i].name, info[i].name);
newfiles[i].filepos = info[i].filepos;
newfiles[i].filelen = info[i].filelen;
}
pack = Hunk_Alloc(sizeof(pack_t));
strcpy(pack->filename, packfile);
pack->handle = packhandle;
pack->numfiles = numpackfiles;
pack->files = newfiles;
Con_Printf("Added packfile %s (%i files)\n", packfile, numpackfiles);
return pack;
}
static void COM_AddGameDirectory(char *dir)
{
int32_t i;
searchpath_t *search;
pack_t *pak;
char pakfile[MAX_OSPATH];
strcpy(com_gamedir, dir);
search = Hunk_Alloc(sizeof(searchpath_t));
strcpy(search->filename, dir);
search->next = com_searchpaths;
com_searchpaths = search;
for (i = 0;; i++)
{
sprintf(pakfile, "%s/pak%i.pak", dir, i);
pak = COM_LoadPackFile(pakfile);
if (!pak)
break;
search = Hunk_Alloc(sizeof(searchpath_t));
search->pack = pak;
search->next = com_searchpaths;
com_searchpaths = search;
}
}
static void COM_InitFilesystem(void)
{
int32_t i;
int32_t j;
char basedir[MAX_OSPATH];
searchpath_t *search;
i = COM_CheckParm("-basedir");
if (i && (i < (com_argc - 1)))
strcpy(basedir, com_argv[i + 1]);
else
strcpy(basedir, host_parms.basedir);
j = strlen(basedir);
if (j > 0)
{
if ((basedir[j - 1] == '\\') || (basedir[j - 1] == '/'))
basedir[j - 1] = 0;
}
i = COM_CheckParm("-cachedir");
if (i && (i < (com_argc - 1)))
{
if (com_argv[i + 1][0] == '-')
com_cachedir[0] = 0;
else
strcpy(com_cachedir, com_argv[i + 1]);
}
else
if (host_parms.cachedir)
strcpy(com_cachedir, host_parms.cachedir);
else
com_cachedir[0] = 0;
COM_AddGameDirectory(va("%s/id1", basedir));
if (COM_CheckParm("-rogue"))
COM_AddGameDirectory(va("%s/rogue", basedir));
if (COM_CheckParm("-hipnotic"))
COM_AddGameDirectory(va("%s/hipnotic", basedir));
i = COM_CheckParm("-game");
if (i && (i < (com_argc - 1)))
{
com_modified = 1;
COM_AddGameDirectory(va("%s/%s", basedir, com_argv[i + 1]));
}
i = COM_CheckParm("-path");
if (i)
{
com_modified = 1;
com_searchpaths = 0;
while ((++i) < com_argc)
{
if (((!com_argv[i]) || (com_argv[i][0] == '+')) || (com_argv[i][0] == '-'))
break;
search = Hunk_Alloc(sizeof(searchpath_t));
if (!strcmp(COM_FileExtension(com_argv[i]), "pak"))
{
search->pack = COM_LoadPackFile(com_argv[i]);
if (!search->pack)
Sys_Error("Couldn't load packfile: %s", com_argv[i]);
}
else
strcpy(search->filename, com_argv[i]);
search->next = com_searchpaths;
com_searchpaths = search;
}
}
if (COM_CheckParm("-proghack"))
proghack = 1;
}
void Con_ToggleConsole_f(void)
{
if (key_dest == key_console)
{
if (cls.state == ca_connected)
{
key_dest = key_game;
key_lines[edit_line][1] = 0;
key_linepos = 1;
}
else
{
M_Menu_Main_f();
}
}
else
key_dest = key_console;
SCR_EndLoadingPlaque();
memset(con_times, 0, sizeof(con_times));
}
void Con_Clear_f(void)
{
if (con_text)
memset(con_text, ' ', CON_TEXTSIZE);
}
void Con_ClearNotify(void)
{
int32_t i;
for (i = 0; i < NUM_CON_TIMES; i++)
con_times[i] = 0;
}
static void Con_MessageMode_f(void)
{
key_dest = key_message;
team_message = 0;
}
static void Con_MessageMode2_f(void)
{
key_dest = key_message;
team_message = 1;
}
void Con_CheckResize(void)
{
int32_t i;
int32_t j;
int32_t width;
int32_t oldwidth;
int32_t oldtotallines;
int32_t numlines;
int32_t numchars;
char tbuf[CON_TEXTSIZE];
width = (vid.width >> 3) - 2;
if (width == con_linewidth)
return;
if (width < 1)
{
width = 38;
con_linewidth = width;
con_totallines = CON_TEXTSIZE / con_linewidth;
memset(con_text, ' ', CON_TEXTSIZE);
}
else
{
oldwidth = con_linewidth;
con_linewidth = width;
oldtotallines = con_totallines;
con_totallines = CON_TEXTSIZE / con_linewidth;
numlines = oldtotallines;
if (con_totallines < numlines)
numlines = con_totallines;
numchars = oldwidth;
if (con_linewidth < numchars)
numchars = con_linewidth;
memcpy(tbuf, con_text, CON_TEXTSIZE);
memset(con_text, ' ', CON_TEXTSIZE);
for (i = 0; i < numlines; i++)
{
for (j = 0; j < numchars; j++)
{
con_text[(((con_totallines - 1) - i) * con_linewidth) + j] = tbuf[((((con_current - i) + oldtotallines) % oldtotallines) * oldwidth) + j];
}
}
Con_ClearNotify();
}
con_backscroll = 0;
con_current = con_totallines - 1;
}
void Con_Init(void)
{
char temp[MAXGAMEDIRLEN + 1];
char *t2 = "/qconsole.log";
con_debuglog = COM_CheckParm("-condebug");
if (con_debuglog)
{
if (strlen(com_gamedir) < (MAXGAMEDIRLEN - strlen(t2)))
{
sprintf(temp, "%s%s", com_gamedir, t2);
unlink(temp);
}
}
con_text = Hunk_AllocName(CON_TEXTSIZE, "context");
memset(con_text, ' ', CON_TEXTSIZE);
con_linewidth = -1;
Con_CheckResize();
Con_Printf("Console initialized.\n");
Cvar_RegisterVariable(&con_notifytime);
Cmd_AddCommand("toggleconsole", Con_ToggleConsole_f);
Cmd_AddCommand("messagemode", Con_MessageMode_f);
Cmd_AddCommand("messagemode2", Con_MessageMode2_f);
Cmd_AddCommand("clear", Con_Clear_f);
con_initialized = 1;
}
static void Con_Linefeed(void)
{
con_x = 0;
con_current++;
memset(&con_text[(con_current % con_totallines) * con_linewidth], ' ', con_linewidth);
}
void Con_Print(char *txt)
{
int32_t y;
int32_t c;
int32_t l;
static int32_t cr;
int32_t mask;
con_backscroll = 0;
if (txt[0] == 1)
{
mask = 128;
S_LocalSound("misc/talk.wav");
txt++;
}
else
if (txt[0] == 2)
{
mask = 128;
txt++;
}
else
mask = 0;
while (c = *txt)
{
for (l = 0; l < con_linewidth; l++)
if (txt[l] <= ' ')
break;
if ((l != con_linewidth) && ((con_x + l) > con_linewidth))
con_x = 0;
txt++;
if (cr)
{
con_current--;
cr = 0;
}
if (!con_x)
{
Con_Linefeed();
if (con_current >= 0)
con_times[con_current % NUM_CON_TIMES] = realtime;
}
switch (c)
{
case '\n':
con_x = 0;
break;
case '\r':
con_x = 0;
cr = 1;
break;
default:
y = con_current % con_totallines;
con_text[(y * con_linewidth) + con_x] = c | mask;
con_x++;
if (con_x >= con_linewidth)
con_x = 0;
break;
}
}
}
static void Con_DebugLog(char *file, char *fmt, ...)
{
va_list argptr;
static char data[1024];
int32_t fd;
va_start(argptr, fmt);
vsprintf(data, fmt, argptr);
va_end(argptr);
fd = open(file, (O_WRONLY | O_CREAT) | O_APPEND, 0666);
write(fd, data, strlen(data));
close(fd);
}
void Con_Printf(char *fmt, ...)
{
va_list argptr;
char msg[MAXPRINTMSG];
static bool inupdate;
va_start(argptr, fmt);
vsprintf(msg, fmt, argptr);
va_end(argptr);
Sys_Printf("%s", msg);
if (con_debuglog)
Con_DebugLog(va("%s/qconsole.log", com_gamedir), "%s", msg);
if (!con_initialized)
return;
if (cls.state == ca_dedicated)
return;
Con_Print(msg);
if ((cls.signon != SIGNONS) && (!scr_disabled_for_loading))
{
if (!inupdate)
{
inupdate = 1;
SCR_UpdateScreen();
inupdate = 0;
}
}
}
void Con_DPrintf(char *fmt, ...)
{
va_list argptr;
char msg[MAXPRINTMSG];
if (!developer.value)
return;
va_start(argptr, fmt);
vsprintf(msg, fmt, argptr);
va_end(argptr);
Con_Printf("%s", msg);
}
void Con_SafePrintf(char *fmt, ...)
{
va_list argptr;
char msg[1024];
int32_t temp;
va_start(argptr, fmt);
vsprintf(msg, fmt, argptr);
va_end(argptr);
temp = scr_disabled_for_loading;
scr_disabled_for_loading = 1;
Con_Printf("%s", msg);
scr_disabled_for_loading = temp;
}
static void Con_DrawInput(void)
{
int32_t y;
int32_t i;
char *text;
if ((key_dest != key_console) && (!con_forcedup))
return;
text = key_lines[edit_line];
text[key_linepos] = 10 + (((int32_t) (realtime * con_cursorspeed)) & 1);
for (i = key_linepos + 1; i < con_linewidth; i++)
text[i] = ' ';
if (key_linepos >= con_linewidth)
text += (1 + key_linepos) - con_linewidth;
y = con_vislines - 16;
for (i = 0; i < con_linewidth; i++)
Draw_Character((i + 1) << 3, con_vislines - 16, text[i]);
key_lines[edit_line][key_linepos] = 0;
}
void Con_DrawNotify(void)
{
int32_t x;
int32_t v;
char *text;
int32_t i;
float time;
extern char chat_buffer[];
v = 0;
for (i = (con_current - NUM_CON_TIMES) + 1; i <= con_current; i++)
{
if (i < 0)
continue;
time = con_times[i % NUM_CON_TIMES];
if (time == 0)
continue;
time = realtime - time;
if (time > con_notifytime.value)
continue;
text = con_text + ((i % con_totallines) * con_linewidth);
clearnotify = 0;
scr_copytop = 1;
for (x = 0; x < con_linewidth; x++)
Draw_Character((x + 1) << 3, v, text[x]);
v += 8;
}
if (key_dest == key_message)
{
clearnotify = 0;
scr_copytop = 1;
x = 0;
Draw_String(8, v, "say:");
while (chat_buffer[x])
{
Draw_Character((x + 5) << 3, v, chat_buffer[x]);
x++;
}
Draw_Character((x + 5) << 3, v, 10 + (((int32_t) (realtime * con_cursorspeed)) & 1));
v += 8;
}
if (v > con_notifylines)
con_notifylines = v;
}
void Con_DrawConsole(int32_t lines, bool drawinput)
{
int32_t i;
int32_t x;
int32_t y;
int32_t rows;
char *text;
int32_t j;
if (lines <= 0)
return;
Draw_ConsoleBackground(lines);
con_vislines = lines;
rows = (lines - 16) >> 3;
y = (lines - 16) - (rows << 3);
for (i = (con_current - rows) + 1; i <= con_current; i++, y += 8)
{
j = i - con_backscroll;
if (j < 0)
j = 0;
text = con_text + ((j % con_totallines) * con_linewidth);
for (x = 0; x < con_linewidth; x++)
Draw_Character((x + 1) << 3, y, text[x]);
}
if (drawinput)
Con_DrawInput();
}
void Con_NotifyBox(char *text)
{
double t1;
double t2;
Con_Printf("\n\n\35\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\37\n");
Con_Printf(text);
Con_Printf("Press a key.\n");
Con_Printf("\35\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\37\n");
key_count = -2;
key_dest = key_console;
do
{
t1 = Sys_FloatTime();
SCR_UpdateScreen();
Sys_SendKeyEvents();
t2 = Sys_FloatTime();
realtime += t2 - t1;
}
while (key_count < 0);
Con_Printf("\n");
key_dest = key_game;
realtime = 0;
}
void CRC_Init(uint16_t *crcvalue)
{
*crcvalue = CRC_INIT_VALUE;
}
void CRC_ProcessByte(uint16_t *crcvalue, uint8_t data)
{
*crcvalue = ((*crcvalue) << 8) ^ crctable[((*crcvalue) >> 8) ^ data];
}
uint16_t CRC_Value(uint16_t crcvalue)
{
return crcvalue ^ CRC_XOR_VALUE;
}
cvar_t *Cvar_FindVar(char *var_name)
{
cvar_t *var;
for (var = cvar_vars; var; var = var->next)
if (!strcmp(var_name, var->name))
return var;
return 0;
}
float Cvar_VariableValue(char *var_name)
{
cvar_t *var;
var = Cvar_FindVar(var_name);
if (!var)
return 0;
return atof(var->string);
}
char *Cvar_VariableString(char *var_name)
{
cvar_t *var;
var = Cvar_FindVar(var_name);
if (!var)
return cvar_null_string;
return var->string;
}
char *Cvar_CompleteVariable(char *partial)
{
cvar_t *cvar;
int32_t len;
len = strlen(partial);
if (!len)
return 0;
for (cvar = cvar_vars; cvar; cvar = cvar->next)
if (!strncmp(partial, cvar->name, len))
return cvar->name;
return 0;
}
void Cvar_Set(char *var_name, char *value)
{
cvar_t *var;
bool changed;
var = Cvar_FindVar(var_name);
if (!var)
{
Con_Printf("Cvar_Set: variable %s not found\n", var_name);
return;
}
changed = strcmp(var->string, value);
Z_Free(var->string);
var->string = Z_Malloc(strlen(value) + 1);
strcpy(var->string, value);
var->value = (float) strtod(var->string, 0);
if (var->server && changed)
{
if (sv.active)
SV_BroadcastPrintf("\"%s\" changed to \"%s\"\n", var->name, var->string);
}
}
void Cvar_SetValue(char *var_name, float value)
{
char val[32];
sprintf(val, "%f", value);
Cvar_Set(var_name, val);
}
void Cvar_RegisterVariable(cvar_t *variable)
{
char *oldstr;
if (Cvar_FindVar(variable->name))
{
Con_Printf("Can't register variable %s, allready defined\n", variable->name);
return;
}
if (Cmd_Exists(variable->name))
{
Con_Printf("Cvar_RegisterVariable: %s is a command\n", variable->name);
return;
}
oldstr = variable->string;
variable->string = Z_Malloc(strlen(variable->string) + 1);
strcpy(variable->string, oldstr);
variable->value = (float) strtod(variable->string, 0);
variable->next = cvar_vars;
cvar_vars = variable;
}
bool Cvar_Command(void)
{
cvar_t *v;
v = Cvar_FindVar(Cmd_Argv(0));
if (!v)
return 0;
if (Cmd_Argc() == 1)
{
Con_Printf("\"%s\" is \"%s\"\n", v->name, v->string);
return 1;
}
Cvar_Set(v->name, Cmd_Argv(1));
return 1;
}
void Cvar_WriteVariables(FILE *f)
{
cvar_t *var;
for (var = cvar_vars; var; var = var->next)
if (var->archive)
fprintf(f, "%s \"%s\"\n", var->name, var->string);
}
void D_DrawPoly(void)
{
}
int32_t D_MipLevelForScale(float scale)
{
int32_t lmiplevel;
if (scale >= d_scalemip[0])
lmiplevel = 0;
else
if (scale >= d_scalemip[1])
lmiplevel = 1;
else
if (scale >= d_scalemip[2])
lmiplevel = 2;
else
lmiplevel = 3;
if (lmiplevel < d_minmip)
lmiplevel = d_minmip;
return lmiplevel;
}
static void D_DrawSolidSurface(surf_t *surf, int32_t color)
{
espan_t *span;
uint8_t *pdest;
int32_t u;
int32_t u2;
int32_t pix;
pix = (((color << 24) | (color << 16)) | (color << 8)) | color;
for (span = surf->spans; span; span = span->pnext)
{
pdest = ((uint8_t *) d_viewbuffer) + (screenwidth * span->v);
u = span->u;
u2 = (span->u + span->count) - 1;
((uint8_t *) pdest)[u] = pix;
if ((u2 - u) < 8)
{
for (u++; u <= u2; u++)
((uint8_t *) pdest)[u] = pix;
}
else
{
for (u++; u & 3; u++)
((uint8_t *) pdest)[u] = pix;
u2 -= 4;
for (; u <= u2; u += 4)
*((int32_t *) (((uint8_t *) pdest) + u)) = pix;
u2 += 4;
for (; u <= u2; u++)
((uint8_t *) pdest)[u] = pix;
}
}
}
static void D_CalcGradients(msurface_t *pface)
{
mplane_t *pplane;
float mipscale;
vec3_t p_temp1;
vec3_t p_saxis;
vec3_t p_taxis;
float t;
pplane = pface->plane;
mipscale = 1.0 / ((float) (1 << miplevel));
TransformVector(pface->texinfo->vecs[0], p_saxis);
TransformVector(pface->texinfo->vecs[1], p_taxis);
t = xscaleinv * mipscale;
d_sdivzstepu = p_saxis[0] * t;
d_tdivzstepu = p_taxis[0] * t;
t = yscaleinv * mipscale;
d_sdivzstepv = (-p_saxis[1]) * t;
d_tdivzstepv = (-p_taxis[1]) * t;
d_sdivzorigin = ((p_saxis[2] * mipscale) - (xcenter * d_sdivzstepu)) - (ycenter * d_sdivzstepv);
d_tdivzorigin = ((p_taxis[2] * mipscale) - (xcenter * d_tdivzstepu)) - (ycenter * d_tdivzstepv);
VectorScale(transformed_modelorg, mipscale, p_temp1);
t = 0x10000 * mipscale;
sadjust = (((fixed16_t) ((DotProduct(p_temp1, p_saxis) * 0x10000) + 0.5)) - ((pface->texturemins[0] << 16) >> miplevel)) + (pface->texinfo->vecs[0][3] * t);
tadjust = (((fixed16_t) ((DotProduct(p_temp1, p_taxis) * 0x10000) + 0.5)) - ((pface->texturemins[1] << 16) >> miplevel)) + (pface->texinfo->vecs[1][3] * t);
bbextents = ((pface->extents[0] << 16) >> miplevel) - 1;
bbextentt = ((pface->extents[1] << 16) >> miplevel) - 1;
}
void D_DrawSurfaces(void)
{
surf_t *s;
msurface_t *pface;
surfcache_t *pcurrentcache;
vec3_t world_transformed_modelorg;
vec3_t local_modelorg;
currententity = &cl_entities[0];
TransformVector(modelorg, transformed_modelorg);
VectorCopy(transformed_modelorg, world_transformed_modelorg);
if (r_drawflat.value)
{
for (s = &surfaces[1]; s < surface_p; s++)
{
if (!s->spans)
continue;
d_zistepu = s->d_zistepu;
d_zistepv = s->d_zistepv;
d_ziorigin = s->d_ziorigin;
D_DrawSolidSurface(s, ((int32_t) s->data) & 0xFF);
D_DrawZSpans(s->spans);
}
}
else
{
for (s = &surfaces[1]; s < surface_p; s++)
{
if (!s->spans)
continue;
r_drawnpolycount++;
d_zistepu = s->d_zistepu;
d_zistepv = s->d_zistepv;
d_ziorigin = s->d_ziorigin;
if (s->flags & SURF_DRAWSKY)
{
if (!r_skymade)
{
R_MakeSky();
}
D_DrawSkyScans8(s->spans);
D_DrawZSpans(s->spans);
}
else
if (s->flags & SURF_DRAWBACKGROUND)
{
d_zistepu = 0;
d_zistepv = 0;
d_ziorigin = -0.9;
D_DrawSolidSurface(s, ((int32_t) r_clearcolor.value) & 0xFF);
D_DrawZSpans(s->spans);
}
else
if (s->flags & SURF_DRAWTURB)
{
pface = s->data;
miplevel = 0;
cacheblock = (pixel_t *) (((uint8_t *) pface->texinfo->texture) + pface->texinfo->texture->offsets[0]);
cachewidth = 64;
if (s->insubmodel)
{
currententity = s->entity;
VectorSubtract(r_origin, currententity->origin, local_modelorg);
TransformVector(local_modelorg, transformed_modelorg);
R_RotateBmodel();
}
D_CalcGradients(pface);
Turbulent8(s->spans);
D_DrawZSpans(s->spans);
if (s->insubmodel)
{
currententity = &cl_entities[0];
VectorCopy(world_transformed_modelorg, transformed_modelorg);
VectorCopy(base_vpn, vpn);
VectorCopy(base_vup, vup);
VectorCopy(base_vright, vright);
VectorCopy(base_modelorg, modelorg);
R_TransformFrustum();
}
}
else
{
if (s->insubmodel)
{
currententity = s->entity;
VectorSubtract(r_origin, currententity->origin, local_modelorg);
TransformVector(local_modelorg, transformed_modelorg);
R_RotateBmodel();
}
pface = s->data;
miplevel = D_MipLevelForScale((s->nearzi * scale_for_mip) * pface->texinfo->mipadjust);
pcurrentcache = D_CacheSurface(pface, miplevel);
cacheblock = (pixel_t *) pcurrentcache->data;
cachewidth = pcurrentcache->width;
D_CalcGradients(pface);
(*d_drawspans)(s->spans);
D_DrawZSpans(s->spans);
if (s->insubmodel)
{
currententity = &cl_entities[0];
VectorCopy(world_transformed_modelorg, transformed_modelorg);
VectorCopy(base_vpn, vpn);
VectorCopy(base_vup, vup);
VectorCopy(base_vright, vright);
VectorCopy(base_modelorg, modelorg);
R_TransformFrustum();
}
}
}
}
}
void D_FillRect(const vrect_t *vrect, const int32_t color)
{
int32_t rx = vrect->x;
int32_t ry = vrect->y;
int32_t rwidth = vrect->width;
int32_t rheight = vrect->height;
if (rx < 0)
{
rwidth += rx;
rx = 0;
}
if (ry < 0)
{
rheight += ry;
ry = 0;
}
const int32_t vwidth = (vid.width > ((uint32_t) INT32_MAX)) ? (INT32_MAX) : ((int32_t) vid.width);
const int32_t vheight = (vid.height > ((uint32_t) INT32_MAX)) ? (INT32_MAX) : ((int32_t) vid.height);
if (rx > vwidth)
rwidth = 0;
else
if (rwidth > (vwidth - rx))
rwidth = vwidth - rx;
if (ry > vheight)
rheight = 0;
else
if (rheight > (vheight - ry))
rheight = vheight - ry;
if ((rwidth < 1) || (rheight < 1))
return;
uint8_t *dest = (vid.buffer + (((size_t) ry) * vid.rowbytes)) + rx;
if ((((rwidth & 3) == 0) && ((((uintptr_t) dest) & 3u) == 0)) && ((vid.rowbytes & 3) == 0))
{
uint32_t *ldest = (uint32_t *) dest;
const uint32_t fill32 = 0x01010101u * ((uint8_t) color);
const int32_t words = rwidth >> 2;
for (int32_t y = 0; y < rheight; y++)
{
for (int32_t x = 0; x < words; x++)
ldest[x] = fill32;
ldest = (uint32_t *) (((uint8_t *) ldest) + vid.rowbytes);
}
}
else
{
const uint8_t fill8 = (uint8_t) color;
for (int32_t y = 0; y < rheight; y++)
{
for (int32_t x = 0; x < rwidth; x++)
dest[x] = fill8;
dest += vid.rowbytes;
}
}
}
void D_Init(void)
{
r_skydirect = 1;
Cvar_RegisterVariable(&d_subdiv16);
Cvar_RegisterVariable(&d_mipcap);
Cvar_RegisterVariable(&d_mipscale);
r_drawpolys = 0;
r_worldpolysbacktofront = 0;
r_recursiveaffinetriangles = 1;
r_pixbytes = 1;
r_aliasuvscale = 1.0;
}
void D_EnableBackBufferAccess(void)
{
VID_LockBuffer();
}
void D_TurnZOn(void)
{
}
void D_DisableBackBufferAccess(void)
{
VID_UnlockBuffer();
}
void D_SetupFrame(void)
{
int32_t i;
if (r_dowarp)
d_viewbuffer = r_warpbuffer;
else
d_viewbuffer = (void *) ((uint8_t *) vid.buffer);
if (r_dowarp)
screenwidth = WARP_WIDTH;
else
screenwidth = vid.rowbytes;
d_roverwrapped = 0;
d_initial_rover = sc_rover;
d_minmip = d_mipcap.value;
if (d_minmip > 3)
d_minmip = 3;
else
if (d_minmip < 0)
d_minmip = 0;
for (i = 0; i < (NUM_MIPS - 1); i++)
d_scalemip[i] = basemip[i] * d_mipscale.value;
d_drawspans = D_DrawSpans8;
d_aflatcolor = 0;
}
void D_UpdateRects(vrect_t *prect)
{
UNUSED(prect);
}
void D_ViewChanged(void)
{
int32_t rowbytes;
if (r_dowarp)
rowbytes = WARP_WIDTH;
else
rowbytes = vid.rowbytes;
scale_for_mip = xscale;
if (yscale > xscale)
scale_for_mip = yscale;
d_zrowbytes = vid.width * 2;
d_zwidth = vid.width;
d_pix_min = r_refdef.vrect.width / 320;
if (d_pix_min < 1)
d_pix_min = 1;
d_pix_max = (int32_t) ((((float) r_refdef.vrect.width) / (320.0 / 4.0)) + 0.5);
d_pix_shift = 8 - ((int32_t) ((((float) r_refdef.vrect.width) / 320.0) + 0.5));
if (d_pix_max < 1)
d_pix_max = 1;
if (pixelAspect > 1.4)
d_y_aspect_shift = 1;
else
d_y_aspect_shift = 0;
d_vrectx = r_refdef.vrect.x;
d_vrecty = r_refdef.vrect.y;
d_vrectright_particle = r_refdef.vrectright - d_pix_max;
d_vrectbottom_particle = r_refdef.vrectbottom - (d_pix_max << d_y_aspect_shift);
{
int32_t i;
for (i = 0; i < vid.height; i++)
{
d_scantable[i] = i * rowbytes;
zspantable[i] = d_pzbuffer + (i * d_zwidth);
}
}
}
void D_EndParticles(void)
{
}
void D_StartParticles(void)
{
}
void D_DrawParticle(particle_t *pparticle)
{
vec3_t local;
vec3_t transformed;
float zi;
uint8_t *pdest;
int16_t *pz;
int32_t i;
int32_t izi;
int32_t pix;
int32_t count;
int32_t u;
int32_t v;
VectorSubtract(pparticle->org, r_origin, local);
transformed[0] = DotProduct(local, r_pright);
transformed[1] = DotProduct(local, r_pup);
transformed[2] = DotProduct(local, r_ppn);
if (transformed[2] < PARTICLE_Z_CLIP)
return;
zi = 1.0 / transformed[2];
u = (int32_t) ((xcenter + (zi * transformed[0])) + 0.5);
v = (int32_t) ((ycenter - (zi * transformed[1])) + 0.5);
if ((((v > d_vrectbottom_particle) || (u > d_vrectright_particle)) || (v < d_vrecty)) || (u < d_vrectx))
{
return;
}
pz = (d_pzbuffer + (d_zwidth * v)) + u;
pdest = (d_viewbuffer + d_scantable[v]) + u;
izi = (int32_t) (zi * 0x8000);
pix = izi >> d_pix_shift;
if (pix < d_pix_min)
pix = d_pix_min;
else
if (pix > d_pix_max)
pix = d_pix_max;
switch (pix)
{
case 1:
count = 1 << d_y_aspect_shift;
for (; count; count--, pz += d_zwidth, pdest += screenwidth)
{
if (pz[0] <= izi)
{
pz[0] = izi;
pdest[0] = pparticle->color;
}
}
break;
case 2:
count = 2 << d_y_aspect_shift;
for (; count; count--, pz += d_zwidth, pdest += screenwidth)
{
if (pz[0] <= izi)
{
pz[0] = izi;
pdest[0] = pparticle->color;
}
if (pz[1] <= izi)
{
pz[1] = izi;
pdest[1] = pparticle->color;
}
}
break;
case 3:
count = 3 << d_y_aspect_shift;
for (; count; count--, pz += d_zwidth, pdest += screenwidth)
{
if (pz[0] <= izi)
{
pz[0] = izi;
pdest[0] = pparticle->color;
}
if (pz[1] <= izi)
{
pz[1] = izi;
pdest[1] = pparticle->color;
}
if (pz[2] <= izi)
{
pz[2] = izi;
pdest[2] = pparticle->color;
}
}
break;
case 4:
count = 4 << d_y_aspect_shift;
for (; count; count--, pz += d_zwidth, pdest += screenwidth)
{
if (pz[0] <= izi)
{
pz[0] = izi;
pdest[0] = pparticle->color;
}
if (pz[1] <= izi)
{
pz[1] = izi;
pdest[1] = pparticle->color;
}
if (pz[2] <= izi)
{
pz[2] = izi;
pdest[2] = pparticle->color;
}
if (pz[3] <= izi)
{
pz[3] = izi;
pdest[3] = pparticle->color;
}
}
break;
default:
count = pix << d_y_aspect_shift;
for (; count; count--, pz += d_zwidth, pdest += screenwidth)
{
for (i = 0; i < pix; i++)
{
if (pz[i] <= izi)
{
pz[i] = izi;
pdest[i] = pparticle->color;
}
}
}
break;
}
}
void D_PolysetDraw(void)
{
spanpackage_t spans[((DPS_MAXSPANS + 1) + ((CACHE_SIZE - 1) / (sizeof(spanpackage_t)))) + 1];
a_spans = (spanpackage_t *) ALIGN_PTR(&spans[0], CACHE_SIZE);
if (r_affinetridesc.drawtype)
{
D_DrawSubdiv();
}
else
{
D_DrawNonSubdiv();
}
}
void D_PolysetDrawFinalVerts(finalvert_t *fv, int32_t numverts)
{
int32_t i;
int32_t z;
int16_t *zbuf;
for (i = 0; i < numverts; i++, fv++)
{
if ((fv->v[0] < r_refdef.vrectright) && (fv->v[1] < r_refdef.vrectbottom))
{
z = fv->v[5] >> 16;
if (z > 0x7FFF)
z = 0x7FFF;
if (z < 0)
z = 0;
zbuf = zspantable[fv->v[1]] + fv->v[0];
if (z >= (*zbuf))
{
int32_t pix;
*zbuf = z;
int s = fv->v[2] >> 16;
int t = fv->v[3] >> 16;
if (((unsigned) s) >= ((unsigned) skinwidth))
s = skinwidth - 1;
if (((unsigned) t) >= ((unsigned) MAX_LBM_HEIGHT))
t = MAX_LBM_HEIGHT - 1;
pix = skintable[t][s];
pix = ((uint8_t *) acolormap)[pix + (fv->v[4] & 0xFF00)];
d_viewbuffer[d_scantable[fv->v[1]] + fv->v[0]] = pix;
}
}
}
}
static void D_DrawSubdiv(void)
{
mtriangle_t *ptri;
finalvert_t *pfv;
finalvert_t *index0;
finalvert_t *index1;
finalvert_t *index2;
int32_t i;
int32_t lnumtriangles;
pfv = r_affinetridesc.pfinalverts;
ptri = r_affinetridesc.ptriangles;
lnumtriangles = r_affinetridesc.numtriangles;
for (i = 0; i < lnumtriangles; i++)
{
index0 = pfv + ptri[i].vertindex[0];
index1 = pfv + ptri[i].vertindex[1];
index2 = pfv + ptri[i].vertindex[2];
if ((((index0->v[1] - index1->v[1]) * (index0->v[0] - index2->v[0])) - ((index0->v[0] - index1->v[0]) * (index0->v[1] - index2->v[1]))) >= 0)
{
continue;
}
d_pcolormap = &((uint8_t *) acolormap)[index0->v[4] & 0xFF00];
if (ptri[i].facesfront)
{
D_PolysetRecursiveTriangle(index0->v, index1->v, index2->v);
}
else
{
int32_t s0;
int32_t s1;
int32_t s2;
s0 = index0->v[2];
s1 = index1->v[2];
s2 = index2->v[2];
if (index0->flags & ALIAS_ONSEAM)
index0->v[2] += r_affinetridesc.seamfixupX16;
if (index1->flags & ALIAS_ONSEAM)
index1->v[2] += r_affinetridesc.seamfixupX16;
if (index2->flags & ALIAS_ONSEAM)
index2->v[2] += r_affinetridesc.seamfixupX16;
D_PolysetRecursiveTriangle(index0->v, index1->v, index2->v);
index0->v[2] = s0;
index1->v[2] = s1;
index2->v[2] = s2;
}
}
}
static void D_DrawNonSubdiv(void)
{
mtriangle_t *ptri;
finalvert_t *pfv;
finalvert_t *index0;
finalvert_t *index1;
finalvert_t *index2;
int32_t i;
int32_t lnumtriangles;
pfv = r_affinetridesc.pfinalverts;
ptri = r_affinetridesc.ptriangles;
lnumtriangles = r_affinetridesc.numtriangles;
for (i = 0; i < lnumtriangles; i++, ptri++)
{
index0 = pfv + ptri->vertindex[0];
index1 = pfv + ptri->vertindex[1];
index2 = pfv + ptri->vertindex[2];
d_xdenom = ((index0->v[1] - index1->v[1]) * (index0->v[0] - index2->v[0])) - ((index0->v[0] - index1->v[0]) * (index0->v[1] - index2->v[1]));
if (d_xdenom >= 0)
{
continue;
}
r_p0[0] = index0->v[0];
r_p0[1] = index0->v[1];
r_p0[2] = index0->v[2];
r_p0[3] = index0->v[3];
r_p0[4] = index0->v[4];
r_p0[5] = index0->v[5];
r_p1[0] = index1->v[0];
r_p1[1] = index1->v[1];
r_p1[2] = index1->v[2];
r_p1[3] = index1->v[3];
r_p1[4] = index1->v[4];
r_p1[5] = index1->v[5];
r_p2[0] = index2->v[0];
r_p2[1] = index2->v[1];
r_p2[2] = index2->v[2];
r_p2[3] = index2->v[3];
r_p2[4] = index2->v[4];
r_p2[5] = index2->v[5];
if (!ptri->facesfront)
{
if (index0->flags & ALIAS_ONSEAM)
r_p0[2] += r_affinetridesc.seamfixupX16;
if (index1->flags & ALIAS_ONSEAM)
r_p1[2] += r_affinetridesc.seamfixupX16;
if (index2->flags & ALIAS_ONSEAM)
r_p2[2] += r_affinetridesc.seamfixupX16;
}
D_PolysetSetEdgeTable();
D_RasterizeAliasPolySmooth();
}
}
static void D_PolysetRecursiveTriangle(int32_t *lp1, int32_t *lp2, int32_t *lp3)
{
int32_t *temp;
int32_t d;
int32_t new[6];
int32_t z;
int16_t *zbuf;
d = lp2[0] - lp1[0];
if ((d < (-1)) || (d > 1))
goto split;
d = lp2[1] - lp1[1];
if ((d < (-1)) || (d > 1))
goto split;
d = lp3[0] - lp2[0];
if ((d < (-1)) || (d > 1))
goto split2;
d = lp3[1] - lp2[1];
if ((d < (-1)) || (d > 1))
goto split2;
d = lp1[0] - lp3[0];
if ((d < (-1)) || (d > 1))
goto split3;
d = lp1[1] - lp3[1];
if ((d < (-1)) || (d > 1))
{
split3:
temp = lp1;
lp1 = lp3;
lp3 = lp2;
lp2 = temp;
goto split;
}
return;
split2:
temp = lp1;
lp1 = lp2;
lp2 = lp3;
lp3 = temp;
split:
new[0] = (lp1[0] + lp2[0]) >> 1;
new[1] = (lp1[1] + lp2[1]) >> 1;
new[2] = (lp1[2] + lp2[2]) >> 1;
new[3] = (lp1[3] + lp2[3]) >> 1;
new[5] = (lp1[5] + lp2[5]) >> 1;
if (lp2[1] > lp1[1])
goto nodraw;
if ((lp2[1] == lp1[1]) && (lp2[0] < lp1[0]))
goto nodraw;
z = new[5] >> 16;
zbuf = zspantable[new[1]] + new[0];
if (z >= (*zbuf))
{
int32_t pix;
*zbuf = z;
pix = d_pcolormap[skintable[new[3] >> 16][new[2] >> 16]];
d_viewbuffer[d_scantable[new[1]] + new[0]] = pix;
}
nodraw:
D_PolysetRecursiveTriangle(lp3, lp1, new);
D_PolysetRecursiveTriangle(lp3, new, lp2);
}
void D_PolysetUpdateTables(void)
{
int32_t i;
uint8_t *s;
if ((r_affinetridesc.skinwidth != skinwidth) || (r_affinetridesc.pskin != skinstart))
{
skinwidth = r_affinetridesc.skinwidth;
skinstart = r_affinetridesc.pskin;
s = skinstart;
for (i = 0; i < MAX_LBM_HEIGHT; i++, s += skinwidth)
skintable[i] = s;
}
}
static void D_PolysetScanLeftEdge(int32_t height)
{
do
{
d_pedgespanpackage->pdest = d_pdest;
d_pedgespanpackage->pz = d_pz;
d_pedgespanpackage->count = d_aspancount;
d_pedgespanpackage->ptex = d_ptex;
d_pedgespanpackage->sfrac = d_sfrac;
d_pedgespanpackage->tfrac = d_tfrac;
d_pedgespanpackage->light = d_light;
d_pedgespanpackage->zi = d_zi;
d_pedgespanpackage++;
errorterm += erroradjustup;
if (errorterm >= 0)
{
d_pdest += d_pdestextrastep;
d_pz += d_pzextrastep;
d_aspancount += d_countextrastep;
d_ptex += d_ptexextrastep;
d_sfrac += d_sfracextrastep;
d_ptex += d_sfrac >> 16;
d_sfrac &= 0xFFFF;
d_tfrac += d_tfracextrastep;
if (d_tfrac & 0x10000)
{
d_ptex += r_affinetridesc.skinwidth;
d_tfrac &= 0xFFFF;
}
d_light += d_lightextrastep;
d_zi += d_ziextrastep;
errorterm -= erroradjustdown;
}
else
{
d_pdest += d_pdestbasestep;
d_pz += d_pzbasestep;
d_aspancount += ubasestep;
d_ptex += d_ptexbasestep;
d_sfrac += d_sfracbasestep;
d_ptex += d_sfrac >> 16;
d_sfrac &= 0xFFFF;
d_tfrac += d_tfracbasestep;
if (d_tfrac & 0x10000)
{
d_ptex += r_affinetridesc.skinwidth;
d_tfrac &= 0xFFFF;
}
d_light += d_lightbasestep;
d_zi += d_zibasestep;
}
}
while (--height);
}
static void D_PolysetSetUpForLineScan(fixed8_t startvertu, fixed8_t startvertv, fixed8_t endvertu, fixed8_t endvertv)
{
double dm;
double dn;
int32_t tm;
int32_t tn;
adivtab_t *ptemp;
errorterm = -1;
tm = endvertu - startvertu;
tn = endvertv - startvertv;
if (((tm <= 16) && (tm >= (-15))) && ((tn <= 16) && (tn >= (-15))))
{
ptemp = &adivtab[((tm + 15) << 5) + (tn + 15)];
ubasestep = ptemp->quotient;
erroradjustup = ptemp->remainder;
erroradjustdown = tn;
}
else
{
dm = (double) tm;
dn = (double) tn;
FloorDivMod(dm, dn, &ubasestep, &erroradjustup);
erroradjustdown = dn;
}
}
static void D_PolysetCalcGradients(int32_t skinwidth)
{
float xstepdenominv;
float ystepdenominv;
float t0;
float t1;
float p01_minus_p21;
float p11_minus_p21;
float p00_minus_p20;
float p10_minus_p20;
p00_minus_p20 = r_p0[0] - r_p2[0];
p01_minus_p21 = r_p0[1] - r_p2[1];
p10_minus_p20 = r_p1[0] - r_p2[0];
p11_minus_p21 = r_p1[1] - r_p2[1];
xstepdenominv = 1.0 / ((float) d_xdenom);
ystepdenominv = -xstepdenominv;
t0 = r_p0[4] - r_p2[4];
t1 = r_p1[4] - r_p2[4];
r_lstepx = (int32_t) ceil(((t1 * p01_minus_p21) - (t0 * p11_minus_p21)) * xstepdenominv);
r_lstepy = (int32_t) ceil(((t1 * p00_minus_p20) - (t0 * p10_minus_p20)) * ystepdenominv);
t0 = r_p0[2] - r_p2[2];
t1 = r_p1[2] - r_p2[2];
r_sstepx = (int32_t) (((t1 * p01_minus_p21) - (t0 * p11_minus_p21)) * xstepdenominv);
r_sstepy = (int32_t) (((t1 * p00_minus_p20) - (t0 * p10_minus_p20)) * ystepdenominv);
t0 = r_p0[3] - r_p2[3];
t1 = r_p1[3] - r_p2[3];
r_tstepx = (int32_t) (((t1 * p01_minus_p21) - (t0 * p11_minus_p21)) * xstepdenominv);
r_tstepy = (int32_t) (((t1 * p00_minus_p20) - (t0 * p10_minus_p20)) * ystepdenominv);
t0 = r_p0[5] - r_p2[5];
t1 = r_p1[5] - r_p2[5];
r_zistepx = (int32_t) (((t1 * p01_minus_p21) - (t0 * p11_minus_p21)) * xstepdenominv);
r_zistepy = (int32_t) (((t1 * p00_minus_p20) - (t0 * p10_minus_p20)) * ystepdenominv);
a_sstepxfrac = r_sstepx & 0xFFFF;
a_tstepxfrac = r_tstepx & 0xFFFF;
a_ststepxwhole = (skinwidth * (r_tstepx >> 16)) + (r_sstepx >> 16);
}
static void D_PolysetDrawSpans8(spanpackage_t *pspanpackage)
{
int32_t lcount;
uint8_t *lpdest;
uint8_t *lptex;
int32_t lsfrac;
int32_t ltfrac;
int32_t llight;
int32_t lzi;
int16_t *lpz;
do
{
lcount = d_aspancount - pspanpackage->count;
errorterm += erroradjustup;
if (errorterm >= 0)
{
d_aspancount += d_countextrastep;
errorterm -= erroradjustdown;
}
else
{
d_aspancount += ubasestep;
}
if (lcount)
{
lpdest = pspanpackage->pdest;
lptex = pspanpackage->ptex;
lpz = pspanpackage->pz;
lsfrac = pspanpackage->sfrac;
ltfrac = pspanpackage->tfrac;
llight = pspanpackage->light;
lzi = pspanpackage->zi;
do
{
if ((lzi >> 16) >= (*lpz))
{
*lpdest = ((uint8_t *) acolormap)[(*lptex) + (llight & 0xFF00)];
*lpz = lzi >> 16;
}
lpdest++;
lzi += r_zistepx;
lpz++;
llight += r_lstepx;
lptex += a_ststepxwhole;
lsfrac += a_sstepxfrac;
lptex += lsfrac >> 16;
lsfrac &= 0xFFFF;
ltfrac += a_tstepxfrac;
if (ltfrac & 0x10000)
{
lptex += r_affinetridesc.skinwidth;
ltfrac &= 0xFFFF;
}
}
while (--lcount);
}
pspanpackage++;
}
while (pspanpackage->count != (-999999));
}
static void D_PolysetFillSpans8(spanpackage_t *pspanpackage)
{
int32_t color;
color = d_aflatcolor++;
while (1)
{
int32_t lcount;
uint8_t *lpdest;
lcount = pspanpackage->count;
if (lcount == (-1))
return;
if (lcount)
{
lpdest = pspanpackage->pdest;
do
{
*(lpdest++) = color;
}
while (--lcount);
}
pspanpackage++;
}
}
static void D_RasterizeAliasPolySmooth(void)
{
int32_t initialleftheight;
int32_t initialrightheight;
int32_t *plefttop;
int32_t *prighttop;
int32_t *pleftbottom;
int32_t *prightbottom;
int32_t working_lstepx;
int32_t originalcount;
plefttop = pedgetable->pleftedgevert0;
prighttop = pedgetable->prightedgevert0;
pleftbottom = pedgetable->pleftedgevert1;
prightbottom = pedgetable->prightedgevert1;
initialleftheight = pleftbottom[1] - plefttop[1];
initialrightheight = prightbottom[1] - prighttop[1];
D_PolysetCalcGradients(r_affinetridesc.skinwidth);
d_pedgespanpackage = a_spans;
ystart = plefttop[1];
d_aspancount = plefttop[0] - prighttop[0];
d_ptex = (((uint8_t *) r_affinetridesc.pskin) + (plefttop[2] >> 16)) + ((plefttop[3] >> 16) * r_affinetridesc.skinwidth);
d_sfrac = plefttop[2] & 0xFFFF;
d_tfrac = plefttop[3] & 0xFFFF;
d_light = plefttop[4];
d_zi = plefttop[5];
d_pdest = (((uint8_t *) d_viewbuffer) + (ystart * screenwidth)) + plefttop[0];
d_pz = (d_pzbuffer + (ystart * d_zwidth)) + plefttop[0];
if (initialleftheight == 1)
{
d_pedgespanpackage->pdest = d_pdest;
d_pedgespanpackage->pz = d_pz;
d_pedgespanpackage->count = d_aspancount;
d_pedgespanpackage->ptex = d_ptex;
d_pedgespanpackage->sfrac = d_sfrac;
d_pedgespanpackage->tfrac = d_tfrac;
d_pedgespanpackage->light = d_light;
d_pedgespanpackage->zi = d_zi;
d_pedgespanpackage++;
}
else
{
D_PolysetSetUpForLineScan(plefttop[0], plefttop[1], pleftbottom[0], pleftbottom[1]);
d_pzbasestep = d_zwidth + ubasestep;
d_pzextrastep = d_pzbasestep + 1;
d_pdestbasestep = screenwidth + ubasestep;
d_pdestextrastep = d_pdestbasestep + 1;
if (ubasestep < 0)
working_lstepx = r_lstepx - 1;
else
working_lstepx = r_lstepx;
d_countextrastep = ubasestep + 1;
d_ptexbasestep = ((r_sstepy + (r_sstepx * ubasestep)) >> 16) + (((r_tstepy + (r_tstepx * ubasestep)) >> 16) * r_affinetridesc.skinwidth);
d_sfracbasestep = (r_sstepy + (r_sstepx * ubasestep)) & 0xFFFF;
d_tfracbasestep = (r_tstepy + (r_tstepx * ubasestep)) & 0xFFFF;
d_lightbasestep = r_lstepy + (working_lstepx * ubasestep);
d_zibasestep = r_zistepy + (r_zistepx * ubasestep);
d_ptexextrastep = ((r_sstepy + (r_sstepx * d_countextrastep)) >> 16) + (((r_tstepy + (r_tstepx * d_countextrastep)) >> 16) * r_affinetridesc.skinwidth);
d_sfracextrastep = (r_sstepy + (r_sstepx * d_countextrastep)) & 0xFFFF;
d_tfracextrastep = (r_tstepy + (r_tstepx * d_countextrastep)) & 0xFFFF;
d_lightextrastep = d_lightbasestep + working_lstepx;
d_ziextrastep = d_zibasestep + r_zistepx;
D_PolysetScanLeftEdge(initialleftheight);
}
if (pedgetable->numleftedges == 2)
{
int32_t height;
plefttop = pleftbottom;
pleftbottom = pedgetable->pleftedgevert2;
height = pleftbottom[1] - plefttop[1];
ystart = plefttop[1];
d_aspancount = plefttop[0] - prighttop[0];
d_ptex = (((uint8_t *) r_affinetridesc.pskin) + (plefttop[2] >> 16)) + ((plefttop[3] >> 16) * r_affinetridesc.skinwidth);
d_sfrac = 0;
d_tfrac = 0;
d_light = plefttop[4];
d_zi = plefttop[5];
d_pdest = (((uint8_t *) d_viewbuffer) + (ystart * screenwidth)) + plefttop[0];
d_pz = (d_pzbuffer + (ystart * d_zwidth)) + plefttop[0];
if (height == 1)
{
d_pedgespanpackage->pdest = d_pdest;
d_pedgespanpackage->pz = d_pz;
d_pedgespanpackage->count = d_aspancount;
d_pedgespanpackage->ptex = d_ptex;
d_pedgespanpackage->sfrac = d_sfrac;
d_pedgespanpackage->tfrac = d_tfrac;
d_pedgespanpackage->light = d_light;
d_pedgespanpackage->zi = d_zi;
d_pedgespanpackage++;
}
else
{
D_PolysetSetUpForLineScan(plefttop[0], plefttop[1], pleftbottom[0], pleftbottom[1]);
d_pdestbasestep = screenwidth + ubasestep;
d_pdestextrastep = d_pdestbasestep + 1;
d_pzbasestep = d_zwidth + ubasestep;
d_pzextrastep = d_pzbasestep + 1;
if (ubasestep < 0)
working_lstepx = r_lstepx - 1;
else
working_lstepx = r_lstepx;
d_countextrastep = ubasestep + 1;
d_ptexbasestep = ((r_sstepy + (r_sstepx * ubasestep)) >> 16) + (((r_tstepy + (r_tstepx * ubasestep)) >> 16) * r_affinetridesc.skinwidth);
d_sfracbasestep = (r_sstepy + (r_sstepx * ubasestep)) & 0xFFFF;
d_tfracbasestep = (r_tstepy + (r_tstepx * ubasestep)) & 0xFFFF;
d_lightbasestep = r_lstepy + (working_lstepx * ubasestep);
d_zibasestep = r_zistepy + (r_zistepx * ubasestep);
d_ptexextrastep = ((r_sstepy + (r_sstepx * d_countextrastep)) >> 16) + (((r_tstepy + (r_tstepx * d_countextrastep)) >> 16) * r_affinetridesc.skinwidth);
d_sfracextrastep = (r_sstepy + (r_sstepx * d_countextrastep)) & 0xFFFF;
d_tfracextrastep = (r_tstepy + (r_tstepx * d_countextrastep)) & 0xFFFF;
d_lightextrastep = d_lightbasestep + working_lstepx;
d_ziextrastep = d_zibasestep + r_zistepx;
D_PolysetScanLeftEdge(height);
}
}
d_pedgespanpackage = a_spans;
D_PolysetSetUpForLineScan(prighttop[0], prighttop[1], prightbottom[0], prightbottom[1]);
d_aspancount = 0;
d_countextrastep = ubasestep + 1;
originalcount = a_spans[initialrightheight].count;
a_spans[initialrightheight].count = -999999;
D_PolysetDrawSpans8(a_spans);
if (pedgetable->numrightedges == 2)
{
int32_t height;
spanpackage_t *pstart;
pstart = a_spans + initialrightheight;
pstart->count = originalcount;
d_aspancount = prightbottom[0] - prighttop[0];
prighttop = prightbottom;
prightbottom = pedgetable->prightedgevert2;
height = prightbottom[1] - prighttop[1];
D_PolysetSetUpForLineScan(prighttop[0], prighttop[1], prightbottom[0], prightbottom[1]);
d_countextrastep = ubasestep + 1;
a_spans[initialrightheight + height].count = -999999;
D_PolysetDrawSpans8(pstart);
}
}
static void D_PolysetSetEdgeTable(void)
{
int32_t edgetableindex;
edgetableindex = 0;
if (r_p0[1] >= r_p1[1])
{
if (r_p0[1] == r_p1[1])
{
if (r_p0[1] < r_p2[1])
pedgetable = &edgetables[2];
else
pedgetable = &edgetables[5];
return;
}
else
{
edgetableindex = 1;
}
}
if (r_p0[1] == r_p2[1])
{
if (edgetableindex)
pedgetable = &edgetables[8];
else
pedgetable = &edgetables[9];
return;
}
else
if (r_p1[1] == r_p2[1])
{
if (edgetableindex)
pedgetable = &edgetables[10];
else
pedgetable = &edgetables[11];
return;
}
if (r_p0[1] > r_p2[1])
edgetableindex += 2;
if (r_p1[1] > r_p2[1])
edgetableindex += 4;
pedgetable = &edgetables[edgetableindex];
}
void D_WarpScreen(void)
{
int32_t w;
int32_t h;
int32_t u;
int32_t v;
uint8_t *dest;
int32_t *turb;
int32_t *col;
uint8_t **row;
uint8_t *rowptr[MAXHEIGHT + (AMP2 * 2)];
int32_t column[MAXWIDTH + (AMP2 * 2)];
float wratio;
float hratio;
w = r_refdef.vrect.width;
h = r_refdef.vrect.height;
wratio = w / ((float) scr_vrect.width);
hratio = h / ((float) scr_vrect.height);
for (v = 0; v < (scr_vrect.height + (AMP2 * 2)); v++)
{
rowptr[v] = (d_viewbuffer + (r_refdef.vrect.y * screenwidth)) + (screenwidth * ((int32_t) (((((float) v) * hratio) * h) / (h + (AMP2 * 2)))));
}
for (u = 0; u < (scr_vrect.width + (AMP2 * 2)); u++)
{
column[u] = r_refdef.vrect.x + ((int32_t) (((((float) u) * wratio) * w) / (w + (AMP2 * 2))));
}
turb = intsintable + (((int32_t) (cl.time * SPEED)) & (CYCLE - 1));
dest = (vid.buffer + (scr_vrect.y * vid.rowbytes)) + scr_vrect.x;
for (v = 0; v < scr_vrect.height; v++, dest += vid.rowbytes)
{
col = &column[turb[v]];
row = &rowptr[v];
for (u = 0; u < scr_vrect.width; u += 4)
{
dest[u + 0] = row[turb[u + 0]][col[u + 0]];
dest[u + 1] = row[turb[u + 1]][col[u + 1]];
dest[u + 2] = row[turb[u + 2]][col[u + 2]];
dest[u + 3] = row[turb[u + 3]][col[u + 3]];
}
}
}
static void D_DrawTurbulent8Span(void)
{
int32_t sturb;
int32_t tturb;
do
{
sturb = ((r_turb_s + r_turb_turb[(r_turb_t >> 16) & (CYCLE - 1)]) >> 16) & 63;
tturb = ((r_turb_t + r_turb_turb[(r_turb_s >> 16) & (CYCLE - 1)]) >> 16) & 63;
*(r_turb_pdest++) = *((r_turb_pbase + (tturb << 6)) + sturb);
r_turb_s += r_turb_sstep;
r_turb_t += r_turb_tstep;
}
while ((--r_turb_spancount) > 0);
}
void Turbulent8(espan_t *pspan)
{
int32_t count;
fixed16_t snext;
fixed16_t tnext;
float sdivz;
float tdivz;
float zi;
float z;
float du;
float dv;
float spancountminus1;
float sdivz16stepu;
float tdivz16stepu;
float zi16stepu;
r_turb_turb = sintable + (((int32_t) (cl.time * SPEED)) & (CYCLE - 1));
r_turb_sstep = 0;
r_turb_tstep = 0;
r_turb_pbase = (unsigned char *) cacheblock;
sdivz16stepu = d_sdivzstepu * 16;
tdivz16stepu = d_tdivzstepu * 16;
zi16stepu = d_zistepu * 16;
do
{
r_turb_pdest = (unsigned char *) ((((uint8_t *) d_viewbuffer) + (screenwidth * pspan->v)) + pspan->u);
count = pspan->count;
du = (float) pspan->u;
dv = (float) pspan->v;
sdivz = (d_sdivzorigin + (dv * d_sdivzstepv)) + (du * d_sdivzstepu);
tdivz = (d_tdivzorigin + (dv * d_tdivzstepv)) + (du * d_tdivzstepu);
zi = (d_ziorigin + (dv * d_zistepv)) + (du * d_zistepu);
z = ((float) 0x10000) / zi;
r_turb_s = ((int32_t) (sdivz * z)) + sadjust;
if (r_turb_s > bbextents)
r_turb_s = bbextents;
else
if (r_turb_s < 0)
r_turb_s = 0;
r_turb_t = ((int32_t) (tdivz * z)) + tadjust;
if (r_turb_t > bbextentt)
r_turb_t = bbextentt;
else
if (r_turb_t < 0)
r_turb_t = 0;
do
{
if (count >= 16)
r_turb_spancount = 16;
else
r_turb_spancount = count;
count -= r_turb_spancount;
if (count)
{
sdivz += sdivz16stepu;
tdivz += tdivz16stepu;
zi += zi16stepu;
z = ((float) 0x10000) / zi;
snext = ((int32_t) (sdivz * z)) + sadjust;
if (snext > bbextents)
snext = bbextents;
else
if (snext < 16)
snext = 16;
tnext = ((int32_t) (tdivz * z)) + tadjust;
if (tnext > bbextentt)
tnext = bbextentt;
else
if (tnext < 16)
tnext = 16;
r_turb_sstep = (snext - r_turb_s) >> 4;
r_turb_tstep = (tnext - r_turb_t) >> 4;
}
else
{
spancountminus1 = (float) (r_turb_spancount - 1);
sdivz += d_sdivzstepu * spancountminus1;
tdivz += d_tdivzstepu * spancountminus1;
zi += d_zistepu * spancountminus1;
z = ((float) 0x10000) / zi;
snext = ((int32_t) (sdivz * z)) + sadjust;
if (snext > bbextents)
snext = bbextents;
else
if (snext < 16)
snext = 16;
tnext = ((int32_t) (tdivz * z)) + tadjust;
if (tnext > bbextentt)
tnext = bbextentt;
else
if (tnext < 16)
tnext = 16;
if (r_turb_spancount > 1)
{
r_turb_sstep = (snext - r_turb_s) / (r_turb_spancount - 1);
r_turb_tstep = (tnext - r_turb_t) / (r_turb_spancount - 1);
}
}
r_turb_s = r_turb_s & ((CYCLE << 16) - 1);
r_turb_t = r_turb_t & ((CYCLE << 16) - 1);
D_DrawTurbulent8Span();
r_turb_s = snext;
r_turb_t = tnext;
}
while (count > 0);
}
while ((pspan = pspan->pnext) != 0);
}
void D_DrawSpans8(espan_t *pspan)
{
int32_t count;
int32_t spancount;
unsigned char *pbase;
unsigned char *pdest;
fixed16_t s;
fixed16_t t;
fixed16_t snext;
fixed16_t tnext;
fixed16_t sstep;
fixed16_t tstep;
float sdivz;
float tdivz;
float zi;
float z;
float du;
float dv;
float spancountminus1;
float sdivz8stepu;
float tdivz8stepu;
float zi8stepu;
sstep = 0;
tstep = 0;
pbase = (unsigned char *) cacheblock;
sdivz8stepu = d_sdivzstepu * 8;
tdivz8stepu = d_tdivzstepu * 8;
zi8stepu = d_zistepu * 8;
do
{
pdest = (unsigned char *) ((((uint8_t *) d_viewbuffer) + (screenwidth * pspan->v)) + pspan->u);
count = pspan->count;
du = (float) pspan->u;
dv = (float) pspan->v;
sdivz = (d_sdivzorigin + (dv * d_sdivzstepv)) + (du * d_sdivzstepu);
tdivz = (d_tdivzorigin + (dv * d_tdivzstepv)) + (du * d_tdivzstepu);
zi = (d_ziorigin + (dv * d_zistepv)) + (du * d_zistepu);
z = ((float) 0x10000) / zi;
s = ((int32_t) (sdivz * z)) + sadjust;
if (s > bbextents)
s = bbextents;
else
if (s < 0)
s = 0;
t = ((int32_t) (tdivz * z)) + tadjust;
if (t > bbextentt)
t = bbextentt;
else
if (t < 0)
t = 0;
do
{
if (count >= 8)
spancount = 8;
else
spancount = count;
count -= spancount;
if (count)
{
sdivz += sdivz8stepu;
tdivz += tdivz8stepu;
zi += zi8stepu;
z = ((float) 0x10000) / zi;
snext = ((int32_t) (sdivz * z)) + sadjust;
if (snext > bbextents)
snext = bbextents;
else
if (snext < 8)
snext = 8;
tnext = ((int32_t) (tdivz * z)) + tadjust;
if (tnext > bbextentt)
tnext = bbextentt;
else
if (tnext < 8)
tnext = 8;
sstep = (snext - s) >> 3;
tstep = (tnext - t) >> 3;
}
else
{
spancountminus1 = (float) (spancount - 1);
sdivz += d_sdivzstepu * spancountminus1;
tdivz += d_tdivzstepu * spancountminus1;
zi += d_zistepu * spancountminus1;
z = ((float) 0x10000) / zi;
snext = ((int32_t) (sdivz * z)) + sadjust;
if (snext > bbextents)
snext = bbextents;
else
if (snext < 8)
snext = 8;
tnext = ((int32_t) (tdivz * z)) + tadjust;
if (tnext > bbextentt)
tnext = bbextentt;
else
if (tnext < 8)
tnext = 8;
if (spancount > 1)
{
sstep = (snext - s) / (spancount - 1);
tstep = (tnext - t) / (spancount - 1);
}
}
do
{
*(pdest++) = *((pbase + (s >> 16)) + ((t >> 16) * cachewidth));
s += sstep;
t += tstep;
}
while ((--spancount) > 0);
s = snext;
t = tnext;
}
while (count > 0);
}
while ((pspan = pspan->pnext) != 0);
}
void D_DrawZSpans(espan_t *pspan)
{
int32_t count;
int32_t doublecount;
int32_t izistep;
int32_t izi;
int16_t *pdest;
uint32_t ltemp;
double zi;
float du;
float dv;
izistep = (int32_t) ((d_zistepu * 0x8000) * 0x10000);
do
{
pdest = (d_pzbuffer + (d_zwidth * pspan->v)) + pspan->u;
count = pspan->count;
du = (float) pspan->u;
dv = (float) pspan->v;
zi = (d_ziorigin + (dv * d_zistepv)) + (du * d_zistepu);
izi = (int32_t) ((zi * 0x8000) * 0x10000);
if (((int32_t) pdest) & 0x02)
{
*(pdest++) = (int16_t) (izi >> 16);
izi += izistep;
count--;
}
if ((doublecount = count >> 1) > 0)
{
do
{
ltemp = izi >> 16;
izi += izistep;
ltemp |= izi & 0xFFFF0000;
izi += izistep;
*((int32_t *) pdest) = ltemp;
pdest += 2;
}
while ((--doublecount) > 0);
}
if (count & 1)
*pdest = (int16_t) (izi >> 16);
}
while ((pspan = pspan->pnext) != 0);
}
static void D_Sky_uv_To_st(int32_t u, int32_t v, fixed16_t *s, fixed16_t *t)
{
float wu;
float wv;
float temp;
vec3_t end;
if (r_refdef.vrect.width >= r_refdef.vrect.height)
temp = (float) r_refdef.vrect.width;
else
temp = (float) r_refdef.vrect.height;
wu = (8192.0 * ((float) (u - (((int32_t) vid.width) >> 1)))) / temp;
wv = (8192.0 * ((float) ((((int32_t) vid.height) >> 1) - v))) / temp;
end[0] = ((4096 * vpn[0]) + (wu * vright[0])) + (wv * vup[0]);
end[1] = ((4096 * vpn[1]) + (wu * vright[1])) + (wv * vup[1]);
end[2] = ((4096 * vpn[2]) + (wu * vright[2])) + (wv * vup[2]);
end[2] *= 3;
VectorNormalize(end);
temp = skytime * skyspeed;
*s = (int32_t) ((temp + ((6 * ((SKYSIZE / 2) - 1)) * end[0])) * 0x10000);
*t = (int32_t) ((temp + ((6 * ((SKYSIZE / 2) - 1)) * end[1])) * 0x10000);
}
void D_DrawSkyScans8(espan_t *pspan)
{
int32_t count;
int32_t spancount;
int32_t u;
int32_t v;
unsigned char *pdest;
fixed16_t s;
fixed16_t t;
fixed16_t snext;
fixed16_t tnext;
fixed16_t sstep;
fixed16_t tstep;
int32_t spancountminus1;
sstep = 0;
tstep = 0;
do
{
pdest = (unsigned char *) ((((uint8_t *) d_viewbuffer) + (screenwidth * pspan->v)) + pspan->u);
count = pspan->count;
u = pspan->u;
v = pspan->v;
D_Sky_uv_To_st(u, v, &s, &t);
do
{
if (count >= SKY_SPAN_MAX)
spancount = SKY_SPAN_MAX;
else
spancount = count;
count -= spancount;
if (count)
{
u += spancount;
D_Sky_uv_To_st(u, v, &snext, &tnext);
sstep = (snext - s) >> SKY_SPAN_SHIFT;
tstep = (tnext - t) >> SKY_SPAN_SHIFT;
}
else
{
spancountminus1 = (float) (spancount - 1);
if (spancountminus1 > 0)
{
u += spancountminus1;
D_Sky_uv_To_st(u, v, &snext, &tnext);
sstep = (snext - s) / spancountminus1;
tstep = (tnext - t) / spancountminus1;
}
}
do
{
*(pdest++) = r_skysource[((t & R_SKY_TMASK) >> 8) + ((s & R_SKY_SMASK) >> 16)];
s += sstep;
t += tstep;
}
while ((--spancount) > 0);
s = snext;
t = tnext;
}
while (count > 0);
}
while ((pspan = pspan->pnext) != 0);
}
void D_SpriteDrawSpans(sspan_t *pspan)
{
int32_t count;
int32_t spancount;
int32_t izistep;
int32_t izi;
uint8_t *pbase;
uint8_t *pdest;
fixed16_t s;
fixed16_t t;
fixed16_t snext;
fixed16_t tnext;
fixed16_t sstep;
fixed16_t tstep;
float sdivz;
float tdivz;
float zi;
float z;
float du;
float dv;
float spancountminus1;
float sdivz8stepu;
float tdivz8stepu;
float zi8stepu;
uint8_t btemp;
int16_t *pz;
sstep = 0;
tstep = 0;
pbase = cacheblock;
sdivz8stepu = d_sdivzstepu * 8;
tdivz8stepu = d_tdivzstepu * 8;
zi8stepu = d_zistepu * 8;
izistep = (int32_t) ((d_zistepu * 0x8000) * 0x10000);
do
{
pdest = (((uint8_t *) d_viewbuffer) + (screenwidth * pspan->v)) + pspan->u;
pz = (d_pzbuffer + (d_zwidth * pspan->v)) + pspan->u;
count = pspan->count;
if (count <= 0)
goto NextSpan;
du = (float) pspan->u;
dv = (float) pspan->v;
sdivz = (d_sdivzorigin + (dv * d_sdivzstepv)) + (du * d_sdivzstepu);
tdivz = (d_tdivzorigin + (dv * d_tdivzstepv)) + (du * d_tdivzstepu);
zi = (d_ziorigin + (dv * d_zistepv)) + (du * d_zistepu);
z = ((float) 0x10000) / zi;
izi = (int32_t) ((zi * 0x8000) * 0x10000);
s = ((int32_t) (sdivz * z)) + sadjust;
if (s > bbextents)
s = bbextents;
else
if (s < 0)
s = 0;
t = ((int32_t) (tdivz * z)) + tadjust;
if (t > bbextentt)
t = bbextentt;
else
if (t < 0)
t = 0;
do
{
if (count >= 8)
spancount = 8;
else
spancount = count;
count -= spancount;
if (count)
{
sdivz += sdivz8stepu;
tdivz += tdivz8stepu;
zi += zi8stepu;
z = ((float) 0x10000) / zi;
snext = ((int32_t) (sdivz * z)) + sadjust;
if (snext > bbextents)
snext = bbextents;
else
if (snext < 8)
snext = 8;
tnext = ((int32_t) (tdivz * z)) + tadjust;
if (tnext > bbextentt)
tnext = bbextentt;
else
if (tnext < 8)
tnext = 8;
sstep = (snext - s) >> 3;
tstep = (tnext - t) >> 3;
}
else
{
spancountminus1 = (float) (spancount - 1);
sdivz += d_sdivzstepu * spancountminus1;
tdivz += d_tdivzstepu * spancountminus1;
zi += d_zistepu * spancountminus1;
z = ((float) 0x10000) / zi;
snext = ((int32_t) (sdivz * z)) + sadjust;
if (snext > bbextents)
snext = bbextents;
else
if (snext < 8)
snext = 8;
tnext = ((int32_t) (tdivz * z)) + tadjust;
if (tnext > bbextentt)
tnext = bbextentt;
else
if (tnext < 8)
tnext = 8;
if (spancount > 1)
{
sstep = (snext - s) / (spancount - 1);
tstep = (tnext - t) / (spancount - 1);
}
}
do
{
btemp = *((pbase + (s >> 16)) + ((t >> 16) * cachewidth));
if (btemp != 255)
{
if ((*pz) <= (izi >> 16))
{
*pz = izi >> 16;
*pdest = btemp;
}
}
izi += izistep;
pdest++;
pz++;
s += sstep;
t += tstep;
}
while ((--spancount) > 0);
s = snext;
t = tnext;
}
while (count > 0);
NextSpan:
pspan++;
}
while (pspan->count != DS_SPAN_LIST_END);
}
static void D_SpriteScanLeftEdge(void)
{
int32_t i;
int32_t v;
int32_t itop;
int32_t ibottom;
int32_t lmaxindex;
emitpoint_t *pvert;
emitpoint_t *pnext;
sspan_t *pspan;
float du;
float dv;
float vtop;
float vbottom;
float slope;
fixed16_t u;
fixed16_t u_step;
pspan = sprite_spans;
i = minindex;
if (i == 0)
i = r_spritedesc.nump;
lmaxindex = maxindex;
if (lmaxindex == 0)
lmaxindex = r_spritedesc.nump;
vtop = ceil(r_spritedesc.pverts[i].v);
do
{
pvert = &r_spritedesc.pverts[i];
pnext = pvert - 1;
vbottom = ceil(pnext->v);
if (vtop < vbottom)
{
du = pnext->u - pvert->u;
dv = pnext->v - pvert->v;
slope = du / dv;
u_step = (int32_t) (slope * 0x10000);
u = ((int32_t) ((pvert->u + (slope * (vtop - pvert->v))) * 0x10000)) + (0x10000 - 1);
itop = (int32_t) vtop;
ibottom = (int32_t) vbottom;
for (v = itop; v < ibottom; v++)
{
pspan->u = u >> 16;
pspan->v = v;
u += u_step;
pspan++;
}
}
vtop = vbottom;
i--;
if (i == 0)
i = r_spritedesc.nump;
}
while (i != lmaxindex);
}
static void D_SpriteScanRightEdge(void)
{
int32_t i;
int32_t v;
int32_t itop;
int32_t ibottom;
emitpoint_t *pvert;
emitpoint_t *pnext;
sspan_t *pspan;
float du;
float dv;
float vtop;
float vbottom;
float slope;
float uvert;
float unext;
float vvert;
float vnext;
fixed16_t u;
fixed16_t u_step;
pspan = sprite_spans;
i = minindex;
vvert = r_spritedesc.pverts[i].v;
if (vvert < r_refdef.fvrecty_adj)
vvert = r_refdef.fvrecty_adj;
if (vvert > r_refdef.fvrectbottom_adj)
vvert = r_refdef.fvrectbottom_adj;
vtop = ceil(vvert);
do
{
pvert = &r_spritedesc.pverts[i];
pnext = pvert + 1;
vnext = pnext->v;
if (vnext < r_refdef.fvrecty_adj)
vnext = r_refdef.fvrecty_adj;
if (vnext > r_refdef.fvrectbottom_adj)
vnext = r_refdef.fvrectbottom_adj;
vbottom = ceil(vnext);
if (vtop < vbottom)
{
uvert = pvert->u;
if (uvert < r_refdef.fvrectx_adj)
uvert = r_refdef.fvrectx_adj;
if (uvert > r_refdef.fvrectright_adj)
uvert = r_refdef.fvrectright_adj;
unext = pnext->u;
if (unext < r_refdef.fvrectx_adj)
unext = r_refdef.fvrectx_adj;
if (unext > r_refdef.fvrectright_adj)
unext = r_refdef.fvrectright_adj;
du = unext - uvert;
dv = vnext - vvert;
slope = du / dv;
u_step = (int32_t) (slope * 0x10000);
u = ((int32_t) ((uvert + (slope * (vtop - vvert))) * 0x10000)) + (0x10000 - 1);
itop = (int32_t) vtop;
ibottom = (int32_t) vbottom;
for (v = itop; v < ibottom; v++)
{
pspan->count = (u >> 16) - pspan->u;
u += u_step;
pspan++;
}
}
vtop = vbottom;
vvert = vnext;
i++;
if (i == r_spritedesc.nump)
i = 0;
}
while (i != maxindex);
pspan->count = DS_SPAN_LIST_END;
}
static void D_SpriteCalculateGradients(void)
{
vec3_t p_normal;
vec3_t p_saxis;
vec3_t p_taxis;
vec3_t p_temp1;
float distinv;
TransformVector(r_spritedesc.vpn, p_normal);
TransformVector(r_spritedesc.vright, p_saxis);
TransformVector(r_spritedesc.vup, p_taxis);
VectorInverse(p_taxis);
distinv = 1.0 / (-DotProduct(modelorg, r_spritedesc.vpn));
d_sdivzstepu = p_saxis[0] * xscaleinv;
d_tdivzstepu = p_taxis[0] * xscaleinv;
d_sdivzstepv = (-p_saxis[1]) * yscaleinv;
d_tdivzstepv = (-p_taxis[1]) * yscaleinv;
d_zistepu = (p_normal[0] * xscaleinv) * distinv;
d_zistepv = ((-p_normal[1]) * yscaleinv) * distinv;
d_sdivzorigin = (p_saxis[2] - (xcenter * d_sdivzstepu)) - (ycenter * d_sdivzstepv);
d_tdivzorigin = (p_taxis[2] - (xcenter * d_tdivzstepu)) - (ycenter * d_tdivzstepv);
d_ziorigin = ((p_normal[2] * distinv) - (xcenter * d_zistepu)) - (ycenter * d_zistepv);
TransformVector(modelorg, p_temp1);
sadjust = ((fixed16_t) ((DotProduct(p_temp1, p_saxis) * 0x10000) + 0.5)) - ((-(cachewidth >> 1)) << 16);
tadjust = ((fixed16_t) ((DotProduct(p_temp1, p_taxis) * 0x10000) + 0.5)) - ((-(sprite_height >> 1)) << 16);
bbextents = (cachewidth << 16) - 1;
bbextentt = (sprite_height << 16) - 1;
}
void D_DrawSprite(void)
{
int32_t i;
int32_t nump;
float ymin;
float ymax;
emitpoint_t *pverts;
sspan_t spans[MAXHEIGHT + 1];
sprite_spans = spans;
ymin = 999999.9;
ymax = -999999.9;
pverts = r_spritedesc.pverts;
for (i = 0; i < r_spritedesc.nump; i++)
{
if (pverts->v < ymin)
{
ymin = pverts->v;
minindex = i;
}
if (pverts->v > ymax)
{
ymax = pverts->v;
maxindex = i;
}
pverts++;
}
ymin = ceil(ymin);
ymax = ceil(ymax);
if (ymin >= ymax)
return;
cachewidth = r_spritedesc.pspriteframe->width;
sprite_height = r_spritedesc.pspriteframe->height;
cacheblock = (uint8_t *) (&r_spritedesc.pspriteframe->pixels[0]);
nump = r_spritedesc.nump;
pverts = r_spritedesc.pverts;
pverts[nump] = pverts[0];
D_SpriteCalculateGradients();
D_SpriteScanLeftEdge();
D_SpriteScanRightEdge();
D_SpriteDrawSpans(sprite_spans);
}
int32_t D_SurfaceCacheForRes(int32_t width, int32_t height)
{
int32_t size;
int32_t pix;
if (COM_CheckParm("-surfcachesize"))
{
size = ((int32_t) strtol(com_argv[COM_CheckParm("-surfcachesize") + 1], 0, 0)) * 1024;
return size;
}
size = SURFCACHE_SIZE_AT_320X200;
pix = width * height;
if (pix > 64000)
size += (pix - 64000) * 3;
return size;
}
void D_CheckCacheGuard(void)
{
uint8_t *s;
int32_t i;
s = ((uint8_t *) sc_base) + sc_size;
for (i = 0; i < GUARDSIZE; i++)
if (s[i] != ((uint8_t) i))
Sys_Error("D_CheckCacheGuard: failed");
}
void D_ClearCacheGuard(void)
{
uint8_t *s;
int32_t i;
s = ((uint8_t *) sc_base) + sc_size;
for (i = 0; i < GUARDSIZE; i++)
s[i] = (uint8_t) i;
}
void D_InitCaches(void *buffer, int32_t size)
{
if (!msg_suppress_1)
Con_Printf("%ik surface cache\n", size / 1024);
sc_size = size - GUARDSIZE;
sc_base = (surfcache_t *) buffer;
sc_rover = sc_base;
sc_base->next = 0;
sc_base->owner = 0;
sc_base->size = sc_size;
D_ClearCacheGuard();
}
void D_FlushCaches(void)
{
surfcache_t *c;
if (!sc_base)
return;
for (c = sc_base; c; c = c->next)
{
if (c->owner)
*c->owner = 0;
}
sc_rover = sc_base;
sc_base->next = 0;
sc_base->owner = 0;
sc_base->size = sc_size;
}
surfcache_t *D_SCAlloc(int32_t width, int32_t size)
{
surfcache_t *new;
bool wrapped_this_time;
if ((width < 0) || (width > 256))
Sys_Error("D_SCAlloc: bad cache width %d\n", width);
if ((size <= 0) || (size > 0x10000))
Sys_Error("D_SCAlloc: bad cache size %d\n", size);
size = (int32_t) (&((surfcache_t *) 0)->data[size]);
size = (size + 3) & (~3);
if (size > sc_size)
Sys_Error("D_SCAlloc: %i > cache size", size);
wrapped_this_time = 0;
if ((!sc_rover) || ((((uint8_t *) sc_rover) - ((uint8_t *) sc_base)) > (sc_size - size)))
{
if (sc_rover)
{
wrapped_this_time = 1;
}
sc_rover = sc_base;
}
new = sc_rover;
if (sc_rover->owner)
*sc_rover->owner = 0;
while (new->size < size)
{
sc_rover = sc_rover->next;
if (!sc_rover)
Sys_Error("D_SCAlloc: hit the end of memory");
if (sc_rover->owner)
*sc_rover->owner = 0;
new->size += sc_rover->size;
new->next = sc_rover->next;
}
if ((new->size - size) > 256)
{
sc_rover = (surfcache_t *) (((uint8_t *) new) + size);
sc_rover->size = new->size - size;
sc_rover->next = new->next;
sc_rover->width = 0;
sc_rover->owner = 0;
new->next = sc_rover;
new->size = size;
}
else
sc_rover = new->next;
new->width = width;
if (width > 0)
new->height = ((size - (sizeof(*new))) + (sizeof(new->data))) / width;
new->owner = 0;
if (d_roverwrapped)
{
if (wrapped_this_time || (sc_rover >= d_initial_rover))
r_cache_thrash = 1;
}
else
if (wrapped_this_time)
{
d_roverwrapped = 1;
}
D_CheckCacheGuard();
return new;
}
void D_SCDump(void)
{
surfcache_t *test;
for (test = sc_base; test; test = test->next)
{
if (test == sc_rover)
Sys_Printf("ROVER:\n");
printf("%p : %i bytes %i width\n", test, test->size, test->width);
}
}
int32_t MaskForNum(int32_t num)
{
if (num == 128)
return 127;
if (num == 64)
return 63;
if (num == 32)
return 31;
if (num == 16)
return 15;
return 255;
}
int32_t D_log2(int32_t num)
{
int32_t c;
c = 0;
while (num >>= 1)
c++;
return c;
}
surfcache_t *D_CacheSurface(msurface_t *surface, int32_t miplevel)
{
surfcache_t *cache;
r_drawsurf.texture = R_TextureAnimation(surface->texinfo->texture);
r_drawsurf.lightadj[0] = d_lightstylevalue[surface->styles[0]];
r_drawsurf.lightadj[1] = d_lightstylevalue[surface->styles[1]];
r_drawsurf.lightadj[2] = d_lightstylevalue[surface->styles[2]];
r_drawsurf.lightadj[3] = d_lightstylevalue[surface->styles[3]];
cache = surface->cachespots[miplevel];
if (((((((cache && (!cache->dlight)) && (surface->dlightframe != r_framecount)) && (cache->texture == r_drawsurf.texture)) && (cache->lightadj[0] == r_drawsurf.lightadj[0])) && (cache->lightadj[1] == r_drawsurf.lightadj[1])) && (cache->lightadj[2] == r_drawsurf.lightadj[2])) && (cache->lightadj[3] == r_drawsurf.lightadj[3]))
return cache;
surfscale = 1.0 / (1 << miplevel);
r_drawsurf.surfmip = miplevel;
r_drawsurf.surfwidth = surface->extents[0] >> miplevel;
r_drawsurf.rowbytes = r_drawsurf.surfwidth;
r_drawsurf.surfheight = surface->extents[1] >> miplevel;
if (!cache)
{
cache = D_SCAlloc(r_drawsurf.surfwidth, r_drawsurf.surfwidth * r_drawsurf.surfheight);
surface->cachespots[miplevel] = cache;
cache->owner = &surface->cachespots[miplevel];
cache->mipscale = surfscale;
}
if (surface->dlightframe == r_framecount)
cache->dlight = 1;
else
cache->dlight = 0;
r_drawsurf.surfdat = (pixel_t *) cache->data;
cache->texture = r_drawsurf.texture;
cache->lightadj[0] = r_drawsurf.lightadj[0];
cache->lightadj[1] = r_drawsurf.lightadj[1];
cache->lightadj[2] = r_drawsurf.lightadj[2];
cache->lightadj[3] = r_drawsurf.lightadj[3];
r_drawsurf.surf = surface;
c_surf++;
R_DrawSurface();
return surface->cachespots[miplevel];
}
void D_DrawZPoint(void)
{
uint8_t *pdest;
int16_t *pz;
int32_t izi;
pz = (d_pzbuffer + (d_zwidth * r_zpointdesc.v)) + r_zpointdesc.u;
pdest = (d_viewbuffer + d_scantable[r_zpointdesc.v]) + r_zpointdesc.u;
izi = (int32_t) (r_zpointdesc.zi * 0x8000);
if ((*pz) <= izi)
{
*pz = izi;
*pdest = r_zpointdesc.color;
}
}
qpic_t *Draw_PicFromWad(char *name)
{
return W_GetLumpName(name);
}
qpic_t *Draw_CachePic(char *path)
{
cachepic_t *pic;
int32_t i;
qpic_t *dat;
for (pic = menu_cachepics, i = 0; i < menu_numcachepics; pic++, i++)
if (!strcmp(path, pic->name))
break;
if (i == menu_numcachepics)
{
if (menu_numcachepics == MAX_CACHED_PICS)
Sys_Error("menu_numcachepics == MAX_CACHED_PICS");
menu_numcachepics++;
strcpy(pic->name, path);
}
dat = Cache_Check(&pic->cache);
if (dat)
return dat;
COM_LoadCacheFile(path, &pic->cache);
dat = (qpic_t *) pic->cache.data;
if (!dat)
{
Sys_Error("Draw_CachePic: failed to load %s", path);
}
SwapPic(dat);
return dat;
}
void Draw_Init(void)
{
int32_t i;
draw_chars = W_GetLumpName("conchars");
draw_disc = W_GetLumpName("disc");
draw_backtile = W_GetLumpName("backtile");
r_rectdesc.width = draw_backtile->width;
r_rectdesc.height = draw_backtile->height;
r_rectdesc.ptexbytes = draw_backtile->data;
r_rectdesc.rowbytes = draw_backtile->width;
}
void Draw_Character(int32_t x, int32_t y, int32_t num)
{
uint8_t *dest;
uint8_t *source;
uint16_t *pusdest;
int32_t drawline;
int32_t row;
int32_t col;
num &= 255;
if (y <= (-8))
return;
row = num >> 4;
col = num & 15;
source = (draw_chars + (row << 10)) + (col << 3);
if (y < 0)
{
drawline = 8 + y;
source -= 128 * y;
y = 0;
}
else
drawline = 8;
if (r_pixbytes == 1)
{
dest = (vid.conbuffer + (y * vid.conrowbytes)) + x;
while (drawline--)
{
if (source[0])
dest[0] = source[0];
if (source[1])
dest[1] = source[1];
if (source[2])
dest[2] = source[2];
if (source[3])
dest[3] = source[3];
if (source[4])
dest[4] = source[4];
if (source[5])
dest[5] = source[5];
if (source[6])
dest[6] = source[6];
if (source[7])
dest[7] = source[7];
source += 128;
dest += vid.conrowbytes;
}
}
else
{
pusdest = (uint16_t *) ((((uint8_t *) vid.conbuffer) + (y * vid.conrowbytes)) + (x << 1));
while (drawline--)
{
if (source[0])
pusdest[0] = d_8to16table[source[0]];
if (source[1])
pusdest[1] = d_8to16table[source[1]];
if (source[2])
pusdest[2] = d_8to16table[source[2]];
if (source[3])
pusdest[3] = d_8to16table[source[3]];
if (source[4])
pusdest[4] = d_8to16table[source[4]];
if (source[5])
pusdest[5] = d_8to16table[source[5]];
if (source[6])
pusdest[6] = d_8to16table[source[6]];
if (source[7])
pusdest[7] = d_8to16table[source[7]];
source += 128;
pusdest += vid.conrowbytes >> 1;
}
}
}
void Draw_String(int32_t x, int32_t y, char *str)
{
while (*str)
{
Draw_Character(x, y, *str);
str++;
x += 8;
}
}
void Draw_DebugChar(char num)
{
uint8_t *dest;
uint8_t *source;
int32_t drawline;
extern uint8_t *draw_chars;
int32_t row;
int32_t col;
if (!vid.direct)
return;
drawline = 8;
row = num >> 4;
col = num & 15;
source = (draw_chars + (row << 10)) + (col << 3);
dest = vid.direct + 312;
while (drawline--)
{
dest[0] = source[0];
dest[1] = source[1];
dest[2] = source[2];
dest[3] = source[3];
dest[4] = source[4];
dest[5] = source[5];
dest[6] = source[6];
dest[7] = source[7];
source += 128;
dest += 320;
}
}
void Draw_Pic(int32_t x, int32_t y, qpic_t *pic)
{
uint8_t *dest;
uint8_t *source;
uint16_t *pusdest;
int32_t v;
int32_t u;
if ((((x < 0) || ((x + pic->width) > vid.width)) || (y < 0)) || ((y + pic->height) > vid.height))
{
Sys_Error("Draw_Pic: bad coordinates");
}
source = pic->data;
if (r_pixbytes == 1)
{
dest = (vid.buffer + (y * vid.rowbytes)) + x;
for (v = 0; v < pic->height; v++)
{
memcpy(dest, source, pic->width);
dest += vid.rowbytes;
source += pic->width;
}
}
else
{
pusdest = (((uint16_t *) vid.buffer) + (y * (vid.rowbytes >> 1))) + x;
for (v = 0; v < pic->height; v++)
{
for (u = 0; u < pic->width; u++)
{
pusdest[u] = d_8to16table[source[u]];
}
pusdest += vid.rowbytes >> 1;
source += pic->width;
}
}
}
void Draw_TransPic(int32_t x, int32_t y, qpic_t *pic)
{
uint8_t *dest;
uint8_t *source;
uint8_t tbyte;
uint16_t *pusdest;
int32_t v;
int32_t u;
if ((((x < 0) || (((uint32_t) (x + pic->width)) > vid.width)) || (y < 0)) || (((uint32_t) (y + pic->height)) > vid.height))
{
Sys_Error("Draw_TransPic: bad coordinates");
}
source = pic->data;
if (r_pixbytes == 1)
{
dest = (vid.buffer + (y * vid.rowbytes)) + x;
if (pic->width & 7)
{
for (v = 0; v < pic->height; v++)
{
for (u = 0; u < pic->width; u++)
if ((tbyte = source[u]) != TRANSPARENT_COLOR)
dest[u] = tbyte;
dest += vid.rowbytes;
source += pic->width;
}
}
else
{
for (v = 0; v < pic->height; v++)
{
for (u = 0; u < pic->width; u += 8)
{
if ((tbyte = source[u]) != TRANSPARENT_COLOR)
dest[u] = tbyte;
if ((tbyte = source[u + 1]) != TRANSPARENT_COLOR)
dest[u + 1] = tbyte;
if ((tbyte = source[u + 2]) != TRANSPARENT_COLOR)
dest[u + 2] = tbyte;
if ((tbyte = source[u + 3]) != TRANSPARENT_COLOR)
dest[u + 3] = tbyte;
if ((tbyte = source[u + 4]) != TRANSPARENT_COLOR)
dest[u + 4] = tbyte;
if ((tbyte = source[u + 5]) != TRANSPARENT_COLOR)
dest[u + 5] = tbyte;
if ((tbyte = source[u + 6]) != TRANSPARENT_COLOR)
dest[u + 6] = tbyte;
if ((tbyte = source[u + 7]) != TRANSPARENT_COLOR)
dest[u + 7] = tbyte;
}
dest += vid.rowbytes;
source += pic->width;
}
}
}
else
{
pusdest = (((uint16_t *) vid.buffer) + (y * (vid.rowbytes >> 1))) + x;
for (v = 0; v < pic->height; v++)
{
for (u = 0; u < pic->width; u++)
{
tbyte = source[u];
if (tbyte != TRANSPARENT_COLOR)
{
pusdest[u] = d_8to16table[tbyte];
}
}
pusdest += vid.rowbytes >> 1;
source += pic->width;
}
}
}
void Draw_TransPicTranslate(int32_t x, int32_t y, qpic_t *pic, uint8_t *translation)
{
uint8_t *dest;
uint8_t *source;
uint8_t tbyte;
uint16_t *pusdest;
int32_t v;
int32_t u;
if ((((x < 0) || (((uint32_t) (x + pic->width)) > vid.width)) || (y < 0)) || (((uint32_t) (y + pic->height)) > vid.height))
{
Sys_Error("Draw_TransPic: bad coordinates");
}
source = pic->data;
if (r_pixbytes == 1)
{
dest = (vid.buffer + (y * vid.rowbytes)) + x;
if (pic->width & 7)
{
for (v = 0; v < pic->height; v++)
{
for (u = 0; u < pic->width; u++)
if ((tbyte = source[u]) != TRANSPARENT_COLOR)
dest[u] = translation[tbyte];
dest += vid.rowbytes;
source += pic->width;
}
}
else
{
for (v = 0; v < pic->height; v++)
{
for (u = 0; u < pic->width; u += 8)
{
if ((tbyte = source[u]) != TRANSPARENT_COLOR)
dest[u] = translation[tbyte];
if ((tbyte = source[u + 1]) != TRANSPARENT_COLOR)
dest[u + 1] = translation[tbyte];
if ((tbyte = source[u + 2]) != TRANSPARENT_COLOR)
dest[u + 2] = translation[tbyte];
if ((tbyte = source[u + 3]) != TRANSPARENT_COLOR)
dest[u + 3] = translation[tbyte];
if ((tbyte = source[u + 4]) != TRANSPARENT_COLOR)
dest[u + 4] = translation[tbyte];
if ((tbyte = source[u + 5]) != TRANSPARENT_COLOR)
dest[u + 5] = translation[tbyte];
if ((tbyte = source[u + 6]) != TRANSPARENT_COLOR)
dest[u + 6] = translation[tbyte];
if ((tbyte = source[u + 7]) != TRANSPARENT_COLOR)
dest[u + 7] = translation[tbyte];
}
dest += vid.rowbytes;
source += pic->width;
}
}
}
else
{
pusdest = (((uint16_t *) vid.buffer) + (y * (vid.rowbytes >> 1))) + x;
for (v = 0; v < pic->height; v++)
{
for (u = 0; u < pic->width; u++)
{
tbyte = source[u];
if (tbyte != TRANSPARENT_COLOR)
{
pusdest[u] = d_8to16table[tbyte];
}
}
pusdest += vid.rowbytes >> 1;
source += pic->width;
}
}
}
void Draw_CharToConback(int32_t num, uint8_t *dest)
{
int32_t row;
int32_t col;
uint8_t *source;
int32_t drawline;
int32_t x;
row = num >> 4;
col = num & 15;
source = (draw_chars + (row << 10)) + (col << 3);
drawline = 8;
while (drawline--)
{
for (x = 0; x < 8; x++)
if (source[x])
dest[x] = 0x60 + source[x];
source += 128;
dest += 320;
}
}
void Draw_ConsoleBackground(int32_t lines)
{
int32_t x;
int32_t y;
int32_t v;
uint8_t *src;
uint8_t *dest;
uint16_t *pusdest;
int32_t f;
int32_t fstep;
qpic_t *conback;
char ver[100];
conback = Draw_CachePic("gfx/conback.lmp");
dest = ((conback->data + 320) - 43) + (320 * 186);
sprintf(ver, "%4.2f", VERSION);
for (x = 0; x < strlen(ver); x++)
Draw_CharToConback(ver[x], dest + (x << 3));
if (r_pixbytes == 1)
{
dest = vid.conbuffer;
for (y = 0; y < lines; y++, dest += vid.conrowbytes)
{
v = (((vid.conheight - lines) + y) * 200) / vid.conheight;
src = conback->data + (v * 320);
if (vid.conwidth == 320)
memcpy(dest, src, vid.conwidth);
else
{
f = 0;
fstep = (320 * 0x10000) / vid.conwidth;
for (x = 0; x < vid.conwidth; x += 4)
{
dest[x] = src[f >> 16];
f += fstep;
dest[x + 1] = src[f >> 16];
f += fstep;
dest[x + 2] = src[f >> 16];
f += fstep;
dest[x + 3] = src[f >> 16];
f += fstep;
}
}
}
}
else
{
pusdest = (uint16_t *) vid.conbuffer;
for (y = 0; y < lines; y++, pusdest += vid.conrowbytes >> 1)
{
v = (((vid.conheight - lines) + y) * 200) / vid.conheight;
src = conback->data + (v * 320);
f = 0;
fstep = (320 * 0x10000) / vid.conwidth;
for (x = 0; x < vid.conwidth; x += 4)
{
pusdest[x] = d_8to16table[src[f >> 16]];
f += fstep;
pusdest[x + 1] = d_8to16table[src[f >> 16]];
f += fstep;
pusdest[x + 2] = d_8to16table[src[f >> 16]];
f += fstep;
pusdest[x + 3] = d_8to16table[src[f >> 16]];
f += fstep;
}
}
}
}
void R_DrawRect8(vrect_t *prect, int32_t rowbytes, uint8_t *psrc, int32_t transparent)
{
uint8_t t;
int32_t i;
int32_t j;
int32_t srcdelta;
int32_t destdelta;
uint8_t *pdest;
pdest = (vid.buffer + (prect->y * vid.rowbytes)) + prect->x;
srcdelta = rowbytes - prect->width;
destdelta = vid.rowbytes - prect->width;
if (transparent)
{
for (i = 0; i < prect->height; i++)
{
for (j = 0; j < prect->width; j++)
{
t = *psrc;
if (t != TRANSPARENT_COLOR)
{
*pdest = t;
}
psrc++;
pdest++;
}
psrc += srcdelta;
pdest += destdelta;
}
}
else
{
for (i = 0; i < prect->height; i++)
{
memcpy(pdest, psrc, prect->width);
psrc += rowbytes;
pdest += vid.rowbytes;
}
}
}
void R_DrawRect16(vrect_t *prect, int32_t rowbytes, uint8_t *psrc, int32_t transparent)
{
uint8_t t;
int32_t i;
int32_t j;
int32_t srcdelta;
int32_t destdelta;
uint16_t *pdest;
pdest = (((uint16_t *) vid.buffer) + (prect->y * (vid.rowbytes >> 1))) + prect->x;
srcdelta = rowbytes - prect->width;
destdelta = (vid.rowbytes >> 1) - prect->width;
if (transparent)
{
for (i = 0; i < prect->height; i++)
{
for (j = 0; j < prect->width; j++)
{
t = *psrc;
if (t != TRANSPARENT_COLOR)
{
*pdest = d_8to16table[t];
}
psrc++;
pdest++;
}
psrc += srcdelta;
pdest += destdelta;
}
}
else
{
for (i = 0; i < prect->height; i++)
{
for (j = 0; j < prect->width; j++)
{
*pdest = d_8to16table[*psrc];
psrc++;
pdest++;
}
psrc += srcdelta;
pdest += destdelta;
}
}
}
void Draw_TileClear(int32_t x, int32_t y, int32_t w, int32_t h)
{
int32_t width;
int32_t height;
int32_t tileoffsetx;
int32_t tileoffsety;
uint8_t *psrc;
vrect_t vr;
r_rectdesc.rect.x = x;
r_rectdesc.rect.y = y;
r_rectdesc.rect.width = w;
r_rectdesc.rect.height = h;
vr.y = r_rectdesc.rect.y;
height = r_rectdesc.rect.height;
tileoffsety = vr.y % r_rectdesc.height;
while (height > 0)
{
vr.x = r_rectdesc.rect.x;
width = r_rectdesc.rect.width;
if (tileoffsety != 0)
vr.height = r_rectdesc.height - tileoffsety;
else
vr.height = r_rectdesc.height;
if (vr.height > height)
vr.height = height;
tileoffsetx = vr.x % r_rectdesc.width;
while (width > 0)
{
if (tileoffsetx != 0)
vr.width = r_rectdesc.width - tileoffsetx;
else
vr.width = r_rectdesc.width;
if (vr.width > width)
vr.width = width;
psrc = (r_rectdesc.ptexbytes + (tileoffsety * r_rectdesc.rowbytes)) + tileoffsetx;
if (r_pixbytes == 1)
{
R_DrawRect8(&vr, r_rectdesc.rowbytes, psrc, 0);
}
else
{
R_DrawRect16(&vr, r_rectdesc.rowbytes, psrc, 0);
}
vr.x += vr.width;
width -= vr.width;
tileoffsetx = 0;
}
vr.y += vr.height;
height -= vr.height;
tileoffsety = 0;
}
}
void Draw_Fill(int32_t x, int32_t y, int32_t w, int32_t h, int32_t c)
{
uint8_t *dest;
uint16_t *pusdest;
uint32_t uc;
int32_t u;
int32_t v;
if (r_pixbytes == 1)
{
dest = (vid.buffer + (y * vid.rowbytes)) + x;
for (v = 0; v < h; v++, dest += vid.rowbytes)
for (u = 0; u < w; u++)
dest[u] = c;
}
else
{
uc = d_8to16table[c];
pusdest = (((uint16_t *) vid.buffer) + (y * (vid.rowbytes >> 1))) + x;
for (v = 0; v < h; v++, pusdest += vid.rowbytes >> 1)
for (u = 0; u < w; u++)
pusdest[u] = uc;
}
}
void Draw_FadeScreen(void)
{
int32_t x;
int32_t y;
uint8_t *pbuf;
VID_UnlockBuffer();
S_ExtraUpdate();
VID_LockBuffer();
for (y = 0; y < vid.height; y++)
{
int32_t t;
pbuf = (uint8_t *) (vid.buffer + (vid.rowbytes * y));
t = (y & 1) << 1;
for (x = 0; x < vid.width; x++)
{
if ((x & 3) != t)
pbuf[x] = 0;
}
}
VID_UnlockBuffer();
S_ExtraUpdate();
VID_LockBuffer();
}
void Draw_BeginDisc(void)
{
}
void Draw_EndDisc(void)
{
}
void Host_EndGame(char *message, ...)
{
va_list argptr;
char string[1024];
va_start(argptr, message);
vsprintf(string, message, argptr);
va_end(argptr);
Con_DPrintf("Host_EndGame: %s\n", string);
if (sv.active)
Host_ShutdownServer(0);
if (cls.state == ca_dedicated)
Sys_Error("Host_EndGame: %s\n", string);
if (cls.demonum != (-1))
CL_NextDemo();
else
CL_Disconnect();
longjmp(host_abortserver, 1);
}
void Host_Error(char *error, ...)
{
va_list argptr;
char string[1024];
static bool inerror = 0;
if (inerror)
Sys_Error("Host_Error: recursively entered");
inerror = 1;
SCR_EndLoadingPlaque();
va_start(argptr, error);
vsprintf(string, error, argptr);
va_end(argptr);
Con_Printf("Host_Error: %s\n", string);
if (sv.active)
Host_ShutdownServer(0);
if (cls.state == ca_dedicated)
Sys_Error("Host_Error: %s\n", string);
CL_Disconnect();
cls.demonum = -1;
inerror = 0;
longjmp(host_abortserver, 1);
}
void Host_FindMaxClients(void)
{
int32_t i;
svs.maxclients = 1;
i = COM_CheckParm("-dedicated");
if (i)
{
cls.state = ca_dedicated;
if (i != (com_argc - 1))
{
svs.maxclients = (int32_t) strtol(com_argv[i + 1], 0, 0);
}
else
svs.maxclients = 8;
}
else
cls.state = ca_disconnected;
i = COM_CheckParm("-listen");
if (i)
{
if (cls.state == ca_dedicated)
Sys_Error("Only one of -dedicated or -listen can be specified");
if (i != (com_argc - 1))
svs.maxclients = (int32_t) strtol(com_argv[i + 1], 0, 0);
else
svs.maxclients = 8;
}
if (svs.maxclients < 1)
svs.maxclients = 8;
else
if (svs.maxclients > MAX_SCOREBOARD)
svs.maxclients = MAX_SCOREBOARD;
svs.maxclientslimit = svs.maxclients;
if (svs.maxclientslimit < 4)
svs.maxclientslimit = 4;
svs.clients = Hunk_AllocName(svs.maxclientslimit * (sizeof(client_t)), "clients");
if (svs.maxclients > 1)
Cvar_SetValue("deathmatch", 1.0);
else
Cvar_SetValue("deathmatch", 0.0);
}
void Host_InitLocal(void)
{
Host_InitCommands();
Cvar_RegisterVariable(&host_framerate);
Cvar_RegisterVariable(&host_speeds);
Cvar_RegisterVariable(&sys_ticrate);
Cvar_RegisterVariable(&serverprofile);
Cvar_RegisterVariable(&fraglimit);
Cvar_RegisterVariable(&timelimit);
Cvar_RegisterVariable(&teamplay);
Cvar_RegisterVariable(&samelevel);
Cvar_RegisterVariable(&noexit);
Cvar_RegisterVariable(&skill);
Cvar_RegisterVariable(&developer);
Cvar_RegisterVariable(&deathmatch);
Cvar_RegisterVariable(&coop);
Cvar_RegisterVariable(&pausable);
Cvar_RegisterVariable(&temp1);
Host_FindMaxClients();
host_time = 1.0;
}
void Host_WriteConfiguration(void)
{
FILE *f;
if (host_initialized & (!isDedicated))
{
f = fopen(va("%s/config.cfg", com_gamedir), "w");
if (!f)
{
Con_Printf("Couldn't write config.cfg.\n");
return;
}
Key_WriteBindings(f);
Cvar_WriteVariables(f);
fclose(f);
}
}
void SV_ClientPrintf(char *fmt, ...)
{
va_list argptr;
char string[1024];
va_start(argptr, fmt);
vsprintf(string, fmt, argptr);
va_end(argptr);
MSG_WriteByte(&host_client->message, svc_print);
MSG_WriteString(&host_client->message, string);
}
void SV_BroadcastPrintf(char *fmt, ...)
{
va_list argptr;
char string[1024];
int32_t i;
va_start(argptr, fmt);
vsprintf(string, fmt, argptr);
va_end(argptr);
for (i = 0; i < svs.maxclients; i++)
if (svs.clients[i].active && svs.clients[i].spawned)
{
MSG_WriteByte(&svs.clients[i].message, svc_print);
MSG_WriteString(&svs.clients[i].message, string);
}
}
void Host_ClientCommands(char *fmt, ...)
{
va_list argptr;
char string[1024];
va_start(argptr, fmt);
vsprintf(string, fmt, argptr);
va_end(argptr);
MSG_WriteByte(&host_client->message, svc_stufftext);
MSG_WriteString(&host_client->message, string);
}
void SV_DropClient(bool crash)
{
int32_t saveSelf;
int32_t i;
client_t *client;
if (!crash)
{
if (NET_CanSendMessage(host_client->netconnection))
{
MSG_WriteByte(&host_client->message, svc_disconnect);
NET_SendMessage(host_client->netconnection, &host_client->message);
}
if (host_client->edict && host_client->spawned)
{
saveSelf = pr_global_struct->self;
pr_global_struct->self = EDICT_TO_PROG(host_client->edict);
PR_ExecuteProgram(pr_global_struct->ClientDisconnect);
pr_global_struct->self = saveSelf;
}
Sys_Printf("Client %s removed\n", host_client->name);
}
NET_Close(host_client->netconnection);
host_client->netconnection = 0;
host_client->active = 0;
host_client->name[0] = 0;
host_client->old_frags = -999999;
net_activeconnections--;
for (i = 0, client = svs.clients; i < svs.maxclients; i++, client++)
{
if (!client->active)
continue;
MSG_WriteByte(&client->message, svc_updatename);
MSG_WriteByte(&client->message, host_client - svs.clients);
MSG_WriteString(&client->message, "");
MSG_WriteByte(&client->message, svc_updatefrags);
MSG_WriteByte(&client->message, host_client - svs.clients);
MSG_WriteShort(&client->message, 0);
MSG_WriteByte(&client->message, svc_updatecolors);
MSG_WriteByte(&client->message, host_client - svs.clients);
MSG_WriteByte(&client->message, 0);
}
}
void Host_ShutdownServer(bool crash)
{
int32_t i;
int32_t count;
sizebuf_t buf;
char message[4];
double start;
if (!sv.active)
return;
sv.active = 0;
if (cls.state == ca_connected)
CL_Disconnect();
start = Sys_FloatTime();
do
{
count = 0;
for (i = 0, host_client = svs.clients; i < svs.maxclients; i++, host_client++)
{
if (host_client->active && host_client->message.cursize)
{
if (NET_CanSendMessage(host_client->netconnection))
{
NET_SendMessage(host_client->netconnection, &host_client->message);
SZ_Clear(&host_client->message);
}
else
{
NET_GetMessage(host_client->netconnection);
count++;
}
}
}
if ((Sys_FloatTime() - start) > 3.0)
break;
}
while (count);
buf.data = message;
buf.maxsize = 4;
buf.cursize = 0;
MSG_WriteByte(&buf, svc_disconnect);
count = NET_SendToAll(&buf, 5);
if (count)
Con_Printf("Host_ShutdownServer: NET_SendToAll failed for %u clients\n", count);
for (i = 0, host_client = svs.clients; i < svs.maxclients; i++, host_client++)
if (host_client->active)
SV_DropClient(crash);
memset(&sv, 0, sizeof(sv));
memset(svs.clients, 0, svs.maxclientslimit * (sizeof(client_t)));
}
void Host_ClearMemory(void)
{
Con_DPrintf("Clearing memory\n");
D_FlushCaches();
Mod_ClearAll();
if (host_hunklevel)
Hunk_FreeToLowMark(host_hunklevel);
cls.signon = 0;
memset(&sv, 0, sizeof(sv));
memset(&cl, 0, sizeof(cl));
}
bool Host_FilterTime(float time)
{
realtime += time;
if ((!cls.timedemo) && ((realtime - oldrealtime) < (1.0 / 72.0)))
return 0;
host_frametime = realtime - oldrealtime;
oldrealtime = realtime;
if (host_framerate.value > 0)
host_frametime = host_framerate.value;
else
{
if (host_frametime > 0.1)
host_frametime = 0.1;
if (host_frametime < 0.001)
host_frametime = 0.001;
}
return 1;
}
void Host_GetConsoleCommands(void)
{
char *cmd;
while (1)
{
cmd = Sys_ConsoleInput();
if (!cmd)
break;
Cbuf_AddText(cmd);
}
}
void Host_ServerFrame(void)
{
pr_global_struct->frametime = host_frametime;
SV_ClearDatagram();
SV_CheckForNewClients();
SV_RunClients();
if ((!sv.paused) && ((svs.maxclients > 1) || (key_dest == key_game)))
SV_Physics();
SV_SendClientMessages();
}
void _Host_Frame(float time)
{
static double time1 = 0;
static double time2 = 0;
static double time3 = 0;
int32_t pass1;
int32_t pass2;
int32_t pass3;
if (setjmp(host_abortserver))
return;
rand();
if (!Host_FilterTime(time))
return;
Sys_SendKeyEvents();
IN_Commands();
Cbuf_Execute();
NET_Poll();
if (sv.active)
CL_SendCmd();
Host_GetConsoleCommands();
if (sv.active)
Host_ServerFrame();
if (!sv.active)
CL_SendCmd();
host_time += host_frametime;
if (cls.state == ca_connected)
{
CL_ReadFromServer();
}
if (host_speeds.value)
time1 = Sys_FloatTime();
SCR_UpdateScreen();
if (host_speeds.value)
time2 = Sys_FloatTime();
if (cls.signon == SIGNONS)
{
S_Update(r_origin, vpn, vright, vup);
CL_DecayLights();
}
else
S_Update(vec3_origin, vec3_origin, vec3_origin, vec3_origin);
CDAudio_Update();
if (host_speeds.value)
{
pass1 = (time1 - time3) * 1000;
time3 = Sys_FloatTime();
pass2 = (time2 - time1) * 1000;
pass3 = (time3 - time2) * 1000;
Con_Printf("%3i tot %3i server %3i gfx %3i snd\n", (pass1 + pass2) + pass3, pass1, pass2, pass3);
}
host_framecount++;
}
void Host_Frame(float time)
{
double time1;
double time2;
static double timetotal;
static int32_t timecount;
int32_t i;
int32_t c;
int32_t m;
if (!serverprofile.value)
{
_Host_Frame(time);
return;
}
time1 = Sys_FloatTime();
_Host_Frame(time);
time2 = Sys_FloatTime();
timetotal += time2 - time1;
timecount++;
if (timecount < 1000)
return;
m = (timetotal * 1000) / timecount;
timecount = 0;
timetotal = 0;
c = 0;
for (i = 0; i < svs.maxclients; i++)
{
if (svs.clients[i].active)
c++;
}
Con_Printf("serverprofile: %2i clients %2i msec\n", c, m);
}
void Host_InitVCR(quakeparms_t *parms)
{
int32_t i;
int32_t len;
int32_t n;
char *p;
if (COM_CheckParm("-playback"))
{
if (com_argc != 2)
Sys_Error("No other parameters allowed with -playback\n");
Sys_FileOpenRead("quake.vcr", &vcrFile);
if (vcrFile == (-1))
Sys_Error("playback file not found\n");
Sys_FileRead(vcrFile, &i, sizeof(int32_t));
if (i != VCR_SIGNATURE)
Sys_Error("Invalid signature in vcr file\n");
Sys_FileRead(vcrFile, &com_argc, sizeof(int32_t));
com_argv = malloc(com_argc * (sizeof(char *)));
com_argv[0] = parms->argv[0];
for (i = 0; i < com_argc; i++)
{
Sys_FileRead(vcrFile, &len, sizeof(int32_t));
p = malloc(len);
Sys_FileRead(vcrFile, p, len);
com_argv[i + 1] = p;
}
com_argc++;
parms->argc = com_argc;
parms->argv = com_argv;
}
if ((n = COM_CheckParm("-record")) != 0)
{
vcrFile = Sys_FileOpenWrite("quake.vcr");
i = VCR_SIGNATURE;
Sys_FileWrite(vcrFile, &i, sizeof(int32_t));
i = com_argc - 1;
Sys_FileWrite(vcrFile, &i, sizeof(int32_t));
for (i = 1; i < com_argc; i++)
{
if (i == n)
{
len = 10;
Sys_FileWrite(vcrFile, &len, sizeof(int32_t));
Sys_FileWrite(vcrFile, "-playback", len);
continue;
}
len = strlen(com_argv[i]) + 1;
Sys_FileWrite(vcrFile, &len, sizeof(int32_t));
Sys_FileWrite(vcrFile, com_argv[i], len);
}
}
}
void Host_Init(quakeparms_t *parms)
{
if (standard_quake)
minimum_memory = MINIMUM_MEMORY;
else
minimum_memory = MINIMUM_MEMORY_LEVELPAK;
if (COM_CheckParm("-minmemory"))
parms->memsize = minimum_memory;
host_parms = *parms;
if (parms->memsize < minimum_memory)
Sys_Error("Only %4.1f megs of memory available, can't execute game", parms->memsize / ((float) 0x100000));
com_argc = parms->argc;
com_argv = parms->argv;
Memory_Init(parms->membase, parms->memsize);
Cbuf_Init();
Cmd_Init();
V_Init();
Chase_Init();
Host_InitVCR(parms);
COM_Init(parms->basedir);
Host_InitLocal();
W_LoadWadFile("gfx.wad");
Key_Init();
Con_Init();
M_Init();
PR_Init();
Mod_Init();
NET_Init();
SV_Init();
Con_Printf("Exe: 15:39:14 Sep 12 2025\n");
Con_Printf("%4.1f megabyte heap\n", parms->memsize / (1024 * 1024.0));
R_InitTextures();
if (cls.state != ca_dedicated)
{
host_basepal = (uint8_t *) COM_LoadHunkFile("gfx/palette.lmp");
if (!host_basepal)
Sys_Error("Couldn't load gfx/palette.lmp");
host_colormap = (uint8_t *) COM_LoadHunkFile("gfx/colormap.lmp");
if (!host_colormap)
Sys_Error("Couldn't load gfx/colormap.lmp");
IN_Init();
VID_Init(host_basepal);
Draw_Init();
SCR_Init();
R_Init();
S_Init();
CDAudio_Init();
Sbar_Init();
CL_Init();
}
Cbuf_InsertText("exec quake.rc\n");
Hunk_AllocName(0, "-HOST_HUNKLEVEL-");
host_hunklevel = Hunk_LowMark();
host_initialized = 1;
Sys_Printf("========Quake Initialized=========\n");
}
void Host_Shutdown(void)
{
static bool isdown = 0;
if (isdown)
{
printf("recursive shutdown\n");
return;
}
isdown = 1;
scr_disabled_for_loading = 1;
Host_WriteConfiguration();
CDAudio_Shutdown();
NET_Shutdown();
S_Shutdown();
IN_Shutdown();
if (cls.state != ca_dedicated)
{
VID_Shutdown();
}
}
void Host_Quit_f(void)
{
if ((key_dest != key_console) && (cls.state != ca_dedicated))
{
M_Menu_Quit_f();
return;
}
CL_Disconnect();
Host_ShutdownServer(0);
Sys_Quit();
}
void Host_Status_f(void)
{
client_t *client;
int32_t seconds;
int32_t minutes;
int32_t hours = 0;
int32_t j;
void (*print)(char *fmt, ...);
if (cmd_source == src_command)
{
if (!sv.active)
{
Cmd_ForwardToServer();
return;
}
print = Con_Printf;
}
else
print = SV_ClientPrintf;
print("host: %s\n", Cvar_VariableString("hostname"));
print("version: %4.2f\n", VERSION);
if (tcpipAvailable)
print("tcp/ip: %s\n", my_tcpip_address);
if (ipxAvailable)
print("ipx: %s\n", my_ipx_address);
print("map: %s\n", sv.name);
print("players: %i active (%i max)\n\n", net_activeconnections, svs.maxclients);
for (j = 0, client = svs.clients; j < svs.maxclients; j++, client++)
{
if (!client->active)
continue;
seconds = (int32_t) (net_time - client->netconnection->connecttime);
minutes = seconds / 60;
if (minutes)
{
seconds -= minutes * 60;
hours = minutes / 60;
if (hours)
minutes -= hours * 60;
}
else
hours = 0;
print("#%-2u %-16.16s %3i %2i:%02i:%02i\n", j + 1, client->name, (int32_t) client->edict->v.frags, hours, minutes, seconds);
print(" %s\n", client->netconnection->address);
}
}
void Host_God_f(void)
{
if (cmd_source == src_command)
{
Cmd_ForwardToServer();
return;
}
if (pr_global_struct->deathmatch && (!host_client->privileged))
return;
sv_player->v.flags = ((int32_t) sv_player->v.flags) ^ FL_GODMODE;
if (!(((int32_t) sv_player->v.flags) & FL_GODMODE))
SV_ClientPrintf("godmode OFF\n");
else
SV_ClientPrintf("godmode ON\n");
}
void Host_Notarget_f(void)
{
if (cmd_source == src_command)
{
Cmd_ForwardToServer();
return;
}
if (pr_global_struct->deathmatch && (!host_client->privileged))
return;
sv_player->v.flags = ((int32_t) sv_player->v.flags) ^ FL_NOTARGET;
if (!(((int32_t) sv_player->v.flags) & FL_NOTARGET))
SV_ClientPrintf("notarget OFF\n");
else
SV_ClientPrintf("notarget ON\n");
}
void Host_Noclip_f(void)
{
if (cmd_source == src_command)
{
Cmd_ForwardToServer();
return;
}
if (pr_global_struct->deathmatch && (!host_client->privileged))
return;
if (sv_player->v.movetype != MOVETYPE_NOCLIP)
{
noclip_anglehack = 1;
sv_player->v.movetype = MOVETYPE_NOCLIP;
SV_ClientPrintf("noclip ON\n");
}
else
{
noclip_anglehack = 0;
sv_player->v.movetype = MOVETYPE_WALK;
SV_ClientPrintf("noclip OFF\n");
}
}
void Host_Fly_f(void)
{
if (cmd_source == src_command)
{
Cmd_ForwardToServer();
return;
}
if (pr_global_struct->deathmatch && (!host_client->privileged))
return;
if (sv_player->v.movetype != MOVETYPE_FLY)
{
sv_player->v.movetype = MOVETYPE_FLY;
SV_ClientPrintf("flymode ON\n");
}
else
{
sv_player->v.movetype = MOVETYPE_WALK;
SV_ClientPrintf("flymode OFF\n");
}
}
void Host_Ping_f(void)
{
int32_t i;
int32_t j;
float total;
client_t *client;
if (cmd_source == src_command)
{
Cmd_ForwardToServer();
return;
}
SV_ClientPrintf("Client ping times:\n");
for (i = 0, client = svs.clients; i < svs.maxclients; i++, client++)
{
if (!client->active)
continue;
total = 0;
for (j = 0; j < NUM_PING_TIMES; j++)
total += client->ping_times[j];
total /= NUM_PING_TIMES;
SV_ClientPrintf("%4i %s\n", (int32_t) (total * 1000), client->name);
}
}
void Host_Map_f(void)
{
int32_t i;
char name[MAX_QPATH];
if (cmd_source != src_command)
return;
cls.demonum = -1;
CL_Disconnect();
Host_ShutdownServer(0);
key_dest = key_game;
SCR_BeginLoadingPlaque();
cls.mapstring[0] = 0;
for (i = 0; i < Cmd_Argc(); i++)
{
strcat(cls.mapstring, Cmd_Argv(i));
strcat(cls.mapstring, " ");
}
strcat(cls.mapstring, "\n");
svs.serverflags = 0;
strcpy(name, Cmd_Argv(1));
SV_SpawnServer(name);
if (!sv.active)
return;
if (cls.state != ca_dedicated)
{
strcpy(cls.spawnparms, "");
for (i = 2; i < Cmd_Argc(); i++)
{
strcat(cls.spawnparms, Cmd_Argv(i));
strcat(cls.spawnparms, " ");
}
Cmd_ExecuteString("connect local", src_command);
}
}
void Host_Changelevel_f(void)
{
char level[MAX_QPATH];
if (Cmd_Argc() != 2)
{
Con_Printf("changelevel <levelname> : continue game on a new level\n");
return;
}
if ((!sv.active) || cls.demoplayback)
{
Con_Printf("Only the server may changelevel\n");
return;
}
SV_SaveSpawnparms();
strcpy(level, Cmd_Argv(1));
SV_SpawnServer(level);
}
void Host_Restart_f(void)
{
char mapname[MAX_QPATH];
if (cls.demoplayback || (!sv.active))
return;
if (cmd_source != src_command)
return;
strcpy(mapname, sv.name);
SV_SpawnServer(mapname);
}
void Host_Reconnect_f(void)
{
SCR_BeginLoadingPlaque();
cls.signon = 0;
}
void Host_Connect_f(void)
{
char name[MAX_QPATH];
cls.demonum = -1;
if (cls.demoplayback)
{
CL_StopPlayback();
CL_Disconnect();
}
strcpy(name, Cmd_Argv(1));
CL_EstablishConnection(name);
Host_Reconnect_f();
}
void Host_SavegameComment(char *text)
{
int32_t i;
char kills[20];
for (i = 0; i < SAVEGAME_COMMENT_LENGTH; i++)
text[i] = ' ';
memcpy(text, cl.levelname, strlen(cl.levelname));
sprintf(kills, "kills:%3i/%3i", cl.stats[STAT_MONSTERS], cl.stats[STAT_TOTALMONSTERS]);
memcpy(text + 22, kills, strlen(kills));
for (i = 0; i < SAVEGAME_COMMENT_LENGTH; i++)
if (text[i] == ' ')
text[i] = '_';
text[SAVEGAME_COMMENT_LENGTH] = '\0';
}
void Host_Savegame_f(void)
{
char name[256];
FILE *f;
int32_t i;
char comment[SAVEGAME_COMMENT_LENGTH + 1];
if (cmd_source != src_command)
return;
if (!sv.active)
{
Con_Printf("Not playing a local game.\n");
return;
}
if (cl.intermission)
{
Con_Printf("Can't save in intermission.\n");
return;
}
if (svs.maxclients != 1)
{
Con_Printf("Can't save multiplayer games.\n");
return;
}
if (Cmd_Argc() != 2)
{
Con_Printf("save <savename> : save a game\n");
return;
}
if (strstr(Cmd_Argv(1), ".."))
{
Con_Printf("Relative pathnames are not allowed.\n");
return;
}
for (i = 0; i < svs.maxclients; i++)
{
if (svs.clients[i].active && (svs.clients[i].edict->v.health <= 0))
{
Con_Printf("Can't savegame with a dead player\n");
return;
}
}
sprintf(name, "%s/%s", com_gamedir, Cmd_Argv(1));
COM_DefaultExtension(name, ".sav");
Con_Printf("Saving game to %s...\n", name);
f = fopen(name, "w");
if (!f)
{
Con_Printf("ERROR: couldn't open.\n");
return;
}
fprintf(f, "%i\n", SAVEGAME_VERSION);
Host_SavegameComment(comment);
fprintf(f, "%s\n", comment);
for (i = 0; i < NUM_SPAWN_PARMS; i++)
fprintf(f, "%f\n", svs.clients->spawn_parms[i]);
fprintf(f, "%d\n", current_skill);
fprintf(f, "%s\n", sv.name);
fprintf(f, "%f\n", sv.time);
for (i = 0; i < MAX_LIGHTSTYLES; i++)
{
if (sv.lightstyles[i])
fprintf(f, "%s\n", sv.lightstyles[i]);
else
fprintf(f, "m\n");
}
ED_WriteGlobals(f);
for (i = 0; i < sv.num_edicts; i++)
{
ED_Write(f, EDICT_NUM(i));
fflush(f);
}
fclose(f);
Con_Printf("done.\n");
}
void Host_Loadgame_f(void)
{
char name[MAX_OSPATH];
FILE *f;
char mapname[MAX_QPATH];
float time;
float tfloat;
char str[32768];
char *start;
int32_t i;
int32_t r;
edict_t *ent;
int32_t entnum;
int32_t version;
float spawn_parms[NUM_SPAWN_PARMS];
if (cmd_source != src_command)
return;
if (Cmd_Argc() != 2)
{
Con_Printf("load <savename> : load a game\n");
return;
}
cls.demonum = -1;
sprintf(name, "%s/%s", com_gamedir, Cmd_Argv(1));
COM_DefaultExtension(name, ".sav");
Con_Printf("Loading game from %s...\n", name);
f = fopen(name, "r");
if (!f)
{
Con_Printf("ERROR: couldn't open.\n");
return;
}
fscanf(f, "%i\n", &version);
if (version != SAVEGAME_VERSION)
{
fclose(f);
Con_Printf("Savegame is version %i, not %i\n", version, SAVEGAME_VERSION);
return;
}
fscanf(f, "%s\n", str);
for (i = 0; i < NUM_SPAWN_PARMS; i++)
fscanf(f, "%f\n", &spawn_parms[i]);
fscanf(f, "%f\n", &tfloat);
current_skill = (int32_t) (tfloat + 0.1);
Cvar_SetValue("skill", (float) current_skill);
fscanf(f, "%s\n", mapname);
fscanf(f, "%f\n", &time);
CL_Disconnect_f();
SV_SpawnServer(mapname);
if (!sv.active)
{
Con_Printf("Couldn't load map\n");
return;
}
sv.paused = 1;
sv.loadgame = 1;
for (i = 0; i < MAX_LIGHTSTYLES; i++)
{
fscanf(f, "%s\n", str);
sv.lightstyles[i] = Hunk_Alloc(strlen(str) + 1);
strcpy(sv.lightstyles[i], str);
}
entnum = -1;
while (!feof(f))
{
for (i = 0; i < ((sizeof(str)) - 1); i++)
{
r = fgetc(f);
if ((r == EOF) || (!r))
break;
str[i] = r;
if (r == '}')
{
i++;
break;
}
}
if (i == ((sizeof(str)) - 1))
Sys_Error("Loadgame buffer overflow");
str[i] = 0;
start = str;
start = COM_Parse(str);
if (!com_token[0])
break;
if (strcmp(com_token, "{"))
Sys_Error("First token isn't a brace");
if (entnum == (-1))
{
ED_ParseGlobals(start);
}
else
{
ent = EDICT_NUM(entnum);
memset(&ent->v, 0, progs->entityfields * 4);
ent->free = 0;
ED_ParseEdict(start, ent);
if (!ent->free)
SV_LinkEdict(ent, 0);
}
entnum++;
}
sv.num_edicts = entnum;
sv.time = time;
fclose(f);
for (i = 0; i < NUM_SPAWN_PARMS; i++)
svs.clients->spawn_parms[i] = spawn_parms[i];
if (cls.state != ca_dedicated)
{
CL_EstablishConnection("local");
Host_Reconnect_f();
}
}
void Host_Name_f(void)
{
char *newName;
if (Cmd_Argc() == 1)
{
Con_Printf("\"name\" is \"%s\"\n", cl_name.string);
return;
}
if (Cmd_Argc() == 2)
newName = Cmd_Argv(1);
else
newName = Cmd_Args();
newName[15] = 0;
if (cmd_source == src_command)
{
if (strcmp(cl_name.string, newName) == 0)
return;
Cvar_Set("_cl_name", newName);
if (cls.state == ca_connected)
Cmd_ForwardToServer();
return;
}
if (host_client->name[0] && strcmp(host_client->name, "unconnected"))
if (strcmp(host_client->name, newName) != 0)
Con_Printf("%s renamed to %s\n", host_client->name, newName);
strcpy(host_client->name, newName);
host_client->edict->v.netname = host_client->name - pr_strings;
MSG_WriteByte(&sv.reliable_datagram, svc_updatename);
MSG_WriteByte(&sv.reliable_datagram, host_client - svs.clients);
MSG_WriteString(&sv.reliable_datagram, host_client->name);
}
void Host_Version_f(void)
{
Con_Printf("Version %4.2f\n", VERSION);
Con_Printf("Exe: 15:39:14 Sep 12 2025\n");
}
void Host_Say(bool teamonly)
{
client_t *client;
client_t *save;
int32_t j;
char *p;
unsigned char text[64];
bool fromServer = 0;
if (cmd_source == src_command)
{
if (cls.state == ca_dedicated)
{
fromServer = 1;
teamonly = 0;
}
else
{
Cmd_ForwardToServer();
return;
}
}
if (Cmd_Argc() < 2)
return;
save = host_client;
p = Cmd_Args();
if ((*p) == '"')
{
p++;
p[strlen(p) - 1] = 0;
}
if (!fromServer)
sprintf(text, "%c%s: ", 1, save->name);
else
sprintf(text, "%c<%s> ", 1, hostname.string);
j = ((sizeof(text)) - 2) - strlen(text);
if (strlen(p) > j)
p[j] = 0;
strcat(text, p);
strcat(text, "\n");
for (j = 0, client = svs.clients; j < svs.maxclients; j++, client++)
{
if (((!client) || (!client->active)) || (!client->spawned))
continue;
if ((teamplay.value && teamonly) && (client->edict->v.team != save->edict->v.team))
continue;
host_client = client;
SV_ClientPrintf("%s", text);
}
host_client = save;
Sys_Printf("%s", &text[1]);
}
void Host_Say_f(void)
{
Host_Say(0);
}
void Host_Say_Team_f(void)
{
Host_Say(1);
}
void Host_Tell_f(void)
{
client_t *client;
client_t *save;
int32_t j;
char *p;
char text[64];
if (cmd_source == src_command)
{
Cmd_ForwardToServer();
return;
}
if (Cmd_Argc() < 3)
return;
strcpy(text, host_client->name);
strcat(text, ": ");
p = Cmd_Args();
if ((*p) == '"')
{
p++;
p[strlen(p) - 1] = 0;
}
j = ((sizeof(text)) - 2) - strlen(text);
if (strlen(p) > j)
p[j] = 0;
strcat(text, p);
strcat(text, "\n");
save = host_client;
for (j = 0, client = svs.clients; j < svs.maxclients; j++, client++)
{
if ((!client->active) || (!client->spawned))
continue;
if (strcasecmp(client->name, Cmd_Argv(1)))
continue;
host_client = client;
SV_ClientPrintf("%s", text);
break;
}
host_client = save;
}
void Host_Color_f(void)
{
int32_t top;
int32_t bottom;
int32_t playercolor;
if (Cmd_Argc() == 1)
{
Con_Printf("\"color\" is \"%i %i\"\n", ((int32_t) cl_color.value) >> 4, ((int32_t) cl_color.value) & 0x0f);
Con_Printf("color <0-13> [0-13]\n");
return;
}
if (Cmd_Argc() == 2)
top = (bottom = atoi(Cmd_Argv(1)));
else
{
top = atoi(Cmd_Argv(1));
bottom = atoi(Cmd_Argv(2));
}
top &= 15;
if (top > 13)
top = 13;
bottom &= 15;
if (bottom > 13)
bottom = 13;
playercolor = (top * 16) + bottom;
if (cmd_source == src_command)
{
Cvar_SetValue("_cl_color", playercolor);
if (cls.state == ca_connected)
Cmd_ForwardToServer();
return;
}
host_client->colors = playercolor;
host_client->edict->v.team = bottom + 1;
MSG_WriteByte(&sv.reliable_datagram, svc_updatecolors);
MSG_WriteByte(&sv.reliable_datagram, host_client - svs.clients);
MSG_WriteByte(&sv.reliable_datagram, host_client->colors);
}
void Host_Kill_f(void)
{
if (cmd_source == src_command)
{
Cmd_ForwardToServer();
return;
}
if (sv_player->v.health <= 0)
{
SV_ClientPrintf("Can't suicide -- allready dead!\n");
return;
}
pr_global_struct->time = sv.time;
pr_global_struct->self = EDICT_TO_PROG(sv_player);
PR_ExecuteProgram(pr_global_struct->ClientKill);
}
void Host_Pause_f(void)
{
if (cmd_source == src_command)
{
Cmd_ForwardToServer();
return;
}
if (!pausable.value)
SV_ClientPrintf("Pause not allowed.\n");
else
{
sv.paused ^= 1;
if (sv.paused)
{
SV_BroadcastPrintf("%s paused the game\n", pr_strings + sv_player->v.netname);
}
else
{
SV_BroadcastPrintf("%s unpaused the game\n", pr_strings + sv_player->v.netname);
}
MSG_WriteByte(&sv.reliable_datagram, svc_setpause);
MSG_WriteByte(&sv.reliable_datagram, sv.paused);
}
}
void Host_PreSpawn_f(void)
{
if (cmd_source == src_command)
{
Con_Printf("prespawn is not valid from the console\n");
return;
}
if (host_client->spawned)
{
Con_Printf("prespawn not valid -- allready spawned\n");
return;
}
SZ_Write(&host_client->message, sv.signon.data, sv.signon.cursize);
MSG_WriteByte(&host_client->message, svc_signonnum);
MSG_WriteByte(&host_client->message, 2);
host_client->sendsignon = 1;
}
void Host_Spawn_f(void)
{
int32_t i;
client_t *client;
edict_t *ent;
if (cmd_source == src_command)
{
Con_Printf("spawn is not valid from the console\n");
return;
}
if (host_client->spawned)
{
Con_Printf("Spawn not valid -- allready spawned\n");
return;
}
if (sv.loadgame)
{
sv.paused = 0;
}
else
{
ent = host_client->edict;
memset(&ent->v, 0, progs->entityfields * 4);
ent->v.colormap = NUM_FOR_EDICT(ent);
ent->v.team = (host_client->colors & 15) + 1;
ent->v.netname = host_client->name - pr_strings;
for (i = 0; i < NUM_SPAWN_PARMS; i++)
(&pr_global_struct->parm1)[i] = host_client->spawn_parms[i];
pr_global_struct->time = sv.time;
pr_global_struct->self = EDICT_TO_PROG(sv_player);
PR_ExecuteProgram(pr_global_struct->ClientConnect);
if ((Sys_FloatTime() - host_client->netconnection->connecttime) <= sv.time)
Sys_Printf("%s entered the game\n", host_client->name);
PR_ExecuteProgram(pr_global_struct->PutClientInServer);
}
SZ_Clear(&host_client->message);
MSG_WriteByte(&host_client->message, svc_time);
MSG_WriteFloat(&host_client->message, sv.time);
for (i = 0, client = svs.clients; i < svs.maxclients; i++, client++)
{
MSG_WriteByte(&host_client->message, svc_updatename);
MSG_WriteByte(&host_client->message, i);
MSG_WriteString(&host_client->message, client->name);
MSG_WriteByte(&host_client->message, svc_updatefrags);
MSG_WriteByte(&host_client->message, i);
MSG_WriteShort(&host_client->message, client->old_frags);
MSG_WriteByte(&host_client->message, svc_updatecolors);
MSG_WriteByte(&host_client->message, i);
MSG_WriteByte(&host_client->message, client->colors);
}
for (i = 0; i < MAX_LIGHTSTYLES; i++)
{
MSG_WriteByte(&host_client->message, svc_lightstyle);
MSG_WriteByte(&host_client->message, (char) i);
MSG_WriteString(&host_client->message, sv.lightstyles[i]);
}
MSG_WriteByte(&host_client->message, svc_updatestat);
MSG_WriteByte(&host_client->message, STAT_TOTALSECRETS);
MSG_WriteLong(&host_client->message, pr_global_struct->total_secrets);
MSG_WriteByte(&host_client->message, svc_updatestat);
MSG_WriteByte(&host_client->message, STAT_TOTALMONSTERS);
MSG_WriteLong(&host_client->message, pr_global_struct->total_monsters);
MSG_WriteByte(&host_client->message, svc_updatestat);
MSG_WriteByte(&host_client->message, STAT_SECRETS);
MSG_WriteLong(&host_client->message, pr_global_struct->found_secrets);
MSG_WriteByte(&host_client->message, svc_updatestat);
MSG_WriteByte(&host_client->message, STAT_MONSTERS);
MSG_WriteLong(&host_client->message, pr_global_struct->killed_monsters);
ent = EDICT_NUM(1 + (host_client - svs.clients));
MSG_WriteByte(&host_client->message, svc_setangle);
for (i = 0; i < 2; i++)
MSG_WriteAngle(&host_client->message, ent->v.angles[i]);
MSG_WriteAngle(&host_client->message, 0);
SV_WriteClientdataToMessage(sv_player, &host_client->message);
MSG_WriteByte(&host_client->message, svc_signonnum);
MSG_WriteByte(&host_client->message, 3);
host_client->sendsignon = 1;
}
void Host_Begin_f(void)
{
if (cmd_source == src_command)
{
Con_Printf("begin is not valid from the console\n");
return;
}
host_client->spawned = 1;
}
void Host_Kick_f(void)
{
char *who;
char *message = 0;
client_t *save;
int32_t i;
bool byNumber = 0;
if (cmd_source == src_command)
{
if (!sv.active)
{
Cmd_ForwardToServer();
return;
}
}
else
if (pr_global_struct->deathmatch && (!host_client->privileged))
return;
save = host_client;
if ((Cmd_Argc() > 2) && (strcmp(Cmd_Argv(1), "#") == 0))
{
i = ((float) strtod(Cmd_Argv(2), 0)) - 1;
if ((i < 0) || (i >= svs.maxclients))
return;
if (!svs.clients[i].active)
return;
host_client = &svs.clients[i];
byNumber = 1;
}
else
{
for (i = 0, host_client = svs.clients; i < svs.maxclients; i++, host_client++)
{
if (!host_client->active)
continue;
if (strcasecmp(host_client->name, Cmd_Argv(1)) == 0)
break;
}
}
if (i < svs.maxclients)
{
if (cmd_source == src_command)
if (cls.state == ca_dedicated)
who = "Console";
else
who = cl_name.string;
else
who = save->name;
if (host_client == save)
return;
if (Cmd_Argc() > 2)
{
message = COM_Parse(Cmd_Args());
if (byNumber)
{
message++;
while ((*message) == ' ')
message++;
message += strlen(Cmd_Argv(2));
}
while ((*message) && ((*message) == ' '))
message++;
}
if (message)
SV_ClientPrintf("Kicked by %s: %s\n", who, message);
else
SV_ClientPrintf("Kicked by %s\n", who);
SV_DropClient(0);
}
host_client = save;
}
void Host_Give_f(void)
{
char *t;
int32_t v;
int32_t w;
eval_t *val;
if (cmd_source == src_command)
{
Cmd_ForwardToServer();
return;
}
if (pr_global_struct->deathmatch && (!host_client->privileged))
return;
t = Cmd_Argv(1);
v = atoi(Cmd_Argv(2));
switch (t[0])
{
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
if (hipnotic)
{
if (t[0] == '6')
{
if (t[1] == 'a')
sv_player->v.items = ((int32_t) sv_player->v.items) | HIT_PROXIMITY_GUN;
else
sv_player->v.items = ((int32_t) sv_player->v.items) | IT_GRENADE_LAUNCHER;
}
else
if (t[0] == '9')
sv_player->v.items = ((int32_t) sv_player->v.items) | HIT_LASER_CANNON;
else
if (t[0] == '0')
sv_player->v.items = ((int32_t) sv_player->v.items) | HIT_MJOLNIR;
else
if (t[0] >= '2')
sv_player->v.items = ((int32_t) sv_player->v.items) | (IT_SHOTGUN << (t[0] - '2'));
}
else
{
if (t[0] >= '2')
sv_player->v.items = ((int32_t) sv_player->v.items) | (IT_SHOTGUN << (t[0] - '2'));
}
break;
case 's':
if (rogue)
{
val = GetEdictFieldValue(sv_player, "ammo_shells1");
if (val)
val->_float = v;
}
sv_player->v.ammo_shells = v;
break;
case 'n':
if (rogue)
{
val = GetEdictFieldValue(sv_player, "ammo_nails1");
if (val)
{
val->_float = v;
if (sv_player->v.weapon <= IT_LIGHTNING)
sv_player->v.ammo_nails = v;
}
}
else
{
sv_player->v.ammo_nails = v;
}
break;
case 'l':
if (rogue)
{
val = GetEdictFieldValue(sv_player, "ammo_lava_nails");
if (val)
{
val->_float = v;
if (sv_player->v.weapon > IT_LIGHTNING)
sv_player->v.ammo_nails = v;
}
}
break;
case 'r':
if (rogue)
{
val = GetEdictFieldValue(sv_player, "ammo_rockets1");
if (val)
{
val->_float = v;
if (sv_player->v.weapon <= IT_LIGHTNING)
sv_player->v.ammo_rockets = v;
}
}
else
{
sv_player->v.ammo_rockets = v;
}
break;
case 'm':
if (rogue)
{
val = GetEdictFieldValue(sv_player, "ammo_multi_rockets");
if (val)
{
val->_float = v;
if (sv_player->v.weapon > IT_LIGHTNING)
sv_player->v.ammo_rockets = v;
}
}
break;
case 'h':
sv_player->v.health = v;
break;
case 'c':
if (rogue)
{
val = GetEdictFieldValue(sv_player, "ammo_cells1");
if (val)
{
val->_float = v;
if (sv_player->v.weapon <= IT_LIGHTNING)
sv_player->v.ammo_cells = v;
}
}
else
{
sv_player->v.ammo_cells = v;
}
break;
case 'p':
if (rogue)
{
val = GetEdictFieldValue(sv_player, "ammo_plasma");
if (val)
{
val->_float = v;
if (sv_player->v.weapon > IT_LIGHTNING)
sv_player->v.ammo_cells = v;
}
}
break;
}
}
edict_t *FindViewthing(void)
{
int32_t i;
edict_t *e;
for (i = 0; i < sv.num_edicts; i++)
{
e = EDICT_NUM(i);
if (!strcmp(pr_strings + e->v.classname, "viewthing"))
return e;
}
Con_Printf("No viewthing on map\n");
return 0;
}
void Host_Viewmodel_f(void)
{
edict_t *e;
model_t *m;
e = FindViewthing();
if (!e)
return;
m = Mod_ForName(Cmd_Argv(1), 0);
if (!m)
{
Con_Printf("Can't load %s\n", Cmd_Argv(1));
return;
}
e->v.frame = 0;
cl.model_precache[(int32_t) e->v.modelindex] = m;
}
void Host_Viewframe_f(void)
{
edict_t *e;
int32_t f;
model_t *m;
e = FindViewthing();
if (!e)
return;
m = cl.model_precache[(int32_t) e->v.modelindex];
f = atoi(Cmd_Argv(1));
if (f >= m->numframes)
f = m->numframes - 1;
e->v.frame = f;
}
void PrintFrameName(model_t *m, int32_t frame)
{
aliashdr_t *hdr;
maliasframedesc_t *pframedesc;
hdr = (aliashdr_t *) Mod_Extradata(m);
if (!hdr)
return;
pframedesc = &hdr->frames[frame];
Con_Printf("frame %i: %s\n", frame, pframedesc->name);
}
void Host_Viewnext_f(void)
{
edict_t *e;
model_t *m;
e = FindViewthing();
if (!e)
return;
m = cl.model_precache[(int32_t) e->v.modelindex];
e->v.frame = e->v.frame + 1;
if (e->v.frame >= m->numframes)
e->v.frame = m->numframes - 1;
PrintFrameName(m, e->v.frame);
}
void Host_Viewprev_f(void)
{
edict_t *e;
model_t *m;
e = FindViewthing();
if (!e)
return;
m = cl.model_precache[(int32_t) e->v.modelindex];
e->v.frame = e->v.frame - 1;
if (e->v.frame < 0)
e->v.frame = 0;
PrintFrameName(m, e->v.frame);
}
void Host_Startdemos_f(void)
{
int32_t i;
int32_t c;
if (cls.state == ca_dedicated)
{
if (!sv.active)
Cbuf_AddText("map start\n");
return;
}
c = Cmd_Argc() - 1;
if (c > MAX_DEMOS)
{
Con_Printf("Max %i demos in demoloop\n", MAX_DEMOS);
c = MAX_DEMOS;
}
Con_Printf("%i demo(s) in loop\n", c);
for (i = 1; i < (c + 1); i++)
strncpy(cls.demos[i - 1], Cmd_Argv(i), (sizeof(cls.demos[0])) - 1);
if (((!sv.active) && (cls.demonum != (-1))) && (!cls.demoplayback))
{
cls.demonum = 0;
CL_NextDemo();
}
else
cls.demonum = -1;
}
void Host_Demos_f(void)
{
if (cls.state == ca_dedicated)
return;
if (cls.demonum == (-1))
cls.demonum = 1;
CL_Disconnect_f();
CL_NextDemo();
}
void Host_Stopdemo_f(void)
{
if (cls.state == ca_dedicated)
return;
if (!cls.demoplayback)
return;
CL_StopPlayback();
CL_Disconnect();
}
void Host_InitCommands(void)
{
Cmd_AddCommand("status", Host_Status_f);
Cmd_AddCommand("quit", Host_Quit_f);
Cmd_AddCommand("god", Host_God_f);
Cmd_AddCommand("notarget", Host_Notarget_f);
Cmd_AddCommand("fly", Host_Fly_f);
Cmd_AddCommand("map", Host_Map_f);
Cmd_AddCommand("restart", Host_Restart_f);
Cmd_AddCommand("changelevel", Host_Changelevel_f);
Cmd_AddCommand("connect", Host_Connect_f);
Cmd_AddCommand("reconnect", Host_Reconnect_f);
Cmd_AddCommand("name", Host_Name_f);
Cmd_AddCommand("noclip", Host_Noclip_f);
Cmd_AddCommand("version", Host_Version_f);
Cmd_AddCommand("say", Host_Say_f);
Cmd_AddCommand("say_team", Host_Say_Team_f);
Cmd_AddCommand("tell", Host_Tell_f);
Cmd_AddCommand("color", Host_Color_f);
Cmd_AddCommand("kill", Host_Kill_f);
Cmd_AddCommand("pause", Host_Pause_f);
Cmd_AddCommand("spawn", Host_Spawn_f);
Cmd_AddCommand("begin", Host_Begin_f);
Cmd_AddCommand("prespawn", Host_PreSpawn_f);
Cmd_AddCommand("kick", Host_Kick_f);
Cmd_AddCommand("ping", Host_Ping_f);
Cmd_AddCommand("load", Host_Loadgame_f);
Cmd_AddCommand("save", Host_Savegame_f);
Cmd_AddCommand("give", Host_Give_f);
Cmd_AddCommand("startdemos", Host_Startdemos_f);
Cmd_AddCommand("demos", Host_Demos_f);
Cmd_AddCommand("stopdemo", Host_Stopdemo_f);
Cmd_AddCommand("viewmodel", Host_Viewmodel_f);
Cmd_AddCommand("viewframe", Host_Viewframe_f);
Cmd_AddCommand("viewnext", Host_Viewnext_f);
Cmd_AddCommand("viewprev", Host_Viewprev_f);
Cmd_AddCommand("mcache", Mod_Print);
}
void Key_Console(int32_t key)
{
char *cmd;
if (key == K_ENTER)
{
Cbuf_AddText(key_lines[edit_line] + 1);
Cbuf_AddText("\n");
Con_Printf("%s\n", key_lines[edit_line]);
edit_line = (edit_line + 1) & 31;
history_line = edit_line;
key_lines[edit_line][0] = ']';
key_linepos = 1;
if (cls.state == ca_disconnected)
SCR_UpdateScreen();
return;
}
if (key == K_TAB)
{
cmd = Cmd_CompleteCommand(key_lines[edit_line] + 1);
if (!cmd)
cmd = Cvar_CompleteVariable(key_lines[edit_line] + 1);
if (cmd)
{
strcpy(key_lines[edit_line] + 1, cmd);
key_linepos = strlen(cmd) + 1;
key_lines[edit_line][key_linepos] = ' ';
key_linepos++;
key_lines[edit_line][key_linepos] = 0;
return;
}
}
if ((key == K_BACKSPACE) || (key == K_LEFTARROW))
{
if (key_linepos > 1)
key_linepos--;
return;
}
if (key == K_UPARROW)
{
do
{
history_line = (history_line - 1) & 31;
}
while ((history_line != edit_line) && (!key_lines[history_line][1]));
if (history_line == edit_line)
history_line = (edit_line + 1) & 31;
strcpy(key_lines[edit_line], key_lines[history_line]);
key_linepos = strlen(key_lines[edit_line]);
return;
}
if (key == K_DOWNARROW)
{
if (history_line == edit_line)
return;
do
{
history_line = (history_line + 1) & 31;
}
while ((history_line != edit_line) && (!key_lines[history_line][1]));
if (history_line == edit_line)
{
key_lines[edit_line][0] = ']';
key_linepos = 1;
}
else
{
strcpy(key_lines[edit_line], key_lines[history_line]);
key_linepos = strlen(key_lines[edit_line]);
}
return;
}
if ((key == K_PGUP) || (key == K_MWHEELUP))
{
con_backscroll += 2;
if (con_backscroll > ((con_totallines - (vid.height >> 3)) - 1))
con_backscroll = (con_totallines - (vid.height >> 3)) - 1;
return;
}
if ((key == K_PGDN) || (key == K_MWHEELDOWN))
{
con_backscroll -= 2;
if (con_backscroll < 0)
con_backscroll = 0;
return;
}
if (key == K_HOME)
{
con_backscroll = (con_totallines - (vid.height >> 3)) - 1;
return;
}
if (key == K_END)
{
con_backscroll = 0;
return;
}
if ((key < 32) || (key > 127))
return;
if (key_linepos < (MAXCMDLINE - 1))
{
key_lines[edit_line][key_linepos] = key;
key_linepos++;
key_lines[edit_line][key_linepos] = 0;
}
}
void Key_Message(int32_t key)
{
static int32_t chat_bufferlen = 0;
if (key == K_ENTER)
{
if (team_message)
Cbuf_AddText("say_team \"");
else
Cbuf_AddText("say \"");
Cbuf_AddText(chat_buffer);
Cbuf_AddText("\"\n");
key_dest = key_game;
chat_bufferlen = 0;
chat_buffer[0] = 0;
return;
}
if (key == K_ESCAPE)
{
key_dest = key_game;
chat_bufferlen = 0;
chat_buffer[0] = 0;
return;
}
if ((key < 32) || (key > 127))
return;
if (key == K_BACKSPACE)
{
if (chat_bufferlen)
{
chat_bufferlen--;
chat_buffer[chat_bufferlen] = 0;
}
return;
}
if (chat_bufferlen == 31)
return;
chat_buffer[chat_bufferlen++] = key;
chat_buffer[chat_bufferlen] = 0;
}
int32_t Key_StringToKeynum(char *str)
{
keyname_t *kn;
if ((!str) || (!str[0]))
return -1;
if (!str[1])
return str[0];
for (kn = keynames; kn->name; kn++)
{
if (!strcasecmp(str, kn->name))
return kn->keynum;
}
return -1;
}
char *Key_KeynumToString(int32_t keynum)
{
keyname_t *kn;
static char tinystr[2];
if (keynum == (-1))
return "<KEY NOT FOUND>";
if ((keynum > 32) && (keynum < 127))
{
tinystr[0] = keynum;
tinystr[1] = 0;
return tinystr;
}
for (kn = keynames; kn->name; kn++)
if (keynum == kn->keynum)
return kn->name;
return "<UNKNOWN KEYNUM>";
}
void Key_SetBinding(int32_t keynum, char *binding)
{
char *new;
int32_t l;
if (keynum == (-1))
return;
if (keybindings[keynum])
{
Z_Free(keybindings[keynum]);
keybindings[keynum] = 0;
}
l = strlen(binding);
new = Z_Malloc(l + 1);
strcpy(new, binding);
new[l] = 0;
keybindings[keynum] = new;
}
void Key_Unbind_f(void)
{
int32_t b;
if (Cmd_Argc() != 2)
{
Con_Printf("unbind <key> : remove commands from a key\n");
return;
}
b = Key_StringToKeynum(Cmd_Argv(1));
if (b == (-1))
{
Con_Printf("\"%s\" isn't a valid key\n", Cmd_Argv(1));
return;
}
Key_SetBinding(b, "");
}
void Key_Unbindall_f(void)
{
int32_t i;
for (i = 0; i < 256; i++)
if (keybindings[i])
Key_SetBinding(i, "");
}
void Key_Bind_f(void)
{
int32_t i;
int32_t c;
int32_t b;
char cmd[1024];
c = Cmd_Argc();
if ((c != 2) && (c != 3))
{
Con_Printf("bind <key> [command] : attach a command to a key\n");
return;
}
b = Key_StringToKeynum(Cmd_Argv(1));
if (b == (-1))
{
Con_Printf("\"%s\" isn't a valid key\n", Cmd_Argv(1));
return;
}
if (c == 2)
{
if (keybindings[b])
Con_Printf("\"%s\" = \"%s\"\n", Cmd_Argv(1), keybindings[b]);
else
Con_Printf("\"%s\" is not bound\n", Cmd_Argv(1));
return;
}
cmd[0] = 0;
for (i = 2; i < c; i++)
{
if (i > 2)
strcat(cmd, " ");
strcat(cmd, Cmd_Argv(i));
}
Key_SetBinding(b, cmd);
}
void Key_WriteBindings(FILE *f)
{
int32_t i;
for (i = 0; i < 256; i++)
if (keybindings[i])
if (*keybindings[i])
fprintf(f, "bind \"%s\" \"%s\"\n", Key_KeynumToString(i), keybindings[i]);
}
void Key_Init(void)
{
int32_t i;
for (i = 0; i < 32; i++)
{
key_lines[i][0] = ']';
key_lines[i][1] = 0;
}
key_linepos = 1;
for (i = 32; i < 128; i++)
consolekeys[i] = 1;
consolekeys[K_ENTER] = 1;
consolekeys[K_TAB] = 1;
consolekeys[K_LEFTARROW] = 1;
consolekeys[K_RIGHTARROW] = 1;
consolekeys[K_UPARROW] = 1;
consolekeys[K_DOWNARROW] = 1;
consolekeys[K_BACKSPACE] = 1;
consolekeys[K_PGUP] = 1;
consolekeys[K_PGDN] = 1;
consolekeys[K_SHIFT] = 1;
consolekeys[K_MWHEELUP] = 1;
consolekeys[K_MWHEELDOWN] = 1;
consolekeys['`'] = 0;
consolekeys['~'] = 0;
for (i = 0; i < 256; i++)
keyshift[i] = i;
for (i = 'a'; i <= 'z'; i++)
keyshift[i] = (i - 'a') + 'A';
keyshift['1'] = '!';
keyshift['2'] = '@';
keyshift['3'] = '#';
keyshift['4'] = '$';
keyshift['5'] = '%';
keyshift['6'] = '^';
keyshift['7'] = '&';
keyshift['8'] = '*';
keyshift['9'] = '(';
keyshift['0'] = ')';
keyshift['-'] = '_';
keyshift['='] = '+';
keyshift[','] = '<';
keyshift['.'] = '>';
keyshift['/'] = '?';
keyshift[';'] = ':';
keyshift['\''] = '"';
keyshift['['] = '{';
keyshift[']'] = '}';
keyshift['`'] = '~';
keyshift['\\'] = '|';
menubound[K_ESCAPE] = 1;
for (i = 0; i < 12; i++)
menubound[K_F1 + i] = 1;
Cmd_AddCommand("bind", Key_Bind_f);
Cmd_AddCommand("unbind", Key_Unbind_f);
Cmd_AddCommand("unbindall", Key_Unbindall_f);
}
void Key_Event(int32_t key, bool down)
{
char *kb;
char cmd[1024];
keydown[key] = down;
if (!down)
key_repeats[key] = 0;
key_lastpress = key;
key_count++;
if (key_count <= 0)
{
return;
}
if (down)
{
key_repeats[key]++;
if (((key != K_BACKSPACE) && (key != K_PAUSE)) && (key_repeats[key] > 1))
{
return;
}
if ((key >= 200) && (!keybindings[key]))
Con_Printf("%s is unbound, hit F4 to set.\n", Key_KeynumToString(key));
}
if (key == K_SHIFT)
shift_down = down;
if (key == K_ESCAPE)
{
if (!down)
return;
switch (key_dest)
{
case key_message:
Key_Message(key);
break;
case key_menu:
M_Keydown(key);
break;
case key_game:
case key_console:
M_ToggleMenu_f();
break;
default:
Sys_Error("Bad key_dest");
}
return;
}
if (!down)
{
kb = keybindings[key];
if (kb && (kb[0] == '+'))
{
sprintf(cmd, "-%s %i\n", kb + 1, key);
Cbuf_AddText(cmd);
}
if (keyshift[key] != key)
{
kb = keybindings[keyshift[key]];
if (kb && (kb[0] == '+'))
{
sprintf(cmd, "-%s %i\n", kb + 1, key);
Cbuf_AddText(cmd);
}
}
return;
}
if (((cls.demoplayback && down) && consolekeys[key]) && (key_dest == key_game))
{
M_ToggleMenu_f();
return;
}
if ((((key_dest == key_menu) && menubound[key]) || ((key_dest == key_console) && (!consolekeys[key]))) || ((key_dest == key_game) && ((!con_forcedup) || (!consolekeys[key]))))
{
kb = keybindings[key];
if (kb)
{
if (kb[0] == '+')
{
sprintf(cmd, "%s %i\n", kb, key);
Cbuf_AddText(cmd);
}
else
{
Cbuf_AddText(kb);
Cbuf_AddText("\n");
}
}
return;
}
if (!down)
return;
if (shift_down)
{
key = keyshift[key];
}
switch (key_dest)
{
case key_message:
Key_Message(key);
break;
case key_menu:
M_Keydown(key);
break;
case key_game:
case key_console:
Key_Console(key);
break;
default:
Sys_Error("Bad key_dest");
}
}
void Key_ClearStates(void)
{
int32_t i;
for (i = 0; i < 256; i++)
{
keydown[i] = 0;
key_repeats[i] = 0;
}
}
void ProjectPointOnPlane(vec3_t dst, const vec3_t p, const vec3_t normal)
{
float d;
vec3_t n;
float inv_denom;
inv_denom = 1.0F / DotProduct(normal, normal);
d = DotProduct(normal, p) * inv_denom;
n[0] = normal[0] * inv_denom;
n[1] = normal[1] * inv_denom;
n[2] = normal[2] * inv_denom;
dst[0] = p[0] - (d * n[0]);
dst[1] = p[1] - (d * n[1]);
dst[2] = p[2] - (d * n[2]);
}
void PerpendicularVector(vec3_t dst, const vec3_t src)
{
int32_t pos;
int32_t i;
float minelem = 1.0F;
vec3_t tempvec;
for (pos = 0, i = 0; i < 3; i++)
{
if (fabs(src[i]) < minelem)
{
pos = i;
minelem = fabs(src[i]);
}
}
tempvec[0] = (tempvec[1] = (tempvec[2] = 0.0F));
tempvec[pos] = 1.0F;
ProjectPointOnPlane(dst, tempvec, src);
VectorNormalize(dst);
}
void RotatePointAroundVector(vec3_t dst, const vec3_t dir, const vec3_t point, float degrees)
{
float m[3][3];
float im[3][3];
float zrot[3][3];
float tmpmat[3][3];
float rot[3][3];
int32_t i;
vec3_t vr;
vec3_t vup;
vec3_t vf;
vf[0] = dir[0];
vf[1] = dir[1];
vf[2] = dir[2];
PerpendicularVector(vr, dir);
CrossProduct(vr, vf, vup);
m[0][0] = vr[0];
m[1][0] = vr[1];
m[2][0] = vr[2];
m[0][1] = vup[0];
m[1][1] = vup[1];
m[2][1] = vup[2];
m[0][2] = vf[0];
m[1][2] = vf[1];
m[2][2] = vf[2];
memcpy(im, m, sizeof(im));
im[0][1] = m[1][0];
im[0][2] = m[2][0];
im[1][0] = m[0][1];
im[1][2] = m[2][1];
im[2][0] = m[0][2];
im[2][1] = m[1][2];
memset(zrot, 0, sizeof(zrot));
zrot[0][0] = (zrot[1][1] = (zrot[2][2] = 1.0F));
zrot[0][0] = cos(DEG2RAD(degrees));
zrot[0][1] = sin(DEG2RAD(degrees));
zrot[1][0] = -sin(DEG2RAD(degrees));
zrot[1][1] = cos(DEG2RAD(degrees));
R_ConcatRotations(m, zrot, tmpmat);
R_ConcatRotations(tmpmat, im, rot);
for (i = 0; i < 3; i++)
{
dst[i] = ((rot[i][0] * point[0]) + (rot[i][1] * point[1])) + (rot[i][2] * point[2]);
}
}
float anglemod(float a)
{
a = (360.0 / 65536) * (((int32_t) (a * (65536 / 360.0))) & 65535);
return a;
}
void BOPS_Error(void)
{
Sys_Error("BoxOnPlaneSide: Bad signbits");
}
int32_t BoxOnPlaneSide(vec3_t emins, vec3_t emaxs, mplane_t *p)
{
float dist1;
float dist2;
int32_t sides;
switch (p->signbits)
{
case 0:
dist1 = ((p->normal[0] * emaxs[0]) + (p->normal[1] * emaxs[1])) + (p->normal[2] * emaxs[2]);
dist2 = ((p->normal[0] * emins[0]) + (p->normal[1] * emins[1])) + (p->normal[2] * emins[2]);
break;
case 1:
dist1 = ((p->normal[0] * emins[0]) + (p->normal[1] * emaxs[1])) + (p->normal[2] * emaxs[2]);
dist2 = ((p->normal[0] * emaxs[0]) + (p->normal[1] * emins[1])) + (p->normal[2] * emins[2]);
break;
case 2:
dist1 = ((p->normal[0] * emaxs[0]) + (p->normal[1] * emins[1])) + (p->normal[2] * emaxs[2]);
dist2 = ((p->normal[0] * emins[0]) + (p->normal[1] * emaxs[1])) + (p->normal[2] * emins[2]);
break;
case 3:
dist1 = ((p->normal[0] * emins[0]) + (p->normal[1] * emins[1])) + (p->normal[2] * emaxs[2]);
dist2 = ((p->normal[0] * emaxs[0]) + (p->normal[1] * emaxs[1])) + (p->normal[2] * emins[2]);
break;
case 4:
dist1 = ((p->normal[0] * emaxs[0]) + (p->normal[1] * emaxs[1])) + (p->normal[2] * emins[2]);
dist2 = ((p->normal[0] * emins[0]) + (p->normal[1] * emins[1])) + (p->normal[2] * emaxs[2]);
break;
case 5:
dist1 = ((p->normal[0] * emins[0]) + (p->normal[1] * emaxs[1])) + (p->normal[2] * emins[2]);
dist2 = ((p->normal[0] * emaxs[0]) + (p->normal[1] * emins[1])) + (p->normal[2] * emaxs[2]);
break;
case 6:
dist1 = ((p->normal[0] * emaxs[0]) + (p->normal[1] * emins[1])) + (p->normal[2] * emins[2]);
dist2 = ((p->normal[0] * emins[0]) + (p->normal[1] * emaxs[1])) + (p->normal[2] * emaxs[2]);
break;
case 7:
dist1 = ((p->normal[0] * emins[0]) + (p->normal[1] * emins[1])) + (p->normal[2] * emins[2]);
dist2 = ((p->normal[0] * emaxs[0]) + (p->normal[1] * emaxs[1])) + (p->normal[2] * emaxs[2]);
break;
default:
dist1 = (dist2 = 0);
BOPS_Error();
break;
}
sides = 0;
if (dist1 >= p->dist)
sides = 1;
if (dist2 < p->dist)
sides |= 2;
return sides;
}
void AngleVectors(vec3_t angles, vec3_t forward, vec3_t right, vec3_t up)
{
float angle;
float sr;
float sp;
float sy;
float cr;
float cp;
float cy;
angle = angles[YAW] * ((M_PI * 2) / 360);
sy = sin(angle);
cy = cos(angle);
angle = angles[PITCH] * ((M_PI * 2) / 360);
sp = sin(angle);
cp = cos(angle);
angle = angles[ROLL] * ((M_PI * 2) / 360);
sr = sin(angle);
cr = cos(angle);
forward[0] = cp * cy;
forward[1] = cp * sy;
forward[2] = -sp;
right[0] = ((((-1) * sr) * sp) * cy) + (((-1) * cr) * (-sy));
right[1] = ((((-1) * sr) * sp) * sy) + (((-1) * cr) * cy);
right[2] = ((-1) * sr) * cp;
up[0] = ((cr * sp) * cy) + ((-sr) * (-sy));
up[1] = ((cr * sp) * sy) + ((-sr) * cy);
up[2] = cr * cp;
}
int32_t VectorCompare(vec3_t v1, vec3_t v2)
{
int32_t i;
for (i = 0; i < 3; i++)
if (v1[i] != v2[i])
return 0;
return 1;
}
void VectorMA(vec3_t veca, float scale, vec3_t vecb, vec3_t vecc)
{
vecc[0] = veca[0] + (scale * vecb[0]);
vecc[1] = veca[1] + (scale * vecb[1]);
vecc[2] = veca[2] + (scale * vecb[2]);
}
vec_t _DotProduct(vec3_t v1, vec3_t v2)
{
return ((v1[0] * v2[0]) + (v1[1] * v2[1])) + (v1[2] * v2[2]);
}
void _VectorSubtract(vec3_t veca, vec3_t vecb, vec3_t out)
{
out[0] = veca[0] - vecb[0];
out[1] = veca[1] - vecb[1];
out[2] = veca[2] - vecb[2];
}
void _VectorAdd(vec3_t veca, vec3_t vecb, vec3_t out)
{
out[0] = veca[0] + vecb[0];
out[1] = veca[1] + vecb[1];
out[2] = veca[2] + vecb[2];
}
void _VectorCopy(vec3_t in, vec3_t out)
{
out[0] = in[0];
out[1] = in[1];
out[2] = in[2];
}
void CrossProduct(vec3_t v1, vec3_t v2, vec3_t cross)
{
cross[0] = (v1[1] * v2[2]) - (v1[2] * v2[1]);
cross[1] = (v1[2] * v2[0]) - (v1[0] * v2[2]);
cross[2] = (v1[0] * v2[1]) - (v1[1] * v2[0]);
}
vec_t Length(vec3_t v)
{
int32_t i;
float length;
length = 0;
for (i = 0; i < 3; i++)
length += v[i] * v[i];
length = sqrt(length);
return length;
}
float VectorNormalize(vec3_t v)
{
float length;
float ilength;
length = ((v[0] * v[0]) + (v[1] * v[1])) + (v[2] * v[2]);
length = sqrt(length);
if (length)
{
ilength = 1 / length;
v[0] *= ilength;
v[1] *= ilength;
v[2] *= ilength;
}
return length;
}
void VectorInverse(vec3_t v)
{
v[0] = -v[0];
v[1] = -v[1];
v[2] = -v[2];
}
void VectorScale(vec3_t in, vec_t scale, vec3_t out)
{
out[0] = in[0] * scale;
out[1] = in[1] * scale;
out[2] = in[2] * scale;
}
int32_t Q_log2(int32_t val)
{
int32_t answer = 0;
while (val >>= 1)
answer++;
return answer;
}
void R_ConcatRotations(float in1[3][3], float in2[3][3], float out[3][3])
{
out[0][0] = ((in1[0][0] * in2[0][0]) + (in1[0][1] * in2[1][0])) + (in1[0][2] * in2[2][0]);
out[0][1] = ((in1[0][0] * in2[0][1]) + (in1[0][1] * in2[1][1])) + (in1[0][2] * in2[2][1]);
out[0][2] = ((in1[0][0] * in2[0][2]) + (in1[0][1] * in2[1][2])) + (in1[0][2] * in2[2][2]);
out[1][0] = ((in1[1][0] * in2[0][0]) + (in1[1][1] * in2[1][0])) + (in1[1][2] * in2[2][0]);
out[1][1] = ((in1[1][0] * in2[0][1]) + (in1[1][1] * in2[1][1])) + (in1[1][2] * in2[2][1]);
out[1][2] = ((in1[1][0] * in2[0][2]) + (in1[1][1] * in2[1][2])) + (in1[1][2] * in2[2][2]);
out[2][0] = ((in1[2][0] * in2[0][0]) + (in1[2][1] * in2[1][0])) + (in1[2][2] * in2[2][0]);
out[2][1] = ((in1[2][0] * in2[0][1]) + (in1[2][1] * in2[1][1])) + (in1[2][2] * in2[2][1]);
out[2][2] = ((in1[2][0] * in2[0][2]) + (in1[2][1] * in2[1][2])) + (in1[2][2] * in2[2][2]);
}
void R_ConcatTransforms(float in1[3][4], float in2[3][4], float out[3][4])
{
out[0][0] = ((in1[0][0] * in2[0][0]) + (in1[0][1] * in2[1][0])) + (in1[0][2] * in2[2][0]);
out[0][1] = ((in1[0][0] * in2[0][1]) + (in1[0][1] * in2[1][1])) + (in1[0][2] * in2[2][1]);
out[0][2] = ((in1[0][0] * in2[0][2]) + (in1[0][1] * in2[1][2])) + (in1[0][2] * in2[2][2]);
out[0][3] = (((in1[0][0] * in2[0][3]) + (in1[0][1] * in2[1][3])) + (in1[0][2] * in2[2][3])) + in1[0][3];
out[1][0] = ((in1[1][0] * in2[0][0]) + (in1[1][1] * in2[1][0])) + (in1[1][2] * in2[2][0]);
out[1][1] = ((in1[1][0] * in2[0][1]) + (in1[1][1] * in2[1][1])) + (in1[1][2] * in2[2][1]);
out[1][2] = ((in1[1][0] * in2[0][2]) + (in1[1][1] * in2[1][2])) + (in1[1][2] * in2[2][2]);
out[1][3] = (((in1[1][0] * in2[0][3]) + (in1[1][1] * in2[1][3])) + (in1[1][2] * in2[2][3])) + in1[1][3];
out[2][0] = ((in1[2][0] * in2[0][0]) + (in1[2][1] * in2[1][0])) + (in1[2][2] * in2[2][0]);
out[2][1] = ((in1[2][0] * in2[0][1]) + (in1[2][1] * in2[1][1])) + (in1[2][2] * in2[2][1]);
out[2][2] = ((in1[2][0] * in2[0][2]) + (in1[2][1] * in2[1][2])) + (in1[2][2] * in2[2][2]);
out[2][3] = (((in1[2][0] * in2[0][3]) + (in1[2][1] * in2[1][3])) + (in1[2][2] * in2[2][3])) + in1[2][3];
}
void FloorDivMod(double numer, double denom, int32_t *quotient, int32_t *rem)
{
int32_t q;
int32_t r;
double x;
if (denom <= 0.0)
Sys_Error("FloorDivMod: bad denominator %d\n", denom);
if (numer >= 0.0)
{
x = floor(numer / denom);
q = (int32_t) x;
r = (int32_t) floor(numer - (x * denom));
}
else
{
x = floor((-numer) / denom);
q = -((int32_t) x);
r = (int32_t) floor((-numer) - (x * denom));
if (r != 0)
{
q--;
r = ((int32_t) denom) - r;
}
}
*quotient = q;
*rem = r;
}
int32_t GreatestCommonDivisor(int32_t i1, int32_t i2)
{
if (i1 > i2)
{
if (i2 == 0)
return i1;
return GreatestCommonDivisor(i2, i1 % i2);
}
else
{
if (i1 == 0)
return i2;
return GreatestCommonDivisor(i1, i2 % i1);
}
}
fixed16_t Invert24To16(fixed16_t val)
{
if (val < 256)
return 0xFFFFFFFF;
return (fixed16_t) (((((double) 0x10000) * ((double) 0x1000000)) / ((double) val)) + 0.5);
}
void M_DrawCharacter(int32_t cx, int32_t line, int32_t num)
{
Draw_Character(cx + ((vid.width - 320) >> 1), line, num);
}
void M_Print(int32_t cx, int32_t cy, char *str)
{
while (*str)
{
M_DrawCharacter(cx, cy, (*str) + 128);
str++;
cx += 8;
}
}
void M_PrintWhite(int32_t cx, int32_t cy, char *str)
{
while (*str)
{
M_DrawCharacter(cx, cy, *str);
str++;
cx += 8;
}
}
void M_DrawTransPic(int32_t x, int32_t y, qpic_t *pic)
{
Draw_TransPic(x + ((vid.width - 320) >> 1), y, pic);
}
void M_DrawPic(int32_t x, int32_t y, qpic_t *pic)
{
Draw_Pic(x + ((vid.width - 320) >> 1), y, pic);
}
void M_BuildTranslationTable(int32_t top, int32_t bottom)
{
int32_t j;
uint8_t *dest;
uint8_t *source;
for (j = 0; j < 256; j++)
identityTable[j] = j;
dest = translationTable;
source = identityTable;
memcpy(dest, source, 256);
if (top < 128)
memcpy(dest + TOP_RANGE, source + top, 16);
else
for (j = 0; j < 16; j++)
dest[TOP_RANGE + j] = source[(top + 15) - j];
if (bottom < 128)
memcpy(dest + BOTTOM_RANGE, source + bottom, 16);
else
for (j = 0; j < 16; j++)
dest[BOTTOM_RANGE + j] = source[(bottom + 15) - j];
}
void M_DrawTransPicTranslate(int32_t x, int32_t y, qpic_t *pic)
{
Draw_TransPicTranslate(x + ((vid.width - 320) >> 1), y, pic, translationTable);
}
void M_DrawTextBox(int32_t x, int32_t y, int32_t width, int32_t lines)
{
qpic_t *p;
int32_t cx;
int32_t cy;
int32_t n;
cx = x;
cy = y;
p = Draw_CachePic("gfx/box_tl.lmp");
M_DrawTransPic(cx, cy, p);
p = Draw_CachePic("gfx/box_ml.lmp");
for (n = 0; n < lines; n++)
{
cy += 8;
M_DrawTransPic(cx, cy, p);
}
p = Draw_CachePic("gfx/box_bl.lmp");
M_DrawTransPic(cx, cy + 8, p);
cx += 8;
while (width > 0)
{
cy = y;
p = Draw_CachePic("gfx/box_tm.lmp");
M_DrawTransPic(cx, cy, p);
p = Draw_CachePic("gfx/box_mm.lmp");
for (n = 0; n < lines; n++)
{
cy += 8;
if (n == 1)
p = Draw_CachePic("gfx/box_mm2.lmp");
M_DrawTransPic(cx, cy, p);
}
p = Draw_CachePic("gfx/box_bm.lmp");
M_DrawTransPic(cx, cy + 8, p);
width -= 2;
cx += 16;
}
cy = y;
p = Draw_CachePic("gfx/box_tr.lmp");
M_DrawTransPic(cx, cy, p);
p = Draw_CachePic("gfx/box_mr.lmp");
for (n = 0; n < lines; n++)
{
cy += 8;
M_DrawTransPic(cx, cy, p);
}
p = Draw_CachePic("gfx/box_br.lmp");
M_DrawTransPic(cx, cy + 8, p);
}
void M_ToggleMenu_f(void)
{
m_entersound = 1;
if (key_dest == key_menu)
{
if (m_state != m_main)
{
M_Menu_Main_f();
return;
}
key_dest = key_game;
m_state = m_none;
return;
}
if (key_dest == key_console)
{
Con_ToggleConsole_f();
}
else
{
M_Menu_Main_f();
}
}
void M_Menu_Main_f(void)
{
if (key_dest != key_menu)
{
m_save_demonum = cls.demonum;
cls.demonum = -1;
}
key_dest = key_menu;
m_state = m_main;
m_entersound = 1;
}
void M_Main_Draw(void)
{
int32_t f;
qpic_t *p;
M_DrawTransPic(16, 4, Draw_CachePic("gfx/qplaque.lmp"));
p = Draw_CachePic("gfx/ttl_main.lmp");
M_DrawPic((320 - p->width) / 2, 4, p);
M_DrawTransPic(72, 32, Draw_CachePic("gfx/mainmenu.lmp"));
f = ((int32_t) (host_time * 10)) % 6;
M_DrawTransPic(54, 32 + (m_main_cursor * 20), Draw_CachePic(va("gfx/menudot%i.lmp", f + 1)));
}
void M_Main_Key(int32_t key)
{
switch (key)
{
case K_ESCAPE:
key_dest = key_game;
m_state = m_none;
cls.demonum = m_save_demonum;
if (((cls.demonum != (-1)) && (!cls.demoplayback)) && (cls.state != ca_connected))
CL_NextDemo();
break;
case K_DOWNARROW:
S_LocalSound("misc/menu1.wav");
if ((++m_main_cursor) >= MAIN_ITEMS)
m_main_cursor = 0;
break;
case K_UPARROW:
S_LocalSound("misc/menu1.wav");
if ((--m_main_cursor) < 0)
m_main_cursor = MAIN_ITEMS - 1;
break;
case K_ENTER:
m_entersound = 1;
switch (m_main_cursor)
{
case 0:
M_Menu_SinglePlayer_f();
break;
case 1:
M_Menu_MultiPlayer_f();
break;
case 2:
M_Menu_Options_f();
break;
case 3:
M_Menu_Help_f();
break;
case 4:
M_Menu_Quit_f();
break;
}
}
}
void M_Menu_SinglePlayer_f(void)
{
key_dest = key_menu;
m_state = m_singleplayer;
m_entersound = 1;
}
void M_SinglePlayer_Draw(void)
{
int32_t f;
qpic_t *p;
M_DrawTransPic(16, 4, Draw_CachePic("gfx/qplaque.lmp"));
p = Draw_CachePic("gfx/ttl_sgl.lmp");
M_DrawPic((320 - p->width) / 2, 4, p);
M_DrawTransPic(72, 32, Draw_CachePic("gfx/sp_menu.lmp"));
f = ((int32_t) (host_time * 10)) % 6;
M_DrawTransPic(54, 32 + (m_singleplayer_cursor * 20), Draw_CachePic(va("gfx/menudot%i.lmp", f + 1)));
}
void M_SinglePlayer_Key(int32_t key)
{
switch (key)
{
case K_ESCAPE:
M_Menu_Main_f();
break;
case K_DOWNARROW:
S_LocalSound("misc/menu1.wav");
if ((++m_singleplayer_cursor) >= SINGLEPLAYER_ITEMS)
m_singleplayer_cursor = 0;
break;
case K_UPARROW:
S_LocalSound("misc/menu1.wav");
if ((--m_singleplayer_cursor) < 0)
m_singleplayer_cursor = SINGLEPLAYER_ITEMS - 1;
break;
case K_ENTER:
m_entersound = 1;
switch (m_singleplayer_cursor)
{
case 0:
if (sv.active)
if (!SCR_ModalMessage("Are you sure you want to\nstart a new game?\n"))
break;
key_dest = key_game;
if (sv.active)
Cbuf_AddText("disconnect\n");
Cbuf_AddText("maxplayers 1\n");
Cbuf_AddText("map start\n");
break;
case 1:
M_Menu_Load_f();
break;
case 2:
M_Menu_Save_f();
break;
}
}
}
void M_ScanSaves(void)
{
int32_t i;
int32_t j;
char name[MAX_OSPATH];
FILE *f;
int32_t version;
for (i = 0; i < MAX_SAVEGAMES; i++)
{
strcpy(m_filenames[i], "--- UNUSED SLOT ---");
loadable[i] = 0;
sprintf(name, "%s/s%i.sav", com_gamedir, i);
f = fopen(name, "r");
if (!f)
continue;
fscanf(f, "%i\n", &version);
fscanf(f, "%79s\n", name);
strncpy(m_filenames[i], name, (sizeof(m_filenames[i])) - 1);
for (j = 0; j < SAVEGAME_COMMENT_LENGTH; j++)
if (m_filenames[i][j] == '_')
m_filenames[i][j] = ' ';
loadable[i] = 1;
fclose(f);
}
}
void M_Menu_Load_f(void)
{
m_entersound = 1;
m_state = m_load;
key_dest = key_menu;
M_ScanSaves();
}
void M_Menu_Save_f(void)
{
if (!sv.active)
return;
if (cl.intermission)
return;
if (svs.maxclients != 1)
return;
m_entersound = 1;
m_state = m_save;
key_dest = key_menu;
M_ScanSaves();
}
void M_Load_Draw(void)
{
int32_t i;
qpic_t *p;
p = Draw_CachePic("gfx/p_load.lmp");
M_DrawPic((320 - p->width) / 2, 4, p);
for (i = 0; i < MAX_SAVEGAMES; i++)
M_Print(16, 32 + (8 * i), m_filenames[i]);
M_DrawCharacter(8, 32 + (load_cursor * 8), 12 + (((int32_t) (realtime * 4)) & 1));
}
void M_Save_Draw(void)
{
int32_t i;
qpic_t *p;
p = Draw_CachePic("gfx/p_save.lmp");
M_DrawPic((320 - p->width) / 2, 4, p);
for (i = 0; i < MAX_SAVEGAMES; i++)
M_Print(16, 32 + (8 * i), m_filenames[i]);
M_DrawCharacter(8, 32 + (load_cursor * 8), 12 + (((int32_t) (realtime * 4)) & 1));
}
void M_Load_Key(int32_t k)
{
switch (k)
{
case K_ESCAPE:
M_Menu_SinglePlayer_f();
break;
case K_ENTER:
S_LocalSound("misc/menu2.wav");
if (!loadable[load_cursor])
return;
m_state = m_none;
key_dest = key_game;
SCR_BeginLoadingPlaque();
Cbuf_AddText(va("load s%i\n", load_cursor));
return;
case K_UPARROW:
case K_LEFTARROW:
S_LocalSound("misc/menu1.wav");
load_cursor--;
if (load_cursor < 0)
load_cursor = MAX_SAVEGAMES - 1;
break;
case K_DOWNARROW:
case K_RIGHTARROW:
S_LocalSound("misc/menu1.wav");
load_cursor++;
if (load_cursor >= MAX_SAVEGAMES)
load_cursor = 0;
break;
}
}
void M_Save_Key(int32_t k)
{
switch (k)
{
case K_ESCAPE:
M_Menu_SinglePlayer_f();
break;
case K_ENTER:
m_state = m_none;
key_dest = key_game;
Cbuf_AddText(va("save s%i\n", load_cursor));
return;
case K_UPARROW:
case K_LEFTARROW:
S_LocalSound("misc/menu1.wav");
load_cursor--;
if (load_cursor < 0)
load_cursor = MAX_SAVEGAMES - 1;
break;
case K_DOWNARROW:
case K_RIGHTARROW:
S_LocalSound("misc/menu1.wav");
load_cursor++;
if (load_cursor >= MAX_SAVEGAMES)
load_cursor = 0;
break;
}
}
void M_Menu_MultiPlayer_f(void)
{
key_dest = key_menu;
m_state = m_multiplayer;
m_entersound = 1;
}
void M_MultiPlayer_Draw(void)
{
int32_t f;
qpic_t *p;
M_DrawTransPic(16, 4, Draw_CachePic("gfx/qplaque.lmp"));
p = Draw_CachePic("gfx/p_multi.lmp");
M_DrawPic((320 - p->width) / 2, 4, p);
M_DrawTransPic(72, 32, Draw_CachePic("gfx/mp_menu.lmp"));
f = ((int32_t) (host_time * 10)) % 6;
M_DrawTransPic(54, 32 + (m_multiplayer_cursor * 20), Draw_CachePic(va("gfx/menudot%i.lmp", f + 1)));
if ((serialAvailable || ipxAvailable) || tcpipAvailable)
return;
M_PrintWhite((320 / 2) - ((27 * 8) / 2), 148, "No Communications Available");
}
void M_MultiPlayer_Key(int32_t key)
{
switch (key)
{
case K_ESCAPE:
M_Menu_Main_f();
break;
case K_DOWNARROW:
S_LocalSound("misc/menu1.wav");
if ((++m_multiplayer_cursor) >= MULTIPLAYER_ITEMS)
m_multiplayer_cursor = 0;
break;
case K_UPARROW:
S_LocalSound("misc/menu1.wav");
if ((--m_multiplayer_cursor) < 0)
m_multiplayer_cursor = MULTIPLAYER_ITEMS - 1;
break;
case K_ENTER:
m_entersound = 1;
switch (m_multiplayer_cursor)
{
case 0:
if ((serialAvailable || ipxAvailable) || tcpipAvailable)
M_Menu_Net_f();
break;
case 1:
if ((serialAvailable || ipxAvailable) || tcpipAvailable)
M_Menu_Net_f();
break;
case 2:
M_Menu_Setup_f();
break;
}
}
}
void M_Menu_Setup_f(void)
{
key_dest = key_menu;
m_state = m_setup;
m_entersound = 1;
strcpy(setup_myname, cl_name.string);
strcpy(setup_hostname, hostname.string);
setup_top = (setup_oldtop = ((int32_t) cl_color.value) >> 4);
setup_bottom = (setup_oldbottom = ((int32_t) cl_color.value) & 15);
}
void M_Setup_Draw(void)
{
qpic_t *p;
M_DrawTransPic(16, 4, Draw_CachePic("gfx/qplaque.lmp"));
p = Draw_CachePic("gfx/p_multi.lmp");
M_DrawPic((320 - p->width) / 2, 4, p);
M_Print(64, 40, "Hostname");
M_DrawTextBox(160, 32, 16, 1);
M_Print(168, 40, setup_hostname);
M_Print(64, 56, "Your name");
M_DrawTextBox(160, 48, 16, 1);
M_Print(168, 56, setup_myname);
M_Print(64, 80, "Shirt color");
M_Print(64, 104, "Pants color");
M_DrawTextBox(64, 140 - 8, 14, 1);
M_Print(72, 140, "Accept Changes");
p = Draw_CachePic("gfx/bigbox.lmp");
M_DrawTransPic(160, 64, p);
p = Draw_CachePic("gfx/menuplyr.lmp");
M_BuildTranslationTable(setup_top * 16, setup_bottom * 16);
M_DrawTransPicTranslate(172, 72, p);
M_DrawCharacter(56, setup_cursor_table[setup_cursor], 12 + (((int32_t) (realtime * 4)) & 1));
if (setup_cursor == 0)
M_DrawCharacter(168 + (8 * strlen(setup_hostname)), setup_cursor_table[setup_cursor], 10 + (((int32_t) (realtime * 4)) & 1));
if (setup_cursor == 1)
M_DrawCharacter(168 + (8 * strlen(setup_myname)), setup_cursor_table[setup_cursor], 10 + (((int32_t) (realtime * 4)) & 1));
}
void M_Setup_Key(int32_t k)
{
int32_t l;
switch (k)
{
case K_ESCAPE:
M_Menu_MultiPlayer_f();
break;
case K_UPARROW:
S_LocalSound("misc/menu1.wav");
setup_cursor--;
if (setup_cursor < 0)
setup_cursor = NUM_SETUP_CMDS - 1;
break;
case K_DOWNARROW:
S_LocalSound("misc/menu1.wav");
setup_cursor++;
if (setup_cursor >= NUM_SETUP_CMDS)
setup_cursor = 0;
break;
case K_LEFTARROW:
if (setup_cursor < 2)
return;
S_LocalSound("misc/menu3.wav");
if (setup_cursor == 2)
setup_top = setup_top - 1;
if (setup_cursor == 3)
setup_bottom = setup_bottom - 1;
break;
case K_RIGHTARROW:
if (setup_cursor < 2)
return;
forward:
S_LocalSound("misc/menu3.wav");
if (setup_cursor == 2)
setup_top = setup_top + 1;
if (setup_cursor == 3)
setup_bottom = setup_bottom + 1;
break;
case K_ENTER:
if ((setup_cursor == 0) || (setup_cursor == 1))
return;
if ((setup_cursor == 2) || (setup_cursor == 3))
goto forward;
if (strcmp(cl_name.string, setup_myname) != 0)
Cbuf_AddText(va("name \"%s\"\n", setup_myname));
if (strcmp(hostname.string, setup_hostname) != 0)
Cvar_Set("hostname", setup_hostname);
if ((setup_top != setup_oldtop) || (setup_bottom != setup_oldbottom))
Cbuf_AddText(va("color %i %i\n", setup_top, setup_bottom));
m_entersound = 1;
M_Menu_MultiPlayer_f();
break;
case K_BACKSPACE:
if (setup_cursor == 0)
{
if (strlen(setup_hostname))
setup_hostname[strlen(setup_hostname) - 1] = 0;
}
if (setup_cursor == 1)
{
if (strlen(setup_myname))
setup_myname[strlen(setup_myname) - 1] = 0;
}
break;
default:
if ((k < 32) || (k > 127))
break;
if (setup_cursor == 0)
{
l = strlen(setup_hostname);
if (l < 15)
{
setup_hostname[l + 1] = 0;
setup_hostname[l] = k;
}
}
if (setup_cursor == 1)
{
l = strlen(setup_myname);
if (l < 15)
{
setup_myname[l + 1] = 0;
setup_myname[l] = k;
}
}
}
if (setup_top > 13)
setup_top = 0;
if (setup_top < 0)
setup_top = 13;
if (setup_bottom > 13)
setup_bottom = 0;
if (setup_bottom < 0)
setup_bottom = 13;
}
void M_Menu_Net_f(void)
{
key_dest = key_menu;
m_state = m_net;
m_entersound = 1;
m_net_items = 4;
if (m_net_cursor >= m_net_items)
m_net_cursor = 0;
m_net_cursor--;
M_Net_Key(K_DOWNARROW);
}
void M_Net_Draw(void)
{
int32_t f;
qpic_t *p;
M_DrawTransPic(16, 4, Draw_CachePic("gfx/qplaque.lmp"));
p = Draw_CachePic("gfx/p_multi.lmp");
M_DrawPic((320 - p->width) / 2, 4, p);
f = 32;
if (serialAvailable)
{
p = Draw_CachePic("gfx/netmen1.lmp");
}
else
{
p = Draw_CachePic("gfx/dim_modm.lmp");
}
if (p)
M_DrawTransPic(72, f, p);
f += 19;
if (serialAvailable)
{
p = Draw_CachePic("gfx/netmen2.lmp");
}
else
{
p = Draw_CachePic("gfx/dim_drct.lmp");
}
if (p)
M_DrawTransPic(72, f, p);
f += 19;
if (ipxAvailable)
p = Draw_CachePic("gfx/netmen3.lmp");
else
p = Draw_CachePic("gfx/dim_ipx.lmp");
M_DrawTransPic(72, f, p);
f += 19;
if (tcpipAvailable)
p = Draw_CachePic("gfx/netmen4.lmp");
else
p = Draw_CachePic("gfx/dim_tcp.lmp");
M_DrawTransPic(72, f, p);
if (m_net_items == 5)
{
f += 19;
p = Draw_CachePic("gfx/netmen5.lmp");
M_DrawTransPic(72, f, p);
}
f = (320 - (26 * 8)) / 2;
M_DrawTextBox(f, 134, 24, 4);
f += 8;
M_Print(f, 142, net_helpMessage[(m_net_cursor * 4) + 0]);
M_Print(f, 150, net_helpMessage[(m_net_cursor * 4) + 1]);
M_Print(f, 158, net_helpMessage[(m_net_cursor * 4) + 2]);
M_Print(f, 166, net_helpMessage[(m_net_cursor * 4) + 3]);
f = ((int32_t) (host_time * 10)) % 6;
M_DrawTransPic(54, 32 + (m_net_cursor * 20), Draw_CachePic(va("gfx/menudot%i.lmp", f + 1)));
}
void M_Net_Key(int32_t k)
{
again:
switch (k)
{
case K_ESCAPE:
M_Menu_MultiPlayer_f();
break;
case K_DOWNARROW:
S_LocalSound("misc/menu1.wav");
if ((++m_net_cursor) >= m_net_items)
m_net_cursor = 0;
break;
case K_UPARROW:
S_LocalSound("misc/menu1.wav");
if ((--m_net_cursor) < 0)
m_net_cursor = m_net_items - 1;
break;
case K_ENTER:
m_entersound = 1;
switch (m_net_cursor)
{
case 0:
M_Menu_SerialConfig_f();
break;
case 1:
M_Menu_SerialConfig_f();
break;
case 2:
M_Menu_LanConfig_f();
break;
case 3:
M_Menu_LanConfig_f();
break;
case 4:
break;
}
}
if ((m_net_cursor == 0) && (!serialAvailable))
goto again;
if ((m_net_cursor == 1) && (!serialAvailable))
goto again;
if ((m_net_cursor == 2) && (!ipxAvailable))
goto again;
if ((m_net_cursor == 3) && (!tcpipAvailable))
goto again;
}
void M_Menu_Options_f(void)
{
key_dest = key_menu;
m_state = m_options;
m_entersound = 1;
}
void M_AdjustSliders(int32_t dir)
{
S_LocalSound("misc/menu3.wav");
switch (options_cursor)
{
case 3:
scr_viewsize.value += dir * 10;
if (scr_viewsize.value < 30)
scr_viewsize.value = 30;
if (scr_viewsize.value > 120)
scr_viewsize.value = 120;
Cvar_SetValue("viewsize", scr_viewsize.value);
break;
case 4:
v_gamma.value -= dir * 0.05;
if (v_gamma.value < 0.5)
v_gamma.value = 0.5;
if (v_gamma.value > 1)
v_gamma.value = 1;
Cvar_SetValue("gamma", v_gamma.value);
break;
case 5:
sensitivity.value += dir * 0.5;
if (sensitivity.value < 1)
sensitivity.value = 1;
if (sensitivity.value > 11)
sensitivity.value = 11;
Cvar_SetValue("sensitivity", sensitivity.value);
break;
case 6:
bgmvolume.value += dir * 0.1;
if (bgmvolume.value < 0)
bgmvolume.value = 0;
if (bgmvolume.value > 1)
bgmvolume.value = 1;
Cvar_SetValue("bgmvolume", bgmvolume.value);
break;
case 7:
volume.value += dir * 0.1;
if (volume.value < 0)
volume.value = 0;
if (volume.value > 1)
volume.value = 1;
Cvar_SetValue("volume", volume.value);
break;
case 8:
if (cl_forwardspeed.value > 200)
{
Cvar_SetValue("cl_forwardspeed", 200);
Cvar_SetValue("cl_backspeed", 200);
}
else
{
Cvar_SetValue("cl_forwardspeed", 400);
Cvar_SetValue("cl_backspeed", 400);
}
break;
case 9:
Cvar_SetValue("m_pitch", -m_pitch.value);
break;
case 10:
Cvar_SetValue("lookspring", !lookspring.value);
break;
case 11:
Cvar_SetValue("lookstrafe", !lookstrafe.value);
break;
}
}
void M_DrawSlider(int32_t x, int32_t y, float range)
{
int32_t i;
if (range < 0)
range = 0;
if (range > 1)
range = 1;
M_DrawCharacter(x - 8, y, 128);
for (i = 0; i < SLIDER_RANGE; i++)
M_DrawCharacter(x + (i * 8), y, 129);
M_DrawCharacter(x + (i * 8), y, 130);
M_DrawCharacter(x + (((SLIDER_RANGE - 1) * 8) * range), y, 131);
}
void M_DrawCheckbox(int32_t x, int32_t y, int32_t on)
{
if (on)
M_Print(x, y, "on");
else
M_Print(x, y, "off");
}
void M_Options_Draw(void)
{
float r;
qpic_t *p;
M_DrawTransPic(16, 4, Draw_CachePic("gfx/qplaque.lmp"));
p = Draw_CachePic("gfx/p_option.lmp");
M_DrawPic((320 - p->width) / 2, 4, p);
M_Print(16, 32, " Customize controls");
M_Print(16, 40, " Go to console");
M_Print(16, 48, " Reset to defaults");
M_Print(16, 56, " Screen size");
r = (scr_viewsize.value - 30) / (120 - 30);
M_DrawSlider(220, 56, r);
M_Print(16, 64, " Brightness");
r = (1.0 - v_gamma.value) / 0.5;
M_DrawSlider(220, 64, r);
M_Print(16, 72, " Mouse Speed");
r = (sensitivity.value - 1) / 10;
M_DrawSlider(220, 72, r);
M_Print(16, 80, " Music Volume");
r = bgmvolume.value;
M_DrawSlider(220, 80, r);
M_Print(16, 88, " Sound Volume");
r = volume.value;
M_DrawSlider(220, 88, r);
M_Print(16, 96, " Always Run");
M_DrawCheckbox(220, 96, cl_forwardspeed.value > 200);
M_Print(16, 104, " Invert Mouse");
M_DrawCheckbox(220, 104, m_pitch.value < 0);
M_Print(16, 112, " Lookspring");
M_DrawCheckbox(220, 112, lookspring.value);
M_Print(16, 120, " Lookstrafe");
M_DrawCheckbox(220, 120, lookstrafe.value);
if (vid_menudrawfn)
M_Print(16, 128, " Video Options");
M_DrawCharacter(200, 32 + (options_cursor * 8), 12 + (((int32_t) (realtime * 4)) & 1));
}
void M_Options_Key(int32_t k)
{
switch (k)
{
case K_ESCAPE:
M_Menu_Main_f();
break;
case K_ENTER:
m_entersound = 1;
switch (options_cursor)
{
case 0:
M_Menu_Keys_f();
break;
case 1:
m_state = m_none;
Con_ToggleConsole_f();
break;
case 2:
Cbuf_AddText("exec default.cfg\n");
break;
case 12:
M_Menu_Video_f();
break;
default:
M_AdjustSliders(1);
break;
}
return;
case K_UPARROW:
S_LocalSound("misc/menu1.wav");
options_cursor--;
if (options_cursor < 0)
options_cursor = OPTIONS_ITEMS - 1;
break;
case K_DOWNARROW:
S_LocalSound("misc/menu1.wav");
options_cursor++;
if (options_cursor >= OPTIONS_ITEMS)
options_cursor = 0;
break;
case K_LEFTARROW:
M_AdjustSliders(-1);
break;
case K_RIGHTARROW:
M_AdjustSliders(1);
break;
}
if ((options_cursor == 12) && (vid_menudrawfn == 0))
{
if (k == K_UPARROW)
options_cursor = 11;
else
options_cursor = 0;
}
}
void M_Menu_Keys_f(void)
{
key_dest = key_menu;
m_state = m_keys;
m_entersound = 1;
}
void M_FindKeysForCommand(char *command, int32_t *twokeys)
{
int32_t count;
int32_t j;
int32_t l;
char *b;
twokeys[0] = (twokeys[1] = -1);
l = strlen(command);
count = 0;
for (j = 0; j < 256; j++)
{
b = keybindings[j];
if (!b)
continue;
if (!strncmp(b, command, l))
{
twokeys[count] = j;
count++;
if (count == 2)
break;
}
}
}
void M_UnbindCommand(char *command)
{
int32_t j;
int32_t l;
char *b;
l = strlen(command);
for (j = 0; j < 256; j++)
{
b = keybindings[j];
if (!b)
continue;
if (!strncmp(b, command, l))
Key_SetBinding(j, "");
}
}
void M_Keys_Draw(void)
{
int32_t i;
int32_t l;
int32_t keys[2];
char *name;
int32_t x;
int32_t y;
qpic_t *p;
p = Draw_CachePic("gfx/ttl_cstm.lmp");
M_DrawPic((320 - p->width) / 2, 4, p);
if (bind_grab)
M_Print(12, 32, "Press a key or button for this action");
else
M_Print(18, 32, "Enter to change, backspace to clear");
for (i = 0; i < NUMCOMMANDS; i++)
{
y = 48 + (8 * i);
M_Print(16, y, bindnames[i][1]);
l = strlen(bindnames[i][0]);
M_FindKeysForCommand(bindnames[i][0], keys);
if (keys[0] == (-1))
{
M_Print(140, y, "???");
}
else
{
name = Key_KeynumToString(keys[0]);
M_Print(140, y, name);
x = strlen(name) * 8;
if (keys[1] != (-1))
{
M_Print((140 + x) + 8, y, "or");
M_Print((140 + x) + 32, y, Key_KeynumToString(keys[1]));
}
}
}
if (bind_grab)
M_DrawCharacter(130, 48 + (keys_cursor * 8), '=');
else
M_DrawCharacter(130, 48 + (keys_cursor * 8), 12 + (((int32_t) (realtime * 4)) & 1));
}
void M_Keys_Key(int32_t k)
{
char cmd[80];
int32_t keys[2];
if (bind_grab)
{
S_LocalSound("misc/menu1.wav");
if (k == K_ESCAPE)
{
bind_grab = 0;
}
else
if (k != '`')
{
sprintf(cmd, "bind \"%s\" \"%s\"\n", Key_KeynumToString(k), bindnames[keys_cursor][0]);
Cbuf_InsertText(cmd);
}
bind_grab = 0;
return;
}
switch (k)
{
case K_ESCAPE:
M_Menu_Options_f();
break;
case K_LEFTARROW:
case K_UPARROW:
S_LocalSound("misc/menu1.wav");
keys_cursor--;
if (keys_cursor < 0)
keys_cursor = NUMCOMMANDS - 1;
break;
case K_DOWNARROW:
case K_RIGHTARROW:
S_LocalSound("misc/menu1.wav");
keys_cursor++;
if (keys_cursor >= NUMCOMMANDS)
keys_cursor = 0;
break;
case K_ENTER:
M_FindKeysForCommand(bindnames[keys_cursor][0], keys);
S_LocalSound("misc/menu2.wav");
if (keys[1] != (-1))
M_UnbindCommand(bindnames[keys_cursor][0]);
bind_grab = 1;
break;
case K_BACKSPACE:
case K_DEL:
S_LocalSound("misc/menu2.wav");
M_UnbindCommand(bindnames[keys_cursor][0]);
break;
}
}
void M_Menu_Video_f(void)
{
key_dest = key_menu;
m_state = m_video;
m_entersound = 1;
}
void M_Video_Draw(void)
{
(*vid_menudrawfn)();
}
void M_Video_Key(int32_t key)
{
(*vid_menukeyfn)(key);
}
void M_Menu_Help_f(void)
{
key_dest = key_menu;
m_state = m_help;
m_entersound = 1;
help_page = 0;
}
void M_Help_Draw(void)
{
M_DrawPic(0, 0, Draw_CachePic(va("gfx/help%i.lmp", help_page)));
}
void M_Help_Key(int32_t key)
{
switch (key)
{
case K_ESCAPE:
M_Menu_Main_f();
break;
case K_UPARROW:
case K_RIGHTARROW:
m_entersound = 1;
if ((++help_page) >= NUM_HELP_PAGES)
help_page = 0;
break;
case K_DOWNARROW:
case K_LEFTARROW:
m_entersound = 1;
if ((--help_page) < 0)
help_page = NUM_HELP_PAGES - 1;
break;
}
}
void M_Menu_Quit_f(void)
{
if (m_state == m_quit)
return;
wasInMenus = key_dest == key_menu;
key_dest = key_menu;
m_quit_prevstate = m_state;
m_state = m_quit;
m_entersound = 1;
msgNumber = rand() & 7;
}
void M_Quit_Key(int32_t key)
{
switch (key)
{
case K_ESCAPE:
case 'n':
case 'N':
if (wasInMenus)
{
m_state = m_quit_prevstate;
m_entersound = 1;
}
else
{
key_dest = key_game;
m_state = m_none;
}
break;
case 'Y':
case 'y':
key_dest = key_console;
Host_Quit_f();
break;
default:
break;
}
}
void M_Quit_Draw(void)
{
if (wasInMenus)
{
m_state = m_quit_prevstate;
m_recursiveDraw = 1;
M_Draw();
m_state = m_quit;
}
M_DrawTextBox(56, 76, 24, 4);
M_Print(64, 84, quitMessage[(msgNumber * 4) + 0]);
M_Print(64, 92, quitMessage[(msgNumber * 4) + 1]);
M_Print(64, 100, quitMessage[(msgNumber * 4) + 2]);
M_Print(64, 108, quitMessage[(msgNumber * 4) + 3]);
}
void M_Menu_SerialConfig_f(void)
{
int32_t n;
int32_t port;
int32_t baudrate;
bool useModem;
key_dest = key_menu;
m_state = m_serialconfig;
m_entersound = 1;
if (JoiningGame && SerialConfig)
serialConfig_cursor = 4;
else
serialConfig_cursor = 5;
(*GetComPortConfig)(0, &port, &serialConfig_irq, &baudrate, &useModem);
for (n = 0; n < 4; n++)
if (ISA_uarts[n] == port)
break;
if (n == 4)
{
n = 0;
serialConfig_irq = 4;
}
serialConfig_comport = n + 1;
for (n = 0; n < 6; n++)
if (serialConfig_baudrate[n] == baudrate)
break;
if (n == 6)
n = 5;
serialConfig_baud = n;
m_return_onerror = 0;
m_return_reason[0] = 0;
}
void M_SerialConfig_Draw(void)
{
qpic_t *p;
int32_t basex;
char *startJoin;
char *directModem;
M_DrawTransPic(16, 4, Draw_CachePic("gfx/qplaque.lmp"));
p = Draw_CachePic("gfx/p_multi.lmp");
basex = (320 - p->width) / 2;
M_DrawPic(basex, 4, p);
if (StartingGame)
startJoin = "New Game";
else
startJoin = "Join Game";
if (SerialConfig)
directModem = "Modem";
else
directModem = "Direct Connect";
M_Print(basex, 32, va("%s - %s", startJoin, directModem));
basex += 8;
M_Print(basex, serialConfig_cursor_table[0], "Port");
M_DrawTextBox(160, 40, 4, 1);
M_Print(168, serialConfig_cursor_table[0], va("COM%u", serialConfig_comport));
M_Print(basex, serialConfig_cursor_table[1], "IRQ");
M_DrawTextBox(160, serialConfig_cursor_table[1] - 8, 1, 1);
M_Print(168, serialConfig_cursor_table[1], va("%u", serialConfig_irq));
M_Print(basex, serialConfig_cursor_table[2], "Baud");
M_DrawTextBox(160, serialConfig_cursor_table[2] - 8, 5, 1);
M_Print(168, serialConfig_cursor_table[2], va("%u", serialConfig_baudrate[serialConfig_baud]));
if (SerialConfig)
{
M_Print(basex, serialConfig_cursor_table[3], "Modem Setup...");
if (JoiningGame)
{
M_Print(basex, serialConfig_cursor_table[4], "Phone number");
M_DrawTextBox(160, serialConfig_cursor_table[4] - 8, 16, 1);
M_Print(168, serialConfig_cursor_table[4], serialConfig_phone);
}
}
if (JoiningGame)
{
M_DrawTextBox(basex, serialConfig_cursor_table[5] - 8, 7, 1);
M_Print(basex + 8, serialConfig_cursor_table[5], "Connect");
}
else
{
M_DrawTextBox(basex, serialConfig_cursor_table[5] - 8, 2, 1);
M_Print(basex + 8, serialConfig_cursor_table[5], "OK");
}
M_DrawCharacter(basex - 8, serialConfig_cursor_table[serialConfig_cursor], 12 + (((int32_t) (realtime * 4)) & 1));
if (serialConfig_cursor == 4)
M_DrawCharacter(168 + (8 * strlen(serialConfig_phone)), serialConfig_cursor_table[serialConfig_cursor], 10 + (((int32_t) (realtime * 4)) & 1));
if (*m_return_reason)
M_PrintWhite(basex, 148, m_return_reason);
}
void M_SerialConfig_Key(int32_t key)
{
int32_t l;
switch (key)
{
case K_ESCAPE:
M_Menu_Net_f();
break;
case K_UPARROW:
S_LocalSound("misc/menu1.wav");
serialConfig_cursor--;
if (serialConfig_cursor < 0)
serialConfig_cursor = NUM_SERIALCONFIG_CMDS - 1;
break;
case K_DOWNARROW:
S_LocalSound("misc/menu1.wav");
serialConfig_cursor++;
if (serialConfig_cursor >= NUM_SERIALCONFIG_CMDS)
serialConfig_cursor = 0;
break;
case K_LEFTARROW:
if (serialConfig_cursor > 2)
break;
S_LocalSound("misc/menu3.wav");
if (serialConfig_cursor == 0)
{
serialConfig_comport--;
if (serialConfig_comport == 0)
serialConfig_comport = 4;
serialConfig_irq = ISA_IRQs[serialConfig_comport - 1];
}
if (serialConfig_cursor == 1)
{
serialConfig_irq--;
if (serialConfig_irq == 6)
serialConfig_irq = 5;
if (serialConfig_irq == 1)
serialConfig_irq = 7;
}
if (serialConfig_cursor == 2)
{
serialConfig_baud--;
if (serialConfig_baud < 0)
serialConfig_baud = 5;
}
break;
case K_RIGHTARROW:
if (serialConfig_cursor > 2)
break;
forward:
S_LocalSound("misc/menu3.wav");
if (serialConfig_cursor == 0)
{
serialConfig_comport++;
if (serialConfig_comport > 4)
serialConfig_comport = 1;
serialConfig_irq = ISA_IRQs[serialConfig_comport - 1];
}
if (serialConfig_cursor == 1)
{
serialConfig_irq++;
if (serialConfig_irq == 6)
serialConfig_irq = 7;
if (serialConfig_irq == 8)
serialConfig_irq = 2;
}
if (serialConfig_cursor == 2)
{
serialConfig_baud++;
if (serialConfig_baud > 5)
serialConfig_baud = 0;
}
break;
case K_ENTER:
if (serialConfig_cursor < 3)
goto forward;
m_entersound = 1;
if (serialConfig_cursor == 3)
{
(*SetComPortConfig)(0, ISA_uarts[serialConfig_comport - 1], serialConfig_irq, serialConfig_baudrate[serialConfig_baud], SerialConfig);
M_Menu_ModemConfig_f();
break;
}
if (serialConfig_cursor == 4)
{
serialConfig_cursor = 5;
break;
}
(*SetComPortConfig)(0, ISA_uarts[serialConfig_comport - 1], serialConfig_irq, serialConfig_baudrate[serialConfig_baud], SerialConfig);
M_ConfigureNetSubsystem();
if (StartingGame)
{
M_Menu_GameOptions_f();
break;
}
m_return_state = m_state;
m_return_onerror = 1;
key_dest = key_game;
m_state = m_none;
if (SerialConfig)
Cbuf_AddText(va("connect \"%s\"\n", serialConfig_phone));
else
Cbuf_AddText("connect\n");
break;
case K_BACKSPACE:
if (serialConfig_cursor == 4)
{
if (strlen(serialConfig_phone))
serialConfig_phone[strlen(serialConfig_phone) - 1] = 0;
}
break;
default:
if ((key < 32) || (key > 127))
break;
if (serialConfig_cursor == 4)
{
l = strlen(serialConfig_phone);
if (l < 15)
{
serialConfig_phone[l + 1] = 0;
serialConfig_phone[l] = key;
}
}
}
if (DirectConfig && ((serialConfig_cursor == 3) || (serialConfig_cursor == 4)))
if (key == K_UPARROW)
serialConfig_cursor = 2;
else
serialConfig_cursor = 5;
if ((SerialConfig && StartingGame) && (serialConfig_cursor == 4))
if (key == K_UPARROW)
serialConfig_cursor = 3;
else
serialConfig_cursor = 5;
}
void M_Menu_ModemConfig_f(void)
{
key_dest = key_menu;
m_state = m_modemconfig;
m_entersound = 1;
(*GetModemConfig)(0, &modemConfig_dialing, modemConfig_clear, modemConfig_init, modemConfig_hangup);
}
void M_ModemConfig_Draw(void)
{
qpic_t *p;
int32_t basex;
M_DrawTransPic(16, 4, Draw_CachePic("gfx/qplaque.lmp"));
p = Draw_CachePic("gfx/p_multi.lmp");
basex = (320 - p->width) / 2;
M_DrawPic(basex, 4, p);
basex += 8;
if (modemConfig_dialing == 'P')
M_Print(basex, modemConfig_cursor_table[0], "Pulse Dialing");
else
M_Print(basex, modemConfig_cursor_table[0], "Touch Tone Dialing");
M_Print(basex, modemConfig_cursor_table[1], "Clear");
M_DrawTextBox(basex, modemConfig_cursor_table[1] + 4, 16, 1);
M_Print(basex + 8, modemConfig_cursor_table[1] + 12, modemConfig_clear);
if (modemConfig_cursor == 1)
M_DrawCharacter((basex + 8) + (8 * strlen(modemConfig_clear)), modemConfig_cursor_table[1] + 12, 10 + (((int32_t) (realtime * 4)) & 1));
M_Print(basex, modemConfig_cursor_table[2], "Init");
M_DrawTextBox(basex, modemConfig_cursor_table[2] + 4, 30, 1);
M_Print(basex + 8, modemConfig_cursor_table[2] + 12, modemConfig_init);
if (modemConfig_cursor == 2)
M_DrawCharacter((basex + 8) + (8 * strlen(modemConfig_init)), modemConfig_cursor_table[2] + 12, 10 + (((int32_t) (realtime * 4)) & 1));
M_Print(basex, modemConfig_cursor_table[3], "Hangup");
M_DrawTextBox(basex, modemConfig_cursor_table[3] + 4, 16, 1);
M_Print(basex + 8, modemConfig_cursor_table[3] + 12, modemConfig_hangup);
if (modemConfig_cursor == 3)
M_DrawCharacter((basex + 8) + (8 * strlen(modemConfig_hangup)), modemConfig_cursor_table[3] + 12, 10 + (((int32_t) (realtime * 4)) & 1));
M_DrawTextBox(basex, modemConfig_cursor_table[4] - 8, 2, 1);
M_Print(basex + 8, modemConfig_cursor_table[4], "OK");
M_DrawCharacter(basex - 8, modemConfig_cursor_table[modemConfig_cursor], 12 + (((int32_t) (realtime * 4)) & 1));
}
void M_ModemConfig_Key(int32_t key)
{
int32_t l;
switch (key)
{
case K_ESCAPE:
M_Menu_SerialConfig_f();
break;
case K_UPARROW:
S_LocalSound("misc/menu1.wav");
modemConfig_cursor--;
if (modemConfig_cursor < 0)
modemConfig_cursor = NUM_MODEMCONFIG_CMDS - 1;
break;
case K_DOWNARROW:
S_LocalSound("misc/menu1.wav");
modemConfig_cursor++;
if (modemConfig_cursor >= NUM_MODEMCONFIG_CMDS)
modemConfig_cursor = 0;
break;
case K_LEFTARROW:
case K_RIGHTARROW:
if (modemConfig_cursor == 0)
{
if (modemConfig_dialing == 'P')
modemConfig_dialing = 'T';
else
modemConfig_dialing = 'P';
S_LocalSound("misc/menu1.wav");
}
break;
case K_ENTER:
if (modemConfig_cursor == 0)
{
if (modemConfig_dialing == 'P')
modemConfig_dialing = 'T';
else
modemConfig_dialing = 'P';
m_entersound = 1;
}
if (modemConfig_cursor == 4)
{
(*SetModemConfig)(0, va("%c", modemConfig_dialing), modemConfig_clear, modemConfig_init, modemConfig_hangup);
m_entersound = 1;
M_Menu_SerialConfig_f();
}
break;
case K_BACKSPACE:
if (modemConfig_cursor == 1)
{
if (strlen(modemConfig_clear))
modemConfig_clear[strlen(modemConfig_clear) - 1] = 0;
}
if (modemConfig_cursor == 2)
{
if (strlen(modemConfig_init))
modemConfig_init[strlen(modemConfig_init) - 1] = 0;
}
if (modemConfig_cursor == 3)
{
if (strlen(modemConfig_hangup))
modemConfig_hangup[strlen(modemConfig_hangup) - 1] = 0;
}
break;
default:
if ((key < 32) || (key > 127))
break;
if (modemConfig_cursor == 1)
{
l = strlen(modemConfig_clear);
if (l < 15)
{
modemConfig_clear[l + 1] = 0;
modemConfig_clear[l] = key;
}
}
if (modemConfig_cursor == 2)
{
l = strlen(modemConfig_init);
if (l < 29)
{
modemConfig_init[l + 1] = 0;
modemConfig_init[l] = key;
}
}
if (modemConfig_cursor == 3)
{
l = strlen(modemConfig_hangup);
if (l < 15)
{
modemConfig_hangup[l + 1] = 0;
modemConfig_hangup[l] = key;
}
}
}
}
void M_Menu_LanConfig_f(void)
{
key_dest = key_menu;
m_state = m_lanconfig;
m_entersound = 1;
if (lanConfig_cursor == (-1))
{
if (JoiningGame && TCPIPConfig)
lanConfig_cursor = 2;
else
lanConfig_cursor = 1;
}
if (StartingGame && (lanConfig_cursor == 2))
lanConfig_cursor = 1;
lanConfig_port = DEFAULTnet_hostport;
sprintf(lanConfig_portname, "%u", lanConfig_port);
m_return_onerror = 0;
m_return_reason[0] = 0;
}
void M_LanConfig_Draw(void)
{
qpic_t *p;
int32_t basex;
char *startJoin;
char *protocol;
M_DrawTransPic(16, 4, Draw_CachePic("gfx/qplaque.lmp"));
p = Draw_CachePic("gfx/p_multi.lmp");
basex = (320 - p->width) / 2;
M_DrawPic(basex, 4, p);
if (StartingGame)
startJoin = "New Game";
else
startJoin = "Join Game";
if (IPXConfig)
protocol = "IPX";
else
protocol = "TCP/IP";
M_Print(basex, 32, va("%s - %s", startJoin, protocol));
basex += 8;
M_Print(basex, 52, "Address:");
if (IPXConfig)
M_Print(basex + (9 * 8), 52, my_ipx_address);
else
M_Print(basex + (9 * 8), 52, my_tcpip_address);
M_Print(basex, lanConfig_cursor_table[0], "Port");
M_DrawTextBox(basex + (8 * 8), lanConfig_cursor_table[0] - 8, 6, 1);
M_Print(basex + (9 * 8), lanConfig_cursor_table[0], lanConfig_portname);
if (JoiningGame)
{
M_Print(basex, lanConfig_cursor_table[1], "Search for local games...");
M_Print(basex, 108, "Join game at:");
M_DrawTextBox(basex + 8, lanConfig_cursor_table[2] - 8, 22, 1);
M_Print(basex + 16, lanConfig_cursor_table[2], lanConfig_joinname);
}
else
{
M_DrawTextBox(basex, lanConfig_cursor_table[1] - 8, 2, 1);
M_Print(basex + 8, lanConfig_cursor_table[1], "OK");
}
M_DrawCharacter(basex - 8, lanConfig_cursor_table[lanConfig_cursor], 12 + (((int32_t) (realtime * 4)) & 1));
if (lanConfig_cursor == 0)
M_DrawCharacter((basex + (9 * 8)) + (8 * strlen(lanConfig_portname)), lanConfig_cursor_table[0], 10 + (((int32_t) (realtime * 4)) & 1));
if (lanConfig_cursor == 2)
M_DrawCharacter((basex + 16) + (8 * strlen(lanConfig_joinname)), lanConfig_cursor_table[2], 10 + (((int32_t) (realtime * 4)) & 1));
if (*m_return_reason)
M_PrintWhite(basex, 148, m_return_reason);
}
void M_LanConfig_Key(int32_t key)
{
int32_t l;
switch (key)
{
case K_ESCAPE:
M_Menu_Net_f();
break;
case K_UPARROW:
S_LocalSound("misc/menu1.wav");
lanConfig_cursor--;
if (lanConfig_cursor < 0)
lanConfig_cursor = NUM_LANCONFIG_CMDS - 1;
break;
case K_DOWNARROW:
S_LocalSound("misc/menu1.wav");
lanConfig_cursor++;
if (lanConfig_cursor >= NUM_LANCONFIG_CMDS)
lanConfig_cursor = 0;
break;
case K_ENTER:
if (lanConfig_cursor == 0)
break;
m_entersound = 1;
M_ConfigureNetSubsystem();
if (lanConfig_cursor == 1)
{
if (StartingGame)
{
M_Menu_GameOptions_f();
break;
}
M_Menu_Search_f();
break;
}
if (lanConfig_cursor == 2)
{
m_return_state = m_state;
m_return_onerror = 1;
key_dest = key_game;
m_state = m_none;
Cbuf_AddText(va("connect \"%s\"\n", lanConfig_joinname));
break;
}
break;
case K_BACKSPACE:
if (lanConfig_cursor == 0)
{
if (strlen(lanConfig_portname))
lanConfig_portname[strlen(lanConfig_portname) - 1] = 0;
}
if (lanConfig_cursor == 2)
{
if (strlen(lanConfig_joinname))
lanConfig_joinname[strlen(lanConfig_joinname) - 1] = 0;
}
break;
default:
if ((key < 32) || (key > 127))
break;
if (lanConfig_cursor == 2)
{
l = strlen(lanConfig_joinname);
if (l < 21)
{
lanConfig_joinname[l + 1] = 0;
lanConfig_joinname[l] = key;
}
}
if ((key < '0') || (key > '9'))
break;
if (lanConfig_cursor == 0)
{
l = strlen(lanConfig_portname);
if (l < 5)
{
lanConfig_portname[l + 1] = 0;
lanConfig_portname[l] = key;
}
}
}
if (StartingGame && (lanConfig_cursor == 2))
if (key == K_UPARROW)
lanConfig_cursor = 1;
else
lanConfig_cursor = 0;
l = (int32_t) strtol(lanConfig_portname, 0, 0);
if (l > 65535)
l = lanConfig_port;
else
lanConfig_port = l;
sprintf(lanConfig_portname, "%u", lanConfig_port);
}
void M_Menu_GameOptions_f(void)
{
key_dest = key_menu;
m_state = m_gameoptions;
m_entersound = 1;
if (maxplayers == 0)
maxplayers = svs.maxclients;
if (maxplayers < 2)
maxplayers = svs.maxclientslimit;
}
void M_GameOptions_Draw(void)
{
qpic_t *p;
int32_t x;
M_DrawTransPic(16, 4, Draw_CachePic("gfx/qplaque.lmp"));
p = Draw_CachePic("gfx/p_multi.lmp");
M_DrawPic((320 - p->width) / 2, 4, p);
M_DrawTextBox(152, 32, 10, 1);
M_Print(160, 40, "begin game");
M_Print(0, 56, " Max players");
M_Print(160, 56, va("%i", maxplayers));
M_Print(0, 64, " Game Type");
if (coop.value)
M_Print(160, 64, "Cooperative");
else
M_Print(160, 64, "Deathmatch");
M_Print(0, 72, " Teamplay");
if (rogue)
{
char *msg;
switch ((int32_t) teamplay.value)
{
case 1:
msg = "No Friendly Fire";
break;
case 2:
msg = "Friendly Fire";
break;
case 3:
msg = "Tag";
break;
case 4:
msg = "Capture the Flag";
break;
case 5:
msg = "One Flag CTF";
break;
case 6:
msg = "Three Team CTF";
break;
default:
msg = "Off";
break;
}
M_Print(160, 72, msg);
}
else
{
char *msg;
switch ((int32_t) teamplay.value)
{
case 1:
msg = "No Friendly Fire";
break;
case 2:
msg = "Friendly Fire";
break;
default:
msg = "Off";
break;
}
M_Print(160, 72, msg);
}
M_Print(0, 80, " Skill");
if (skill.value == 0)
M_Print(160, 80, "Easy difficulty");
else
if (skill.value == 1)
M_Print(160, 80, "Normal difficulty");
else
if (skill.value == 2)
M_Print(160, 80, "Hard difficulty");
else
M_Print(160, 80, "Nightmare difficulty");
M_Print(0, 88, " Frag Limit");
if (fraglimit.value == 0)
M_Print(160, 88, "none");
else
M_Print(160, 88, va("%i frags", (int32_t) fraglimit.value));
M_Print(0, 96, " Time Limit");
if (timelimit.value == 0)
M_Print(160, 96, "none");
else
M_Print(160, 96, va("%i minutes", (int32_t) timelimit.value));
M_Print(0, 112, " Episode");
if (hipnotic)
M_Print(160, 112, hipnoticepisodes[startepisode].description);
else
if (rogue)
M_Print(160, 112, rogueepisodes[startepisode].description);
else
M_Print(160, 112, episodes[startepisode].description);
M_Print(0, 120, " Level");
if (hipnotic)
{
M_Print(160, 120, hipnoticlevels[hipnoticepisodes[startepisode].firstLevel + startlevel].description);
M_Print(160, 128, hipnoticlevels[hipnoticepisodes[startepisode].firstLevel + startlevel].name);
}
else
if (rogue)
{
M_Print(160, 120, roguelevels[rogueepisodes[startepisode].firstLevel + startlevel].description);
M_Print(160, 128, roguelevels[rogueepisodes[startepisode].firstLevel + startlevel].name);
}
else
{
M_Print(160, 120, levels[episodes[startepisode].firstLevel + startlevel].description);
M_Print(160, 128, levels[episodes[startepisode].firstLevel + startlevel].name);
}
M_DrawCharacter(144, gameoptions_cursor_table[gameoptions_cursor], 12 + (((int32_t) (realtime * 4)) & 1));
if (m_serverInfoMessage)
{
if ((realtime - m_serverInfoMessageTime) < 5.0)
{
x = (320 - (26 * 8)) / 2;
M_DrawTextBox(x, 138, 24, 4);
x += 8;
M_Print(x, 146, " More than 4 players ");
M_Print(x, 154, " requires using command ");
M_Print(x, 162, "line parameters; please ");
M_Print(x, 170, " see techinfo.txt. ");
}
else
{
m_serverInfoMessage = 0;
}
}
}
void M_NetStart_Change(int32_t dir)
{
int32_t count;
switch (gameoptions_cursor)
{
case 1:
maxplayers += dir;
if (maxplayers > svs.maxclientslimit)
{
maxplayers = svs.maxclientslimit;
m_serverInfoMessage = 1;
m_serverInfoMessageTime = realtime;
}
if (maxplayers < 2)
maxplayers = 2;
break;
case 2:
Cvar_SetValue("coop", (coop.value) ? (0) : (1));
break;
case 3:
if (rogue)
count = 6;
else
count = 2;
Cvar_SetValue("teamplay", teamplay.value + dir);
if (teamplay.value > count)
Cvar_SetValue("teamplay", 0);
else
if (teamplay.value < 0)
Cvar_SetValue("teamplay", count);
break;
case 4:
Cvar_SetValue("skill", skill.value + dir);
if (skill.value > 3)
Cvar_SetValue("skill", 0);
if (skill.value < 0)
Cvar_SetValue("skill", 3);
break;
case 5:
Cvar_SetValue("fraglimit", fraglimit.value + (dir * 10));
if (fraglimit.value > 100)
Cvar_SetValue("fraglimit", 0);
if (fraglimit.value < 0)
Cvar_SetValue("fraglimit", 100);
break;
case 6:
Cvar_SetValue("timelimit", timelimit.value + (dir * 5));
if (timelimit.value > 60)
Cvar_SetValue("timelimit", 0);
if (timelimit.value < 0)
Cvar_SetValue("timelimit", 60);
break;
case 7:
startepisode += dir;
if (hipnotic)
count = 6;
else
if (rogue)
count = 4;
else
if (registered.value)
count = 7;
else
count = 2;
if (startepisode < 0)
startepisode = count - 1;
if (startepisode >= count)
startepisode = 0;
startlevel = 0;
break;
case 8:
startlevel += dir;
if (hipnotic)
count = hipnoticepisodes[startepisode].levels;
else
if (rogue)
count = rogueepisodes[startepisode].levels;
else
count = episodes[startepisode].levels;
if (startlevel < 0)
startlevel = count - 1;
if (startlevel >= count)
startlevel = 0;
break;
}
}
void M_GameOptions_Key(int32_t key)
{
switch (key)
{
case K_ESCAPE:
M_Menu_Net_f();
break;
case K_UPARROW:
S_LocalSound("misc/menu1.wav");
gameoptions_cursor--;
if (gameoptions_cursor < 0)
gameoptions_cursor = NUM_GAMEOPTIONS - 1;
break;
case K_DOWNARROW:
S_LocalSound("misc/menu1.wav");
gameoptions_cursor++;
if (gameoptions_cursor >= NUM_GAMEOPTIONS)
gameoptions_cursor = 0;
break;
case K_LEFTARROW:
if (gameoptions_cursor == 0)
break;
S_LocalSound("misc/menu3.wav");
M_NetStart_Change(-1);
break;
case K_RIGHTARROW:
if (gameoptions_cursor == 0)
break;
S_LocalSound("misc/menu3.wav");
M_NetStart_Change(1);
break;
case K_ENTER:
S_LocalSound("misc/menu2.wav");
if (gameoptions_cursor == 0)
{
if (sv.active)
Cbuf_AddText("disconnect\n");
Cbuf_AddText("listen 0\n");
Cbuf_AddText(va("maxplayers %u\n", maxplayers));
SCR_BeginLoadingPlaque();
if (hipnotic)
Cbuf_AddText(va("map %s\n", hipnoticlevels[hipnoticepisodes[startepisode].firstLevel + startlevel].name));
else
if (rogue)
Cbuf_AddText(va("map %s\n", roguelevels[rogueepisodes[startepisode].firstLevel + startlevel].name));
else
Cbuf_AddText(va("map %s\n", levels[episodes[startepisode].firstLevel + startlevel].name));
return;
}
M_NetStart_Change(1);
break;
}
}
void M_Menu_Search_f(void)
{
key_dest = key_menu;
m_state = m_search;
m_entersound = 0;
slistSilent = 1;
slistLocal = 0;
searchComplete = 0;
NET_Slist_f();
}
void M_Search_Draw(void)
{
qpic_t *p;
int32_t x;
p = Draw_CachePic("gfx/p_multi.lmp");
M_DrawPic((320 - p->width) / 2, 4, p);
x = ((320 / 2) - ((12 * 8) / 2)) + 4;
M_DrawTextBox(x - 8, 32, 12, 1);
M_Print(x, 40, "Searching...");
if (slistInProgress)
{
NET_Poll();
return;
}
if (!searchComplete)
{
searchComplete = 1;
searchCompleteTime = realtime;
}
if (hostCacheCount)
{
M_Menu_ServerList_f();
return;
}
M_PrintWhite((320 / 2) - ((22 * 8) / 2), 64, "No Quake servers found");
if ((realtime - searchCompleteTime) < 3.0)
return;
M_Menu_LanConfig_f();
}
void M_Search_Key(int32_t key)
{
}
void M_Menu_ServerList_f(void)
{
key_dest = key_menu;
m_state = m_slist;
m_entersound = 1;
slist_cursor = 0;
m_return_onerror = 0;
m_return_reason[0] = 0;
slist_sorted = 0;
}
void M_ServerList_Draw(void)
{
int32_t n;
char string[64];
qpic_t *p;
if (!slist_sorted)
{
if (hostCacheCount > 1)
{
int32_t i;
int32_t j;
hostcache_t temp;
for (i = 0; i < hostCacheCount; i++)
for (j = i + 1; j < hostCacheCount; j++)
if (strcmp(hostcache[j].name, hostcache[i].name) < 0)
{
memcpy(&temp, &hostcache[j], sizeof(hostcache_t));
memcpy(&hostcache[j], &hostcache[i], sizeof(hostcache_t));
memcpy(&hostcache[i], &temp, sizeof(hostcache_t));
}
}
slist_sorted = 1;
}
p = Draw_CachePic("gfx/p_multi.lmp");
M_DrawPic((320 - p->width) / 2, 4, p);
for (n = 0; n < hostCacheCount; n++)
{
if (hostcache[n].maxusers)
sprintf(string, "%-15.15s %-15.15s %2u/%2u\n", hostcache[n].name, hostcache[n].map, hostcache[n].users, hostcache[n].maxusers);
else
sprintf(string, "%-15.15s %-15.15s\n", hostcache[n].name, hostcache[n].map);
M_Print(16, 32 + (8 * n), string);
}
M_DrawCharacter(0, 32 + (slist_cursor * 8), 12 + (((int32_t) (realtime * 4)) & 1));
if (*m_return_reason)
M_PrintWhite(16, 148, m_return_reason);
}
void M_ServerList_Key(int32_t k)
{
switch (k)
{
case K_ESCAPE:
M_Menu_LanConfig_f();
break;
case K_SPACE:
M_Menu_Search_f();
break;
case K_UPARROW:
case K_LEFTARROW:
S_LocalSound("misc/menu1.wav");
slist_cursor--;
if (slist_cursor < 0)
slist_cursor = hostCacheCount - 1;
break;
case K_DOWNARROW:
case K_RIGHTARROW:
S_LocalSound("misc/menu1.wav");
slist_cursor++;
if (slist_cursor >= hostCacheCount)
slist_cursor = 0;
break;
case K_ENTER:
S_LocalSound("misc/menu2.wav");
m_return_state = m_state;
m_return_onerror = 1;
slist_sorted = 0;
key_dest = key_game;
m_state = m_none;
Cbuf_AddText(va("connect \"%s\"\n", hostcache[slist_cursor].cname));
break;
default:
break;
}
}
void M_Init(void)
{
Cmd_AddCommand("togglemenu", M_ToggleMenu_f);
Cmd_AddCommand("menu_main", M_Menu_Main_f);
Cmd_AddCommand("menu_singleplayer", M_Menu_SinglePlayer_f);
Cmd_AddCommand("menu_load", M_Menu_Load_f);
Cmd_AddCommand("menu_save", M_Menu_Save_f);
Cmd_AddCommand("menu_multiplayer", M_Menu_MultiPlayer_f);
Cmd_AddCommand("menu_setup", M_Menu_Setup_f);
Cmd_AddCommand("menu_options", M_Menu_Options_f);
Cmd_AddCommand("menu_keys", M_Menu_Keys_f);
Cmd_AddCommand("menu_video", M_Menu_Video_f);
Cmd_AddCommand("help", M_Menu_Help_f);
Cmd_AddCommand("menu_quit", M_Menu_Quit_f);
}
void M_Draw(void)
{
if ((m_state == m_none) || (key_dest != key_menu))
return;
if (!m_recursiveDraw)
{
scr_copyeverything = 1;
if (scr_con_current)
{
Draw_ConsoleBackground(vid.height);
VID_UnlockBuffer();
S_ExtraUpdate();
VID_LockBuffer();
}
else
Draw_FadeScreen();
scr_fullupdate = 0;
}
else
{
m_recursiveDraw = 0;
}
switch (m_state)
{
case m_none:
break;
case m_main:
M_Main_Draw();
break;
case m_singleplayer:
M_SinglePlayer_Draw();
break;
case m_load:
M_Load_Draw();
break;
case m_save:
M_Save_Draw();
break;
case m_multiplayer:
M_MultiPlayer_Draw();
break;
case m_setup:
M_Setup_Draw();
break;
case m_net:
M_Net_Draw();
break;
case m_options:
M_Options_Draw();
break;
case m_keys:
M_Keys_Draw();
break;
case m_video:
M_Video_Draw();
break;
case m_help:
M_Help_Draw();
break;
case m_quit:
M_Quit_Draw();
break;
case m_serialconfig:
M_SerialConfig_Draw();
break;
case m_modemconfig:
M_ModemConfig_Draw();
break;
case m_lanconfig:
M_LanConfig_Draw();
break;
case m_gameoptions:
M_GameOptions_Draw();
break;
case m_search:
M_Search_Draw();
break;
case m_slist:
M_ServerList_Draw();
break;
}
if (m_entersound)
{
S_LocalSound("misc/menu2.wav");
m_entersound = 0;
}
VID_UnlockBuffer();
S_ExtraUpdate();
VID_LockBuffer();
}
void M_Keydown(int32_t key)
{
switch (m_state)
{
case m_none:
return;
case m_main:
M_Main_Key(key);
return;
case m_singleplayer:
M_SinglePlayer_Key(key);
return;
case m_load:
M_Load_Key(key);
return;
case m_save:
M_Save_Key(key);
return;
case m_multiplayer:
M_MultiPlayer_Key(key);
return;
case m_setup:
M_Setup_Key(key);
return;
case m_net:
M_Net_Key(key);
return;
case m_options:
M_Options_Key(key);
return;
case m_keys:
M_Keys_Key(key);
return;
case m_video:
M_Video_Key(key);
return;
case m_help:
M_Help_Key(key);
return;
case m_quit:
M_Quit_Key(key);
return;
case m_serialconfig:
M_SerialConfig_Key(key);
return;
case m_modemconfig:
M_ModemConfig_Key(key);
return;
case m_lanconfig:
M_LanConfig_Key(key);
return;
case m_gameoptions:
M_GameOptions_Key(key);
return;
case m_search:
M_Search_Key(key);
break;
case m_slist:
M_ServerList_Key(key);
return;
}
}
void M_ConfigureNetSubsystem(void)
{
Cbuf_AddText("stopdemo\n");
if (SerialConfig || DirectConfig)
{
Cbuf_AddText("com1 enable\n");
}
if (IPXConfig || TCPIPConfig)
net_hostport = lanConfig_port;
}
void Mod_Init(void)
{
memset(mod_novis, 0xff, sizeof(mod_novis));
}
void *Mod_Extradata(model_t *mod)
{
void *r;
r = Cache_Check(&mod->cache);
if (r)
return r;
Mod_LoadModel(mod, 1);
if (!mod->cache.data)
Sys_Error("Mod_Extradata: caching failed");
return mod->cache.data;
}
mleaf_t *Mod_PointInLeaf(vec3_t p, model_t *model)
{
mnode_t *node;
float d;
mplane_t *plane;
if ((!model) || (!model->nodes))
Sys_Error("Mod_PointInLeaf: bad model");
node = model->nodes;
while (1)
{
if (node->contents < 0)
return (mleaf_t *) node;
plane = node->plane;
d = DotProduct(p, plane->normal) - plane->dist;
if (d > 0)
node = node->children[0];
else
node = node->children[1];
}
return 0;
}
uint8_t *Mod_DecompressVis(uint8_t *in, model_t *model)
{
static uint8_t decompressed[MAX_MAP_LEAFS / 8];
int32_t c;
uint8_t *out;
int32_t row;
row = (model->numleafs + 7) >> 3;
out = decompressed;
if (!in)
{
while (row)
{
*(out++) = 0xff;
row--;
}
return decompressed;
}
do
{
if (*in)
{
*(out++) = *(in++);
continue;
}
c = in[1];
in += 2;
while (c)
{
*(out++) = 0;
c--;
}
}
while ((out - decompressed) < row);
return decompressed;
}
uint8_t *Mod_LeafPVS(mleaf_t *leaf, model_t *model)
{
if (leaf == model->leafs)
return mod_novis;
return Mod_DecompressVis(leaf->compressed_vis, model);
}
void Mod_ClearAll(void)
{
int32_t i;
model_t *mod;
for (i = 0, mod = mod_known; i < mod_numknown; i++, mod++)
{
mod->needload = NL_UNREFERENCED;
if (mod->type == mod_sprite)
mod->cache.data = 0;
}
}
model_t *Mod_FindName(char *name)
{
int32_t i;
model_t *mod;
model_t *avail = 0;
if (!name[0])
Sys_Error("Mod_ForName: NULL name");
for (i = 0, mod = mod_known; i < mod_numknown; i++, mod++)
{
if (!strcmp(mod->name, name))
break;
if (mod->needload == NL_UNREFERENCED)
if ((!avail) || (mod->type != mod_alias))
avail = mod;
}
if (i == mod_numknown)
{
if (mod_numknown == MAX_MOD_KNOWN)
{
if (avail)
{
mod = avail;
if (mod->type == mod_alias)
if (Cache_Check(&mod->cache))
Cache_Free(&mod->cache);
}
else
Sys_Error("mod_numknown == MAX_MOD_KNOWN");
}
else
mod_numknown++;
strcpy(mod->name, name);
mod->needload = NL_NEEDS_LOADED;
}
return mod;
}
void Mod_TouchModel(char *name)
{
model_t *mod;
mod = Mod_FindName(name);
if (mod->needload == NL_PRESENT)
{
if (mod->type == mod_alias)
Cache_Check(&mod->cache);
}
}
model_t *Mod_LoadModel(model_t *mod, bool crash)
{
uint32_t *buf;
uint8_t stackbuf[1024];
if (mod->type == mod_alias)
{
if (Cache_Check(&mod->cache))
{
mod->needload = NL_PRESENT;
return mod;
}
}
else
{
if (mod->needload == NL_PRESENT)
return mod;
}
buf = (uint32_t *) COM_LoadStackFile(mod->name, stackbuf, sizeof(stackbuf));
if (!buf)
{
if (crash)
Sys_Error("Mod_NumForName: %s not found", mod->name);
return 0;
}
COM_FileBase(mod->name, loadname);
loadmodel = mod;
mod->needload = NL_PRESENT;
switch (*((uint32_t *) buf))
{
case IDPOLYHEADER:
Mod_LoadAliasModel(mod, buf);
break;
case IDSPRITEHEADER:
Mod_LoadSpriteModel(mod, buf);
break;
default:
Mod_LoadBrushModel(mod, buf);
break;
}
return mod;
}
model_t *Mod_ForName(char *name, bool crash)
{
model_t *mod;
mod = Mod_FindName(name);
return Mod_LoadModel(mod, crash);
}
void Mod_LoadTextures(lump_t *l)
{
int32_t i;
int32_t j;
int32_t pixels;
int32_t num;
int32_t max;
int32_t altmax;
miptex_t *mt;
texture_t *tx;
texture_t *tx2;
texture_t *anims[10];
texture_t *altanims[10];
dmiptexlump_t *m;
if (!l->filelen)
{
loadmodel->textures = 0;
return;
}
m = (dmiptexlump_t *) (mod_base + l->fileofs);
m->nummiptex = m->nummiptex;
loadmodel->numtextures = m->nummiptex;
loadmodel->textures = Hunk_AllocName(m->nummiptex * (sizeof(*loadmodel->textures)), loadname);
for (i = 0; i < m->nummiptex; i++)
{
m->dataofs[i] = m->dataofs[i];
if (m->dataofs[i] == (-1))
continue;
mt = (miptex_t *) (((uint8_t *) m) + m->dataofs[i]);
mt->width = mt->width;
mt->height = mt->height;
for (j = 0; j < MIPLEVELS; j++)
mt->offsets[j] = mt->offsets[j];
if ((mt->width & 15) || (mt->height & 15))
Sys_Error("Texture %s is not 16 aligned", mt->name);
pixels = ((mt->width * mt->height) / 64) * 85;
tx = Hunk_AllocName((sizeof(texture_t)) + pixels, loadname);
loadmodel->textures[i] = tx;
memcpy(tx->name, mt->name, sizeof(tx->name));
tx->width = mt->width;
tx->height = mt->height;
for (j = 0; j < MIPLEVELS; j++)
tx->offsets[j] = (mt->offsets[j] + (sizeof(texture_t))) - (sizeof(miptex_t));
memcpy(tx + 1, mt + 1, pixels);
if (!strncmp(mt->name, "sky", 3))
R_InitSky(tx);
}
for (i = 0; i < m->nummiptex; i++)
{
tx = loadmodel->textures[i];
if ((!tx) || (tx->name[0] != '+'))
continue;
if (tx->anim_next)
continue;
memset(anims, 0, sizeof(anims));
memset(altanims, 0, sizeof(altanims));
max = tx->name[1];
altmax = 0;
if ((max >= 'a') && (max <= 'z'))
max -= 'a' - 'A';
if ((max >= '0') && (max <= '9'))
{
max -= '0';
altmax = 0;
anims[max] = tx;
max++;
}
else
if ((max >= 'A') && (max <= 'J'))
{
altmax = max - 'A';
max = 0;
altanims[altmax] = tx;
altmax++;
}
else
Sys_Error("Bad animating texture %s", tx->name);
for (j = i + 1; j < m->nummiptex; j++)
{
tx2 = loadmodel->textures[j];
if ((!tx2) || (tx2->name[0] != '+'))
continue;
if (strcmp(tx2->name + 2, tx->name + 2))
continue;
num = tx2->name[1];
if ((num >= 'a') && (num <= 'z'))
num -= 'a' - 'A';
if ((num >= '0') && (num <= '9'))
{
num -= '0';
anims[num] = tx2;
if ((num + 1) > max)
max = num + 1;
}
else
if ((num >= 'A') && (num <= 'J'))
{
num = num - 'A';
altanims[num] = tx2;
if ((num + 1) > altmax)
altmax = num + 1;
}
else
Sys_Error("Bad animating texture %s", tx->name);
}
for (j = 0; j < max; j++)
{
tx2 = anims[j];
if (!tx2)
Sys_Error("Missing frame %i of %s", j, tx->name);
tx2->anim_total = max * ANIM_CYCLE;
tx2->anim_min = j * ANIM_CYCLE;
tx2->anim_max = (j + 1) * ANIM_CYCLE;
tx2->anim_next = anims[(j + 1) % max];
if (altmax)
tx2->alternate_anims = altanims[0];
}
for (j = 0; j < altmax; j++)
{
tx2 = altanims[j];
if (!tx2)
Sys_Error("Missing frame %i of %s", j, tx->name);
tx2->anim_total = altmax * ANIM_CYCLE;
tx2->anim_min = j * ANIM_CYCLE;
tx2->anim_max = (j + 1) * ANIM_CYCLE;
tx2->anim_next = altanims[(j + 1) % altmax];
if (max)
tx2->alternate_anims = anims[0];
}
}
}
void Mod_LoadLighting(lump_t *l)
{
if (!l->filelen)
{
loadmodel->lightdata = 0;
return;
}
loadmodel->lightdata = Hunk_AllocName(l->filelen, loadname);
memcpy(loadmodel->lightdata, mod_base + l->fileofs, l->filelen);
}
void Mod_LoadVisibility(lump_t *l)
{
if (!l->filelen)
{
loadmodel->visdata = 0;
return;
}
loadmodel->visdata = Hunk_AllocName(l->filelen, loadname);
memcpy(loadmodel->visdata, mod_base + l->fileofs, l->filelen);
}
void Mod_LoadEntities(lump_t *l)
{
if (!l->filelen)
{
loadmodel->entities = 0;
return;
}
loadmodel->entities = Hunk_AllocName(l->filelen, loadname);
memcpy(loadmodel->entities, mod_base + l->fileofs, l->filelen);
}
void Mod_LoadVertexes(lump_t *l)
{
dvertex_t *in;
mvertex_t *out;
int32_t i;
int32_t count;
in = (void *) (mod_base + l->fileofs);
if (l->filelen % (sizeof(*in)))
Sys_Error("MOD_LoadBmodel: funny lump size in %s", loadmodel->name);
count = l->filelen / (sizeof(*in));
out = Hunk_AllocName(count * (sizeof(*out)), loadname);
loadmodel->vertexes = out;
loadmodel->numvertexes = count;
for (i = 0; i < count; i++, in++, out++)
{
out->position[0] = in->point[0];
out->position[1] = in->point[1];
out->position[2] = in->point[2];
}
}
void Mod_LoadSubmodels(lump_t *l)
{
dmodel_t *in;
dmodel_t *out;
int32_t i;
int32_t j;
int32_t count;
in = (void *) (mod_base + l->fileofs);
if (l->filelen % (sizeof(*in)))
Sys_Error("MOD_LoadBmodel: funny lump size in %s", loadmodel->name);
count = l->filelen / (sizeof(*in));
out = Hunk_AllocName(count * (sizeof(*out)), loadname);
loadmodel->submodels = out;
loadmodel->numsubmodels = count;
for (i = 0; i < count; i++, in++, out++)
{
for (j = 0; j < 3; j++)
{
out->mins[j] = in->mins[j] - 1;
out->maxs[j] = in->maxs[j] + 1;
out->origin[j] = in->origin[j];
}
for (j = 0; j < MAX_MAP_HULLS; j++)
out->headnode[j] = in->headnode[j];
out->visleafs = in->visleafs;
out->firstface = in->firstface;
out->numfaces = in->numfaces;
}
}
void Mod_LoadEdges(lump_t *l)
{
dedge_t *in;
medge_t *out;
int32_t i;
int32_t count;
in = (void *) (mod_base + l->fileofs);
if (l->filelen % (sizeof(*in)))
Sys_Error("MOD_LoadBmodel: funny lump size in %s", loadmodel->name);
count = l->filelen / (sizeof(*in));
out = Hunk_AllocName((count + 1) * (sizeof(*out)), loadname);
loadmodel->edges = out;
loadmodel->numedges = count;
for (i = 0; i < count; i++, in++, out++)
{
out->v[0] = (uint16_t) in->v[0];
out->v[1] = (uint16_t) in->v[1];
}
}
void Mod_LoadTexinfo(lump_t *l)
{
texinfo_t *in;
mtexinfo_t *out;
int32_t i;
int32_t j;
int32_t count;
int32_t miptex;
float len1;
float len2;
in = (void *) (mod_base + l->fileofs);
if (l->filelen % (sizeof(*in)))
Sys_Error("MOD_LoadBmodel: funny lump size in %s", loadmodel->name);
count = l->filelen / (sizeof(*in));
out = Hunk_AllocName(count * (sizeof(*out)), loadname);
loadmodel->texinfo = out;
loadmodel->numtexinfo = count;
for (i = 0; i < count; i++, in++, out++)
{
for (j = 0; j < 8; j++)
out->vecs[0][j] = in->vecs[0][j];
len1 = Length(out->vecs[0]);
len2 = Length(out->vecs[1]);
len1 = (len1 + len2) / 2;
if (len1 < 0.32)
out->mipadjust = 4;
else
if (len1 < 0.49)
out->mipadjust = 3;
else
if (len1 < 0.99)
out->mipadjust = 2;
else
out->mipadjust = 1;
miptex = in->miptex;
out->flags = in->flags;
if (!loadmodel->textures)
{
out->texture = r_notexture_mip;
out->flags = 0;
}
else
{
if (miptex >= loadmodel->numtextures)
Sys_Error("miptex >= loadmodel->numtextures");
out->texture = loadmodel->textures[miptex];
if (!out->texture)
{
out->texture = r_notexture_mip;
out->flags = 0;
}
}
}
}
void CalcSurfaceExtents(msurface_t *s)
{
float mins[2];
float maxs[2];
float val;
int32_t i;
int32_t j;
int32_t e;
mvertex_t *v;
mtexinfo_t *tex;
int32_t bmins[2];
int32_t bmaxs[2];
mins[0] = (mins[1] = 999999);
maxs[0] = (maxs[1] = -99999);
tex = s->texinfo;
for (i = 0; i < s->numedges; i++)
{
e = loadmodel->surfedges[s->firstedge + i];
if (e >= 0)
v = &loadmodel->vertexes[loadmodel->edges[e].v[0]];
else
v = &loadmodel->vertexes[loadmodel->edges[-e].v[1]];
for (j = 0; j < 2; j++)
{
val = (((v->position[0] * tex->vecs[j][0]) + (v->position[1] * tex->vecs[j][1])) + (v->position[2] * tex->vecs[j][2])) + tex->vecs[j][3];
if (val < mins[j])
mins[j] = val;
if (val > maxs[j])
maxs[j] = val;
}
}
for (i = 0; i < 2; i++)
{
bmins[i] = floor(mins[i] / 16);
bmaxs[i] = ceil(maxs[i] / 16);
s->texturemins[i] = bmins[i] * 16;
s->extents[i] = (bmaxs[i] - bmins[i]) * 16;
if ((!(tex->flags & TEX_SPECIAL)) && (s->extents[i] > 256))
Sys_Error("Bad surface extents");
}
}
void Mod_LoadFaces(lump_t *l)
{
dface_t *in;
msurface_t *out;
int32_t i;
int32_t count;
int32_t surfnum;
int32_t planenum;
int32_t side;
in = (void *) (mod_base + l->fileofs);
if (l->filelen % (sizeof(*in)))
Sys_Error("MOD_LoadBmodel: funny lump size in %s", loadmodel->name);
count = l->filelen / (sizeof(*in));
out = Hunk_AllocName(count * (sizeof(*out)), loadname);
loadmodel->surfaces = out;
loadmodel->numsurfaces = count;
for (surfnum = 0; surfnum < count; surfnum++, in++, out++)
{
out->firstedge = in->firstedge;
out->numedges = in->numedges;
out->flags = 0;
planenum = in->planenum;
side = in->side;
if (side)
out->flags |= SURF_PLANEBACK;
out->plane = loadmodel->planes + planenum;
out->texinfo = loadmodel->texinfo + in->texinfo;
CalcSurfaceExtents(out);
for (i = 0; i < MAXLIGHTMAPS; i++)
out->styles[i] = in->styles[i];
i = in->lightofs;
if (i == (-1))
out->samples = 0;
else
out->samples = loadmodel->lightdata + i;
if (!strncmp(out->texinfo->texture->name, "sky", 3))
{
out->flags |= SURF_DRAWSKY | SURF_DRAWTILED;
continue;
}
if (!strncmp(out->texinfo->texture->name, "*", 1))
{
out->flags |= SURF_DRAWTURB | SURF_DRAWTILED;
for (i = 0; i < 2; i++)
{
out->extents[i] = 16384;
out->texturemins[i] = -8192;
}
continue;
}
}
}
void Mod_SetParent(mnode_t *node, mnode_t *parent)
{
node->parent = parent;
if (node->contents < 0)
return;
Mod_SetParent(node->children[0], node);
Mod_SetParent(node->children[1], node);
}
void Mod_LoadNodes(lump_t *l)
{
int32_t i;
int32_t j;
int32_t count;
int32_t p;
dnode_t *in;
mnode_t *out;
in = (void *) (mod_base + l->fileofs);
if (l->filelen % (sizeof(*in)))
Sys_Error("MOD_LoadBmodel: funny lump size in %s", loadmodel->name);
count = l->filelen / (sizeof(*in));
out = Hunk_AllocName(count * (sizeof(*out)), loadname);
loadmodel->nodes = out;
loadmodel->numnodes = count;
for (i = 0; i < count; i++, in++, out++)
{
for (j = 0; j < 3; j++)
{
out->minmaxs[j] = in->mins[j];
out->minmaxs[3 + j] = in->maxs[j];
}
p = in->planenum;
out->plane = loadmodel->planes + p;
out->firstsurface = in->firstface;
out->numsurfaces = in->numfaces;
for (j = 0; j < 2; j++)
{
p = in->children[j];
if (p >= 0)
out->children[j] = loadmodel->nodes + p;
else
out->children[j] = (mnode_t *) (loadmodel->leafs + ((-1) - p));
}
}
Mod_SetParent(loadmodel->nodes, 0);
}
void Mod_LoadLeafs(lump_t *l)
{
dleaf_t *in;
mleaf_t *out;
int32_t i;
int32_t j;
int32_t count;
int32_t p;
in = (void *) (mod_base + l->fileofs);
if (l->filelen % (sizeof(*in)))
Sys_Error("MOD_LoadBmodel: funny lump size in %s", loadmodel->name);
count = l->filelen / (sizeof(*in));
out = Hunk_AllocName(count * (sizeof(*out)), loadname);
loadmodel->leafs = out;
loadmodel->numleafs = count;
for (i = 0; i < count; i++, in++, out++)
{
for (j = 0; j < 3; j++)
{
out->minmaxs[j] = in->mins[j];
out->minmaxs[3 + j] = in->maxs[j];
}
p = in->contents;
out->contents = p;
out->firstmarksurface = loadmodel->marksurfaces + in->firstmarksurface;
out->nummarksurfaces = in->nummarksurfaces;
p = in->visofs;
if (p == (-1))
out->compressed_vis = 0;
else
out->compressed_vis = loadmodel->visdata + p;
out->efrags = 0;
for (j = 0; j < 4; j++)
out->ambient_sound_level[j] = in->ambient_level[j];
}
}
void Mod_LoadClipnodes(lump_t *l)
{
dclipnode_t *in;
dclipnode_t *out;
int32_t i;
int32_t count;
hull_t *hull;
in = (void *) (mod_base + l->fileofs);
if (l->filelen % (sizeof(*in)))
Sys_Error("MOD_LoadBmodel: funny lump size in %s", loadmodel->name);
count = l->filelen / (sizeof(*in));
out = Hunk_AllocName(count * (sizeof(*out)), loadname);
loadmodel->clipnodes = out;
loadmodel->numclipnodes = count;
hull = &loadmodel->hulls[1];
hull->clipnodes = out;
hull->firstclipnode = 0;
hull->lastclipnode = count - 1;
hull->planes = loadmodel->planes;
hull->clip_mins[0] = -16;
hull->clip_mins[1] = -16;
hull->clip_mins[2] = -24;
hull->clip_maxs[0] = 16;
hull->clip_maxs[1] = 16;
hull->clip_maxs[2] = 32;
hull = &loadmodel->hulls[2];
hull->clipnodes = out;
hull->firstclipnode = 0;
hull->lastclipnode = count - 1;
hull->planes = loadmodel->planes;
hull->clip_mins[0] = -32;
hull->clip_mins[1] = -32;
hull->clip_mins[2] = -24;
hull->clip_maxs[0] = 32;
hull->clip_maxs[1] = 32;
hull->clip_maxs[2] = 64;
for (i = 0; i < count; i++, out++, in++)
{
out->planenum = in->planenum;
out->children[0] = in->children[0];
out->children[1] = in->children[1];
}
}
void Mod_MakeHull0(void)
{
mnode_t *in;
mnode_t *child;
dclipnode_t *out;
int32_t i;
int32_t j;
int32_t count;
hull_t *hull;
hull = &loadmodel->hulls[0];
in = loadmodel->nodes;
count = loadmodel->numnodes;
out = Hunk_AllocName(count * (sizeof(*out)), loadname);
hull->clipnodes = out;
hull->firstclipnode = 0;
hull->lastclipnode = count - 1;
hull->planes = loadmodel->planes;
for (i = 0; i < count; i++, out++, in++)
{
out->planenum = in->plane - loadmodel->planes;
for (j = 0; j < 2; j++)
{
child = in->children[j];
if (child->contents < 0)
out->children[j] = child->contents;
else
out->children[j] = child - loadmodel->nodes;
}
}
}
void Mod_LoadMarksurfaces(lump_t *l)
{
int32_t i;
int32_t j;
int32_t count;
int16_t *in;
msurface_t **out;
in = (void *) (mod_base + l->fileofs);
if (l->filelen % (sizeof(*in)))
Sys_Error("MOD_LoadBmodel: funny lump size in %s", loadmodel->name);
count = l->filelen / (sizeof(*in));
out = Hunk_AllocName(count * (sizeof(*out)), loadname);
loadmodel->marksurfaces = out;
loadmodel->nummarksurfaces = count;
for (i = 0; i < count; i++)
{
j = in[i];
if (j >= loadmodel->numsurfaces)
Sys_Error("Mod_ParseMarksurfaces: bad surface number");
out[i] = loadmodel->surfaces + j;
}
}
void Mod_LoadSurfedges(lump_t *l)
{
int32_t i;
int32_t count;
int32_t *in;
int32_t *out;
in = (void *) (mod_base + l->fileofs);
if (l->filelen % (sizeof(*in)))
Sys_Error("MOD_LoadBmodel: funny lump size in %s", loadmodel->name);
count = l->filelen / (sizeof(*in));
out = Hunk_AllocName(count * (sizeof(*out)), loadname);
loadmodel->surfedges = out;
loadmodel->numsurfedges = count;
for (i = 0; i < count; i++)
out[i] = in[i];
}
void Mod_LoadPlanes(lump_t *l)
{
int32_t i;
int32_t j;
mplane_t *out;
dplane_t *in;
int32_t count;
int32_t bits;
in = (void *) (mod_base + l->fileofs);
if (l->filelen % (sizeof(*in)))
Sys_Error("MOD_LoadBmodel: funny lump size in %s", loadmodel->name);
count = l->filelen / (sizeof(*in));
out = Hunk_AllocName((count * 2) * (sizeof(*out)), loadname);
loadmodel->planes = out;
loadmodel->numplanes = count;
for (i = 0; i < count; i++, in++, out++)
{
bits = 0;
for (j = 0; j < 3; j++)
{
out->normal[j] = in->normal[j];
if (out->normal[j] < 0)
bits |= 1 << j;
}
out->dist = in->dist;
out->type = in->type;
out->signbits = bits;
}
}
float RadiusFromBounds(vec3_t mins, vec3_t maxs)
{
int32_t i;
vec3_t corner;
for (i = 0; i < 3; i++)
{
corner[i] = (fabs(mins[i]) > fabs(maxs[i])) ? (fabs(mins[i])) : (fabs(maxs[i]));
}
return Length(corner);
}
void Mod_LoadBrushModel(model_t *mod, void *buffer)
{
int32_t i;
int32_t j;
dheader_t *header;
dmodel_t *bm;
loadmodel->type = mod_brush;
header = (dheader_t *) buffer;
i = header->version;
if (i != BSPVERSION)
Sys_Error("Mod_LoadBrushModel: %s has wrong version number (%i should be %i)", mod->name, i, BSPVERSION);
mod_base = (uint8_t *) header;
for (i = 0; i < ((sizeof(dheader_t)) / 4); i++)
((int32_t *) header)[i] = ((int32_t *) header)[i];
Mod_LoadVertexes(&header->lumps[LUMP_VERTEXES]);
Mod_LoadEdges(&header->lumps[LUMP_EDGES]);
Mod_LoadSurfedges(&header->lumps[LUMP_SURFEDGES]);
Mod_LoadTextures(&header->lumps[LUMP_TEXTURES]);
Mod_LoadLighting(&header->lumps[LUMP_LIGHTING]);
Mod_LoadPlanes(&header->lumps[LUMP_PLANES]);
Mod_LoadTexinfo(&header->lumps[LUMP_TEXINFO]);
Mod_LoadFaces(&header->lumps[LUMP_FACES]);
Mod_LoadMarksurfaces(&header->lumps[LUMP_MARKSURFACES]);
Mod_LoadVisibility(&header->lumps[LUMP_VISIBILITY]);
Mod_LoadLeafs(&header->lumps[LUMP_LEAFS]);
Mod_LoadNodes(&header->lumps[LUMP_NODES]);
Mod_LoadClipnodes(&header->lumps[LUMP_CLIPNODES]);
Mod_LoadEntities(&header->lumps[LUMP_ENTITIES]);
Mod_LoadSubmodels(&header->lumps[LUMP_MODELS]);
Mod_MakeHull0();
mod->numframes = 2;
mod->flags = 0;
for (i = 0; i < mod->numsubmodels; i++)
{
bm = &mod->submodels[i];
mod->hulls[0].firstclipnode = bm->headnode[0];
for (j = 1; j < MAX_MAP_HULLS; j++)
{
mod->hulls[j].firstclipnode = bm->headnode[j];
mod->hulls[j].lastclipnode = mod->numclipnodes - 1;
}
mod->firstmodelsurface = bm->firstface;
mod->nummodelsurfaces = bm->numfaces;
VectorCopy(bm->maxs, mod->maxs);
VectorCopy(bm->mins, mod->mins);
mod->radius = RadiusFromBounds(mod->mins, mod->maxs);
mod->numleafs = bm->visleafs;
if (i < (mod->numsubmodels - 1))
{
char name[10];
sprintf(name, "*%i", i + 1);
loadmodel = Mod_FindName(name);
*loadmodel = *mod;
strcpy(loadmodel->name, name);
mod = loadmodel;
}
}
}
void *Mod_LoadAliasFrame(void *pin, int32_t *pframeindex, int32_t numv, trivertx_t *pbboxmin, trivertx_t *pbboxmax, aliashdr_t *pheader, char *name)
{
trivertx_t *pframe;
trivertx_t *pinframe;
int32_t i;
int32_t j;
daliasframe_t *pdaliasframe;
pdaliasframe = (daliasframe_t *) pin;
strcpy(name, pdaliasframe->name);
for (i = 0; i < 3; i++)
{
pbboxmin->v[i] = pdaliasframe->bboxmin.v[i];
pbboxmax->v[i] = pdaliasframe->bboxmax.v[i];
}
pinframe = (trivertx_t *) (pdaliasframe + 1);
pframe = Hunk_AllocName(numv * (sizeof(*pframe)), loadname);
*pframeindex = ((uint8_t *) pframe) - ((uint8_t *) pheader);
for (j = 0; j < numv; j++)
{
int32_t k;
pframe[j].lightnormalindex = pinframe[j].lightnormalindex;
for (k = 0; k < 3; k++)
{
pframe[j].v[k] = pinframe[j].v[k];
}
}
pinframe += numv;
return (void *) pinframe;
}
void *Mod_LoadAliasGroup(void *pin, int32_t *pframeindex, int32_t numv, trivertx_t *pbboxmin, trivertx_t *pbboxmax, aliashdr_t *pheader, char *name)
{
daliasgroup_t *pingroup;
maliasgroup_t *paliasgroup;
int32_t i;
int32_t numframes;
daliasinterval_t *pin_intervals;
float *poutintervals;
void *ptemp;
pingroup = (daliasgroup_t *) pin;
numframes = pingroup->numframes;
paliasgroup = Hunk_AllocName((sizeof(maliasgroup_t)) + ((numframes - 1) * (sizeof(paliasgroup->frames[0]))), loadname);
paliasgroup->numframes = numframes;
for (i = 0; i < 3; i++)
{
pbboxmin->v[i] = pingroup->bboxmin.v[i];
pbboxmax->v[i] = pingroup->bboxmax.v[i];
}
*pframeindex = ((uint8_t *) paliasgroup) - ((uint8_t *) pheader);
pin_intervals = (daliasinterval_t *) (pingroup + 1);
poutintervals = Hunk_AllocName(numframes * (sizeof(float)), loadname);
paliasgroup->intervals = ((uint8_t *) poutintervals) - ((uint8_t *) pheader);
for (i = 0; i < numframes; i++)
{
*poutintervals = pin_intervals->interval;
if ((*poutintervals) <= 0.0)
Sys_Error("Mod_LoadAliasGroup: interval<=0");
poutintervals++;
pin_intervals++;
}
ptemp = (void *) pin_intervals;
for (i = 0; i < numframes; i++)
{
ptemp = Mod_LoadAliasFrame(ptemp, &paliasgroup->frames[i].frame, numv, &paliasgroup->frames[i].bboxmin, &paliasgroup->frames[i].bboxmax, pheader, name);
}
return ptemp;
}
void *Mod_LoadAliasSkin(void *pin, int32_t *pskinindex, int32_t skinsize, aliashdr_t *pheader)
{
int32_t i;
uint8_t *pskin;
uint8_t *pinskin;
uint16_t *pusskin;
pskin = Hunk_AllocName(skinsize * r_pixbytes, loadname);
pinskin = (uint8_t *) pin;
*pskinindex = ((uint8_t *) pskin) - ((uint8_t *) pheader);
if (r_pixbytes == 1)
{
memcpy(pskin, pinskin, skinsize);
}
else
if (r_pixbytes == 2)
{
pusskin = (uint16_t *) pskin;
for (i = 0; i < skinsize; i++)
pusskin[i] = d_8to16table[pinskin[i]];
}
else
{
Sys_Error("Mod_LoadAliasSkin: driver set invalid r_pixbytes: %d\n", r_pixbytes);
}
pinskin += skinsize;
return (void *) pinskin;
}
void *Mod_LoadAliasSkinGroup(void *pin, int32_t *pskinindex, int32_t skinsize, aliashdr_t *pheader)
{
daliasskingroup_t *pinskingroup;
maliasskingroup_t *paliasskingroup;
int32_t i;
int32_t numskins;
daliasskininterval_t *pinskinintervals;
float *poutskinintervals;
void *ptemp;
pinskingroup = (daliasskingroup_t *) pin;
numskins = pinskingroup->numskins;
paliasskingroup = Hunk_AllocName((sizeof(maliasskingroup_t)) + ((numskins - 1) * (sizeof(paliasskingroup->skindescs[0]))), loadname);
paliasskingroup->numskins = numskins;
*pskinindex = ((uint8_t *) paliasskingroup) - ((uint8_t *) pheader);
pinskinintervals = (daliasskininterval_t *) (pinskingroup + 1);
poutskinintervals = Hunk_AllocName(numskins * (sizeof(float)), loadname);
paliasskingroup->intervals = ((uint8_t *) poutskinintervals) - ((uint8_t *) pheader);
for (i = 0; i < numskins; i++)
{
*poutskinintervals = pinskinintervals->interval;
if ((*poutskinintervals) <= 0)
Sys_Error("Mod_LoadAliasSkinGroup: interval<=0");
poutskinintervals++;
pinskinintervals++;
}
ptemp = (void *) pinskinintervals;
for (i = 0; i < numskins; i++)
{
ptemp = Mod_LoadAliasSkin(ptemp, &paliasskingroup->skindescs[i].skin, skinsize, pheader);
}
return ptemp;
}
void Mod_LoadAliasModel(model_t *mod, void *buffer)
{
int32_t i;
mdl_t *pmodel;
mdl_t *pinmodel;
stvert_t *pstverts;
stvert_t *pinstverts;
aliashdr_t *pheader;
mtriangle_t *ptri;
dtriangle_t *pintriangles;
int32_t version;
int32_t numframes;
int32_t numskins;
int32_t size;
daliasframetype_t *pframetype;
daliasskintype_t *pskintype;
maliasskindesc_t *pskindesc;
int32_t skinsize;
int32_t start;
int32_t end;
int32_t total;
start = Hunk_LowMark();
pinmodel = (mdl_t *) buffer;
version = pinmodel->version;
if (version != ALIAS_VERSION)
Sys_Error("%s has wrong version number (%i should be %i)", mod->name, version, ALIAS_VERSION);
size = ((((sizeof(aliashdr_t)) + ((pinmodel->numframes - 1) * (sizeof(pheader->frames[0])))) + (sizeof(mdl_t))) + (pinmodel->numverts * (sizeof(stvert_t)))) + (pinmodel->numtris * (sizeof(mtriangle_t)));
pheader = Hunk_AllocName(size, loadname);
pmodel = (mdl_t *) (((uint8_t *) (&pheader[1])) + ((pinmodel->numframes - 1) * (sizeof(pheader->frames[0]))));
mod->flags = pinmodel->flags;
pmodel->boundingradius = pinmodel->boundingradius;
pmodel->numskins = pinmodel->numskins;
pmodel->skinwidth = pinmodel->skinwidth;
pmodel->skinheight = pinmodel->skinheight;
if (pmodel->skinheight > MAX_LBM_HEIGHT)
Sys_Error("model %s has a skin taller than %d", mod->name, MAX_LBM_HEIGHT);
pmodel->numverts = pinmodel->numverts;
if (pmodel->numverts <= 0)
Sys_Error("model %s has no vertices", mod->name);
if (pmodel->numverts > MAXALIASVERTS)
Sys_Error("model %s has too many vertices", mod->name);
pmodel->numtris = pinmodel->numtris;
if (pmodel->numtris <= 0)
Sys_Error("model %s has no triangles", mod->name);
pmodel->numframes = pinmodel->numframes;
pmodel->size = pinmodel->size * ALIAS_BASE_SIZE_RATIO;
mod->synctype = pinmodel->synctype;
mod->numframes = pmodel->numframes;
for (i = 0; i < 3; i++)
{
pmodel->scale[i] = pinmodel->scale[i];
pmodel->scale_origin[i] = pinmodel->scale_origin[i];
pmodel->eyeposition[i] = pinmodel->eyeposition[i];
}
numskins = pmodel->numskins;
numframes = pmodel->numframes;
if (pmodel->skinwidth & 0x03)
Sys_Error("Mod_LoadAliasModel: skinwidth not multiple of 4");
pheader->model = ((uint8_t *) pmodel) - ((uint8_t *) pheader);
skinsize = pmodel->skinheight * pmodel->skinwidth;
if (numskins < 1)
Sys_Error("Mod_LoadAliasModel: Invalid # of skins: %d\n", numskins);
pskintype = (daliasskintype_t *) (&pinmodel[1]);
pskindesc = Hunk_AllocName(numskins * (sizeof(maliasskindesc_t)), loadname);
pheader->skindesc = ((uint8_t *) pskindesc) - ((uint8_t *) pheader);
for (i = 0; i < numskins; i++)
{
aliasskintype_t skintype;
skintype = pskintype->type;
pskindesc[i].type = skintype;
if (skintype == ALIAS_SKIN_SINGLE)
{
pskintype = (daliasskintype_t *) Mod_LoadAliasSkin(pskintype + 1, &pskindesc[i].skin, skinsize, pheader);
}
else
{
pskintype = (daliasskintype_t *) Mod_LoadAliasSkinGroup(pskintype + 1, &pskindesc[i].skin, skinsize, pheader);
}
}
pstverts = (stvert_t *) (&pmodel[1]);
pinstverts = (stvert_t *) pskintype;
pheader->stverts = ((uint8_t *) pstverts) - ((uint8_t *) pheader);
for (i = 0; i < pmodel->numverts; i++)
{
pstverts[i].onseam = pinstverts[i].onseam;
pstverts[i].s = pinstverts[i].s << 16;
pstverts[i].t = pinstverts[i].t << 16;
}
ptri = (mtriangle_t *) (&pstverts[pmodel->numverts]);
pintriangles = (dtriangle_t *) (&pinstverts[pmodel->numverts]);
pheader->triangles = ((uint8_t *) ptri) - ((uint8_t *) pheader);
for (i = 0; i < pmodel->numtris; i++)
{
int32_t j;
ptri[i].facesfront = pintriangles[i].facesfront;
for (j = 0; j < 3; j++)
{
ptri[i].vertindex[j] = pintriangles[i].vertindex[j];
}
}
if (numframes < 1)
Sys_Error("Mod_LoadAliasModel: Invalid # of frames: %d\n", numframes);
pframetype = (daliasframetype_t *) (&pintriangles[pmodel->numtris]);
for (i = 0; i < numframes; i++)
{
aliasframetype_t frametype;
frametype = pframetype->type;
pheader->frames[i].type = frametype;
if (frametype == ALIAS_SINGLE)
{
pframetype = (daliasframetype_t *) Mod_LoadAliasFrame(pframetype + 1, &pheader->frames[i].frame, pmodel->numverts, &pheader->frames[i].bboxmin, &pheader->frames[i].bboxmax, pheader, pheader->frames[i].name);
}
else
{
pframetype = (daliasframetype_t *) Mod_LoadAliasGroup(pframetype + 1, &pheader->frames[i].frame, pmodel->numverts, &pheader->frames[i].bboxmin, &pheader->frames[i].bboxmax, pheader, pheader->frames[i].name);
}
}
mod->type = mod_alias;
mod->mins[0] = (mod->mins[1] = (mod->mins[2] = -16));
mod->maxs[0] = (mod->maxs[1] = (mod->maxs[2] = 16));
end = Hunk_LowMark();
total = end - start;
Cache_Alloc(&mod->cache, total, loadname);
if (!mod->cache.data)
return;
memcpy(mod->cache.data, pheader, total);
Hunk_FreeToLowMark(start);
}
void *Mod_LoadSpriteFrame(void *pin, mspriteframe_t **ppframe)
{
dspriteframe_t *pinframe;
mspriteframe_t *pspriteframe;
int32_t i;
int32_t width;
int32_t height;
int32_t size;
int32_t origin[2];
uint16_t *ppixout;
uint8_t *ppixin;
pinframe = (dspriteframe_t *) pin;
width = pinframe->width;
height = pinframe->height;
size = width * height;
pspriteframe = Hunk_AllocName((sizeof(mspriteframe_t)) + (size * r_pixbytes), loadname);
memset(pspriteframe, 0, (sizeof(mspriteframe_t)) + size);
*ppframe = pspriteframe;
pspriteframe->width = width;
pspriteframe->height = height;
origin[0] = pinframe->origin[0];
origin[1] = pinframe->origin[1];
pspriteframe->up = origin[1];
pspriteframe->down = origin[1] - height;
pspriteframe->left = origin[0];
pspriteframe->right = width + origin[0];
if (r_pixbytes == 1)
{
memcpy(&pspriteframe->pixels[0], (uint8_t *) (pinframe + 1), size);
}
else
if (r_pixbytes == 2)
{
ppixin = (uint8_t *) (pinframe + 1);
ppixout = (uint16_t *) (&pspriteframe->pixels[0]);
for (i = 0; i < size; i++)
ppixout[i] = d_8to16table[ppixin[i]];
}
else
{
Sys_Error("Mod_LoadSpriteFrame: driver set invalid r_pixbytes: %d\n", r_pixbytes);
}
return (void *) ((((uint8_t *) pinframe) + (sizeof(dspriteframe_t))) + size);
}
void *Mod_LoadSpriteGroup(void *pin, mspriteframe_t **ppframe)
{
dspritegroup_t *pingroup;
mspritegroup_t *pspritegroup;
int32_t i;
int32_t numframes;
dspriteinterval_t *pin_intervals;
float *poutintervals;
void *ptemp;
pingroup = (dspritegroup_t *) pin;
numframes = pingroup->numframes;
pspritegroup = Hunk_AllocName((sizeof(mspritegroup_t)) + ((numframes - 1) * (sizeof(pspritegroup->frames[0]))), loadname);
pspritegroup->numframes = numframes;
*ppframe = (mspriteframe_t *) pspritegroup;
pin_intervals = (dspriteinterval_t *) (pingroup + 1);
poutintervals = Hunk_AllocName(numframes * (sizeof(float)), loadname);
pspritegroup->intervals = poutintervals;
for (i = 0; i < numframes; i++)
{
*poutintervals = pin_intervals->interval;
if ((*poutintervals) <= 0.0)
Sys_Error("Mod_LoadSpriteGroup: interval<=0");
poutintervals++;
pin_intervals++;
}
ptemp = (void *) pin_intervals;
for (i = 0; i < numframes; i++)
{
ptemp = Mod_LoadSpriteFrame(ptemp, &pspritegroup->frames[i]);
}
return ptemp;
}
void Mod_LoadSpriteModel(model_t *mod, void *buffer)
{
int32_t i;
int32_t version;
dsprite_t *pin;
msprite_t *psprite;
int32_t numframes;
int32_t size;
dspriteframetype_t *pframetype;
pin = (dsprite_t *) buffer;
version = pin->version;
if (version != SPRITE_VERSION)
Sys_Error("%s has wrong version number (%i should be %i)", mod->name, version, SPRITE_VERSION);
numframes = pin->numframes;
size = (sizeof(msprite_t)) + ((numframes - 1) * (sizeof(psprite->frames)));
psprite = Hunk_AllocName(size, loadname);
mod->cache.data = psprite;
psprite->type = pin->type;
psprite->maxwidth = pin->width;
psprite->maxheight = pin->height;
psprite->beamlength = pin->beamlength;
mod->synctype = pin->synctype;
psprite->numframes = numframes;
mod->mins[0] = (mod->mins[1] = (-psprite->maxwidth) / 2);
mod->maxs[0] = (mod->maxs[1] = psprite->maxwidth / 2);
mod->mins[2] = (-psprite->maxheight) / 2;
mod->maxs[2] = psprite->maxheight / 2;
if (numframes < 1)
Sys_Error("Mod_LoadSpriteModel: Invalid # of frames: %d\n", numframes);
mod->numframes = numframes;
mod->flags = 0;
pframetype = (dspriteframetype_t *) (pin + 1);
for (i = 0; i < numframes; i++)
{
spriteframetype_t frametype;
frametype = pframetype->type;
psprite->frames[i].type = frametype;
if (frametype == SPR_SINGLE)
{
pframetype = (dspriteframetype_t *) Mod_LoadSpriteFrame(pframetype + 1, &psprite->frames[i].frameptr);
}
else
{
pframetype = (dspriteframetype_t *) Mod_LoadSpriteGroup(pframetype + 1, &psprite->frames[i].frameptr);
}
}
mod->type = mod_sprite;
}
void Mod_Print(void)
{
int32_t i;
model_t *mod;
Con_Printf("Cached models:\n");
for (i = 0, mod = mod_known; i < mod_numknown; i++, mod++)
{
Con_Printf("%8p : %s", mod->cache.data, mod->name);
if (mod->needload & NL_UNREFERENCED)
Con_Printf(" (!R)");
if (mod->needload & NL_NEEDS_LOADED)
Con_Printf(" (!P)");
Con_Printf("\n");
}
}
int32_t Datagram_SendMessage(qsocket_t *sock, sizebuf_t *data)
{
uint32_t packetLen;
uint32_t dataLen;
uint32_t eom;
memcpy(sock->sendMessage, data->data, data->cursize);
sock->sendMessageLength = data->cursize;
if (data->cursize <= MAX_DATAGRAM)
{
dataLen = data->cursize;
eom = NETFLAG_EOM;
}
else
{
dataLen = MAX_DATAGRAM;
eom = 0;
}
packetLen = NET_HEADERSIZE + dataLen;
packetBuffer.length = packetLen | (NETFLAG_DATA | eom);
packetBuffer.sequence = sock->sendSequence++;
memcpy(packetBuffer.data, sock->sendMessage, dataLen);
sock->canSend = 0;
if (sfunc.Write(sock->socket, (uint8_t *) (&packetBuffer), packetLen, &sock->addr) == (-1))
return -1;
sock->lastSendTime = net_time;
packetsSent++;
return 1;
}
int32_t SendMessageNext(qsocket_t *sock)
{
uint32_t packetLen;
uint32_t dataLen;
uint32_t eom;
if (sock->sendMessageLength <= MAX_DATAGRAM)
{
dataLen = sock->sendMessageLength;
eom = NETFLAG_EOM;
}
else
{
dataLen = MAX_DATAGRAM;
eom = 0;
}
packetLen = NET_HEADERSIZE + dataLen;
packetBuffer.length = packetLen | (NETFLAG_DATA | eom);
packetBuffer.sequence = sock->sendSequence++;
memcpy(packetBuffer.data, sock->sendMessage, dataLen);
sock->sendNext = 0;
if (sfunc.Write(sock->socket, (uint8_t *) (&packetBuffer), packetLen, &sock->addr) == (-1))
return -1;
sock->lastSendTime = net_time;
packetsSent++;
return 1;
}
int32_t ReSendMessage(qsocket_t *sock)
{
uint32_t packetLen;
uint32_t dataLen;
uint32_t eom;
if (sock->sendMessageLength <= MAX_DATAGRAM)
{
dataLen = sock->sendMessageLength;
eom = NETFLAG_EOM;
}
else
{
dataLen = MAX_DATAGRAM;
eom = 0;
}
packetLen = NET_HEADERSIZE + dataLen;
packetBuffer.length = packetLen | (NETFLAG_DATA | eom);
packetBuffer.sequence = sock->sendSequence - 1;
memcpy(packetBuffer.data, sock->sendMessage, dataLen);
sock->sendNext = 0;
if (sfunc.Write(sock->socket, (uint8_t *) (&packetBuffer), packetLen, &sock->addr) == (-1))
return -1;
sock->lastSendTime = net_time;
packetsReSent++;
return 1;
}
bool Datagram_CanSendMessage(qsocket_t *sock)
{
if (sock->sendNext)
SendMessageNext(sock);
return sock->canSend;
}
bool Datagram_CanSendUnreliableMessage(qsocket_t *sock)
{
return 1;
}
int32_t Datagram_SendUnreliableMessage(qsocket_t *sock, sizebuf_t *data)
{
int32_t packetLen;
packetLen = NET_HEADERSIZE + data->cursize;
packetBuffer.length = packetLen | NETFLAG_UNRELIABLE;
packetBuffer.sequence = sock->unreliableSendSequence++;
memcpy(packetBuffer.data, data->data, data->cursize);
if (sfunc.Write(sock->socket, (uint8_t *) (&packetBuffer), packetLen, &sock->addr) == (-1))
return -1;
packetsSent++;
return 1;
}
int32_t Datagram_GetMessage(qsocket_t *sock)
{
uint32_t length;
uint32_t flags;
int32_t ret = 0;
struct qsockaddr readaddr;
uint32_t sequence;
uint32_t count;
if (!sock->canSend)
if ((net_time - sock->lastSendTime) > 1.0)
ReSendMessage(sock);
while (1)
{
length = sfunc.Read(sock->socket, (uint8_t *) (&packetBuffer), NET_DATAGRAMSIZE, &readaddr);
if (length == 0)
break;
if (length == (-1))
{
Con_Printf("Read error\n");
return -1;
}
if (sfunc.AddrCompare(&readaddr, &sock->addr) != 0)
{
continue;
}
if (length < NET_HEADERSIZE)
{
shortPacketCount++;
continue;
}
length = packetBuffer.length;
flags = length & (~NETFLAG_LENGTH_MASK);
length &= NETFLAG_LENGTH_MASK;
if (flags & NETFLAG_CTL)
continue;
sequence = packetBuffer.sequence;
packetsReceived++;
if (flags & NETFLAG_UNRELIABLE)
{
if (sequence < sock->unreliableReceiveSequence)
{
Con_DPrintf("Got a stale datagram\n");
ret = 0;
break;
}
if (sequence != sock->unreliableReceiveSequence)
{
count = sequence - sock->unreliableReceiveSequence;
droppedDatagrams += count;
Con_DPrintf("Dropped %u datagram(s)\n", count);
}
sock->unreliableReceiveSequence = sequence + 1;
length -= NET_HEADERSIZE;
SZ_Clear(&net_message);
SZ_Write(&net_message, packetBuffer.data, length);
ret = 2;
break;
}
if (flags & NETFLAG_ACK)
{
if (sequence != (sock->sendSequence - 1))
{
Con_DPrintf("Stale ACK received\n");
continue;
}
if (sequence == sock->ackSequence)
{
sock->ackSequence++;
if (sock->ackSequence != sock->sendSequence)
Con_DPrintf("ack sequencing error\n");
}
else
{
Con_DPrintf("Duplicate ACK received\n");
continue;
}
sock->sendMessageLength -= MAX_DATAGRAM;
if (sock->sendMessageLength > 0)
{
memcpy(sock->sendMessage, sock->sendMessage + MAX_DATAGRAM, sock->sendMessageLength);
sock->sendNext = 1;
}
else
{
sock->sendMessageLength = 0;
sock->canSend = 1;
}
continue;
}
if (flags & NETFLAG_DATA)
{
packetBuffer.length = NET_HEADERSIZE | NETFLAG_ACK;
packetBuffer.sequence = sequence;
sfunc.Write(sock->socket, (uint8_t *) (&packetBuffer), NET_HEADERSIZE, &readaddr);
if (sequence != sock->receiveSequence)
{
receivedDuplicateCount++;
continue;
}
sock->receiveSequence++;
length -= NET_HEADERSIZE;
if (flags & NETFLAG_EOM)
{
SZ_Clear(&net_message);
SZ_Write(&net_message, sock->receiveMessage, sock->receiveMessageLength);
SZ_Write(&net_message, packetBuffer.data, length);
sock->receiveMessageLength = 0;
ret = 1;
break;
}
memcpy(sock->receiveMessage + sock->receiveMessageLength, packetBuffer.data, length);
sock->receiveMessageLength += length;
continue;
}
}
if (sock->sendNext)
SendMessageNext(sock);
return ret;
}
void PrintStats(qsocket_t *s)
{
Con_Printf("canSend = %4u \n", s->canSend);
Con_Printf("sendSeq = %4u ", s->sendSequence);
Con_Printf("recvSeq = %4u \n", s->receiveSequence);
Con_Printf("\n");
}
void NET_Stats_f(void)
{
qsocket_t *s;
if (Cmd_Argc() == 1)
{
Con_Printf("unreliable messages sent = %i\n", unreliableMessagesSent);
Con_Printf("unreliable messages recv = %i\n", unreliableMessagesReceived);
Con_Printf("reliable messages sent = %i\n", messagesSent);
Con_Printf("reliable messages received = %i\n", messagesReceived);
Con_Printf("packetsSent = %i\n", packetsSent);
Con_Printf("packetsReSent = %i\n", packetsReSent);
Con_Printf("packetsReceived = %i\n", packetsReceived);
Con_Printf("receivedDuplicateCount = %i\n", receivedDuplicateCount);
Con_Printf("shortPacketCount = %i\n", shortPacketCount);
Con_Printf("droppedDatagrams = %i\n", droppedDatagrams);
}
else
if (strcmp(Cmd_Argv(1), "*") == 0)
{
for (s = net_activeSockets; s; s = s->next)
PrintStats(s);
for (s = net_freeSockets; s; s = s->next)
PrintStats(s);
}
else
{
for (s = net_activeSockets; s; s = s->next)
if (strcasecmp(Cmd_Argv(1), s->address) == 0)
break;
if (s == 0)
for (s = net_freeSockets; s; s = s->next)
if (strcasecmp(Cmd_Argv(1), s->address) == 0)
break;
if (s == 0)
return;
PrintStats(s);
}
}
static void Test_Poll(void)
{
struct qsockaddr clientaddr;
int32_t control;
int32_t len;
char name[32];
char address[64];
int32_t colors;
int32_t frags;
int32_t connectTime;
uint8_t playerNumber;
net_landriverlevel = testDriver;
while (1)
{
len = dfunc.Read(testSocket, net_message.data, net_message.maxsize, &clientaddr);
if (len < (sizeof(int32_t)))
break;
net_message.cursize = len;
MSG_BeginReading();
control = *((int32_t *) net_message.data);
MSG_ReadLong();
if (control == (-1))
break;
if ((control & (~NETFLAG_LENGTH_MASK)) != NETFLAG_CTL)
break;
if ((control & NETFLAG_LENGTH_MASK) != len)
break;
if (MSG_ReadByte() != CCREP_PLAYER_INFO)
Sys_Error("Unexpected repsonse to Player Info request\n");
playerNumber = MSG_ReadByte();
strcpy(name, MSG_ReadString());
colors = MSG_ReadLong();
frags = MSG_ReadLong();
connectTime = MSG_ReadLong();
strcpy(address, MSG_ReadString());
Con_Printf("%s\n frags:%3i colors:%u %u time:%u\n %s\n", name, frags, colors >> 4, colors & 0x0f, connectTime / 60, address);
}
testPollCount--;
if (testPollCount)
{
SchedulePollProcedure(&testPollProcedure, 0.1);
}
else
{
dfunc.CloseSocket(testSocket);
testInProgress = 0;
}
}
static void Test_f(void)
{
char *host;
int32_t n;
int32_t max = MAX_SCOREBOARD;
struct qsockaddr sendaddr;
if (testInProgress)
return;
host = Cmd_Argv(1);
if (host && hostCacheCount)
{
for (n = 0; n < hostCacheCount; n++)
if (strcasecmp(host, hostcache[n].name) == 0)
{
if (hostcache[n].driver != myDriverLevel)
continue;
net_landriverlevel = hostcache[n].ldriver;
max = hostcache[n].maxusers;
memcpy(&sendaddr, &hostcache[n].addr, sizeof(struct qsockaddr));
break;
}
if (n < hostCacheCount)
goto JustDoIt;
}
for (net_landriverlevel = 0; net_landriverlevel < net_numlandrivers; net_landriverlevel++)
{
if (!net_landrivers[net_landriverlevel].initialized)
continue;
if (dfunc.GetAddrFromName(host, &sendaddr) != (-1))
break;
}
if (net_landriverlevel == net_numlandrivers)
return;
JustDoIt:
testSocket = dfunc.OpenSocket(0);
if (testSocket == (-1))
return;
testInProgress = 1;
testPollCount = 20;
testDriver = net_landriverlevel;
for (n = 0; n < max; n++)
{
SZ_Clear(&net_message);
MSG_WriteLong(&net_message, 0);
MSG_WriteByte(&net_message, CCREQ_PLAYER_INFO);
MSG_WriteByte(&net_message, n);
*((int32_t *) net_message.data) = NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK);
dfunc.Write(testSocket, net_message.data, net_message.cursize, &sendaddr);
}
SZ_Clear(&net_message);
SchedulePollProcedure(&testPollProcedure, 0.1);
}
static void Test2_Poll(void)
{
struct qsockaddr clientaddr;
int32_t control;
int32_t len;
char name[256];
char value[256];
net_landriverlevel = test2Driver;
name[0] = 0;
len = dfunc.Read(test2Socket, net_message.data, net_message.maxsize, &clientaddr);
if (len < (sizeof(int32_t)))
goto Reschedule;
net_message.cursize = len;
MSG_BeginReading();
control = *((int32_t *) net_message.data);
MSG_ReadLong();
if (control == (-1))
goto Error;
if ((control & (~NETFLAG_LENGTH_MASK)) != NETFLAG_CTL)
goto Error;
if ((control & NETFLAG_LENGTH_MASK) != len)
goto Error;
if (MSG_ReadByte() != CCREP_RULE_INFO)
goto Error;
strcpy(name, MSG_ReadString());
if (name[0] == 0)
goto Done;
strcpy(value, MSG_ReadString());
Con_Printf("%-16.16s %-16.16s\n", name, value);
SZ_Clear(&net_message);
MSG_WriteLong(&net_message, 0);
MSG_WriteByte(&net_message, CCREQ_RULE_INFO);
MSG_WriteString(&net_message, name);
*((int32_t *) net_message.data) = NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK);
dfunc.Write(test2Socket, net_message.data, net_message.cursize, &clientaddr);
SZ_Clear(&net_message);
Reschedule:
SchedulePollProcedure(&test2PollProcedure, 0.05);
return;
Error:
Con_Printf("Unexpected repsonse to Rule Info request\n");
Done:
dfunc.CloseSocket(test2Socket);
test2InProgress = 0;
return;
}
static void Test2_f(void)
{
char *host;
int32_t n;
struct qsockaddr sendaddr;
if (test2InProgress)
return;
host = Cmd_Argv(1);
if (host && hostCacheCount)
{
for (n = 0; n < hostCacheCount; n++)
if (strcasecmp(host, hostcache[n].name) == 0)
{
if (hostcache[n].driver != myDriverLevel)
continue;
net_landriverlevel = hostcache[n].ldriver;
memcpy(&sendaddr, &hostcache[n].addr, sizeof(struct qsockaddr));
break;
}
if (n < hostCacheCount)
goto JustDoIt;
}
for (net_landriverlevel = 0; net_landriverlevel < net_numlandrivers; net_landriverlevel++)
{
if (!net_landrivers[net_landriverlevel].initialized)
continue;
if (dfunc.GetAddrFromName(host, &sendaddr) != (-1))
break;
}
if (net_landriverlevel == net_numlandrivers)
return;
JustDoIt:
test2Socket = dfunc.OpenSocket(0);
if (test2Socket == (-1))
return;
test2InProgress = 1;
test2Driver = net_landriverlevel;
SZ_Clear(&net_message);
MSG_WriteLong(&net_message, 0);
MSG_WriteByte(&net_message, CCREQ_RULE_INFO);
MSG_WriteString(&net_message, "");
*((int32_t *) net_message.data) = NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK);
dfunc.Write(test2Socket, net_message.data, net_message.cursize, &sendaddr);
SZ_Clear(&net_message);
SchedulePollProcedure(&test2PollProcedure, 0.05);
}
int32_t Datagram_Init(void)
{
int32_t i;
int32_t csock;
myDriverLevel = net_driverlevel;
Cmd_AddCommand("net_stats", NET_Stats_f);
if (COM_CheckParm("-nolan"))
return -1;
for (i = 0; i < net_numlandrivers; i++)
{
csock = net_landrivers[i].Init();
if (csock == (-1))
continue;
net_landrivers[i].initialized = 1;
net_landrivers[i].controlSock = csock;
}
Cmd_AddCommand("test", Test_f);
Cmd_AddCommand("test2", Test2_f);
return 0;
}
void Datagram_Shutdown(void)
{
int32_t i;
for (i = 0; i < net_numlandrivers; i++)
{
if (net_landrivers[i].initialized)
{
net_landrivers[i].Shutdown();
net_landrivers[i].initialized = 0;
}
}
}
void Datagram_Close(qsocket_t *sock)
{
sfunc.CloseSocket(sock->socket);
}
void Datagram_Listen(bool state)
{
int32_t i;
for (i = 0; i < net_numlandrivers; i++)
if (net_landrivers[i].initialized)
net_landrivers[i].Listen(state);
}
static qsocket_t *_Datagram_CheckNewConnections(void)
{
struct qsockaddr clientaddr;
struct qsockaddr newaddr;
int32_t newsock;
int32_t acceptsock;
qsocket_t *sock;
qsocket_t *s;
int32_t len;
int32_t command;
int32_t control;
int32_t ret;
acceptsock = dfunc.CheckNewConnections();
if (acceptsock == (-1))
return 0;
SZ_Clear(&net_message);
len = dfunc.Read(acceptsock, net_message.data, net_message.maxsize, &clientaddr);
if (len < (sizeof(int32_t)))
return 0;
net_message.cursize = len;
MSG_BeginReading();
control = *((int32_t *) net_message.data);
MSG_ReadLong();
if (control == (-1))
return 0;
if ((control & (~NETFLAG_LENGTH_MASK)) != NETFLAG_CTL)
return 0;
if ((control & NETFLAG_LENGTH_MASK) != len)
return 0;
command = MSG_ReadByte();
if (command == CCREQ_SERVER_INFO)
{
if (strcmp(MSG_ReadString(), "QUAKE") != 0)
return 0;
SZ_Clear(&net_message);
MSG_WriteLong(&net_message, 0);
MSG_WriteByte(&net_message, CCREP_SERVER_INFO);
dfunc.GetSocketAddr(acceptsock, &newaddr);
MSG_WriteString(&net_message, dfunc.AddrToString(&newaddr));
MSG_WriteString(&net_message, hostname.string);
MSG_WriteString(&net_message, sv.name);
MSG_WriteByte(&net_message, net_activeconnections);
MSG_WriteByte(&net_message, svs.maxclients);
MSG_WriteByte(&net_message, NET_PROTOCOL_VERSION);
*((int32_t *) net_message.data) = NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK);
dfunc.Write(acceptsock, net_message.data, net_message.cursize, &clientaddr);
SZ_Clear(&net_message);
return 0;
}
if (command == CCREQ_PLAYER_INFO)
{
int32_t playerNumber;
int32_t activeNumber;
int32_t clientNumber;
client_t *client;
playerNumber = MSG_ReadByte();
activeNumber = -1;
for (clientNumber = 0, client = svs.clients; clientNumber < svs.maxclients; clientNumber++, client++)
{
if (client->active)
{
activeNumber++;
if (activeNumber == playerNumber)
break;
}
}
if (clientNumber == svs.maxclients)
return 0;
SZ_Clear(&net_message);
MSG_WriteLong(&net_message, 0);
MSG_WriteByte(&net_message, CCREP_PLAYER_INFO);
MSG_WriteByte(&net_message, playerNumber);
MSG_WriteString(&net_message, client->name);
MSG_WriteLong(&net_message, client->colors);
MSG_WriteLong(&net_message, (int32_t) client->edict->v.frags);
MSG_WriteLong(&net_message, (int32_t) (net_time - client->netconnection->connecttime));
MSG_WriteString(&net_message, client->netconnection->address);
*((int32_t *) net_message.data) = NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK);
dfunc.Write(acceptsock, net_message.data, net_message.cursize, &clientaddr);
SZ_Clear(&net_message);
return 0;
}
if (command == CCREQ_RULE_INFO)
{
char *prevCvarName;
cvar_t *var;
prevCvarName = MSG_ReadString();
if (*prevCvarName)
{
var = Cvar_FindVar(prevCvarName);
if (!var)
return 0;
var = var->next;
}
else
var = cvar_vars;
while (var)
{
if (var->server)
break;
var = var->next;
}
SZ_Clear(&net_message);
MSG_WriteLong(&net_message, 0);
MSG_WriteByte(&net_message, CCREP_RULE_INFO);
if (var)
{
MSG_WriteString(&net_message, var->name);
MSG_WriteString(&net_message, var->string);
}
*((int32_t *) net_message.data) = NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK);
dfunc.Write(acceptsock, net_message.data, net_message.cursize, &clientaddr);
SZ_Clear(&net_message);
return 0;
}
if (command != CCREQ_CONNECT)
return 0;
if (strcmp(MSG_ReadString(), "QUAKE") != 0)
return 0;
if (MSG_ReadByte() != NET_PROTOCOL_VERSION)
{
SZ_Clear(&net_message);
MSG_WriteLong(&net_message, 0);
MSG_WriteByte(&net_message, CCREP_REJECT);
MSG_WriteString(&net_message, "Incompatible version.\n");
*((int32_t *) net_message.data) = NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK);
dfunc.Write(acceptsock, net_message.data, net_message.cursize, &clientaddr);
SZ_Clear(&net_message);
return 0;
}
for (s = net_activeSockets; s; s = s->next)
{
if (s->driver != net_driverlevel)
continue;
ret = dfunc.AddrCompare(&clientaddr, &s->addr);
if (ret >= 0)
{
if ((ret == 0) && ((net_time - s->connecttime) < 2.0))
{
SZ_Clear(&net_message);
MSG_WriteLong(&net_message, 0);
MSG_WriteByte(&net_message, CCREP_ACCEPT);
dfunc.GetSocketAddr(s->socket, &newaddr);
MSG_WriteLong(&net_message, dfunc.GetSocketPort(&newaddr));
*((int32_t *) net_message.data) = NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK);
dfunc.Write(acceptsock, net_message.data, net_message.cursize, &clientaddr);
SZ_Clear(&net_message);
return 0;
}
NET_Close(s);
return 0;
}
}
sock = NET_NewQSocket();
if (sock == 0)
{
SZ_Clear(&net_message);
MSG_WriteLong(&net_message, 0);
MSG_WriteByte(&net_message, CCREP_REJECT);
MSG_WriteString(&net_message, "Server is full.\n");
*((int32_t *) net_message.data) = NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK);
dfunc.Write(acceptsock, net_message.data, net_message.cursize, &clientaddr);
SZ_Clear(&net_message);
return 0;
}
newsock = dfunc.OpenSocket(0);
if (newsock == (-1))
{
NET_FreeQSocket(sock);
return 0;
}
if (dfunc.Connect(newsock, &clientaddr) == (-1))
{
dfunc.CloseSocket(newsock);
NET_FreeQSocket(sock);
return 0;
}
sock->socket = newsock;
sock->landriver = net_landriverlevel;
sock->addr = clientaddr;
strcpy(sock->address, dfunc.AddrToString(&clientaddr));
SZ_Clear(&net_message);
MSG_WriteLong(&net_message, 0);
MSG_WriteByte(&net_message, CCREP_ACCEPT);
dfunc.GetSocketAddr(newsock, &newaddr);
MSG_WriteLong(&net_message, dfunc.GetSocketPort(&newaddr));
*((int32_t *) net_message.data) = NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK);
dfunc.Write(acceptsock, net_message.data, net_message.cursize, &clientaddr);
SZ_Clear(&net_message);
return sock;
}
qsocket_t *Datagram_CheckNewConnections(void)
{
qsocket_t *ret = 0;
for (net_landriverlevel = 0; net_landriverlevel < net_numlandrivers; net_landriverlevel++)
if (net_landrivers[net_landriverlevel].initialized)
if ((ret = _Datagram_CheckNewConnections()) != 0)
break;
return ret;
}
static void _Datagram_SearchForHosts(bool xmit)
{
int32_t ret;
int32_t n;
int32_t i;
struct qsockaddr readaddr;
struct qsockaddr myaddr;
int32_t control;
dfunc.GetSocketAddr(dfunc.controlSock, &myaddr);
if (xmit)
{
SZ_Clear(&net_message);
MSG_WriteLong(&net_message, 0);
MSG_WriteByte(&net_message, CCREQ_SERVER_INFO);
MSG_WriteString(&net_message, "QUAKE");
MSG_WriteByte(&net_message, NET_PROTOCOL_VERSION);
*((int32_t *) net_message.data) = NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK);
dfunc.Broadcast(dfunc.controlSock, net_message.data, net_message.cursize);
SZ_Clear(&net_message);
}
while ((ret = dfunc.Read(dfunc.controlSock, net_message.data, net_message.maxsize, &readaddr)) > 0)
{
if (ret < (sizeof(int32_t)))
continue;
net_message.cursize = ret;
if (dfunc.AddrCompare(&readaddr, &myaddr) >= 0)
continue;
if (hostCacheCount == HOSTCACHESIZE)
continue;
MSG_BeginReading();
control = *((int32_t *) net_message.data);
MSG_ReadLong();
if (control == (-1))
continue;
if ((control & (~NETFLAG_LENGTH_MASK)) != NETFLAG_CTL)
continue;
if ((control & NETFLAG_LENGTH_MASK) != ret)
continue;
if (MSG_ReadByte() != CCREP_SERVER_INFO)
continue;
dfunc.GetAddrFromName(MSG_ReadString(), &readaddr);
for (n = 0; n < hostCacheCount; n++)
if (dfunc.AddrCompare(&readaddr, &hostcache[n].addr) == 0)
break;
if (n < hostCacheCount)
continue;
hostCacheCount++;
strcpy(hostcache[n].name, MSG_ReadString());
strcpy(hostcache[n].map, MSG_ReadString());
hostcache[n].users = MSG_ReadByte();
hostcache[n].maxusers = MSG_ReadByte();
if (MSG_ReadByte() != NET_PROTOCOL_VERSION)
{
strcpy(hostcache[n].cname, hostcache[n].name);
hostcache[n].cname[14] = 0;
strcpy(hostcache[n].name, "*");
strcat(hostcache[n].name, hostcache[n].cname);
}
memcpy(&hostcache[n].addr, &readaddr, sizeof(struct qsockaddr));
hostcache[n].driver = net_driverlevel;
hostcache[n].ldriver = net_landriverlevel;
strcpy(hostcache[n].cname, dfunc.AddrToString(&readaddr));
for (i = 0; i < hostCacheCount; i++)
{
if (i == n)
continue;
if (strcasecmp(hostcache[n].name, hostcache[i].name) == 0)
{
i = strlen(hostcache[n].name);
if ((i < 15) && (hostcache[n].name[i - 1] > '8'))
{
hostcache[n].name[i] = '0';
hostcache[n].name[i + 1] = 0;
}
else
hostcache[n].name[i - 1]++;
i = -1;
}
}
}
}
void Datagram_SearchForHosts(bool xmit)
{
for (net_landriverlevel = 0; net_landriverlevel < net_numlandrivers; net_landriverlevel++)
{
if (hostCacheCount == HOSTCACHESIZE)
break;
if (net_landrivers[net_landriverlevel].initialized)
_Datagram_SearchForHosts(xmit);
}
}
static qsocket_t *_Datagram_Connect(char *host)
{
struct qsockaddr sendaddr;
struct qsockaddr readaddr;
qsocket_t *sock;
int32_t newsock;
int32_t ret;
int32_t reps;
double start_time;
int32_t control;
char *reason;
if (dfunc.GetAddrFromName(host, &sendaddr) == (-1))
return 0;
newsock = dfunc.OpenSocket(0);
if (newsock == (-1))
return 0;
sock = NET_NewQSocket();
if (sock == 0)
goto ErrorReturn2;
sock->socket = newsock;
sock->landriver = net_landriverlevel;
if (dfunc.Connect(newsock, &sendaddr) == (-1))
goto ErrorReturn;
Con_Printf("trying...\n");
SCR_UpdateScreen();
start_time = net_time;
for (reps = 0; reps < 3; reps++)
{
SZ_Clear(&net_message);
MSG_WriteLong(&net_message, 0);
MSG_WriteByte(&net_message, CCREQ_CONNECT);
MSG_WriteString(&net_message, "QUAKE");
MSG_WriteByte(&net_message, NET_PROTOCOL_VERSION);
*((int32_t *) net_message.data) = NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK);
dfunc.Write(newsock, net_message.data, net_message.cursize, &sendaddr);
SZ_Clear(&net_message);
do
{
ret = dfunc.Read(newsock, net_message.data, net_message.maxsize, &readaddr);
if (ret > 0)
{
if (sfunc.AddrCompare(&readaddr, &sendaddr) != 0)
{
ret = 0;
continue;
}
if (ret < (sizeof(int32_t)))
{
ret = 0;
continue;
}
net_message.cursize = ret;
MSG_BeginReading();
control = *((int32_t *) net_message.data);
MSG_ReadLong();
if (control == (-1))
{
ret = 0;
continue;
}
if ((control & (~NETFLAG_LENGTH_MASK)) != NETFLAG_CTL)
{
ret = 0;
continue;
}
if ((control & NETFLAG_LENGTH_MASK) != ret)
{
ret = 0;
continue;
}
}
}
while ((ret == 0) && ((SetNetTime() - start_time) < 2.5));
if (ret)
break;
Con_Printf("still trying...\n");
SCR_UpdateScreen();
start_time = SetNetTime();
}
if (ret == 0)
{
reason = "No Response";
Con_Printf("%s\n", reason);
strcpy(m_return_reason, reason);
goto ErrorReturn;
}
if (ret == (-1))
{
reason = "Network Error";
Con_Printf("%s\n", reason);
strcpy(m_return_reason, reason);
goto ErrorReturn;
}
ret = MSG_ReadByte();
if (ret == CCREP_REJECT)
{
reason = MSG_ReadString();
Con_Printf(reason);
strncpy(m_return_reason, reason, 31);
goto ErrorReturn;
}
if (ret == CCREP_ACCEPT)
{
memcpy(&sock->addr, &sendaddr, sizeof(struct qsockaddr));
dfunc.SetSocketPort(&sock->addr, MSG_ReadLong());
}
else
{
reason = "Bad Response";
Con_Printf("%s\n", reason);
strcpy(m_return_reason, reason);
goto ErrorReturn;
}
dfunc.GetNameFromAddr(&sendaddr, sock->address);
Con_Printf("Connection accepted\n");
sock->lastMessageTime = SetNetTime();
if (dfunc.Connect(newsock, &sock->addr) == (-1))
{
reason = "Connect to Game failed";
Con_Printf("%s\n", reason);
strcpy(m_return_reason, reason);
goto ErrorReturn;
}
m_return_onerror = 0;
return sock;
ErrorReturn:
NET_FreeQSocket(sock);
ErrorReturn2:
dfunc.CloseSocket(newsock);
if (m_return_onerror)
{
key_dest = key_menu;
m_state = m_return_state;
m_return_onerror = 0;
}
return 0;
}
qsocket_t *Datagram_Connect(char *host)
{
qsocket_t *ret = 0;
for (net_landriverlevel = 0; net_landriverlevel < net_numlandrivers; net_landriverlevel++)
if (net_landrivers[net_landriverlevel].initialized)
if ((ret = _Datagram_Connect(host)) != 0)
break;
return ret;
}
int32_t Loop_Init(void)
{
if (cls.state == ca_dedicated)
return -1;
return 0;
}
void Loop_Shutdown(void)
{
}
void Loop_Listen(bool state)
{
}
void Loop_SearchForHosts(bool xmit)
{
if (!sv.active)
return;
hostCacheCount = 1;
if (strcmp(hostname.string, "UNNAMED") == 0)
strcpy(hostcache[0].name, "local");
else
strcpy(hostcache[0].name, hostname.string);
strcpy(hostcache[0].map, sv.name);
hostcache[0].users = net_activeconnections;
hostcache[0].maxusers = svs.maxclients;
hostcache[0].driver = net_driverlevel;
strcpy(hostcache[0].cname, "local");
}
qsocket_t *Loop_Connect(char *host)
{
if (strcmp(host, "local") != 0)
return 0;
localconnectpending = 1;
if (!loop_client)
{
if ((loop_client = NET_NewQSocket()) == 0)
{
Con_Printf("Loop_Connect: no qsocket available\n");
return 0;
}
strcpy(loop_client->address, "localhost");
}
loop_client->receiveMessageLength = 0;
loop_client->sendMessageLength = 0;
loop_client->canSend = 1;
if (!loop_server)
{
if ((loop_server = NET_NewQSocket()) == 0)
{
Con_Printf("Loop_Connect: no qsocket available\n");
return 0;
}
strcpy(loop_server->address, "LOCAL");
}
loop_server->receiveMessageLength = 0;
loop_server->sendMessageLength = 0;
loop_server->canSend = 1;
loop_client->driverdata = (void *) loop_server;
loop_server->driverdata = (void *) loop_client;
return loop_client;
}
qsocket_t *Loop_CheckNewConnections(void)
{
if (!localconnectpending)
return 0;
localconnectpending = 0;
loop_server->sendMessageLength = 0;
loop_server->receiveMessageLength = 0;
loop_server->canSend = 1;
loop_client->sendMessageLength = 0;
loop_client->receiveMessageLength = 0;
loop_client->canSend = 1;
return loop_server;
}
static int32_t IntAlign(int32_t value)
{
return (value + ((sizeof(int32_t)) - 1)) & (~((sizeof(int32_t)) - 1));
}
int32_t Loop_GetMessage(qsocket_t *sock)
{
int32_t ret;
int32_t length;
if (sock->receiveMessageLength == 0)
return 0;
ret = sock->receiveMessage[0];
length = sock->receiveMessage[1] + (sock->receiveMessage[2] << 8);
SZ_Clear(&net_message);
SZ_Write(&net_message, &sock->receiveMessage[4], length);
length = IntAlign(length + 4);
sock->receiveMessageLength -= length;
if (sock->receiveMessageLength)
memcpy(sock->receiveMessage, &sock->receiveMessage[length], sock->receiveMessageLength);
if (sock->driverdata && (ret == 1))
((qsocket_t *) sock->driverdata)->canSend = 1;
return ret;
}
int32_t Loop_SendMessage(qsocket_t *sock, sizebuf_t *data)
{
uint8_t *buffer;
int32_t *bufferLength;
if (!sock->driverdata)
return -1;
bufferLength = &((qsocket_t *) sock->driverdata)->receiveMessageLength;
if ((((*bufferLength) + data->cursize) + 4) > NET_MAXMESSAGE)
Sys_Error("Loop_SendMessage: overflow\n");
buffer = ((qsocket_t *) sock->driverdata)->receiveMessage + (*bufferLength);
*(buffer++) = 1;
*(buffer++) = data->cursize & 0xff;
*(buffer++) = data->cursize >> 8;
buffer++;
memcpy(buffer, data->data, data->cursize);
*bufferLength = IntAlign(((*bufferLength) + data->cursize) + 4);
sock->canSend = 0;
return 1;
}
int32_t Loop_SendUnreliableMessage(qsocket_t *sock, sizebuf_t *data)
{
uint8_t *buffer;
int32_t *bufferLength;
if (!sock->driverdata)
return -1;
bufferLength = &((qsocket_t *) sock->driverdata)->receiveMessageLength;
if (((((*bufferLength) + data->cursize) + (sizeof(uint8_t))) + (sizeof(int16_t))) > NET_MAXMESSAGE)
return 0;
buffer = ((qsocket_t *) sock->driverdata)->receiveMessage + (*bufferLength);
*(buffer++) = 2;
*(buffer++) = data->cursize & 0xff;
*(buffer++) = data->cursize >> 8;
buffer++;
memcpy(buffer, data->data, data->cursize);
*bufferLength = IntAlign(((*bufferLength) + data->cursize) + 4);
return 1;
}
bool Loop_CanSendMessage(qsocket_t *sock)
{
if (!sock->driverdata)
return 0;
return sock->canSend;
}
bool Loop_CanSendUnreliableMessage(qsocket_t *sock)
{
return 1;
}
void Loop_Close(qsocket_t *sock)
{
if (sock->driverdata)
((qsocket_t *) sock->driverdata)->driverdata = 0;
sock->receiveMessageLength = 0;
sock->sendMessageLength = 0;
sock->canSend = 1;
if (sock == loop_client)
loop_client = 0;
else
loop_server = 0;
}
double SetNetTime(void)
{
net_time = Sys_FloatTime();
return net_time;
}
qsocket_t *NET_NewQSocket(void)
{
qsocket_t *sock;
if (net_freeSockets == 0)
return 0;
if (net_activeconnections >= svs.maxclients)
return 0;
sock = net_freeSockets;
net_freeSockets = sock->next;
sock->next = net_activeSockets;
net_activeSockets = sock;
sock->disconnected = 0;
sock->connecttime = net_time;
strcpy(sock->address, "UNSET ADDRESS");
sock->driver = net_driverlevel;
sock->socket = 0;
sock->driverdata = 0;
sock->canSend = 1;
sock->sendNext = 0;
sock->lastMessageTime = net_time;
sock->ackSequence = 0;
sock->sendSequence = 0;
sock->unreliableSendSequence = 0;
sock->sendMessageLength = 0;
sock->receiveSequence = 0;
sock->unreliableReceiveSequence = 0;
sock->receiveMessageLength = 0;
return sock;
}
void NET_FreeQSocket(qsocket_t *sock)
{
qsocket_t *s;
if (sock == net_activeSockets)
net_activeSockets = net_activeSockets->next;
else
{
for (s = net_activeSockets; s; s = s->next)
if (s->next == sock)
{
s->next = sock->next;
break;
}
if (!s)
Sys_Error("NET_FreeQSocket: not active\n");
}
sock->next = net_freeSockets;
net_freeSockets = sock;
sock->disconnected = 1;
}
static void NET_Listen_f(void)
{
if (Cmd_Argc() != 2)
{
Con_Printf("\"listen\" is \"%u\"\n", (listening) ? (1) : (0));
return;
}
listening = ((int32_t) strtol(Cmd_Argv(1), 0, 0)) ? (1) : (0);
for (net_driverlevel = 0; net_driverlevel < net_numdrivers; net_driverlevel++)
{
if (net_drivers[net_driverlevel].initialized == 0)
continue;
net_drivers[net_driverlevel].Listen(listening);
}
}
static void MaxPlayers_f(void)
{
int32_t n;
if (Cmd_Argc() != 2)
{
Con_Printf("\"maxplayers\" is \"%u\"\n", svs.maxclients);
return;
}
if (sv.active)
{
Con_Printf("maxplayers can not be changed while a server is running.\n");
return;
}
n = (int32_t) strtol(Cmd_Argv(1), 0, 0);
if (n < 1)
n = 1;
if (n > svs.maxclientslimit)
{
n = svs.maxclientslimit;
Con_Printf("\"maxplayers\" set to \"%u\"\n", n);
}
if ((n == 1) && listening)
Cbuf_AddText("listen 0\n");
if ((n > 1) && (!listening))
Cbuf_AddText("listen 1\n");
svs.maxclients = n;
if (n == 1)
Cvar_Set("deathmatch", "0");
else
Cvar_Set("deathmatch", "1");
}
static void NET_Port_f(void)
{
int32_t n;
if (Cmd_Argc() != 2)
{
Con_Printf("\"port\" is \"%u\"\n", net_hostport);
return;
}
n = (int32_t) strtol(Cmd_Argv(1), 0, 0);
if ((n < 1) || (n > 65534))
{
Con_Printf("Bad value, must be between 1 and 65534\n");
return;
}
DEFAULTnet_hostport = n;
net_hostport = n;
if (listening)
{
Cbuf_AddText("listen 0\n");
Cbuf_AddText("listen 1\n");
}
}
static void PrintSlistHeader(void)
{
Con_Printf("Server Map Users\n");
Con_Printf("--------------- --------------- -----\n");
slistLastShown = 0;
}
static void PrintSlist(void)
{
int32_t n;
for (n = slistLastShown; n < hostCacheCount; n++)
{
if (hostcache[n].maxusers)
Con_Printf("%-15.15s %-15.15s %2u/%2u\n", hostcache[n].name, hostcache[n].map, hostcache[n].users, hostcache[n].maxusers);
else
Con_Printf("%-15.15s %-15.15s\n", hostcache[n].name, hostcache[n].map);
}
slistLastShown = n;
}
static void PrintSlistTrailer(void)
{
if (hostCacheCount)
Con_Printf("== end list ==\n\n");
else
Con_Printf("No Quake servers found.\n\n");
}
void NET_Slist_f(void)
{
if (slistInProgress)
return;
if (!slistSilent)
{
Con_Printf("Looking for Quake servers...\n");
PrintSlistHeader();
}
slistInProgress = 1;
slistStartTime = Sys_FloatTime();
SchedulePollProcedure(&slistSendProcedure, 0.0);
SchedulePollProcedure(&slistPollProcedure, 0.1);
hostCacheCount = 0;
}
static void Slist_Send(void)
{
for (net_driverlevel = 0; net_driverlevel < net_numdrivers; net_driverlevel++)
{
if ((!slistLocal) && (net_driverlevel == 0))
continue;
if (net_drivers[net_driverlevel].initialized == 0)
continue;
net_drivers[net_driverlevel].SearchForHosts(1);
}
if ((Sys_FloatTime() - slistStartTime) < 0.5)
SchedulePollProcedure(&slistSendProcedure, 0.75);
}
static void Slist_Poll(void)
{
for (net_driverlevel = 0; net_driverlevel < net_numdrivers; net_driverlevel++)
{
if ((!slistLocal) && (net_driverlevel == 0))
continue;
if (net_drivers[net_driverlevel].initialized == 0)
continue;
net_drivers[net_driverlevel].SearchForHosts(0);
}
if (!slistSilent)
PrintSlist();
if ((Sys_FloatTime() - slistStartTime) < 1.5)
{
SchedulePollProcedure(&slistPollProcedure, 0.1);
return;
}
if (!slistSilent)
PrintSlistTrailer();
slistInProgress = 0;
slistSilent = 0;
slistLocal = 1;
}
qsocket_t *NET_Connect(char *host)
{
qsocket_t *ret;
int32_t n;
int32_t numdrivers = net_numdrivers;
SetNetTime();
if (host && ((*host) == 0))
host = 0;
if (host)
{
if (strcasecmp(host, "local") == 0)
{
numdrivers = 1;
goto JustDoIt;
}
if (hostCacheCount)
{
for (n = 0; n < hostCacheCount; n++)
if (strcasecmp(host, hostcache[n].name) == 0)
{
host = hostcache[n].cname;
break;
}
if (n < hostCacheCount)
goto JustDoIt;
}
}
slistSilent = (host) ? (1) : (0);
NET_Slist_f();
while (slistInProgress)
NET_Poll();
if (host == 0)
{
if (hostCacheCount != 1)
return 0;
host = hostcache[0].cname;
Con_Printf("Connecting to...\n%s @ %s\n\n", hostcache[0].name, host);
}
if (hostCacheCount)
for (n = 0; n < hostCacheCount; n++)
if (strcasecmp(host, hostcache[n].name) == 0)
{
host = hostcache[n].cname;
break;
}
JustDoIt:
for (net_driverlevel = 0; net_driverlevel < numdrivers; net_driverlevel++)
{
if (net_drivers[net_driverlevel].initialized == 0)
continue;
ret = net_drivers[net_driverlevel].Connect(host);
if (ret)
return ret;
}
if (host)
{
Con_Printf("\n");
PrintSlistHeader();
PrintSlist();
PrintSlistTrailer();
}
return 0;
}
qsocket_t *NET_CheckNewConnections(void)
{
qsocket_t *ret;
SetNetTime();
for (net_driverlevel = 0; net_driverlevel < net_numdrivers; net_driverlevel++)
{
if (net_drivers[net_driverlevel].initialized == 0)
continue;
if (net_driverlevel && (listening == 0))
continue;
ret = net_drivers[net_driverlevel].CheckNewConnections();
if (ret)
{
if (recording)
{
vcrConnect.time = host_time;
vcrConnect.op = VCR_OP_CONNECT;
vcrConnect.session = (int32_t) ret;
Sys_FileWrite(vcrFile, &vcrConnect, sizeof(vcrConnect));
Sys_FileWrite(vcrFile, ret->address, NET_NAMELEN);
}
return ret;
}
}
if (recording)
{
vcrConnect.time = host_time;
vcrConnect.op = VCR_OP_CONNECT;
vcrConnect.session = 0;
Sys_FileWrite(vcrFile, &vcrConnect, sizeof(vcrConnect));
}
return 0;
}
void NET_Close(qsocket_t *sock)
{
if (!sock)
return;
if (sock->disconnected)
return;
SetNetTime();
net_drivers[sock->driver].Close(sock);
NET_FreeQSocket(sock);
}
int32_t NET_GetMessage(qsocket_t *sock)
{
int32_t ret;
if (!sock)
return -1;
if (sock->disconnected)
{
Con_Printf("NET_GetMessage: disconnected socket\n");
return -1;
}
SetNetTime();
ret = net_drivers[sock->driver].QGetMessage(sock);
if ((ret == 0) && sock->driver)
{
if ((net_time - sock->lastMessageTime) > net_messagetimeout.value)
{
NET_Close(sock);
return -1;
}
}
if (ret > 0)
{
if (sock->driver)
{
sock->lastMessageTime = net_time;
if (ret == 1)
messagesReceived++;
else
if (ret == 2)
unreliableMessagesReceived++;
}
if (recording)
{
vcrGetMessage.time = host_time;
vcrGetMessage.op = VCR_OP_GETMESSAGE;
vcrGetMessage.session = (int32_t) sock;
vcrGetMessage.ret = ret;
vcrGetMessage.len = net_message.cursize;
Sys_FileWrite(vcrFile, &vcrGetMessage, 24);
Sys_FileWrite(vcrFile, net_message.data, net_message.cursize);
}
}
else
{
if (recording)
{
vcrGetMessage.time = host_time;
vcrGetMessage.op = VCR_OP_GETMESSAGE;
vcrGetMessage.session = (int32_t) sock;
vcrGetMessage.ret = ret;
Sys_FileWrite(vcrFile, &vcrGetMessage, 20);
}
}
return ret;
}
int32_t NET_SendMessage(qsocket_t *sock, sizebuf_t *data)
{
int32_t r;
if (!sock)
return -1;
if (sock->disconnected)
{
Con_Printf("NET_SendMessage: disconnected socket\n");
return -1;
}
SetNetTime();
r = net_drivers[sock->driver].QSendMessage(sock, data);
if ((r == 1) && sock->driver)
messagesSent++;
if (recording)
{
vcrSendMessage.time = host_time;
vcrSendMessage.op = VCR_OP_SENDMESSAGE;
vcrSendMessage.session = (int32_t) sock;
vcrSendMessage.r = r;
Sys_FileWrite(vcrFile, &vcrSendMessage, 20);
}
return r;
}
int32_t NET_SendUnreliableMessage(qsocket_t *sock, sizebuf_t *data)
{
int32_t r;
if (!sock)
return -1;
if (sock->disconnected)
{
Con_Printf("NET_SendMessage: disconnected socket\n");
return -1;
}
SetNetTime();
r = net_drivers[sock->driver].SendUnreliableMessage(sock, data);
if ((r == 1) && sock->driver)
unreliableMessagesSent++;
if (recording)
{
vcrSendMessage.time = host_time;
vcrSendMessage.op = VCR_OP_SENDMESSAGE;
vcrSendMessage.session = (int32_t) sock;
vcrSendMessage.r = r;
Sys_FileWrite(vcrFile, &vcrSendMessage, 20);
}
return r;
}
bool NET_CanSendMessage(qsocket_t *sock)
{
int32_t r;
if (!sock)
return 0;
if (sock->disconnected)
return 0;
SetNetTime();
r = net_drivers[sock->driver].CanSendMessage(sock);
if (recording)
{
vcrSendMessage.time = host_time;
vcrSendMessage.op = VCR_OP_CANSENDMESSAGE;
vcrSendMessage.session = (int32_t) sock;
vcrSendMessage.r = r;
Sys_FileWrite(vcrFile, &vcrSendMessage, 20);
}
return r;
}
int32_t NET_SendToAll(sizebuf_t *data, int32_t blocktime)
{
double start;
int32_t i;
int32_t count = 0;
bool state1[MAX_SCOREBOARD];
bool state2[MAX_SCOREBOARD];
for (i = 0, host_client = svs.clients; i < svs.maxclients; i++, host_client++)
{
if (!host_client->netconnection)
continue;
if (host_client->active)
{
if (host_client->netconnection->driver == 0)
{
NET_SendMessage(host_client->netconnection, data);
state1[i] = 1;
state2[i] = 1;
continue;
}
count++;
state1[i] = 0;
state2[i] = 0;
}
else
{
state1[i] = 1;
state2[i] = 1;
}
}
start = Sys_FloatTime();
while (count)
{
count = 0;
for (i = 0, host_client = svs.clients; i < svs.maxclients; i++, host_client++)
{
if (!state1[i])
{
if (NET_CanSendMessage(host_client->netconnection))
{
state1[i] = 1;
NET_SendMessage(host_client->netconnection, data);
}
else
{
NET_GetMessage(host_client->netconnection);
}
count++;
continue;
}
if (!state2[i])
{
if (NET_CanSendMessage(host_client->netconnection))
{
state2[i] = 1;
}
else
{
NET_GetMessage(host_client->netconnection);
}
count++;
continue;
}
}
if ((Sys_FloatTime() - start) > blocktime)
break;
}
return count;
}
void NET_Init(void)
{
int32_t i;
int32_t controlSocket;
qsocket_t *s;
if (COM_CheckParm("-playback"))
{
net_numdrivers = 1;
net_drivers[0].Init = VCR_Init;
}
if (COM_CheckParm("-record"))
recording = 1;
i = COM_CheckParm("-port");
if (!i)
i = COM_CheckParm("-udpport");
if (!i)
i = COM_CheckParm("-ipxport");
if (i)
{
if (i < (com_argc - 1))
DEFAULTnet_hostport = (int32_t) strtol(com_argv[i + 1], 0, 0);
else
Sys_Error("NET_Init: you must specify a number after -port");
}
net_hostport = DEFAULTnet_hostport;
if (COM_CheckParm("-listen") || (cls.state == ca_dedicated))
listening = 1;
net_numsockets = svs.maxclientslimit;
if (cls.state != ca_dedicated)
net_numsockets++;
SetNetTime();
for (i = 0; i < net_numsockets; i++)
{
s = (qsocket_t *) Hunk_AllocName(sizeof(qsocket_t), "qsocket");
s->next = net_freeSockets;
net_freeSockets = s;
s->disconnected = 1;
}
SZ_Alloc(&net_message, NET_MAXMESSAGE);
Cvar_RegisterVariable(&net_messagetimeout);
Cvar_RegisterVariable(&hostname);
Cvar_RegisterVariable(&config_com_port);
Cvar_RegisterVariable(&config_com_irq);
Cvar_RegisterVariable(&config_com_baud);
Cvar_RegisterVariable(&config_com_modem);
Cvar_RegisterVariable(&config_modem_dialtype);
Cvar_RegisterVariable(&config_modem_clear);
Cvar_RegisterVariable(&config_modem_init);
Cvar_RegisterVariable(&config_modem_hangup);
Cmd_AddCommand("slist", NET_Slist_f);
Cmd_AddCommand("listen", NET_Listen_f);
Cmd_AddCommand("maxplayers", MaxPlayers_f);
Cmd_AddCommand("port", NET_Port_f);
for (net_driverlevel = 0; net_driverlevel < net_numdrivers; net_driverlevel++)
{
controlSocket = net_drivers[net_driverlevel].Init();
if (controlSocket == (-1))
continue;
net_drivers[net_driverlevel].initialized = 1;
net_drivers[net_driverlevel].controlSock = controlSocket;
if (listening)
net_drivers[net_driverlevel].Listen(1);
}
if (*my_ipx_address)
Con_DPrintf("IPX address %s\n", my_ipx_address);
if (*my_tcpip_address)
Con_DPrintf("TCP/IP address %s\n", my_tcpip_address);
}
void NET_Shutdown(void)
{
qsocket_t *sock;
SetNetTime();
for (sock = net_activeSockets; sock; sock = sock->next)
NET_Close(sock);
for (net_driverlevel = 0; net_driverlevel < net_numdrivers; net_driverlevel++)
{
if (net_drivers[net_driverlevel].initialized == 1)
{
net_drivers[net_driverlevel].Shutdown();
net_drivers[net_driverlevel].initialized = 0;
}
}
if (vcrFile != (-1))
{
Con_Printf("Closing vcrfile.\n");
Sys_FileClose(vcrFile);
}
}
void NET_Poll(void)
{
PollProcedure *pp;
bool useModem;
if (!configRestored)
{
if (serialAvailable)
{
if (config_com_modem.value == 1.0)
useModem = 1;
else
useModem = 0;
SetComPortConfig(0, (int32_t) config_com_port.value, (int32_t) config_com_irq.value, (int32_t) config_com_baud.value, useModem);
SetModemConfig(0, config_modem_dialtype.string, config_modem_clear.string, config_modem_init.string, config_modem_hangup.string);
}
configRestored = 1;
}
SetNetTime();
for (pp = pollProcedureList; pp; pp = pp->next)
{
if (pp->nextTime > net_time)
break;
pollProcedureList = pp->next;
pp->procedure(pp->arg);
}
}
void SchedulePollProcedure(PollProcedure *proc, double timeOffset)
{
PollProcedure *pp;
PollProcedure *prev;
proc->nextTime = Sys_FloatTime() + timeOffset;
for (pp = pollProcedureList, prev = 0; pp; pp = pp->next)
{
if (pp->nextTime >= proc->nextTime)
break;
prev = pp;
}
if (prev == 0)
{
proc->next = pollProcedureList;
pollProcedureList = proc;
return;
}
proc->next = pp;
prev->next = proc;
}
int32_t VCR_Init(void)
{
net_drivers[0].Init = VCR_Init;
net_drivers[0].SearchForHosts = VCR_SearchForHosts;
net_drivers[0].Connect = VCR_Connect;
net_drivers[0].CheckNewConnections = VCR_CheckNewConnections;
net_drivers[0].QGetMessage = VCR_GetMessage;
net_drivers[0].QSendMessage = VCR_SendMessage;
net_drivers[0].CanSendMessage = VCR_CanSendMessage;
net_drivers[0].Close = VCR_Close;
net_drivers[0].Shutdown = VCR_Shutdown;
Sys_FileRead(vcrFile, &next, sizeof(next));
return 0;
}
void VCR_ReadNext(void)
{
if (Sys_FileRead(vcrFile, &next, sizeof(next)) == 0)
{
next.op = 255;
Sys_Error("=== END OF PLAYBACK===\n");
}
if ((next.op < 1) || (next.op > VCR_MAX_MESSAGE))
Sys_Error("VCR_ReadNext: bad op");
}
void VCR_Listen(bool state)
{
}
void VCR_Shutdown(void)
{
}
int32_t VCR_GetMessage(qsocket_t *sock)
{
int32_t ret;
if (((host_time != next.time) || (next.op != VCR_OP_GETMESSAGE)) || (next.session != (*((int32_t *) (&sock->driverdata)))))
Sys_Error("VCR missmatch");
Sys_FileRead(vcrFile, &ret, sizeof(int32_t));
if (ret != 1)
{
VCR_ReadNext();
return ret;
}
Sys_FileRead(vcrFile, &net_message.cursize, sizeof(int32_t));
Sys_FileRead(vcrFile, net_message.data, net_message.cursize);
VCR_ReadNext();
return 1;
}
int32_t VCR_SendMessage(qsocket_t *sock, sizebuf_t *data)
{
int32_t ret;
if (((host_time != next.time) || (next.op != VCR_OP_SENDMESSAGE)) || (next.session != (*((int32_t *) (&sock->driverdata)))))
Sys_Error("VCR missmatch");
Sys_FileRead(vcrFile, &ret, sizeof(int32_t));
VCR_ReadNext();
return ret;
}
bool VCR_CanSendMessage(qsocket_t *sock)
{
bool ret;
if (((host_time != next.time) || (next.op != VCR_OP_CANSENDMESSAGE)) || (next.session != (*((int32_t *) (&sock->driverdata)))))
Sys_Error("VCR missmatch");
Sys_FileRead(vcrFile, &ret, sizeof(int32_t));
VCR_ReadNext();
return ret;
}
void VCR_Close(qsocket_t *sock)
{
}
void VCR_SearchForHosts(bool xmit)
{
}
qsocket_t *VCR_Connect(char *host)
{
return 0;
}
qsocket_t *VCR_CheckNewConnections(void)
{
qsocket_t *sock;
if ((host_time != next.time) || (next.op != VCR_OP_CONNECT))
Sys_Error("VCR missmatch");
if (!next.session)
{
VCR_ReadNext();
return 0;
}
sock = NET_NewQSocket();
*((int32_t *) (&sock->driverdata)) = next.session;
Sys_FileRead(vcrFile, sock->address, NET_NAMELEN);
VCR_ReadNext();
return sock;
}
void R_Surf8Patch()
{
}
void R_Surf16Patch()
{
}
void R_SurfacePatch(void)
{
}
char *PF_VarString(int32_t first)
{
int32_t i;
static char out[256];
out[0] = 0;
for (i = first; i < pr_argc; i++)
{
strcat(out, G_STRING(OFS_PARM0 + (i * 3)));
}
return out;
}
void PF_error(void)
{
char *s;
edict_t *ed;
s = PF_VarString(0);
Con_Printf("======SERVER ERROR in %s:\n%s\n", pr_strings + pr_xfunction->s_name, s);
ed = PROG_TO_EDICT(pr_global_struct->self);
ED_Print(ed);
Host_Error("Program error");
}
void PF_objerror(void)
{
char *s;
edict_t *ed;
s = PF_VarString(0);
Con_Printf("======OBJECT ERROR in %s:\n%s\n", pr_strings + pr_xfunction->s_name, s);
ed = PROG_TO_EDICT(pr_global_struct->self);
ED_Print(ed);
ED_Free(ed);
Host_Error("Program error");
}
void PF_makevectors(void)
{
AngleVectors(G_VECTOR(OFS_PARM0), pr_global_struct->v_forward, pr_global_struct->v_right, pr_global_struct->v_up);
}
void PF_setorigin(void)
{
edict_t *e;
float *org;
e = G_EDICT(OFS_PARM0);
org = G_VECTOR(OFS_PARM1);
VectorCopy(org, e->v.origin);
SV_LinkEdict(e, 0);
}
void SetMinMaxSize(edict_t *e, float *min, float *max, bool rotate)
{
float *angles;
vec3_t rmin;
vec3_t rmax;
float bounds[2][3];
float xvector[2];
float yvector[2];
float a;
vec3_t base;
vec3_t transformed;
int32_t i;
int32_t j;
int32_t k;
int32_t l;
for (i = 0; i < 3; i++)
if (min[i] > max[i])
PR_RunError("backwards mins/maxs");
rotate = 0;
if (!rotate)
{
VectorCopy(min, rmin);
VectorCopy(max, rmax);
}
else
{
angles = e->v.angles;
a = (angles[1] / 180) * M_PI;
xvector[0] = cos(a);
xvector[1] = sin(a);
yvector[0] = -sin(a);
yvector[1] = cos(a);
VectorCopy(min, bounds[0]);
VectorCopy(max, bounds[1]);
rmin[0] = (rmin[1] = (rmin[2] = 9999));
rmax[0] = (rmax[1] = (rmax[2] = -9999));
for (i = 0; i <= 1; i++)
{
base[0] = bounds[i][0];
for (j = 0; j <= 1; j++)
{
base[1] = bounds[j][1];
for (k = 0; k <= 1; k++)
{
base[2] = bounds[k][2];
transformed[0] = (xvector[0] * base[0]) + (yvector[0] * base[1]);
transformed[1] = (xvector[1] * base[0]) + (yvector[1] * base[1]);
transformed[2] = base[2];
for (l = 0; l < 3; l++)
{
if (transformed[l] < rmin[l])
rmin[l] = transformed[l];
if (transformed[l] > rmax[l])
rmax[l] = transformed[l];
}
}
}
}
}
VectorCopy(rmin, e->v.mins);
VectorCopy(rmax, e->v.maxs);
VectorSubtract(max, min, e->v.size);
SV_LinkEdict(e, 0);
}
void PF_setsize(void)
{
edict_t *e;
float *min;
float *max;
e = G_EDICT(OFS_PARM0);
min = G_VECTOR(OFS_PARM1);
max = G_VECTOR(OFS_PARM2);
SetMinMaxSize(e, min, max, 0);
}
void PF_setmodel(void)
{
edict_t *e;
char *m;
char **check;
model_t *mod;
int32_t i;
e = G_EDICT(OFS_PARM0);
m = G_STRING(OFS_PARM1);
for (i = 0, check = sv.model_precache; *check; i++, check++)
if (!strcmp(*check, m))
break;
if (!(*check))
PR_RunError("no precache: %s\n", m);
e->v.model = m - pr_strings;
e->v.modelindex = i;
mod = sv.models[(int32_t) e->v.modelindex];
if (mod)
SetMinMaxSize(e, mod->mins, mod->maxs, 1);
else
SetMinMaxSize(e, vec3_origin, vec3_origin, 1);
}
void PF_bprint(void)
{
char *s;
s = PF_VarString(0);
SV_BroadcastPrintf("%s", s);
}
void PF_sprint(void)
{
char *s;
client_t *client;
int32_t entnum;
entnum = G_EDICTNUM(OFS_PARM0);
s = PF_VarString(1);
if ((entnum < 1) || (entnum > svs.maxclients))
{
Con_Printf("tried to sprint to a non-client\n");
return;
}
client = &svs.clients[entnum - 1];
MSG_WriteChar(&client->message, svc_print);
MSG_WriteString(&client->message, s);
}
void PF_centerprint(void)
{
char *s;
client_t *client;
int32_t entnum;
entnum = G_EDICTNUM(OFS_PARM0);
s = PF_VarString(1);
if ((entnum < 1) || (entnum > svs.maxclients))
{
Con_Printf("tried to sprint to a non-client\n");
return;
}
client = &svs.clients[entnum - 1];
MSG_WriteChar(&client->message, svc_centerprint);
MSG_WriteString(&client->message, s);
}
void PF_normalize(void)
{
float *value1;
vec3_t newvalue;
float new;
value1 = G_VECTOR(OFS_PARM0);
new = ((value1[0] * value1[0]) + (value1[1] * value1[1])) + (value1[2] * value1[2]);
new = sqrt(new);
if (new == 0)
newvalue[0] = (newvalue[1] = (newvalue[2] = 0));
else
{
new = 1 / new;
newvalue[0] = value1[0] * new;
newvalue[1] = value1[1] * new;
newvalue[2] = value1[2] * new;
}
VectorCopy(newvalue, G_VECTOR(OFS_RETURN));
}
void PF_vlen(void)
{
float *value1;
float new;
value1 = G_VECTOR(OFS_PARM0);
new = ((value1[0] * value1[0]) + (value1[1] * value1[1])) + (value1[2] * value1[2]);
new = sqrt(new);
G_FLOAT(OFS_RETURN) = new;
}
void PF_vectoyaw(void)
{
float *value1;
float yaw;
value1 = G_VECTOR(OFS_PARM0);
if ((value1[1] == 0) && (value1[0] == 0))
yaw = 0;
else
{
yaw = (int32_t) ((atan2(value1[1], value1[0]) * 180) / M_PI);
if (yaw < 0)
yaw += 360;
}
G_FLOAT(OFS_RETURN) = yaw;
}
void PF_vectoangles(void)
{
float *value1;
float forward;
float yaw;
float pitch;
value1 = G_VECTOR(OFS_PARM0);
if ((value1[1] == 0) && (value1[0] == 0))
{
yaw = 0;
if (value1[2] > 0)
pitch = 90;
else
pitch = 270;
}
else
{
yaw = (int32_t) ((atan2(value1[1], value1[0]) * 180) / M_PI);
if (yaw < 0)
yaw += 360;
forward = sqrt((value1[0] * value1[0]) + (value1[1] * value1[1]));
pitch = (int32_t) ((atan2(value1[2], forward) * 180) / M_PI);
if (pitch < 0)
pitch += 360;
}
G_FLOAT(OFS_RETURN + 0) = pitch;
G_FLOAT(OFS_RETURN + 1) = yaw;
G_FLOAT(OFS_RETURN + 2) = 0;
}
void PF_random(void)
{
float num;
num = (rand() & 0x7fff) / ((float) 0x7fff);
G_FLOAT(OFS_RETURN) = num;
}
void PF_particle(void)
{
float *org;
float *dir;
float color;
float count;
org = G_VECTOR(OFS_PARM0);
dir = G_VECTOR(OFS_PARM1);
color = G_FLOAT(OFS_PARM2);
count = G_FLOAT(OFS_PARM3);
SV_StartParticle(org, dir, color, count);
}
void PF_ambientsound(void)
{
char **check;
char *samp;
float *pos;
float vol;
float attenuation;
int32_t i;
int32_t soundnum;
pos = G_VECTOR(OFS_PARM0);
samp = G_STRING(OFS_PARM1);
vol = G_FLOAT(OFS_PARM2);
attenuation = G_FLOAT(OFS_PARM3);
for (soundnum = 0, check = sv.sound_precache; *check; check++, soundnum++)
if (!strcmp(*check, samp))
break;
if (!(*check))
{
Con_Printf("no precache: %s\n", samp);
return;
}
MSG_WriteByte(&sv.signon, svc_spawnstaticsound);
for (i = 0; i < 3; i++)
MSG_WriteCoord(&sv.signon, pos[i]);
MSG_WriteByte(&sv.signon, soundnum);
MSG_WriteByte(&sv.signon, vol * 255);
MSG_WriteByte(&sv.signon, attenuation * 64);
}
void PF_sound(void)
{
char *sample;
int32_t channel;
edict_t *entity;
int32_t volume;
float attenuation;
entity = G_EDICT(OFS_PARM0);
channel = G_FLOAT(OFS_PARM1);
sample = G_STRING(OFS_PARM2);
volume = G_FLOAT(OFS_PARM3) * 255;
attenuation = G_FLOAT(OFS_PARM4);
if ((volume < 0) || (volume > 255))
Sys_Error("SV_StartSound: volume = %i", volume);
if ((attenuation < 0) || (attenuation > 4))
Sys_Error("SV_StartSound: attenuation = %f", attenuation);
if ((channel < 0) || (channel > 7))
Sys_Error("SV_StartSound: channel = %i", channel);
SV_StartSound(entity, channel, sample, volume, attenuation);
}
void PF_break(void)
{
Con_Printf("break statement\n");
*((int32_t *) (-4)) = 0;
}
void PF_traceline(void)
{
float *v1;
float *v2;
trace_t trace;
int32_t nomonsters;
edict_t *ent;
v1 = G_VECTOR(OFS_PARM0);
v2 = G_VECTOR(OFS_PARM1);
nomonsters = G_FLOAT(OFS_PARM2);
ent = G_EDICT(OFS_PARM3);
trace = SV_Move(v1, vec3_origin, vec3_origin, v2, nomonsters, ent);
pr_global_struct->trace_allsolid = trace.allsolid;
pr_global_struct->trace_startsolid = trace.startsolid;
pr_global_struct->trace_fraction = trace.fraction;
pr_global_struct->trace_inwater = trace.inwater;
pr_global_struct->trace_inopen = trace.inopen;
VectorCopy(trace.endpos, pr_global_struct->trace_endpos);
VectorCopy(trace.plane.normal, pr_global_struct->trace_plane_normal);
pr_global_struct->trace_plane_dist = trace.plane.dist;
if (trace.ent)
pr_global_struct->trace_ent = EDICT_TO_PROG(trace.ent);
else
pr_global_struct->trace_ent = EDICT_TO_PROG(sv.edicts);
}
void PF_checkpos(void)
{
}
int32_t PF_newcheckclient(int32_t check)
{
int32_t i;
uint8_t *pvs;
edict_t *ent;
mleaf_t *leaf;
vec3_t org;
if (check < 1)
check = 1;
if (check > svs.maxclients)
check = svs.maxclients;
if (check == svs.maxclients)
i = 1;
else
i = check + 1;
for (;; i++)
{
if (i == (svs.maxclients + 1))
i = 1;
ent = EDICT_NUM(i);
if (i == check)
break;
if (ent->free)
continue;
if (ent->v.health <= 0)
continue;
if (((int32_t) ent->v.flags) & FL_NOTARGET)
continue;
break;
}
VectorAdd(ent->v.origin, ent->v.view_ofs, org);
leaf = Mod_PointInLeaf(org, sv.worldmodel);
pvs = Mod_LeafPVS(leaf, sv.worldmodel);
memcpy(checkpvs, pvs, (sv.worldmodel->numleafs + 7) >> 3);
return i;
}
void PF_checkclient(void)
{
edict_t *ent;
edict_t *self;
mleaf_t *leaf;
int32_t l;
vec3_t view;
if ((sv.time - sv.lastchecktime) >= 0.1)
{
sv.lastcheck = PF_newcheckclient(sv.lastcheck);
sv.lastchecktime = sv.time;
}
ent = EDICT_NUM(sv.lastcheck);
if (ent->free || (ent->v.health <= 0))
{
RETURN_EDICT(sv.edicts);
return;
}
self = PROG_TO_EDICT(pr_global_struct->self);
VectorAdd(self->v.origin, self->v.view_ofs, view);
leaf = Mod_PointInLeaf(view, sv.worldmodel);
l = (leaf - sv.worldmodel->leafs) - 1;
if ((l < 0) || (!(checkpvs[l >> 3] & (1 << (l & 7)))))
{
c_notvis++;
RETURN_EDICT(sv.edicts);
return;
}
c_invis++;
RETURN_EDICT(ent);
}
void PF_stuffcmd(void)
{
int32_t entnum;
char *str;
client_t *old;
entnum = G_EDICTNUM(OFS_PARM0);
if ((entnum < 1) || (entnum > svs.maxclients))
PR_RunError("Parm 0 not a client");
str = G_STRING(OFS_PARM1);
old = host_client;
host_client = &svs.clients[entnum - 1];
Host_ClientCommands("%s", str);
host_client = old;
}
void PF_localcmd(void)
{
char *str;
str = G_STRING(OFS_PARM0);
Cbuf_AddText(str);
}
void PF_cvar(void)
{
char *str;
str = G_STRING(OFS_PARM0);
G_FLOAT(OFS_RETURN) = Cvar_VariableValue(str);
}
void PF_cvar_set(void)
{
char *var;
char *val;
var = G_STRING(OFS_PARM0);
val = G_STRING(OFS_PARM1);
Cvar_Set(var, val);
}
void PF_findradius(void)
{
edict_t *ent;
edict_t *chain;
float rad;
float *org;
vec3_t eorg;
int32_t i;
int32_t j;
chain = (edict_t *) sv.edicts;
org = G_VECTOR(OFS_PARM0);
rad = G_FLOAT(OFS_PARM1);
ent = NEXT_EDICT(sv.edicts);
for (i = 1; i < sv.num_edicts; i++, ent = NEXT_EDICT(ent))
{
if (ent->free)
continue;
if (ent->v.solid == SOLID_NOT)
continue;
for (j = 0; j < 3; j++)
eorg[j] = org[j] - (ent->v.origin[j] + ((ent->v.mins[j] + ent->v.maxs[j]) * 0.5));
if (Length(eorg) > rad)
continue;
ent->v.chain = EDICT_TO_PROG(chain);
chain = ent;
}
RETURN_EDICT(chain);
}
void PF_dprint(void)
{
Con_DPrintf("%s", PF_VarString(0));
}
void PF_ftos(void)
{
float v;
v = G_FLOAT(OFS_PARM0);
if (v == ((int32_t) v))
sprintf(pr_string_temp, "%d", (int32_t) v);
else
sprintf(pr_string_temp, "%5.1f", v);
G_INT(OFS_RETURN) = pr_string_temp - pr_strings;
}
void PF_fabs(void)
{
float v;
v = G_FLOAT(OFS_PARM0);
G_FLOAT(OFS_RETURN) = fabs(v);
}
void PF_vtos(void)
{
sprintf(pr_string_temp, "'%5.1f %5.1f %5.1f'", G_VECTOR(OFS_PARM0)[0], G_VECTOR(OFS_PARM0)[1], G_VECTOR(OFS_PARM0)[2]);
G_INT(OFS_RETURN) = pr_string_temp - pr_strings;
}
void PF_Spawn(void)
{
edict_t *ed;
ed = ED_Alloc();
RETURN_EDICT(ed);
}
void PF_Remove(void)
{
edict_t *ed;
ed = G_EDICT(OFS_PARM0);
ED_Free(ed);
}
void PF_Find(void)
{
int32_t e;
int32_t f;
char *s;
char *t;
edict_t *ed;
e = G_EDICTNUM(OFS_PARM0);
f = G_INT(OFS_PARM1);
s = G_STRING(OFS_PARM2);
if (!s)
PR_RunError("PF_Find: bad search string");
for (e++; e < sv.num_edicts; e++)
{
ed = EDICT_NUM(e);
if (ed->free)
continue;
t = E_STRING(ed, f);
if (!t)
continue;
if (!strcmp(t, s))
{
RETURN_EDICT(ed);
return;
}
}
RETURN_EDICT(sv.edicts);
}
void PR_CheckEmptyString(char *s)
{
if (s[0] <= ' ')
PR_RunError("Bad string");
}
void PF_precache_file(void)
{
G_INT(OFS_RETURN) = G_INT(OFS_PARM0);
}
void PF_precache_sound(void)
{
char *s;
int32_t i;
if (sv.state != ss_loading)
PR_RunError("PF_Precache_*: Precache can only be done in spawn functions");
s = G_STRING(OFS_PARM0);
G_INT(OFS_RETURN) = G_INT(OFS_PARM0);
PR_CheckEmptyString(s);
for (i = 0; i < MAX_SOUNDS; i++)
{
if (!sv.sound_precache[i])
{
sv.sound_precache[i] = s;
return;
}
if (!strcmp(sv.sound_precache[i], s))
return;
}
PR_RunError("PF_precache_sound: overflow");
}
void PF_precache_model(void)
{
char *s;
int32_t i;
if (sv.state != ss_loading)
PR_RunError("PF_Precache_*: Precache can only be done in spawn functions");
s = G_STRING(OFS_PARM0);
G_INT(OFS_RETURN) = G_INT(OFS_PARM0);
PR_CheckEmptyString(s);
for (i = 0; i < MAX_MODELS; i++)
{
if (!sv.model_precache[i])
{
sv.model_precache[i] = s;
sv.models[i] = Mod_ForName(s, 1);
return;
}
if (!strcmp(sv.model_precache[i], s))
return;
}
PR_RunError("PF_precache_model: overflow");
}
void PF_coredump(void)
{
ED_PrintEdicts();
}
void PF_traceon(void)
{
pr_trace = 1;
}
void PF_traceoff(void)
{
pr_trace = 0;
}
void PF_eprint(void)
{
ED_PrintNum(G_EDICTNUM(OFS_PARM0));
}
void PF_walkmove(void)
{
edict_t *ent;
float yaw;
float dist;
vec3_t move;
dfunction_t *oldf;
int32_t oldself;
ent = PROG_TO_EDICT(pr_global_struct->self);
yaw = G_FLOAT(OFS_PARM0);
dist = G_FLOAT(OFS_PARM1);
if (!(((int32_t) ent->v.flags) & ((FL_ONGROUND | FL_FLY) | FL_SWIM)))
{
G_FLOAT(OFS_RETURN) = 0;
return;
}
yaw = ((yaw * M_PI) * 2) / 360;
move[0] = cos(yaw) * dist;
move[1] = sin(yaw) * dist;
move[2] = 0;
oldf = pr_xfunction;
oldself = pr_global_struct->self;
G_FLOAT(OFS_RETURN) = SV_movestep(ent, move, 1);
pr_xfunction = oldf;
pr_global_struct->self = oldself;
}
void PF_droptofloor(void)
{
edict_t *ent;
vec3_t end;
trace_t trace;
ent = PROG_TO_EDICT(pr_global_struct->self);
VectorCopy(ent->v.origin, end);
end[2] -= 256;
trace = SV_Move(ent->v.origin, ent->v.mins, ent->v.maxs, end, 0, ent);
if ((trace.fraction == 1) || trace.allsolid)
G_FLOAT(OFS_RETURN) = 0;
else
{
VectorCopy(trace.endpos, ent->v.origin);
SV_LinkEdict(ent, 0);
ent->v.flags = ((int32_t) ent->v.flags) | FL_ONGROUND;
ent->v.groundentity = EDICT_TO_PROG(trace.ent);
G_FLOAT(OFS_RETURN) = 1;
}
}
void PF_lightstyle(void)
{
int32_t style;
char *val;
client_t *client;
int32_t j;
style = G_FLOAT(OFS_PARM0);
val = G_STRING(OFS_PARM1);
sv.lightstyles[style] = val;
if (sv.state != ss_active)
return;
for (j = 0, client = svs.clients; j < svs.maxclients; j++, client++)
if (client->active || client->spawned)
{
MSG_WriteChar(&client->message, svc_lightstyle);
MSG_WriteChar(&client->message, style);
MSG_WriteString(&client->message, val);
}
}
void PF_rint(void)
{
float f;
f = G_FLOAT(OFS_PARM0);
if (f > 0)
G_FLOAT(OFS_RETURN) = (int32_t) (f + 0.5);
else
G_FLOAT(OFS_RETURN) = (int32_t) (f - 0.5);
}
void PF_floor(void)
{
G_FLOAT(OFS_RETURN) = floor(G_FLOAT(OFS_PARM0));
}
void PF_ceil(void)
{
G_FLOAT(OFS_RETURN) = ceil(G_FLOAT(OFS_PARM0));
}
void PF_checkbottom(void)
{
edict_t *ent;
ent = G_EDICT(OFS_PARM0);
G_FLOAT(OFS_RETURN) = SV_CheckBottom(ent);
}
void PF_pointcontents(void)
{
float *v;
v = G_VECTOR(OFS_PARM0);
G_FLOAT(OFS_RETURN) = SV_PointContents(v);
}
void PF_nextent(void)
{
int32_t i;
edict_t *ent;
i = G_EDICTNUM(OFS_PARM0);
while (1)
{
i++;
if (i == sv.num_edicts)
{
RETURN_EDICT(sv.edicts);
return;
}
ent = EDICT_NUM(i);
if (!ent->free)
{
RETURN_EDICT(ent);
return;
}
}
}
void PF_aim(void)
{
edict_t *ent;
edict_t *check;
edict_t *bestent;
vec3_t start;
vec3_t dir;
vec3_t end;
vec3_t bestdir;
int32_t i;
int32_t j;
trace_t tr;
float dist;
float bestdist;
float speed;
ent = G_EDICT(OFS_PARM0);
speed = G_FLOAT(OFS_PARM1);
VectorCopy(ent->v.origin, start);
start[2] += 20;
VectorCopy(pr_global_struct->v_forward, dir);
VectorMA(start, 2048, dir, end);
tr = SV_Move(start, vec3_origin, vec3_origin, end, 0, ent);
if ((tr.ent && (tr.ent->v.takedamage == DAMAGE_AIM)) && (((!teamplay.value) || (ent->v.team <= 0)) || (ent->v.team != tr.ent->v.team)))
{
VectorCopy(pr_global_struct->v_forward, G_VECTOR(OFS_RETURN));
return;
}
VectorCopy(dir, bestdir);
bestdist = sv_aim.value;
bestent = 0;
check = NEXT_EDICT(sv.edicts);
for (i = 1; i < sv.num_edicts; i++, check = NEXT_EDICT(check))
{
if (check->v.takedamage != DAMAGE_AIM)
continue;
if (check == ent)
continue;
if ((teamplay.value && (ent->v.team > 0)) && (ent->v.team == check->v.team))
continue;
for (j = 0; j < 3; j++)
end[j] = check->v.origin[j] + (0.5 * (check->v.mins[j] + check->v.maxs[j]));
VectorSubtract(end, start, dir);
VectorNormalize(dir);
dist = DotProduct(dir, pr_global_struct->v_forward);
if (dist < bestdist)
continue;
tr = SV_Move(start, vec3_origin, vec3_origin, end, 0, ent);
if (tr.ent == check)
{
bestdist = dist;
bestent = check;
}
}
if (bestent)
{
VectorSubtract(bestent->v.origin, ent->v.origin, dir);
dist = DotProduct(dir, pr_global_struct->v_forward);
VectorScale(pr_global_struct->v_forward, dist, end);
end[2] = dir[2];
VectorNormalize(end);
VectorCopy(end, G_VECTOR(OFS_RETURN));
}
else
{
VectorCopy(bestdir, G_VECTOR(OFS_RETURN));
}
}
void PF_changeyaw(void)
{
edict_t *ent;
float ideal;
float current;
float move;
float speed;
ent = PROG_TO_EDICT(pr_global_struct->self);
current = anglemod(ent->v.angles[1]);
ideal = ent->v.ideal_yaw;
speed = ent->v.yaw_speed;
if (current == ideal)
return;
move = ideal - current;
if (ideal > current)
{
if (move >= 180)
move = move - 360;
}
else
{
if (move <= (-180))
move = move + 360;
}
if (move > 0)
{
if (move > speed)
move = speed;
}
else
{
if (move < (-speed))
move = -speed;
}
ent->v.angles[1] = anglemod(current + move);
}
sizebuf_t *WriteDest(void)
{
int32_t entnum;
int32_t dest;
edict_t *ent;
dest = G_FLOAT(OFS_PARM0);
switch (dest)
{
case MSG_BROADCAST:
return &sv.datagram;
case MSG_ONE:
ent = PROG_TO_EDICT(pr_global_struct->msg_entity);
entnum = NUM_FOR_EDICT(ent);
if ((entnum < 1) || (entnum > svs.maxclients))
PR_RunError("WriteDest: not a client");
return &svs.clients[entnum - 1].message;
case MSG_ALL:
return &sv.reliable_datagram;
case MSG_INIT:
return &sv.signon;
default:
PR_RunError("WriteDest: bad destination");
break;
}
return 0;
}
void PF_WriteByte(void)
{
MSG_WriteByte(WriteDest(), G_FLOAT(OFS_PARM1));
}
void PF_WriteChar(void)
{
MSG_WriteChar(WriteDest(), G_FLOAT(OFS_PARM1));
}
void PF_WriteShort(void)
{
MSG_WriteShort(WriteDest(), G_FLOAT(OFS_PARM1));
}
void PF_WriteLong(void)
{
MSG_WriteLong(WriteDest(), G_FLOAT(OFS_PARM1));
}
void PF_WriteAngle(void)
{
MSG_WriteAngle(WriteDest(), G_FLOAT(OFS_PARM1));
}
void PF_WriteCoord(void)
{
MSG_WriteCoord(WriteDest(), G_FLOAT(OFS_PARM1));
}
void PF_WriteString(void)
{
MSG_WriteString(WriteDest(), G_STRING(OFS_PARM1));
}
void PF_WriteEntity(void)
{
MSG_WriteShort(WriteDest(), G_EDICTNUM(OFS_PARM1));
}
void PF_makestatic(void)
{
edict_t *ent;
int32_t i;
ent = G_EDICT(OFS_PARM0);
MSG_WriteByte(&sv.signon, svc_spawnstatic);
MSG_WriteByte(&sv.signon, SV_ModelIndex(pr_strings + ent->v.model));
MSG_WriteByte(&sv.signon, ent->v.frame);
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]);
MSG_WriteAngle(&sv.signon, ent->v.angles[i]);
}
ED_Free(ent);
}
void PF_setspawnparms(void)
{
edict_t *ent;
int32_t i;
client_t *client;
ent = G_EDICT(OFS_PARM0);
i = NUM_FOR_EDICT(ent);
if ((i < 1) || (i > svs.maxclients))
PR_RunError("Entity is not a client");
client = svs.clients + (i - 1);
for (i = 0; i < NUM_SPAWN_PARMS; i++)
(&pr_global_struct->parm1)[i] = client->spawn_parms[i];
}
void PF_changelevel(void)
{
char *s;
if (svs.changelevel_issued)
return;
svs.changelevel_issued = 1;
s = G_STRING(OFS_PARM0);
Cbuf_AddText(va("changelevel %s\n", s));
}
void PF_Fixme(void)
{
PR_RunError("unimplemented bulitin");
}
void ED_ClearEdict(edict_t *e)
{
memset(&e->v, 0, progs->entityfields * 4);
e->free = 0;
}
edict_t *ED_Alloc(void)
{
int32_t i;
edict_t *e;
for (i = svs.maxclients + 1; i < sv.num_edicts; i++)
{
e = EDICT_NUM(i);
if (e->free && ((e->freetime < 2) || ((sv.time - e->freetime) > 0.5)))
{
ED_ClearEdict(e);
return e;
}
}
if (i == MAX_EDICTS)
Sys_Error("ED_Alloc: no free edicts");
sv.num_edicts++;
e = EDICT_NUM(i);
ED_ClearEdict(e);
return e;
}
void ED_Free(edict_t *ed)
{
SV_UnlinkEdict(ed);
ed->free = 1;
ed->v.model = 0;
ed->v.takedamage = 0;
ed->v.modelindex = 0;
ed->v.colormap = 0;
ed->v.skin = 0;
ed->v.frame = 0;
VectorCopy(vec3_origin, ed->v.origin);
VectorCopy(vec3_origin, ed->v.angles);
ed->v.nextthink = -1;
ed->v.solid = 0;
ed->freetime = sv.time;
}
ddef_t *ED_GlobalAtOfs(int32_t ofs)
{
ddef_t *def;
int32_t i;
for (i = 0; i < progs->numglobaldefs; i++)
{
def = &pr_globaldefs[i];
if (def->ofs == ofs)
return def;
}
return 0;
}
ddef_t *ED_FieldAtOfs(int32_t ofs)
{
ddef_t *def;
int32_t i;
for (i = 0; i < progs->numfielddefs; i++)
{
def = &pr_fielddefs[i];
if (def->ofs == ofs)
return def;
}
return 0;
}
ddef_t *ED_FindField(char *name)
{
ddef_t *def;
int32_t i;
for (i = 0; i < progs->numfielddefs; i++)
{
def = &pr_fielddefs[i];
if (!strcmp(pr_strings + def->s_name, name))
return def;
}
return 0;
}
ddef_t *ED_FindGlobal(char *name)
{
ddef_t *def;
int32_t i;
for (i = 0; i < progs->numglobaldefs; i++)
{
def = &pr_globaldefs[i];
if (!strcmp(pr_strings + def->s_name, name))
return def;
}
return 0;
}
dfunction_t *ED_FindFunction(char *name)
{
dfunction_t *func;
int32_t i;
for (i = 0; i < progs->numfunctions; i++)
{
func = &pr_functions[i];
if (!strcmp(pr_strings + func->s_name, name))
return func;
}
return 0;
}
eval_t *GetEdictFieldValue(edict_t *ed, char *field)
{
ddef_t *def = 0;
int32_t i;
static int32_t rep = 0;
for (i = 0; i < GEFV_CACHESIZE; i++)
{
if (!strcmp(field, gefvCache[i].field))
{
def = gefvCache[i].pcache;
goto Done;
}
}
def = ED_FindField(field);
if (strlen(field) < MAX_FIELD_LEN)
{
gefvCache[rep].pcache = def;
strcpy(gefvCache[rep].field, field);
rep ^= 1;
}
Done:
if (!def)
return 0;
return (eval_t *) (((char *) (&ed->v)) + (def->ofs * 4));
}
char *PR_ValueString(etype_t type, eval_t *val)
{
static char line[256];
ddef_t *def;
dfunction_t *f;
type &= ~DEF_SAVEGLOBAL;
switch (type)
{
case ev_string:
sprintf(line, "%s", pr_strings + val->string);
break;
case ev_entity:
sprintf(line, "entity %i", NUM_FOR_EDICT(PROG_TO_EDICT(val->edict)));
break;
case ev_function:
f = pr_functions + val->function;
sprintf(line, "%s()", pr_strings + f->s_name);
break;
case ev_field:
def = ED_FieldAtOfs(val->_int);
sprintf(line, ".%s", pr_strings + def->s_name);
break;
case ev_void:
sprintf(line, "void");
break;
case ev_float:
sprintf(line, "%5.1f", val->_float);
break;
case ev_vector:
sprintf(line, "'%5.1f %5.1f %5.1f'", val->vector[0], val->vector[1], val->vector[2]);
break;
case ev_pointer:
sprintf(line, "pointer");
break;
default:
sprintf(line, "bad type %i", type);
break;
}
return line;
}
char *PR_UglyValueString(etype_t type, eval_t *val)
{
static char line[256];
ddef_t *def;
dfunction_t *f;
type &= ~DEF_SAVEGLOBAL;
switch (type)
{
case ev_string:
sprintf(line, "%s", pr_strings + val->string);
break;
case ev_entity:
sprintf(line, "%i", NUM_FOR_EDICT(PROG_TO_EDICT(val->edict)));
break;
case ev_function:
f = pr_functions + val->function;
sprintf(line, "%s", pr_strings + f->s_name);
break;
case ev_field:
def = ED_FieldAtOfs(val->_int);
sprintf(line, "%s", pr_strings + def->s_name);
break;
case ev_void:
sprintf(line, "void");
break;
case ev_float:
sprintf(line, "%f", val->_float);
break;
case ev_vector:
sprintf(line, "%f %f %f", val->vector[0], val->vector[1], val->vector[2]);
break;
default:
sprintf(line, "bad type %i", type);
break;
}
return line;
}
char *PR_GlobalString(int32_t ofs)
{
char *s;
int32_t i;
ddef_t *def;
void *val;
static char line[128];
val = (void *) (&pr_globals[ofs]);
def = ED_GlobalAtOfs(ofs);
if (!def)
sprintf(line, "%i(?]", ofs);
else
{
s = PR_ValueString(def->type, val);
sprintf(line, "%i(%s)%s", ofs, pr_strings + def->s_name, s);
}
i = strlen(line);
for (; i < 20; i++)
strcat(line, " ");
strcat(line, " ");
return line;
}
char *PR_GlobalStringNoContents(int32_t ofs)
{
int32_t i;
ddef_t *def;
static char line[128];
def = ED_GlobalAtOfs(ofs);
if (!def)
sprintf(line, "%i(?]", ofs);
else
sprintf(line, "%i(%s)", ofs, pr_strings + def->s_name);
i = strlen(line);
for (; i < 20; i++)
strcat(line, " ");
strcat(line, " ");
return line;
}
void ED_Print(edict_t *ed)
{
int32_t l;
ddef_t *d;
int32_t *v;
int32_t i;
int32_t j;
char *name;
int32_t type;
if (ed->free)
{
Con_Printf("FREE\n");
return;
}
Con_Printf("\nEDICT %i:\n", NUM_FOR_EDICT(ed));
for (i = 1; i < progs->numfielddefs; i++)
{
d = &pr_fielddefs[i];
name = pr_strings + d->s_name;
if (name[strlen(name) - 2] == '_')
continue;
v = (int32_t *) (((char *) (&ed->v)) + (d->ofs * 4));
type = d->type & (~DEF_SAVEGLOBAL);
for (j = 0; j < type_size[type]; j++)
if (v[j])
break;
if (j == type_size[type])
continue;
Con_Printf("%s", name);
l = strlen(name);
while ((l++) < 15)
Con_Printf(" ");
Con_Printf("%s\n", PR_ValueString(d->type, (eval_t *) v));
}
}
void ED_Write(FILE *f, edict_t *ed)
{
ddef_t *d;
int32_t *v;
int32_t i;
int32_t j;
char *name;
int32_t type;
fprintf(f, "{\n");
if (ed->free)
{
fprintf(f, "}\n");
return;
}
for (i = 1; i < progs->numfielddefs; i++)
{
d = &pr_fielddefs[i];
name = pr_strings + d->s_name;
if (name[strlen(name) - 2] == '_')
continue;
v = (int32_t *) (((char *) (&ed->v)) + (d->ofs * 4));
type = d->type & (~DEF_SAVEGLOBAL);
for (j = 0; j < type_size[type]; j++)
if (v[j])
break;
if (j == type_size[type])
continue;
fprintf(f, "\"%s\" ", name);
fprintf(f, "\"%s\"\n", PR_UglyValueString(d->type, (eval_t *) v));
}
fprintf(f, "}\n");
}
void ED_PrintNum(int32_t ent)
{
ED_Print(EDICT_NUM(ent));
}
void ED_PrintEdicts(void)
{
int32_t i;
Con_Printf("%i entities\n", sv.num_edicts);
for (i = 0; i < sv.num_edicts; i++)
ED_PrintNum(i);
}
void ED_PrintEdict_f(void)
{
int32_t i;
i = (int32_t) strtol(Cmd_Argv(1), 0, 0);
if (i >= sv.num_edicts)
{
Con_Printf("Bad edict number\n");
return;
}
ED_PrintNum(i);
}
void ED_Count(void)
{
int32_t i;
edict_t *ent;
int32_t active;
int32_t models;
int32_t solid;
int32_t step;
active = (models = (solid = (step = 0)));
for (i = 0; i < sv.num_edicts; i++)
{
ent = EDICT_NUM(i);
if (ent->free)
continue;
active++;
if (ent->v.solid)
solid++;
if (ent->v.model)
models++;
if (ent->v.movetype == MOVETYPE_STEP)
step++;
}
Con_Printf("num_edicts:%3i\n", sv.num_edicts);
Con_Printf("active :%3i\n", active);
Con_Printf("view :%3i\n", models);
Con_Printf("touch :%3i\n", solid);
Con_Printf("step :%3i\n", step);
}
void ED_WriteGlobals(FILE *f)
{
ddef_t *def;
int32_t i;
char *name;
int32_t type;
fprintf(f, "{\n");
for (i = 0; i < progs->numglobaldefs; i++)
{
def = &pr_globaldefs[i];
type = def->type;
if (!(def->type & DEF_SAVEGLOBAL))
continue;
type &= ~DEF_SAVEGLOBAL;
if (((type != ev_string) && (type != ev_float)) && (type != ev_entity))
continue;
name = pr_strings + def->s_name;
fprintf(f, "\"%s\" ", name);
fprintf(f, "\"%s\"\n", PR_UglyValueString(type, (eval_t *) (&pr_globals[def->ofs])));
}
fprintf(f, "}\n");
}
void ED_ParseGlobals(char *data)
{
char keyname[64];
ddef_t *key;
while (1)
{
data = COM_Parse(data);
if (com_token[0] == '}')
break;
if (!data)
Sys_Error("ED_ParseEntity: EOF without closing brace");
strcpy(keyname, com_token);
data = COM_Parse(data);
if (!data)
Sys_Error("ED_ParseEntity: EOF without closing brace");
if (com_token[0] == '}')
Sys_Error("ED_ParseEntity: closing brace without data");
key = ED_FindGlobal(keyname);
if (!key)
{
Con_Printf("'%s' is not a global\n", keyname);
continue;
}
if (!ED_ParseEpair((void *) pr_globals, key, com_token))
Host_Error("ED_ParseGlobals: parse error");
}
}
char *ED_NewString(char *string)
{
char *new;
char *new_p;
int32_t i;
int32_t l;
l = strlen(string) + 1;
new = Hunk_Alloc(l);
new_p = new;
for (i = 0; i < l; i++)
{
if ((string[i] == '\\') && (i < (l - 1)))
{
i++;
if (string[i] == 'n')
*(new_p++) = '\n';
else
*(new_p++) = '\\';
}
else
*(new_p++) = string[i];
}
return new;
}
bool ED_ParseEpair(void *base, ddef_t *key, char *s)
{
int32_t i;
char string[128];
ddef_t *def;
char *v;
char *w;
void *d;
dfunction_t *func;
d = (void *) (((int32_t *) base) + key->ofs);
switch (key->type & (~DEF_SAVEGLOBAL))
{
case ev_string:
*((string_t *) d) = ED_NewString(s) - pr_strings;
break;
case ev_float:
*((float *) d) = atof(s);
break;
case ev_vector:
strcpy(string, s);
v = string;
w = string;
for (i = 0; i < 3; i++)
{
while ((*v) && ((*v) != ' '))
v++;
*v = 0;
((float *) d)[i] = atof(w);
w = (v = v + 1);
}
break;
case ev_entity:
*((int32_t *) d) = EDICT_TO_PROG(EDICT_NUM(atoi(s)));
break;
case ev_field:
def = ED_FindField(s);
if (!def)
{
Con_Printf("Can't find field %s\n", s);
return 0;
}
*((int32_t *) d) = G_INT(def->ofs);
break;
case ev_function:
func = ED_FindFunction(s);
if (!func)
{
Con_Printf("Can't find function %s\n", s);
return 0;
}
*((func_t *) d) = func - pr_functions;
break;
default:
break;
}
return 1;
}
char *ED_ParseEdict(char *data, edict_t *ent)
{
ddef_t *key;
bool anglehack;
bool init;
char keyname[256];
int32_t n;
init = 0;
if (ent != sv.edicts)
memset(&ent->v, 0, progs->entityfields * 4);
while (1)
{
data = COM_Parse(data);
if (com_token[0] == '}')
break;
if (!data)
Sys_Error("ED_ParseEntity: EOF without closing brace");
if (!strcmp(com_token, "angle"))
{
strcpy(com_token, "angles");
anglehack = 1;
}
else
anglehack = 0;
if (!strcmp(com_token, "light"))
strcpy(com_token, "light_lev");
strcpy(keyname, com_token);
n = strlen(keyname);
while (n && (keyname[n - 1] == ' '))
{
keyname[n - 1] = 0;
n--;
}
data = COM_Parse(data);
if (!data)
Sys_Error("ED_ParseEntity: EOF without closing brace");
if (com_token[0] == '}')
Sys_Error("ED_ParseEntity: closing brace without data");
init = 1;
if (keyname[0] == '_')
continue;
key = ED_FindField(keyname);
if (!key)
{
Con_Printf("'%s' is not a field\n", keyname);
continue;
}
if (anglehack)
{
char temp[32];
strcpy(temp, com_token);
sprintf(com_token, "0 %s 0", temp);
}
if (!ED_ParseEpair((void *) (&ent->v), key, com_token))
Host_Error("ED_ParseEdict: parse error");
}
if (!init)
ent->free = 1;
return data;
}
void ED_LoadFromFile(char *data)
{
edict_t *ent;
int32_t inhibit;
dfunction_t *func;
ent = 0;
inhibit = 0;
pr_global_struct->time = sv.time;
while (1)
{
data = COM_Parse(data);
if (!data)
break;
if (com_token[0] != '{')
Sys_Error("ED_LoadFromFile: found %s when expecting {", com_token);
if (!ent)
ent = EDICT_NUM(0);
else
ent = ED_Alloc();
data = ED_ParseEdict(data, ent);
if (deathmatch.value)
{
if (((int32_t) ent->v.spawnflags) & SPAWNFLAG_NOT_DEATHMATCH)
{
ED_Free(ent);
inhibit++;
continue;
}
}
else
if ((((current_skill == 0) && (((int32_t) ent->v.spawnflags) & SPAWNFLAG_NOT_EASY)) || ((current_skill == 1) && (((int32_t) ent->v.spawnflags) & SPAWNFLAG_NOT_MEDIUM))) || ((current_skill >= 2) && (((int32_t) ent->v.spawnflags) & SPAWNFLAG_NOT_HARD)))
{
ED_Free(ent);
inhibit++;
continue;
}
if (!ent->v.classname)
{
Con_Printf("No classname for:\n");
ED_Print(ent);
ED_Free(ent);
continue;
}
func = ED_FindFunction(pr_strings + ent->v.classname);
if (!func)
{
Con_Printf("No spawn function for:\n");
ED_Print(ent);
ED_Free(ent);
continue;
}
pr_global_struct->self = EDICT_TO_PROG(ent);
PR_ExecuteProgram(func - pr_functions);
}
Con_DPrintf("%i entities inhibited\n", inhibit);
}
void PR_LoadProgs(void)
{
int32_t i;
for (i = 0; i < GEFV_CACHESIZE; i++)
gefvCache[i].field[0] = 0;
CRC_Init(&pr_crc);
progs = (dprograms_t *) COM_LoadHunkFile("progs.dat");
if (!progs)
Sys_Error("PR_LoadProgs: couldn't load progs.dat");
Con_DPrintf("Programs occupy %iK.\n", com_filesize / 1024);
for (i = 0; i < com_filesize; i++)
CRC_ProcessByte(&pr_crc, ((uint8_t *) progs)[i]);
for (i = 0; i < ((sizeof(*progs)) / 4); i++)
((int32_t *) progs)[i] = ((int32_t *) progs)[i];
if (progs->version != PROG_VERSION)
Sys_Error("progs.dat has wrong version number (%i should be %i)", progs->version, PROG_VERSION);
if (progs->crc != PROGHEADER_CRC)
Sys_Error("progs.dat system vars have been modified, progdefs.h is out of date");
pr_functions = (dfunction_t *) (((uint8_t *) progs) + progs->ofs_functions);
pr_strings = ((char *) progs) + progs->ofs_strings;
pr_globaldefs = (ddef_t *) (((uint8_t *) progs) + progs->ofs_globaldefs);
pr_fielddefs = (ddef_t *) (((uint8_t *) progs) + progs->ofs_fielddefs);
pr_statements = (dstatement_t *) (((uint8_t *) progs) + progs->ofs_statements);
pr_global_struct = (globalvars_t *) (((uint8_t *) progs) + progs->ofs_globals);
pr_globals = (float *) pr_global_struct;
pr_edict_size = ((progs->entityfields * 4) + (sizeof(edict_t))) - (sizeof(entvars_t));
for (i = 0; i < progs->numstatements; i++)
{
pr_statements[i].op = pr_statements[i].op;
pr_statements[i].a = pr_statements[i].a;
pr_statements[i].b = pr_statements[i].b;
pr_statements[i].c = pr_statements[i].c;
}
for (i = 0; i < progs->numfunctions; i++)
{
pr_functions[i].first_statement = pr_functions[i].first_statement;
pr_functions[i].parm_start = pr_functions[i].parm_start;
pr_functions[i].s_name = pr_functions[i].s_name;
pr_functions[i].s_file = pr_functions[i].s_file;
pr_functions[i].numparms = pr_functions[i].numparms;
pr_functions[i].locals = pr_functions[i].locals;
}
for (i = 0; i < progs->numglobaldefs; i++)
{
pr_globaldefs[i].type = pr_globaldefs[i].type;
pr_globaldefs[i].ofs = pr_globaldefs[i].ofs;
pr_globaldefs[i].s_name = pr_globaldefs[i].s_name;
}
for (i = 0; i < progs->numfielddefs; i++)
{
pr_fielddefs[i].type = pr_fielddefs[i].type;
if (pr_fielddefs[i].type & DEF_SAVEGLOBAL)
Sys_Error("PR_LoadProgs: pr_fielddefs[i].type & DEF_SAVEGLOBAL");
pr_fielddefs[i].ofs = pr_fielddefs[i].ofs;
pr_fielddefs[i].s_name = pr_fielddefs[i].s_name;
}
for (i = 0; i < progs->numglobals; i++)
((int32_t *) pr_globals)[i] = ((int32_t *) pr_globals)[i];
}
void PR_Init(void)
{
Cmd_AddCommand("edict", ED_PrintEdict_f);
Cmd_AddCommand("edicts", ED_PrintEdicts);
Cmd_AddCommand("edictcount", ED_Count);
Cmd_AddCommand("profile", PR_Profile_f);
Cvar_RegisterVariable(&nomonsters);
Cvar_RegisterVariable(&gamecfg);
Cvar_RegisterVariable(&scratch1);
Cvar_RegisterVariable(&scratch2);
Cvar_RegisterVariable(&scratch3);
Cvar_RegisterVariable(&scratch4);
Cvar_RegisterVariable(&savedgamecfg);
Cvar_RegisterVariable(&saved1);
Cvar_RegisterVariable(&saved2);
Cvar_RegisterVariable(&saved3);
Cvar_RegisterVariable(&saved4);
}
edict_t *EDICT_NUM(int32_t n)
{
if ((n < 0) || (n >= sv.max_edicts))
Sys_Error("EDICT_NUM: bad number %i", n);
return (edict_t *) (((uint8_t *) sv.edicts) + (n * pr_edict_size));
}
int32_t NUM_FOR_EDICT(edict_t *e)
{
int32_t b;
b = ((uint8_t *) e) - ((uint8_t *) sv.edicts);
b = b / pr_edict_size;
if ((b < 0) || (b >= sv.num_edicts))
Sys_Error("NUM_FOR_EDICT: bad pointer");
return b;
}
void PR_PrintStatement(dstatement_t *s)
{
int32_t i;
if (((uint32_t) s->op) < ((sizeof(pr_opnames)) / (sizeof(pr_opnames[0]))))
{
Con_Printf("%s ", pr_opnames[s->op]);
i = strlen(pr_opnames[s->op]);
for (; i < 10; i++)
Con_Printf(" ");
}
if ((s->op == OP_IF) || (s->op == OP_IFNOT))
Con_Printf("%sbranch %i", PR_GlobalString(s->a), s->b);
else
if (s->op == OP_GOTO)
{
Con_Printf("branch %i", s->a);
}
else
if (((uint32_t) (s->op - OP_STORE_F)) < 6)
{
Con_Printf("%s", PR_GlobalString(s->a));
Con_Printf("%s", PR_GlobalStringNoContents(s->b));
}
else
{
if (s->a)
Con_Printf("%s", PR_GlobalString(s->a));
if (s->b)
Con_Printf("%s", PR_GlobalString(s->b));
if (s->c)
Con_Printf("%s", PR_GlobalStringNoContents(s->c));
}
Con_Printf("\n");
}
void PR_StackTrace(void)
{
dfunction_t *f;
int32_t i;
if (pr_depth == 0)
{
Con_Printf("<NO STACK>\n");
return;
}
pr_stack[pr_depth].f = pr_xfunction;
for (i = pr_depth; i >= 0; i--)
{
f = pr_stack[i].f;
if (!f)
{
Con_Printf("<NO FUNCTION>\n");
}
else
Con_Printf("%12s : %s\n", pr_strings + f->s_file, pr_strings + f->s_name);
}
}
void PR_Profile_f(void)
{
dfunction_t *f;
dfunction_t *best;
int32_t max;
int32_t num;
int32_t i;
num = 0;
do
{
max = 0;
best = 0;
for (i = 0; i < progs->numfunctions; i++)
{
f = &pr_functions[i];
if (f->profile > max)
{
max = f->profile;
best = f;
}
}
if (best)
{
if (num < 10)
Con_Printf("%7i %s\n", best->profile, pr_strings + best->s_name);
num++;
best->profile = 0;
}
}
while (best);
}
void PR_RunError(char *error, ...)
{
va_list argptr;
char string[1024];
va_start(argptr, error);
vsprintf(string, error, argptr);
va_end(argptr);
PR_PrintStatement(pr_statements + pr_xstatement);
PR_StackTrace();
Con_Printf("%s\n", string);
pr_depth = 0;
Host_Error("Program error");
}
int32_t PR_EnterFunction(dfunction_t *f)
{
int32_t i;
int32_t j;
int32_t c;
int32_t o;
pr_stack[pr_depth].s = pr_xstatement;
pr_stack[pr_depth].f = pr_xfunction;
pr_depth++;
if (pr_depth >= MAX_STACK_DEPTH)
PR_RunError("stack overflow");
c = f->locals;
if ((localstack_used + c) > LOCALSTACK_SIZE)
PR_RunError("PR_ExecuteProgram: locals stack overflow\n");
for (i = 0; i < c; i++)
localstack[localstack_used + i] = ((int32_t *) pr_globals)[f->parm_start + i];
localstack_used += c;
o = f->parm_start;
for (i = 0; i < f->numparms; i++)
{
for (j = 0; j < f->parm_size[i]; j++)
{
((int32_t *) pr_globals)[o] = ((int32_t *) pr_globals)[(OFS_PARM0 + (i * 3)) + j];
o++;
}
}
pr_xfunction = f;
return f->first_statement - 1;
}
int32_t PR_LeaveFunction(void)
{
int32_t i;
int32_t c;
if (pr_depth <= 0)
Sys_Error("prog stack underflow");
c = pr_xfunction->locals;
localstack_used -= c;
if (localstack_used < 0)
PR_RunError("PR_ExecuteProgram: locals stack underflow\n");
for (i = 0; i < c; i++)
((int32_t *) pr_globals)[pr_xfunction->parm_start + i] = localstack[localstack_used + i];
pr_depth--;
pr_xfunction = pr_stack[pr_depth].f;
return pr_stack[pr_depth].s;
}
void PR_ExecuteProgram(func_t fnum)
{
eval_t *a;
eval_t *b;
eval_t *c;
int32_t s;
dstatement_t *st;
dfunction_t *f;
dfunction_t *newf;
int32_t runaway;
int32_t i;
edict_t *ed;
int32_t exitdepth;
eval_t *ptr;
if ((!fnum) || (fnum >= progs->numfunctions))
{
if (pr_global_struct->self)
ED_Print(PROG_TO_EDICT(pr_global_struct->self));
Host_Error("PR_ExecuteProgram: NULL function");
}
f = &pr_functions[fnum];
runaway = 100000;
pr_trace = 0;
exitdepth = pr_depth;
s = PR_EnterFunction(f);
while (1)
{
s++;
st = &pr_statements[s];
a = (eval_t *) (&pr_globals[st->a]);
b = (eval_t *) (&pr_globals[st->b]);
c = (eval_t *) (&pr_globals[st->c]);
if (!(--runaway))
PR_RunError("runaway loop error");
pr_xfunction->profile++;
pr_xstatement = s;
if (pr_trace)
PR_PrintStatement(st);
switch (st->op)
{
case OP_ADD_F:
c->_float = a->_float + b->_float;
break;
case OP_ADD_V:
c->vector[0] = a->vector[0] + b->vector[0];
c->vector[1] = a->vector[1] + b->vector[1];
c->vector[2] = a->vector[2] + b->vector[2];
break;
case OP_SUB_F:
c->_float = a->_float - b->_float;
break;
case OP_SUB_V:
c->vector[0] = a->vector[0] - b->vector[0];
c->vector[1] = a->vector[1] - b->vector[1];
c->vector[2] = a->vector[2] - b->vector[2];
break;
case OP_MUL_F:
c->_float = a->_float * b->_float;
break;
case OP_MUL_V:
c->_float = ((a->vector[0] * b->vector[0]) + (a->vector[1] * b->vector[1])) + (a->vector[2] * b->vector[2]);
break;
case OP_MUL_FV:
c->vector[0] = a->_float * b->vector[0];
c->vector[1] = a->_float * b->vector[1];
c->vector[2] = a->_float * b->vector[2];
break;
case OP_MUL_VF:
c->vector[0] = b->_float * a->vector[0];
c->vector[1] = b->_float * a->vector[1];
c->vector[2] = b->_float * a->vector[2];
break;
case OP_DIV_F:
c->_float = a->_float / b->_float;
break;
case OP_BITAND:
c->_float = ((int32_t) a->_float) & ((int32_t) b->_float);
break;
case OP_BITOR:
c->_float = ((int32_t) a->_float) | ((int32_t) b->_float);
break;
case OP_GE:
c->_float = a->_float >= b->_float;
break;
case OP_LE:
c->_float = a->_float <= b->_float;
break;
case OP_GT:
c->_float = a->_float > b->_float;
break;
case OP_LT:
c->_float = a->_float < b->_float;
break;
case OP_AND:
c->_float = a->_float && b->_float;
break;
case OP_OR:
c->_float = a->_float || b->_float;
break;
case OP_NOT_F:
c->_float = !a->_float;
break;
case OP_NOT_V:
c->_float = ((!a->vector[0]) && (!a->vector[1])) && (!a->vector[2]);
break;
case OP_NOT_S:
c->_float = (!a->string) || (!pr_strings[a->string]);
break;
case OP_NOT_FNC:
c->_float = !a->function;
break;
case OP_NOT_ENT:
c->_float = PROG_TO_EDICT(a->edict) == sv.edicts;
break;
case OP_EQ_F:
c->_float = a->_float == b->_float;
break;
case OP_EQ_V:
c->_float = ((a->vector[0] == b->vector[0]) && (a->vector[1] == b->vector[1])) && (a->vector[2] == b->vector[2]);
break;
case OP_EQ_S:
c->_float = !strcmp(pr_strings + a->string, pr_strings + b->string);
break;
case OP_EQ_E:
c->_float = a->_int == b->_int;
break;
case OP_EQ_FNC:
c->_float = a->function == b->function;
break;
case OP_NE_F:
c->_float = a->_float != b->_float;
break;
case OP_NE_V:
c->_float = ((a->vector[0] != b->vector[0]) || (a->vector[1] != b->vector[1])) || (a->vector[2] != b->vector[2]);
break;
case OP_NE_S:
c->_float = strcmp(pr_strings + a->string, pr_strings + b->string);
break;
case OP_NE_E:
c->_float = a->_int != b->_int;
break;
case OP_NE_FNC:
c->_float = a->function != b->function;
break;
case OP_STORE_F:
case OP_STORE_ENT:
case OP_STORE_FLD:
case OP_STORE_S:
case OP_STORE_FNC:
b->_int = a->_int;
break;
case OP_STORE_V:
b->vector[0] = a->vector[0];
b->vector[1] = a->vector[1];
b->vector[2] = a->vector[2];
break;
case OP_STOREP_F:
case OP_STOREP_ENT:
case OP_STOREP_FLD:
case OP_STOREP_S:
case OP_STOREP_FNC:
ptr = (eval_t *) (((uint8_t *) sv.edicts) + b->_int);
ptr->_int = a->_int;
break;
case OP_STOREP_V:
ptr = (eval_t *) (((uint8_t *) sv.edicts) + b->_int);
ptr->vector[0] = a->vector[0];
ptr->vector[1] = a->vector[1];
ptr->vector[2] = a->vector[2];
break;
case OP_ADDRESS:
ed = PROG_TO_EDICT(a->edict);
if ((ed == ((edict_t *) sv.edicts)) && (sv.state == ss_active))
PR_RunError("assignment to world entity");
c->_int = ((uint8_t *) (((int32_t *) (&ed->v)) + b->_int)) - ((uint8_t *) sv.edicts);
break;
case OP_LOAD_F:
case OP_LOAD_FLD:
case OP_LOAD_ENT:
case OP_LOAD_S:
case OP_LOAD_FNC:
ed = PROG_TO_EDICT(a->edict);
a = (eval_t *) (((int32_t *) (&ed->v)) + b->_int);
c->_int = a->_int;
break;
case OP_LOAD_V:
ed = PROG_TO_EDICT(a->edict);
a = (eval_t *) (((int32_t *) (&ed->v)) + b->_int);
c->vector[0] = a->vector[0];
c->vector[1] = a->vector[1];
c->vector[2] = a->vector[2];
break;
case OP_IFNOT:
if (!a->_int)
s += st->b - 1;
break;
case OP_IF:
if (a->_int)
s += st->b - 1;
break;
case OP_GOTO:
s += st->a - 1;
break;
case OP_CALL0:
case OP_CALL1:
case OP_CALL2:
case OP_CALL3:
case OP_CALL4:
case OP_CALL5:
case OP_CALL6:
case OP_CALL7:
case OP_CALL8:
pr_argc = st->op - OP_CALL0;
if (!a->function)
PR_RunError("NULL function");
newf = &pr_functions[a->function];
if (newf->first_statement < 0)
{
i = -newf->first_statement;
if (i >= pr_numbuiltins)
PR_RunError("Bad builtin call number");
pr_builtins[i]();
break;
}
s = PR_EnterFunction(newf);
break;
case OP_DONE:
case OP_RETURN:
pr_globals[OFS_RETURN] = pr_globals[st->a];
pr_globals[OFS_RETURN + 1] = pr_globals[st->a + 1];
pr_globals[OFS_RETURN + 2] = pr_globals[st->a + 2];
s = PR_LeaveFunction();
if (pr_depth == exitdepth)
return;
break;
case OP_STATE:
ed = PROG_TO_EDICT(pr_global_struct->self);
ed->v.nextthink = pr_global_struct->time + 0.1;
if (a->_float != ed->v.frame)
{
ed->v.frame = a->_float;
}
ed->v.think = b->function;
break;
default:
PR_RunError("Bad opcode %i", st->op);
}
}
}
void R_Alias_clip_z(finalvert_t *pfv0, finalvert_t *pfv1, finalvert_t *out)
{
float scale;
auxvert_t *pav0;
auxvert_t *pav1;
auxvert_t avout;
pav0 = &av[pfv0 - (&_fv[0][0])];
pav1 = &av[pfv1 - (&_fv[0][0])];
if (pfv0->v[1] >= pfv1->v[1])
{
scale = (ALIAS_Z_CLIP_PLANE - pav0->fv[2]) / (pav1->fv[2] - pav0->fv[2]);
avout.fv[0] = pav0->fv[0] + ((pav1->fv[0] - pav0->fv[0]) * scale);
avout.fv[1] = pav0->fv[1] + ((pav1->fv[1] - pav0->fv[1]) * scale);
avout.fv[2] = ALIAS_Z_CLIP_PLANE;
out->v[2] = pfv0->v[2] + ((pfv1->v[2] - pfv0->v[2]) * scale);
out->v[3] = pfv0->v[3] + ((pfv1->v[3] - pfv0->v[3]) * scale);
out->v[4] = pfv0->v[4] + ((pfv1->v[4] - pfv0->v[4]) * scale);
}
else
{
scale = (ALIAS_Z_CLIP_PLANE - pav1->fv[2]) / (pav0->fv[2] - pav1->fv[2]);
avout.fv[0] = pav1->fv[0] + ((pav0->fv[0] - pav1->fv[0]) * scale);
avout.fv[1] = pav1->fv[1] + ((pav0->fv[1] - pav1->fv[1]) * scale);
avout.fv[2] = ALIAS_Z_CLIP_PLANE;
out->v[2] = pfv1->v[2] + ((pfv0->v[2] - pfv1->v[2]) * scale);
out->v[3] = pfv1->v[3] + ((pfv0->v[3] - pfv1->v[3]) * scale);
out->v[4] = pfv1->v[4] + ((pfv0->v[4] - pfv1->v[4]) * scale);
}
R_AliasProjectFinalVert(out, &avout);
if (out->v[0] < r_refdef.aliasvrect.x)
out->flags |= ALIAS_LEFT_CLIP;
if (out->v[1] < r_refdef.aliasvrect.y)
out->flags |= ALIAS_TOP_CLIP;
if (out->v[0] > r_refdef.aliasvrectright)
out->flags |= ALIAS_RIGHT_CLIP;
if (out->v[1] > r_refdef.aliasvrectbottom)
out->flags |= ALIAS_BOTTOM_CLIP;
}
void R_Alias_clip_left(finalvert_t *pfv0, finalvert_t *pfv1, finalvert_t *out)
{
float scale;
int32_t i;
if (pfv0->v[1] >= pfv1->v[1])
{
scale = ((float) (r_refdef.aliasvrect.x - pfv0->v[0])) / (pfv1->v[0] - pfv0->v[0]);
for (i = 0; i < 6; i++)
out->v[i] = (pfv0->v[i] + ((pfv1->v[i] - pfv0->v[i]) * scale)) + 0.5;
}
else
{
scale = ((float) (r_refdef.aliasvrect.x - pfv1->v[0])) / (pfv0->v[0] - pfv1->v[0]);
for (i = 0; i < 6; i++)
out->v[i] = (pfv1->v[i] + ((pfv0->v[i] - pfv1->v[i]) * scale)) + 0.5;
}
}
void R_Alias_clip_right(finalvert_t *pfv0, finalvert_t *pfv1, finalvert_t *out)
{
float scale;
int32_t i;
if (pfv0->v[1] >= pfv1->v[1])
{
scale = ((float) (r_refdef.aliasvrectright - pfv0->v[0])) / (pfv1->v[0] - pfv0->v[0]);
for (i = 0; i < 6; i++)
out->v[i] = (pfv0->v[i] + ((pfv1->v[i] - pfv0->v[i]) * scale)) + 0.5;
}
else
{
scale = ((float) (r_refdef.aliasvrectright - pfv1->v[0])) / (pfv0->v[0] - pfv1->v[0]);
for (i = 0; i < 6; i++)
out->v[i] = (pfv1->v[i] + ((pfv0->v[i] - pfv1->v[i]) * scale)) + 0.5;
}
}
void R_Alias_clip_top(finalvert_t *pfv0, finalvert_t *pfv1, finalvert_t *out)
{
float scale;
int32_t i;
if (pfv0->v[1] >= pfv1->v[1])
{
scale = ((float) (r_refdef.aliasvrect.y - pfv0->v[1])) / (pfv1->v[1] - pfv0->v[1]);
for (i = 0; i < 6; i++)
out->v[i] = (pfv0->v[i] + ((pfv1->v[i] - pfv0->v[i]) * scale)) + 0.5;
}
else
{
scale = ((float) (r_refdef.aliasvrect.y - pfv1->v[1])) / (pfv0->v[1] - pfv1->v[1]);
for (i = 0; i < 6; i++)
out->v[i] = (pfv1->v[i] + ((pfv0->v[i] - pfv1->v[i]) * scale)) + 0.5;
}
}
void R_Alias_clip_bottom(finalvert_t *pfv0, finalvert_t *pfv1, finalvert_t *out)
{
float scale;
int32_t i;
if (pfv0->v[1] >= pfv1->v[1])
{
scale = ((float) (r_refdef.aliasvrectbottom - pfv0->v[1])) / (pfv1->v[1] - pfv0->v[1]);
for (i = 0; i < 6; i++)
out->v[i] = (pfv0->v[i] + ((pfv1->v[i] - pfv0->v[i]) * scale)) + 0.5;
}
else
{
scale = ((float) (r_refdef.aliasvrectbottom - pfv1->v[1])) / (pfv0->v[1] - pfv1->v[1]);
for (i = 0; i < 6; i++)
out->v[i] = (pfv1->v[i] + ((pfv0->v[i] - pfv1->v[i]) * scale)) + 0.5;
}
}
int32_t R_AliasClip(finalvert_t *in, finalvert_t *out, int32_t flag, int32_t count, void (*clip)(finalvert_t *pfv0, finalvert_t *pfv1, finalvert_t *out))
{
int32_t i;
int32_t j;
int32_t k;
int32_t flags;
int32_t oldflags;
j = count - 1;
k = 0;
for (i = 0; i < count; j = i, i++)
{
oldflags = in[j].flags & flag;
flags = in[i].flags & flag;
if (flags && oldflags)
continue;
if (oldflags ^ flags)
{
clip(&in[j], &in[i], &out[k]);
out[k].flags = 0;
if (out[k].v[0] < r_refdef.aliasvrect.x)
out[k].flags |= ALIAS_LEFT_CLIP;
if (out[k].v[1] < r_refdef.aliasvrect.y)
out[k].flags |= ALIAS_TOP_CLIP;
if (out[k].v[0] > r_refdef.aliasvrectright)
out[k].flags |= ALIAS_RIGHT_CLIP;
if (out[k].v[1] > r_refdef.aliasvrectbottom)
out[k].flags |= ALIAS_BOTTOM_CLIP;
k++;
}
if (!flags)
{
out[k] = in[i];
k++;
}
}
return k;
}
void R_AliasClipTriangle(mtriangle_t *ptri)
{
int32_t i;
int32_t k;
int32_t pingpong;
mtriangle_t mtri;
uint32_t clipflags;
if (ptri->facesfront)
{
_fv[0][0] = pfinalverts[ptri->vertindex[0]];
_fv[0][1] = pfinalverts[ptri->vertindex[1]];
_fv[0][2] = pfinalverts[ptri->vertindex[2]];
}
else
{
for (i = 0; i < 3; i++)
{
_fv[0][i] = pfinalverts[ptri->vertindex[i]];
if ((!ptri->facesfront) && (_fv[0][i].flags & ALIAS_ONSEAM))
_fv[0][i].v[2] += r_affinetridesc.seamfixupX16;
}
}
clipflags = (_fv[0][0].flags | _fv[0][1].flags) | _fv[0][2].flags;
if (clipflags & ALIAS_Z_CLIP)
{
for (i = 0; i < 3; i++)
av[i] = pauxverts[ptri->vertindex[i]];
k = R_AliasClip(_fv[0], _fv[1], ALIAS_Z_CLIP, 3, R_Alias_clip_z);
if (k == 0)
return;
pingpong = 1;
clipflags = (_fv[1][0].flags | _fv[1][1].flags) | _fv[1][2].flags;
}
else
{
pingpong = 0;
k = 3;
}
if (clipflags & ALIAS_LEFT_CLIP)
{
k = R_AliasClip(_fv[pingpong], _fv[pingpong ^ 1], ALIAS_LEFT_CLIP, k, R_Alias_clip_left);
if (k == 0)
return;
pingpong ^= 1;
}
if (clipflags & ALIAS_RIGHT_CLIP)
{
k = R_AliasClip(_fv[pingpong], _fv[pingpong ^ 1], ALIAS_RIGHT_CLIP, k, R_Alias_clip_right);
if (k == 0)
return;
pingpong ^= 1;
}
if (clipflags & ALIAS_BOTTOM_CLIP)
{
k = R_AliasClip(_fv[pingpong], _fv[pingpong ^ 1], ALIAS_BOTTOM_CLIP, k, R_Alias_clip_bottom);
if (k == 0)
return;
pingpong ^= 1;
}
if (clipflags & ALIAS_TOP_CLIP)
{
k = R_AliasClip(_fv[pingpong], _fv[pingpong ^ 1], ALIAS_TOP_CLIP, k, R_Alias_clip_top);
if (k == 0)
return;
pingpong ^= 1;
}
for (i = 0; i < k; i++)
{
if (_fv[pingpong][i].v[0] < r_refdef.aliasvrect.x)
_fv[pingpong][i].v[0] = r_refdef.aliasvrect.x;
else
if (_fv[pingpong][i].v[0] > r_refdef.aliasvrectright)
_fv[pingpong][i].v[0] = r_refdef.aliasvrectright;
if (_fv[pingpong][i].v[1] < r_refdef.aliasvrect.y)
_fv[pingpong][i].v[1] = r_refdef.aliasvrect.y;
else
if (_fv[pingpong][i].v[1] > r_refdef.aliasvrectbottom)
_fv[pingpong][i].v[1] = r_refdef.aliasvrectbottom;
_fv[pingpong][i].flags = 0;
}
mtri.facesfront = ptri->facesfront;
r_affinetridesc.ptriangles = &mtri;
r_affinetridesc.pfinalverts = _fv[pingpong];
mtri.vertindex[0] = 0;
for (i = 1; i < (k - 1); i++)
{
mtri.vertindex[1] = i;
mtri.vertindex[2] = i + 1;
D_PolysetDraw();
}
}
bool R_AliasCheckBBox(void)
{
int32_t i;
int32_t flags;
int32_t frame;
int32_t numv;
aliashdr_t *pahdr;
float zi;
float basepts[8][3];
float v0;
float v1;
float frac;
finalvert_t *pv0;
finalvert_t *pv1;
finalvert_t viewpts[16];
auxvert_t *pa0;
auxvert_t *pa1;
auxvert_t viewaux[16];
maliasframedesc_t *pframedesc;
bool zclipped;
bool zfullyclipped;
uint32_t anyclip;
uint32_t allclip;
int32_t minz;
currententity->trivial_accept = 0;
pmodel = currententity->model;
pahdr = Mod_Extradata(pmodel);
pmdl = (mdl_t *) (((uint8_t *) pahdr) + pahdr->model);
R_AliasSetUpTransform(0);
frame = currententity->frame;
if ((frame >= pmdl->numframes) || (frame < 0))
{
Con_DPrintf("No such frame %d %s\n", frame, pmodel->name);
frame = 0;
}
pframedesc = &pahdr->frames[frame];
basepts[0][0] = (basepts[1][0] = (basepts[2][0] = (basepts[3][0] = (float) pframedesc->bboxmin.v[0])));
basepts[4][0] = (basepts[5][0] = (basepts[6][0] = (basepts[7][0] = (float) pframedesc->bboxmax.v[0])));
basepts[0][1] = (basepts[3][1] = (basepts[5][1] = (basepts[6][1] = (float) pframedesc->bboxmin.v[1])));
basepts[1][1] = (basepts[2][1] = (basepts[4][1] = (basepts[7][1] = (float) pframedesc->bboxmax.v[1])));
basepts[0][2] = (basepts[1][2] = (basepts[4][2] = (basepts[5][2] = (float) pframedesc->bboxmin.v[2])));
basepts[2][2] = (basepts[3][2] = (basepts[6][2] = (basepts[7][2] = (float) pframedesc->bboxmax.v[2])));
zclipped = 0;
zfullyclipped = 1;
minz = 9999;
for (i = 0; i < 8; i++)
{
R_AliasTransformVector(&basepts[i][0], &viewaux[i].fv[0]);
if (viewaux[i].fv[2] < ALIAS_Z_CLIP_PLANE)
{
viewpts[i].flags = ALIAS_Z_CLIP;
zclipped = 1;
}
else
{
if (viewaux[i].fv[2] < minz)
minz = viewaux[i].fv[2];
viewpts[i].flags = 0;
zfullyclipped = 0;
}
}
if (zfullyclipped)
{
return 0;
}
numv = 8;
if (zclipped)
{
for (i = 0; i < 12; i++)
{
pv0 = &viewpts[aedges[i].index0];
pv1 = &viewpts[aedges[i].index1];
pa0 = &viewaux[aedges[i].index0];
pa1 = &viewaux[aedges[i].index1];
if (pv0->flags ^ pv1->flags)
{
frac = (ALIAS_Z_CLIP_PLANE - pa0->fv[2]) / (pa1->fv[2] - pa0->fv[2]);
viewaux[numv].fv[0] = pa0->fv[0] + ((pa1->fv[0] - pa0->fv[0]) * frac);
viewaux[numv].fv[1] = pa0->fv[1] + ((pa1->fv[1] - pa0->fv[1]) * frac);
viewaux[numv].fv[2] = ALIAS_Z_CLIP_PLANE;
viewpts[numv].flags = 0;
numv++;
}
}
}
anyclip = 0;
allclip = ALIAS_XY_CLIP_MASK;
for (i = 0; i < numv; i++)
{
if (viewpts[i].flags & ALIAS_Z_CLIP)
continue;
zi = 1.0 / viewaux[i].fv[2];
v0 = ((viewaux[i].fv[0] * xscale) * zi) + xcenter;
v1 = ((viewaux[i].fv[1] * yscale) * zi) + ycenter;
flags = 0;
if (v0 < r_refdef.fvrectx)
flags |= ALIAS_LEFT_CLIP;
if (v1 < r_refdef.fvrecty)
flags |= ALIAS_TOP_CLIP;
if (v0 > r_refdef.fvrectright)
flags |= ALIAS_RIGHT_CLIP;
if (v1 > r_refdef.fvrectbottom)
flags |= ALIAS_BOTTOM_CLIP;
anyclip |= flags;
allclip &= flags;
}
if (allclip)
return 0;
currententity->trivial_accept = (!anyclip) & (!zclipped);
if (currententity->trivial_accept)
{
if (minz > (r_aliastransition + (pmdl->size * r_resfudge)))
{
currententity->trivial_accept |= 2;
}
}
return 1;
}
void R_AliasTransformVector(vec3_t in, vec3_t out)
{
out[0] = DotProduct(in, aliastransform[0]) + aliastransform[0][3];
out[1] = DotProduct(in, aliastransform[1]) + aliastransform[1][3];
out[2] = DotProduct(in, aliastransform[2]) + aliastransform[2][3];
}
void R_AliasPreparePoints(void)
{
int32_t i;
stvert_t *pstverts;
finalvert_t *fv;
auxvert_t *av;
mtriangle_t *ptri;
finalvert_t *pfv[3];
pstverts = (stvert_t *) (((uint8_t *) paliashdr) + paliashdr->stverts);
r_anumverts = pmdl->numverts;
fv = pfinalverts;
av = pauxverts;
for (i = 0; i < r_anumverts; i++, fv++, av++, r_apverts++, pstverts++)
{
R_AliasTransformFinalVert(fv, av, r_apverts, pstverts);
if (av->fv[2] < ALIAS_Z_CLIP_PLANE)
fv->flags |= ALIAS_Z_CLIP;
else
{
R_AliasProjectFinalVert(fv, av);
if (fv->v[0] < r_refdef.aliasvrect.x)
fv->flags |= ALIAS_LEFT_CLIP;
if (fv->v[1] < r_refdef.aliasvrect.y)
fv->flags |= ALIAS_TOP_CLIP;
if (fv->v[0] > r_refdef.aliasvrectright)
fv->flags |= ALIAS_RIGHT_CLIP;
if (fv->v[1] > r_refdef.aliasvrectbottom)
fv->flags |= ALIAS_BOTTOM_CLIP;
}
}
r_affinetridesc.numtriangles = 1;
ptri = (mtriangle_t *) (((uint8_t *) paliashdr) + paliashdr->triangles);
for (i = 0; i < pmdl->numtris; i++, ptri++)
{
pfv[0] = &pfinalverts[ptri->vertindex[0]];
pfv[1] = &pfinalverts[ptri->vertindex[1]];
pfv[2] = &pfinalverts[ptri->vertindex[2]];
if (((pfv[0]->flags & pfv[1]->flags) & pfv[2]->flags) & (ALIAS_XY_CLIP_MASK | ALIAS_Z_CLIP))
continue;
if (!(((pfv[0]->flags | pfv[1]->flags) | pfv[2]->flags) & (ALIAS_XY_CLIP_MASK | ALIAS_Z_CLIP)))
{
r_affinetridesc.pfinalverts = pfinalverts;
r_affinetridesc.ptriangles = ptri;
D_PolysetDraw();
}
else
{
R_AliasClipTriangle(ptri);
}
}
}
void R_AliasSetUpTransform(int32_t trivial_accept)
{
int32_t i;
float rotationmatrix[3][4];
float t2matrix[3][4];
static float tmatrix[3][4];
static float viewmatrix[3][4];
vec3_t angles;
angles[ROLL] = currententity->angles[ROLL];
angles[PITCH] = -currententity->angles[PITCH];
angles[YAW] = currententity->angles[YAW];
AngleVectors(angles, alias_forward, alias_right, alias_up);
tmatrix[0][0] = pmdl->scale[0];
tmatrix[1][1] = pmdl->scale[1];
tmatrix[2][2] = pmdl->scale[2];
tmatrix[0][3] = pmdl->scale_origin[0];
tmatrix[1][3] = pmdl->scale_origin[1];
tmatrix[2][3] = pmdl->scale_origin[2];
for (i = 0; i < 3; i++)
{
t2matrix[i][0] = alias_forward[i];
t2matrix[i][1] = -alias_right[i];
t2matrix[i][2] = alias_up[i];
}
t2matrix[0][3] = -modelorg[0];
t2matrix[1][3] = -modelorg[1];
t2matrix[2][3] = -modelorg[2];
R_ConcatTransforms(t2matrix, tmatrix, rotationmatrix);
VectorCopy(vright, viewmatrix[0]);
VectorCopy(vup, viewmatrix[1]);
VectorInverse(viewmatrix[1]);
VectorCopy(vpn, viewmatrix[2]);
R_ConcatTransforms(viewmatrix, rotationmatrix, aliastransform);
if (trivial_accept)
{
for (i = 0; i < 4; i++)
{
aliastransform[0][i] *= aliasxscale * (1.0 / (((float) 0x8000) * 0x10000));
aliastransform[1][i] *= aliasyscale * (1.0 / (((float) 0x8000) * 0x10000));
aliastransform[2][i] *= 1.0 / (((float) 0x8000) * 0x10000);
}
}
}
void R_AliasTransformFinalVert(finalvert_t *fv, auxvert_t *av, trivertx_t *pverts, stvert_t *pstverts)
{
int32_t temp;
float lightcos;
float *plightnormal;
av->fv[0] = DotProduct(pverts->v, aliastransform[0]) + aliastransform[0][3];
av->fv[1] = DotProduct(pverts->v, aliastransform[1]) + aliastransform[1][3];
av->fv[2] = DotProduct(pverts->v, aliastransform[2]) + aliastransform[2][3];
fv->v[2] = pstverts->s;
fv->v[3] = pstverts->t;
fv->flags = pstverts->onseam;
plightnormal = r_avertexnormals[pverts->lightnormalindex];
lightcos = DotProduct(plightnormal, r_plightvec);
temp = r_ambientlight;
if (lightcos < 0)
{
temp += (int32_t) (r_shadelight * lightcos);
if (temp < 0)
temp = 0;
}
fv->v[4] = temp;
}
void R_AliasTransformAndProjectFinalVerts(finalvert_t *fv, stvert_t *pstverts)
{
int32_t i;
int32_t temp;
float lightcos;
float *plightnormal;
float zi;
trivertx_t *pverts;
pverts = r_apverts;
for (i = 0; i < r_anumverts; i++, fv++, pverts++, pstverts++)
{
zi = 1.0 / (DotProduct(pverts->v, aliastransform[2]) + aliastransform[2][3]);
fv->v[5] = zi;
fv->v[0] = ((DotProduct(pverts->v, aliastransform[0]) + aliastransform[0][3]) * zi) + aliasxcenter;
fv->v[1] = ((DotProduct(pverts->v, aliastransform[1]) + aliastransform[1][3]) * zi) + aliasycenter;
fv->v[2] = pstverts->s;
fv->v[3] = pstverts->t;
fv->flags = pstverts->onseam;
plightnormal = r_avertexnormals[pverts->lightnormalindex];
lightcos = DotProduct(plightnormal, r_plightvec);
temp = r_ambientlight;
if (lightcos < 0)
{
temp += (int32_t) (r_shadelight * lightcos);
if (temp < 0)
temp = 0;
}
fv->v[4] = temp;
}
}
void R_AliasProjectFinalVert(finalvert_t *fv, auxvert_t *av)
{
float zi;
zi = 1.0 / av->fv[2];
fv->v[5] = zi * ziscale;
fv->v[0] = ((av->fv[0] * aliasxscale) * zi) + aliasxcenter;
fv->v[1] = ((av->fv[1] * aliasyscale) * zi) + aliasycenter;
}
void R_AliasPrepareUnclippedPoints(void)
{
stvert_t *pstverts;
finalvert_t *fv;
pstverts = (stvert_t *) (((uint8_t *) paliashdr) + paliashdr->stverts);
r_anumverts = pmdl->numverts;
fv = pfinalverts;
R_AliasTransformAndProjectFinalVerts(fv, pstverts);
if (r_affinetridesc.drawtype)
D_PolysetDrawFinalVerts(fv, r_anumverts);
r_affinetridesc.pfinalverts = pfinalverts;
r_affinetridesc.ptriangles = (mtriangle_t *) (((uint8_t *) paliashdr) + paliashdr->triangles);
r_affinetridesc.numtriangles = pmdl->numtris;
D_PolysetDraw();
}
void R_AliasSetupSkin(void)
{
int32_t skinnum;
int32_t i;
int32_t numskins;
maliasskingroup_t *paliasskingroup;
float *pskinintervals;
float fullskininterval;
float skintargettime;
float skintime;
skinnum = currententity->skinnum;
if ((skinnum >= pmdl->numskins) || (skinnum < 0))
{
Con_DPrintf("R_AliasSetupSkin: no such skin # %d\n", skinnum);
skinnum = 0;
}
pskindesc = ((maliasskindesc_t *) (((uint8_t *) paliashdr) + paliashdr->skindesc)) + skinnum;
a_skinwidth = pmdl->skinwidth;
if (pskindesc->type == ALIAS_SKIN_GROUP)
{
paliasskingroup = (maliasskingroup_t *) (((uint8_t *) paliashdr) + pskindesc->skin);
pskinintervals = (float *) (((uint8_t *) paliashdr) + paliasskingroup->intervals);
numskins = paliasskingroup->numskins;
fullskininterval = pskinintervals[numskins - 1];
skintime = cl.time + currententity->syncbase;
skintargettime = skintime - (((int32_t) (skintime / fullskininterval)) * fullskininterval);
for (i = 0; i < (numskins - 1); i++)
{
if (pskinintervals[i] > skintargettime)
break;
}
pskindesc = &paliasskingroup->skindescs[i];
}
r_affinetridesc.pskindesc = pskindesc;
r_affinetridesc.pskin = (void *) (((uint8_t *) paliashdr) + pskindesc->skin);
r_affinetridesc.skinwidth = a_skinwidth;
r_affinetridesc.seamfixupX16 = (a_skinwidth >> 1) << 16;
r_affinetridesc.skinheight = pmdl->skinheight;
}
void R_AliasSetupLighting(alight_t *plighting)
{
r_ambientlight = plighting->ambientlight;
if (r_ambientlight < LIGHT_MIN)
r_ambientlight = LIGHT_MIN;
r_ambientlight = (255 - r_ambientlight) << VID_CBITS;
if (r_ambientlight < LIGHT_MIN)
r_ambientlight = LIGHT_MIN;
r_shadelight = plighting->shadelight;
if (r_shadelight < 0)
r_shadelight = 0;
r_shadelight *= VID_GRADES;
r_plightvec[0] = DotProduct(plighting->plightvec, alias_forward);
r_plightvec[1] = -DotProduct(plighting->plightvec, alias_right);
r_plightvec[2] = DotProduct(plighting->plightvec, alias_up);
}
void R_AliasSetupFrame(void)
{
int32_t frame;
int32_t i;
int32_t numframes;
maliasgroup_t *paliasgroup;
float *pintervals;
float fullinterval;
float targettime;
float time;
frame = currententity->frame;
if ((frame >= pmdl->numframes) || (frame < 0))
{
Con_DPrintf("R_AliasSetupFrame: no such frame %d\n", frame);
frame = 0;
}
if (paliashdr->frames[frame].type == ALIAS_SINGLE)
{
r_apverts = (trivertx_t *) (((uint8_t *) paliashdr) + paliashdr->frames[frame].frame);
return;
}
paliasgroup = (maliasgroup_t *) (((uint8_t *) paliashdr) + paliashdr->frames[frame].frame);
pintervals = (float *) (((uint8_t *) paliashdr) + paliasgroup->intervals);
numframes = paliasgroup->numframes;
fullinterval = pintervals[numframes - 1];
time = cl.time + currententity->syncbase;
targettime = time - (((int32_t) (time / fullinterval)) * fullinterval);
for (i = 0; i < (numframes - 1); i++)
{
if (pintervals[i] > targettime)
break;
}
r_apverts = (trivertx_t *) (((uint8_t *) paliashdr) + paliasgroup->frames[i].frame);
}
void R_AliasDrawModel(alight_t *plighting)
{
finalvert_t finalverts[(MAXALIASVERTS + ((CACHE_SIZE - 1) / (sizeof(finalvert_t)))) + 1];
auxvert_t auxverts[MAXALIASVERTS];
r_amodels_drawn++;
pfinalverts = (finalvert_t *) ALIGN_PTR(&finalverts[0], CACHE_SIZE);
pauxverts = &auxverts[0];
paliashdr = (aliashdr_t *) Mod_Extradata(currententity->model);
pmdl = (mdl_t *) (((uint8_t *) paliashdr) + paliashdr->model);
R_AliasSetupSkin();
R_AliasSetUpTransform(currententity->trivial_accept);
R_AliasSetupLighting(plighting);
R_AliasSetupFrame();
if (!currententity->colormap)
Sys_Error("R_AliasDrawModel: !currententity->colormap");
r_affinetridesc.drawtype = (currententity->trivial_accept == 3) && r_recursiveaffinetriangles;
if (r_affinetridesc.drawtype)
{
D_PolysetUpdateTables();
}
acolormap = currententity->colormap;
if (currententity != (&cl.viewent))
ziscale = ((float) 0x8000) * ((float) 0x10000);
else
ziscale = (((float) 0x8000) * ((float) 0x10000)) * 3.0;
if (currententity->trivial_accept)
R_AliasPrepareUnclippedPoints();
else
R_AliasPreparePoints();
}
void R_EntityRotate(vec3_t vec)
{
vec3_t tvec;
VectorCopy(vec, tvec);
vec[0] = DotProduct(entity_rotation[0], tvec);
vec[1] = DotProduct(entity_rotation[1], tvec);
vec[2] = DotProduct(entity_rotation[2], tvec);
}
void R_RotateBmodel(void)
{
float angle;
float s;
float c;
float temp1[3][3];
float temp2[3][3];
float temp3[3][3];
angle = currententity->angles[YAW];
angle = ((angle * M_PI) * 2) / 360;
s = sin(angle);
c = cos(angle);
temp1[0][0] = c;
temp1[0][1] = s;
temp1[0][2] = 0;
temp1[1][0] = -s;
temp1[1][1] = c;
temp1[1][2] = 0;
temp1[2][0] = 0;
temp1[2][1] = 0;
temp1[2][2] = 1;
angle = currententity->angles[PITCH];
angle = ((angle * M_PI) * 2) / 360;
s = sin(angle);
c = cos(angle);
temp2[0][0] = c;
temp2[0][1] = 0;
temp2[0][2] = -s;
temp2[1][0] = 0;
temp2[1][1] = 1;
temp2[1][2] = 0;
temp2[2][0] = s;
temp2[2][1] = 0;
temp2[2][2] = c;
R_ConcatRotations(temp2, temp1, temp3);
angle = currententity->angles[ROLL];
angle = ((angle * M_PI) * 2) / 360;
s = sin(angle);
c = cos(angle);
temp1[0][0] = 1;
temp1[0][1] = 0;
temp1[0][2] = 0;
temp1[1][0] = 0;
temp1[1][1] = c;
temp1[1][2] = s;
temp1[2][0] = 0;
temp1[2][1] = -s;
temp1[2][2] = c;
R_ConcatRotations(temp1, temp3, entity_rotation);
R_EntityRotate(modelorg);
R_EntityRotate(vpn);
R_EntityRotate(vright);
R_EntityRotate(vup);
R_TransformFrustum();
}
void R_RecursiveClipBPoly(bedge_t *pedges, mnode_t *pnode, msurface_t *psurf)
{
bedge_t *psideedges[2];
bedge_t *pnextedge;
bedge_t *ptedge;
int32_t i;
int32_t side;
int32_t lastside;
float dist;
float frac;
float lastdist;
mplane_t *splitplane;
mplane_t tplane;
mvertex_t *pvert;
mvertex_t *plastvert;
mvertex_t *ptvert;
mnode_t *pn;
psideedges[0] = (psideedges[1] = 0);
makeclippededge = 0;
splitplane = pnode->plane;
tplane.dist = splitplane->dist - DotProduct(r_entorigin, splitplane->normal);
tplane.normal[0] = DotProduct(entity_rotation[0], splitplane->normal);
tplane.normal[1] = DotProduct(entity_rotation[1], splitplane->normal);
tplane.normal[2] = DotProduct(entity_rotation[2], splitplane->normal);
for (; pedges; pedges = pnextedge)
{
pnextedge = pedges->pnext;
plastvert = pedges->v[0];
lastdist = DotProduct(plastvert->position, tplane.normal) - tplane.dist;
if (lastdist > 0)
lastside = 0;
else
lastside = 1;
pvert = pedges->v[1];
dist = DotProduct(pvert->position, tplane.normal) - tplane.dist;
if (dist > 0)
side = 0;
else
side = 1;
if (side != lastside)
{
if (numbverts >= MAX_BMODEL_VERTS)
return;
frac = lastdist / (lastdist - dist);
ptvert = &pbverts[numbverts++];
ptvert->position[0] = plastvert->position[0] + (frac * (pvert->position[0] - plastvert->position[0]));
ptvert->position[1] = plastvert->position[1] + (frac * (pvert->position[1] - plastvert->position[1]));
ptvert->position[2] = plastvert->position[2] + (frac * (pvert->position[2] - plastvert->position[2]));
if (numbedges >= (MAX_BMODEL_EDGES - 1))
{
Con_Printf("Out of edges for bmodel\n");
return;
}
ptedge = &pbedges[numbedges];
ptedge->pnext = psideedges[lastside];
psideedges[lastside] = ptedge;
ptedge->v[0] = plastvert;
ptedge->v[1] = ptvert;
ptedge = &pbedges[numbedges + 1];
ptedge->pnext = psideedges[side];
psideedges[side] = ptedge;
ptedge->v[0] = ptvert;
ptedge->v[1] = pvert;
numbedges += 2;
if (side == 0)
{
pfrontenter = ptvert;
makeclippededge = 1;
}
else
{
pfrontexit = ptvert;
makeclippededge = 1;
}
}
else
{
pedges->pnext = psideedges[side];
psideedges[side] = pedges;
}
}
if (makeclippededge)
{
if (numbedges >= (MAX_BMODEL_EDGES - 2))
{
Con_Printf("Out of edges for bmodel\n");
return;
}
ptedge = &pbedges[numbedges];
ptedge->pnext = psideedges[0];
psideedges[0] = ptedge;
ptedge->v[0] = pfrontexit;
ptedge->v[1] = pfrontenter;
ptedge = &pbedges[numbedges + 1];
ptedge->pnext = psideedges[1];
psideedges[1] = ptedge;
ptedge->v[0] = pfrontenter;
ptedge->v[1] = pfrontexit;
numbedges += 2;
}
for (i = 0; i < 2; i++)
{
if (psideedges[i])
{
pn = pnode->children[i];
if (pn->visframe == r_visframecount)
{
if (pn->contents < 0)
{
if (pn->contents != CONTENTS_SOLID)
{
r_currentbkey = ((mleaf_t *) pn)->key;
R_RenderBmodelFace(psideedges[i], psurf);
}
}
else
{
R_RecursiveClipBPoly(psideedges[i], pnode->children[i], psurf);
}
}
}
}
}
void R_DrawSolidClippedSubmodelPolygons(model_t *pmodel)
{
int32_t i;
int32_t j;
int32_t lindex;
vec_t dot;
msurface_t *psurf;
int32_t numsurfaces;
mplane_t *pplane;
mvertex_t bverts[MAX_BMODEL_VERTS];
bedge_t bedges[MAX_BMODEL_EDGES];
bedge_t *pbedge;
medge_t *pedge;
medge_t *pedges;
psurf = &pmodel->surfaces[pmodel->firstmodelsurface];
numsurfaces = pmodel->nummodelsurfaces;
pedges = pmodel->edges;
for (i = 0; i < numsurfaces; i++, psurf++)
{
pplane = psurf->plane;
dot = DotProduct(modelorg, pplane->normal) - pplane->dist;
if (((psurf->flags & SURF_PLANEBACK) && (dot < (-BACKFACE_EPSILON))) || ((!(psurf->flags & SURF_PLANEBACK)) && (dot > BACKFACE_EPSILON)))
{
pbverts = bverts;
pbedges = bedges;
numbverts = (numbedges = 0);
if (psurf->numedges > 0)
{
pbedge = &bedges[numbedges];
numbedges += psurf->numedges;
for (j = 0; j < psurf->numedges; j++)
{
lindex = pmodel->surfedges[psurf->firstedge + j];
if (lindex > 0)
{
pedge = &pedges[lindex];
pbedge[j].v[0] = &r_pcurrentvertbase[pedge->v[0]];
pbedge[j].v[1] = &r_pcurrentvertbase[pedge->v[1]];
}
else
{
lindex = -lindex;
pedge = &pedges[lindex];
pbedge[j].v[0] = &r_pcurrentvertbase[pedge->v[1]];
pbedge[j].v[1] = &r_pcurrentvertbase[pedge->v[0]];
}
pbedge[j].pnext = &pbedge[j + 1];
}
pbedge[j - 1].pnext = 0;
R_RecursiveClipBPoly(pbedge, currententity->topnode, psurf);
}
else
{
Sys_Error("no edges in bmodel");
}
}
}
}
void R_DrawSubmodelPolygons(model_t *pmodel, int32_t clipflags)
{
int32_t i;
vec_t dot;
msurface_t *psurf;
int32_t numsurfaces;
mplane_t *pplane;
psurf = &pmodel->surfaces[pmodel->firstmodelsurface];
numsurfaces = pmodel->nummodelsurfaces;
for (i = 0; i < numsurfaces; i++, psurf++)
{
pplane = psurf->plane;
dot = DotProduct(modelorg, pplane->normal) - pplane->dist;
if (((psurf->flags & SURF_PLANEBACK) && (dot < (-BACKFACE_EPSILON))) || ((!(psurf->flags & SURF_PLANEBACK)) && (dot > BACKFACE_EPSILON)))
{
r_currentkey = ((mleaf_t *) currententity->topnode)->key;
R_RenderFace(psurf, clipflags);
}
}
}
void R_RecursiveWorldNode(mnode_t *node, int32_t clipflags)
{
int32_t i;
int32_t c;
int32_t side;
int32_t *pindex;
vec3_t acceptpt;
vec3_t rejectpt;
mplane_t *plane;
msurface_t *surf;
msurface_t **mark;
mleaf_t *pleaf;
double d;
double dot;
if (node->contents == CONTENTS_SOLID)
return;
if (node->visframe != r_visframecount)
return;
if (clipflags)
{
for (i = 0; i < 4; i++)
{
if (!(clipflags & (1 << i)))
continue;
pindex = pfrustum_indexes[i];
rejectpt[0] = (float) node->minmaxs[pindex[0]];
rejectpt[1] = (float) node->minmaxs[pindex[1]];
rejectpt[2] = (float) node->minmaxs[pindex[2]];
d = DotProduct(rejectpt, view_clipplanes[i].normal);
d -= view_clipplanes[i].dist;
if (d <= 0)
return;
acceptpt[0] = (float) node->minmaxs[pindex[3 + 0]];
acceptpt[1] = (float) node->minmaxs[pindex[3 + 1]];
acceptpt[2] = (float) node->minmaxs[pindex[3 + 2]];
d = DotProduct(acceptpt, view_clipplanes[i].normal);
d -= view_clipplanes[i].dist;
if (d >= 0)
clipflags &= ~(1 << i);
}
}
if (node->contents < 0)
{
pleaf = (mleaf_t *) node;
mark = pleaf->firstmarksurface;
c = pleaf->nummarksurfaces;
if (c)
{
do
{
(*mark)->visframe = r_framecount;
mark++;
}
while (--c);
}
if (pleaf->efrags)
{
R_StoreEfrags(&pleaf->efrags);
}
pleaf->key = r_currentkey;
r_currentkey++;
}
else
{
plane = node->plane;
switch (plane->type)
{
case PLANE_X:
dot = modelorg[0] - plane->dist;
break;
case PLANE_Y:
dot = modelorg[1] - plane->dist;
break;
case PLANE_Z:
dot = modelorg[2] - plane->dist;
break;
default:
dot = DotProduct(modelorg, plane->normal) - plane->dist;
break;
}
if (dot >= 0)
side = 0;
else
side = 1;
R_RecursiveWorldNode(node->children[side], clipflags);
c = node->numsurfaces;
if (c)
{
surf = cl.worldmodel->surfaces + node->firstsurface;
if (dot < (-BACKFACE_EPSILON))
{
do
{
if ((surf->flags & SURF_PLANEBACK) && (surf->visframe == r_framecount))
{
if (r_drawpolys)
{
if (r_worldpolysbacktofront)
{
if (numbtofpolys < MAX_BTOFPOLYS)
{
pbtofpolys[numbtofpolys].clipflags = clipflags;
pbtofpolys[numbtofpolys].psurf = surf;
numbtofpolys++;
}
}
else
{
R_RenderPoly(surf, clipflags);
}
}
else
{
R_RenderFace(surf, clipflags);
}
}
surf++;
}
while (--c);
}
else
if (dot > BACKFACE_EPSILON)
{
do
{
if ((!(surf->flags & SURF_PLANEBACK)) && (surf->visframe == r_framecount))
{
if (r_drawpolys)
{
if (r_worldpolysbacktofront)
{
if (numbtofpolys < MAX_BTOFPOLYS)
{
pbtofpolys[numbtofpolys].clipflags = clipflags;
pbtofpolys[numbtofpolys].psurf = surf;
numbtofpolys++;
}
}
else
{
R_RenderPoly(surf, clipflags);
}
}
else
{
R_RenderFace(surf, clipflags);
}
}
surf++;
}
while (--c);
}
r_currentkey++;
}
R_RecursiveWorldNode(node->children[!side], clipflags);
}
}
void R_RenderWorld(void)
{
int32_t i;
model_t *clmodel;
btofpoly_t btofpolys[MAX_BTOFPOLYS];
pbtofpolys = btofpolys;
currententity = &cl_entities[0];
VectorCopy(r_origin, modelorg);
clmodel = currententity->model;
r_pcurrentvertbase = clmodel->vertexes;
R_RecursiveWorldNode(clmodel->nodes, 15);
if (r_worldpolysbacktofront)
{
for (i = numbtofpolys - 1; i >= 0; i--)
{
R_RenderPoly(btofpolys[i].psurf, btofpolys[i].clipflags);
}
}
}
void R_EmitEdge(mvertex_t *pv0, mvertex_t *pv1)
{
edge_t *edge;
edge_t *pcheck;
int32_t u_check;
float u;
float u_step;
vec3_t local;
vec3_t transformed;
float *world;
int32_t v;
int32_t v2;
int32_t ceilv0;
float scale;
float lzi0;
float u0;
float v0;
int32_t side;
if (r_lastvertvalid)
{
u0 = r_u1;
v0 = r_v1;
lzi0 = r_lzi1;
ceilv0 = r_ceilv1;
}
else
{
world = &pv0->position[0];
VectorSubtract(world, modelorg, local);
TransformVector(local, transformed);
if (transformed[2] < NEAR_CLIP)
transformed[2] = NEAR_CLIP;
lzi0 = 1.0 / transformed[2];
scale = xscale * lzi0;
u0 = xcenter + (scale * transformed[0]);
if (u0 < r_refdef.fvrectx_adj)
u0 = r_refdef.fvrectx_adj;
if (u0 > r_refdef.fvrectright_adj)
u0 = r_refdef.fvrectright_adj;
scale = yscale * lzi0;
v0 = ycenter - (scale * transformed[1]);
if (v0 < r_refdef.fvrecty_adj)
v0 = r_refdef.fvrecty_adj;
if (v0 > r_refdef.fvrectbottom_adj)
v0 = r_refdef.fvrectbottom_adj;
ceilv0 = (int32_t) ceil(v0);
}
world = &pv1->position[0];
VectorSubtract(world, modelorg, local);
TransformVector(local, transformed);
if (transformed[2] < NEAR_CLIP)
transformed[2] = NEAR_CLIP;
r_lzi1 = 1.0 / transformed[2];
scale = xscale * r_lzi1;
r_u1 = xcenter + (scale * transformed[0]);
if (r_u1 < r_refdef.fvrectx_adj)
r_u1 = r_refdef.fvrectx_adj;
if (r_u1 > r_refdef.fvrectright_adj)
r_u1 = r_refdef.fvrectright_adj;
scale = yscale * r_lzi1;
r_v1 = ycenter - (scale * transformed[1]);
if (r_v1 < r_refdef.fvrecty_adj)
r_v1 = r_refdef.fvrecty_adj;
if (r_v1 > r_refdef.fvrectbottom_adj)
r_v1 = r_refdef.fvrectbottom_adj;
if (r_lzi1 > lzi0)
lzi0 = r_lzi1;
if (lzi0 > r_nearzi)
r_nearzi = lzi0;
if (r_nearzionly)
return;
r_emitted = 1;
r_ceilv1 = (int32_t) ceil(r_v1);
if (ceilv0 == r_ceilv1)
{
if (cacheoffset != 0x7FFFFFFF)
{
cacheoffset = FULLY_CLIPPED_CACHED | (r_framecount & FRAMECOUNT_MASK);
}
return;
}
side = ceilv0 > r_ceilv1;
edge = edge_p++;
edge->owner = r_pedge;
edge->nearzi = lzi0;
if (side == 0)
{
v = ceilv0;
v2 = r_ceilv1 - 1;
edge->surfs[0] = surface_p - surfaces;
edge->surfs[1] = 0;
u_step = (r_u1 - u0) / (r_v1 - v0);
u = u0 + ((((float) v) - v0) * u_step);
}
else
{
v2 = ceilv0 - 1;
v = r_ceilv1;
edge->surfs[0] = 0;
edge->surfs[1] = surface_p - surfaces;
u_step = (u0 - r_u1) / (v0 - r_v1);
u = r_u1 + ((((float) v) - r_v1) * u_step);
}
edge->u_step = u_step * 0x100000;
edge->u = (u * 0x100000) + 0xFFFFF;
if (edge->u < r_refdef.vrect_x_adj_shift20)
edge->u = r_refdef.vrect_x_adj_shift20;
if (edge->u > r_refdef.vrectright_adj_shift20)
edge->u = r_refdef.vrectright_adj_shift20;
u_check = edge->u;
if (edge->surfs[0])
u_check++;
if ((!newedges[v]) || (newedges[v]->u >= u_check))
{
edge->next = newedges[v];
newedges[v] = edge;
}
else
{
pcheck = newedges[v];
while (pcheck->next && (pcheck->next->u < u_check))
pcheck = pcheck->next;
edge->next = pcheck->next;
pcheck->next = edge;
}
edge->nextremove = removeedges[v2];
removeedges[v2] = edge;
}
void R_ClipEdge(mvertex_t *pv0, mvertex_t *pv1, clipplane_t *clip)
{
float d0;
float d1;
float f;
mvertex_t clipvert;
if (clip)
{
do
{
d0 = DotProduct(pv0->position, clip->normal) - clip->dist;
d1 = DotProduct(pv1->position, clip->normal) - clip->dist;
if (d0 >= 0)
{
if (d1 >= 0)
{
continue;
}
cacheoffset = 0x7FFFFFFF;
f = d0 / (d0 - d1);
clipvert.position[0] = pv0->position[0] + (f * (pv1->position[0] - pv0->position[0]));
clipvert.position[1] = pv0->position[1] + (f * (pv1->position[1] - pv0->position[1]));
clipvert.position[2] = pv0->position[2] + (f * (pv1->position[2] - pv0->position[2]));
if (clip->leftedge)
{
r_leftclipped = 1;
r_leftexit = clipvert;
}
else
if (clip->rightedge)
{
r_rightclipped = 1;
r_rightexit = clipvert;
}
R_ClipEdge(pv0, &clipvert, clip->next);
return;
}
else
{
if (d1 < 0)
{
if (!r_leftclipped)
cacheoffset = FULLY_CLIPPED_CACHED | (r_framecount & FRAMECOUNT_MASK);
return;
}
r_lastvertvalid = 0;
cacheoffset = 0x7FFFFFFF;
f = d0 / (d0 - d1);
clipvert.position[0] = pv0->position[0] + (f * (pv1->position[0] - pv0->position[0]));
clipvert.position[1] = pv0->position[1] + (f * (pv1->position[1] - pv0->position[1]));
clipvert.position[2] = pv0->position[2] + (f * (pv1->position[2] - pv0->position[2]));
if (clip->leftedge)
{
r_leftclipped = 1;
r_leftenter = clipvert;
}
else
if (clip->rightedge)
{
r_rightclipped = 1;
r_rightenter = clipvert;
}
R_ClipEdge(&clipvert, pv1, clip->next);
return;
}
}
while ((clip = clip->next) != 0);
}
R_EmitEdge(pv0, pv1);
}
void R_EmitCachedEdge(void)
{
edge_t *pedge_t;
pedge_t = (edge_t *) (((uint8_t *) r_edges) + r_pedge->cachededgeoffset);
if (!pedge_t->surfs[0])
pedge_t->surfs[0] = (int32_t) (surface_p - surfaces);
else
pedge_t->surfs[1] = (int32_t) (surface_p - surfaces);
if (pedge_t->nearzi > r_nearzi)
r_nearzi = pedge_t->nearzi;
r_emitted = 1;
}
void R_RenderFace(msurface_t *fa, int32_t clipflags)
{
int32_t i;
int32_t lindex;
uint32_t mask;
mplane_t *pplane;
float distinv;
vec3_t p_normal;
medge_t *pedges;
medge_t tedge;
clipplane_t *pclip;
if (surface_p >= surf_max)
{
r_outofsurfaces++;
return;
}
if (((edge_p + fa->numedges) + 4) >= edge_max)
{
r_outofedges += fa->numedges;
return;
}
c_faceclip++;
pclip = 0;
for (i = 3, mask = 0x08; i >= 0; i--, mask >>= 1)
{
if (clipflags & mask)
{
view_clipplanes[i].next = pclip;
pclip = &view_clipplanes[i];
}
}
r_emitted = 0;
r_nearzi = 0;
r_nearzionly = 0;
makeleftedge = (makerightedge = 0);
pedges = currententity->model->edges;
r_lastvertvalid = 0;
for (i = 0; i < fa->numedges; i++)
{
lindex = currententity->model->surfedges[fa->firstedge + i];
if (lindex > 0)
{
r_pedge = &pedges[lindex];
if (!insubmodel)
{
if (r_pedge->cachededgeoffset & FULLY_CLIPPED_CACHED)
{
if ((r_pedge->cachededgeoffset & FRAMECOUNT_MASK) == r_framecount)
{
r_lastvertvalid = 0;
continue;
}
}
else
{
uintptr_t edge_base = (uintptr_t) ((uint8_t *) r_edges);
uintptr_t edge_cur = (uintptr_t) ((uint8_t *) edge_p);
uintptr_t edge_diff = edge_cur - edge_base;
if ((edge_diff > ((uintptr_t) r_pedge->cachededgeoffset)) && (((edge_t *) (((uint8_t *) r_edges) + r_pedge->cachededgeoffset))->owner == r_pedge))
{
R_EmitCachedEdge();
r_lastvertvalid = 0;
continue;
}
}
}
cacheoffset = ((uint8_t *) edge_p) - ((uint8_t *) r_edges);
r_leftclipped = (r_rightclipped = 0);
R_ClipEdge(&r_pcurrentvertbase[r_pedge->v[0]], &r_pcurrentvertbase[r_pedge->v[1]], pclip);
r_pedge->cachededgeoffset = cacheoffset;
if (r_leftclipped)
makeleftedge = 1;
if (r_rightclipped)
makerightedge = 1;
r_lastvertvalid = 1;
}
else
{
lindex = -lindex;
r_pedge = &pedges[lindex];
if (!insubmodel)
{
if (r_pedge->cachededgeoffset & FULLY_CLIPPED_CACHED)
{
if ((r_pedge->cachededgeoffset & FRAMECOUNT_MASK) == r_framecount)
{
r_lastvertvalid = 0;
continue;
}
}
else
{
uintptr_t edge_base = (uintptr_t) ((uint8_t *) r_edges);
uintptr_t edge_cur = (uintptr_t) ((uint8_t *) edge_p);
uintptr_t edge_diff = edge_cur - edge_base;
if ((edge_diff > ((uintptr_t) r_pedge->cachededgeoffset)) && (((edge_t *) (((uint8_t *) r_edges) + r_pedge->cachededgeoffset))->owner == r_pedge))
{
R_EmitCachedEdge();
r_lastvertvalid = 0;
continue;
}
}
}
cacheoffset = ((uint8_t *) edge_p) - ((uint8_t *) r_edges);
r_leftclipped = (r_rightclipped = 0);
R_ClipEdge(&r_pcurrentvertbase[r_pedge->v[1]], &r_pcurrentvertbase[r_pedge->v[0]], pclip);
r_pedge->cachededgeoffset = cacheoffset;
if (r_leftclipped)
makeleftedge = 1;
if (r_rightclipped)
makerightedge = 1;
r_lastvertvalid = 1;
}
}
if (makeleftedge)
{
r_pedge = &tedge;
r_lastvertvalid = 0;
R_ClipEdge(&r_leftexit, &r_leftenter, pclip->next);
}
if (makerightedge)
{
r_pedge = &tedge;
r_lastvertvalid = 0;
r_nearzionly = 1;
R_ClipEdge(&r_rightexit, &r_rightenter, view_clipplanes[1].next);
}
if (!r_emitted)
return;
r_polycount++;
surface_p->data = (void *) fa;
surface_p->nearzi = r_nearzi;
surface_p->flags = fa->flags;
surface_p->insubmodel = insubmodel;
surface_p->spanstate = 0;
surface_p->entity = currententity;
surface_p->key = r_currentkey++;
surface_p->spans = 0;
pplane = fa->plane;
TransformVector(pplane->normal, p_normal);
distinv = 1.0f / (pplane->dist - DotProduct(modelorg, pplane->normal));
surface_p->d_zistepu = (p_normal[0] * xscaleinv) * distinv;
surface_p->d_zistepv = ((-p_normal[1]) * yscaleinv) * distinv;
surface_p->d_ziorigin = ((p_normal[2] * distinv) - (xcenter * surface_p->d_zistepu)) - (ycenter * surface_p->d_zistepv);
surface_p++;
}
void R_RenderBmodelFace(bedge_t *pedges, msurface_t *psurf)
{
int32_t i;
uint32_t mask;
mplane_t *pplane;
float distinv;
vec3_t p_normal;
medge_t tedge;
clipplane_t *pclip;
if (surface_p >= surf_max)
{
r_outofsurfaces++;
return;
}
if (((edge_p + psurf->numedges) + 4) >= edge_max)
{
r_outofedges += psurf->numedges;
return;
}
c_faceclip++;
r_pedge = &tedge;
pclip = 0;
for (i = 3, mask = 0x08; i >= 0; i--, mask >>= 1)
{
if (r_clipflags & mask)
{
view_clipplanes[i].next = pclip;
pclip = &view_clipplanes[i];
}
}
r_emitted = 0;
r_nearzi = 0;
r_nearzionly = 0;
makeleftedge = (makerightedge = 0);
r_lastvertvalid = 0;
for (; pedges; pedges = pedges->pnext)
{
r_leftclipped = (r_rightclipped = 0);
R_ClipEdge(pedges->v[0], pedges->v[1], pclip);
if (r_leftclipped)
makeleftedge = 1;
if (r_rightclipped)
makerightedge = 1;
}
if (makeleftedge)
{
r_pedge = &tedge;
R_ClipEdge(&r_leftexit, &r_leftenter, pclip->next);
}
if (makerightedge)
{
r_pedge = &tedge;
r_nearzionly = 1;
R_ClipEdge(&r_rightexit, &r_rightenter, view_clipplanes[1].next);
}
if (!r_emitted)
return;
r_polycount++;
surface_p->data = (void *) psurf;
surface_p->nearzi = r_nearzi;
surface_p->flags = psurf->flags;
surface_p->insubmodel = 1;
surface_p->spanstate = 0;
surface_p->entity = currententity;
surface_p->key = r_currentbkey;
surface_p->spans = 0;
pplane = psurf->plane;
TransformVector(pplane->normal, p_normal);
distinv = 1.0 / (pplane->dist - DotProduct(modelorg, pplane->normal));
surface_p->d_zistepu = (p_normal[0] * xscaleinv) * distinv;
surface_p->d_zistepv = ((-p_normal[1]) * yscaleinv) * distinv;
surface_p->d_ziorigin = ((p_normal[2] * distinv) - (xcenter * surface_p->d_zistepu)) - (ycenter * surface_p->d_zistepv);
surface_p++;
}
void R_RenderPoly(msurface_t *fa, int32_t clipflags)
{
int32_t i;
int32_t lindex;
int32_t lnumverts;
int32_t s_axis;
int32_t t_axis;
float dist;
float lastdist;
float lzi;
float scale;
float u;
float v;
float frac;
uint32_t mask;
vec3_t local;
vec3_t transformed;
clipplane_t *pclip;
medge_t *pedges;
mplane_t *pplane;
mvertex_t verts[2][100];
polyvert_t pverts[100];
int32_t vertpage;
int32_t newverts;
int32_t newpage;
int32_t lastvert;
bool visible;
s_axis = (t_axis = 0);
pclip = 0;
for (i = 3, mask = 0x08; i >= 0; i--, mask >>= 1)
{
if (clipflags & mask)
{
view_clipplanes[i].next = pclip;
pclip = &view_clipplanes[i];
}
}
pedges = currententity->model->edges;
lnumverts = fa->numedges;
vertpage = 0;
for (i = 0; i < lnumverts; i++)
{
lindex = currententity->model->surfedges[fa->firstedge + i];
if (lindex > 0)
{
r_pedge = &pedges[lindex];
verts[0][i] = r_pcurrentvertbase[r_pedge->v[0]];
}
else
{
r_pedge = &pedges[-lindex];
verts[0][i] = r_pcurrentvertbase[r_pedge->v[1]];
}
}
while (pclip)
{
lastvert = lnumverts - 1;
lastdist = DotProduct(verts[vertpage][lastvert].position, pclip->normal) - pclip->dist;
visible = 0;
newverts = 0;
newpage = vertpage ^ 1;
for (i = 0; i < lnumverts; i++)
{
dist = DotProduct(verts[vertpage][i].position, pclip->normal) - pclip->dist;
if ((lastdist > 0) != (dist > 0))
{
frac = dist / (dist - lastdist);
verts[newpage][newverts].position[0] = verts[vertpage][i].position[0] + ((verts[vertpage][lastvert].position[0] - verts[vertpage][i].position[0]) * frac);
verts[newpage][newverts].position[1] = verts[vertpage][i].position[1] + ((verts[vertpage][lastvert].position[1] - verts[vertpage][i].position[1]) * frac);
verts[newpage][newverts].position[2] = verts[vertpage][i].position[2] + ((verts[vertpage][lastvert].position[2] - verts[vertpage][i].position[2]) * frac);
newverts++;
}
if (dist >= 0)
{
verts[newpage][newverts] = verts[vertpage][i];
newverts++;
visible = 1;
}
lastvert = i;
lastdist = dist;
}
if ((!visible) || (newverts < 3))
return;
lnumverts = newverts;
vertpage ^= 1;
pclip = pclip->next;
}
pplane = fa->plane;
switch (pplane->type)
{
case PLANE_X:
case PLANE_ANYX:
s_axis = 1;
t_axis = 2;
break;
case PLANE_Y:
case PLANE_ANYY:
s_axis = 0;
t_axis = 2;
break;
case PLANE_Z:
case PLANE_ANYZ:
s_axis = 0;
t_axis = 1;
break;
}
r_nearzi = 0;
for (i = 0; i < lnumverts; i++)
{
VectorSubtract(verts[vertpage][i].position, modelorg, local);
TransformVector(local, transformed);
if (transformed[2] < NEAR_CLIP)
transformed[2] = NEAR_CLIP;
lzi = 1.0 / transformed[2];
if (lzi > r_nearzi)
r_nearzi = lzi;
scale = xscale * lzi;
u = xcenter + (scale * transformed[0]);
if (u < r_refdef.fvrectx_adj)
u = r_refdef.fvrectx_adj;
if (u > r_refdef.fvrectright_adj)
u = r_refdef.fvrectright_adj;
scale = yscale * lzi;
v = ycenter - (scale * transformed[1]);
if (v < r_refdef.fvrecty_adj)
v = r_refdef.fvrecty_adj;
if (v > r_refdef.fvrectbottom_adj)
v = r_refdef.fvrectbottom_adj;
pverts[i].u = u;
pverts[i].v = v;
pverts[i].zi = lzi;
pverts[i].s = verts[vertpage][i].position[s_axis];
pverts[i].t = verts[vertpage][i].position[t_axis];
}
r_polydesc.numverts = lnumverts;
r_polydesc.nearzi = r_nearzi;
r_polydesc.pcurrentface = fa;
r_polydesc.pverts = pverts;
D_DrawPoly();
}
void R_ZDrawSubmodelPolys(model_t *pmodel)
{
int32_t i;
int32_t numsurfaces;
msurface_t *psurf;
float dot;
mplane_t *pplane;
psurf = &pmodel->surfaces[pmodel->firstmodelsurface];
numsurfaces = pmodel->nummodelsurfaces;
for (i = 0; i < numsurfaces; i++, psurf++)
{
pplane = psurf->plane;
dot = DotProduct(modelorg, pplane->normal) - pplane->dist;
if (((psurf->flags & SURF_PLANEBACK) && (dot < (-BACKFACE_EPSILON))) || ((!(psurf->flags & SURF_PLANEBACK)) && (dot > BACKFACE_EPSILON)))
{
R_RenderPoly(psurf, 15);
}
}
}
void R_DrawCulledPolys(void)
{
surf_t *s;
msurface_t *pface;
currententity = &cl_entities[0];
if (r_worldpolysbacktofront)
{
for (s = surface_p - 1; s > (&surfaces[1]); s--)
{
if (!s->spans)
continue;
if (!(s->flags & SURF_DRAWBACKGROUND))
{
pface = (msurface_t *) s->data;
R_RenderPoly(pface, 15);
}
}
}
else
{
for (s = &surfaces[1]; s < surface_p; s++)
{
if (!s->spans)
continue;
if (!(s->flags & SURF_DRAWBACKGROUND))
{
pface = (msurface_t *) s->data;
R_RenderPoly(pface, 15);
}
}
}
}
void R_BeginEdgeFrame(void)
{
int32_t v;
edge_p = r_edges;
edge_max = &r_edges[r_numallocatededges];
surface_p = &surfaces[2];
surfaces[1].spans = 0;
surfaces[1].flags = SURF_DRAWBACKGROUND;
if (r_draworder.value)
{
pdrawfunc = R_GenerateSpansBackward;
surfaces[1].key = 0;
r_currentkey = 1;
}
else
{
pdrawfunc = R_GenerateSpans;
surfaces[1].key = 0x7FFFFFFF;
r_currentkey = 0;
}
for (v = r_refdef.vrect.y; v < r_refdef.vrectbottom; v++)
{
newedges[v] = (removeedges[v] = 0);
}
}
void R_InsertNewEdges(edge_t *edgestoadd, edge_t *edgelist)
{
edge_t *next_edge;
do
{
next_edge = edgestoadd->next;
edgesearch:
if (edgelist->u >= edgestoadd->u)
goto addedge;
edgelist = edgelist->next;
if (edgelist->u >= edgestoadd->u)
goto addedge;
edgelist = edgelist->next;
if (edgelist->u >= edgestoadd->u)
goto addedge;
edgelist = edgelist->next;
if (edgelist->u >= edgestoadd->u)
goto addedge;
edgelist = edgelist->next;
goto edgesearch;
addedge:
edgestoadd->next = edgelist;
edgestoadd->prev = edgelist->prev;
edgelist->prev->next = edgestoadd;
edgelist->prev = edgestoadd;
}
while ((edgestoadd = next_edge) != 0);
}
void R_RemoveEdges(edge_t *pedge)
{
do
{
pedge->next->prev = pedge->prev;
pedge->prev->next = pedge->next;
}
while ((pedge = pedge->nextremove) != 0);
}
void R_StepActiveU(edge_t *pedge)
{
edge_t *pnext_edge;
edge_t *pwedge;
while (1)
{
nextedge:
pedge->u += pedge->u_step;
if (pedge->u < pedge->prev->u)
goto pushback;
pedge = pedge->next;
pedge->u += pedge->u_step;
if (pedge->u < pedge->prev->u)
goto pushback;
pedge = pedge->next;
pedge->u += pedge->u_step;
if (pedge->u < pedge->prev->u)
goto pushback;
pedge = pedge->next;
pedge->u += pedge->u_step;
if (pedge->u < pedge->prev->u)
goto pushback;
pedge = pedge->next;
goto nextedge;
pushback:
if (pedge == (&edge_aftertail))
return;
pnext_edge = pedge->next;
pedge->next->prev = pedge->prev;
pedge->prev->next = pedge->next;
pwedge = pedge->prev->prev;
while (pwedge->u > pedge->u)
{
pwedge = pwedge->prev;
}
pedge->next = pwedge->next;
pedge->prev = pwedge;
pedge->next->prev = pedge;
pwedge->next = pedge;
pedge = pnext_edge;
if (pedge == (&edge_tail))
return;
}
}
static void R_CleanupSpan()
{
surf_t *surf;
int32_t iu;
espan_t *span;
surf = surfaces[1].next;
iu = edge_tail_u_shift20;
if (iu > surf->last_u)
{
span = span_p++;
span->u = surf->last_u;
span->count = iu - span->u;
span->v = current_iv;
span->pnext = surf->spans;
surf->spans = span;
}
do
{
surf->spanstate = 0;
surf = surf->next;
}
while (surf != (&surfaces[1]));
}
static void R_LeadingEdgeBackwards(edge_t *edge)
{
espan_t *span;
surf_t *surf;
surf_t *surf2;
int32_t iu;
surf = &surfaces[edge->surfs[1]];
if ((++surf->spanstate) == 1)
{
surf2 = surfaces[1].next;
if (surf->key > surf2->key)
goto newtop;
if (surf->insubmodel && (surf->key == surf2->key))
{
goto newtop;
}
continue_search:
do
{
surf2 = surf2->next;
}
while (surf->key < surf2->key);
if (surf->key == surf2->key)
{
if (!surf->insubmodel)
goto continue_search;
}
goto gotposition;
newtop:
iu = edge->u >> 20;
if (iu > surf2->last_u)
{
span = span_p++;
span->u = surf2->last_u;
span->count = iu - span->u;
span->v = current_iv;
span->pnext = surf2->spans;
surf2->spans = span;
}
surf->last_u = iu;
gotposition:
surf->next = surf2;
surf->prev = surf2->prev;
surf2->prev->next = surf;
surf2->prev = surf;
}
}
static void R_TrailingEdge(surf_t *surf, edge_t *edge)
{
espan_t *span;
int32_t iu;
if ((--surf->spanstate) == 0)
{
if (surf->insubmodel)
r_bmodelactive--;
if (surf == surfaces[1].next)
{
iu = edge->u >> 20;
if (iu > surf->last_u)
{
span = span_p++;
span->u = surf->last_u;
span->count = iu - span->u;
span->v = current_iv;
span->pnext = surf->spans;
surf->spans = span;
}
surf->next->last_u = iu;
}
surf->prev->next = surf->next;
surf->next->prev = surf->prev;
}
}
static void R_LeadingEdge(edge_t *edge)
{
espan_t *span;
surf_t *surf;
surf_t *surf2;
int32_t iu;
double fu;
double newzi;
double testzi;
double newzitop;
double newzibottom;
if (edge->surfs[1])
{
surf = &surfaces[edge->surfs[1]];
if ((++surf->spanstate) == 1)
{
if (surf->insubmodel)
r_bmodelactive++;
surf2 = surfaces[1].next;
if (surf->key < surf2->key)
goto newtop;
if (surf->insubmodel && (surf->key == surf2->key))
{
fu = ((float) (edge->u - 0xFFFFF)) * (1.0 / 0x100000);
newzi = (surf->d_ziorigin + (fv * surf->d_zistepv)) + (fu * surf->d_zistepu);
newzibottom = newzi * 0.99;
testzi = (surf2->d_ziorigin + (fv * surf2->d_zistepv)) + (fu * surf2->d_zistepu);
if (newzibottom >= testzi)
{
goto newtop;
}
newzitop = newzi * 1.01;
if (newzitop >= testzi)
{
if (surf->d_zistepu >= surf2->d_zistepu)
{
goto newtop;
}
}
}
continue_search:
do
{
surf2 = surf2->next;
}
while (surf->key > surf2->key);
if (surf->key == surf2->key)
{
if (!surf->insubmodel)
goto continue_search;
fu = ((float) (edge->u - 0xFFFFF)) * (1.0 / 0x100000);
newzi = (surf->d_ziorigin + (fv * surf->d_zistepv)) + (fu * surf->d_zistepu);
newzibottom = newzi * 0.99;
testzi = (surf2->d_ziorigin + (fv * surf2->d_zistepv)) + (fu * surf2->d_zistepu);
if (newzibottom >= testzi)
{
goto gotposition;
}
newzitop = newzi * 1.01;
if (newzitop >= testzi)
{
if (surf->d_zistepu >= surf2->d_zistepu)
{
goto gotposition;
}
}
goto continue_search;
}
goto gotposition;
newtop:
iu = edge->u >> 20;
if (iu > surf2->last_u)
{
span = span_p++;
span->u = surf2->last_u;
span->count = iu - span->u;
span->v = current_iv;
span->pnext = surf2->spans;
surf2->spans = span;
}
surf->last_u = iu;
gotposition:
surf->next = surf2;
surf->prev = surf2->prev;
surf2->prev->next = surf;
surf2->prev = surf;
}
}
}
static void R_GenerateSpans(void)
{
edge_t *edge;
surf_t *surf;
r_bmodelactive = 0;
surfaces[1].next = (surfaces[1].prev = &surfaces[1]);
surfaces[1].last_u = edge_head_u_shift20;
for (edge = edge_head.next; edge != (&edge_tail); edge = edge->next)
{
if (edge->surfs[0])
{
surf = &surfaces[edge->surfs[0]];
R_TrailingEdge(surf, edge);
if (!edge->surfs[1])
continue;
}
R_LeadingEdge(edge);
}
R_CleanupSpan();
}
static void R_GenerateSpansBackward(void)
{
edge_t *edge;
r_bmodelactive = 0;
surfaces[1].next = (surfaces[1].prev = &surfaces[1]);
surfaces[1].last_u = edge_head_u_shift20;
for (edge = edge_head.next; edge != (&edge_tail); edge = edge->next)
{
if (edge->surfs[0])
R_TrailingEdge(&surfaces[edge->surfs[0]], edge);
if (edge->surfs[1])
R_LeadingEdgeBackwards(edge);
}
R_CleanupSpan();
}
void R_ScanEdges(void)
{
int32_t iv;
int32_t bottom;
uint8_t basespans[(MAXSPANS * (sizeof(espan_t))) + CACHE_SIZE];
espan_t *basespan_p;
surf_t *s;
basespan_p = (espan_t *) (((((uintptr_t) basespans) + ((uintptr_t) CACHE_SIZE)) - 1) & (~(((uintptr_t) CACHE_SIZE) - 1)));
max_span_p = &basespan_p[MAXSPANS - r_refdef.vrect.width];
span_p = basespan_p;
edge_head.u = r_refdef.vrect.x << 20;
edge_head_u_shift20 = edge_head.u >> 20;
edge_head.u_step = 0;
edge_head.prev = 0;
edge_head.next = &edge_tail;
edge_head.surfs[0] = 0;
edge_head.surfs[1] = 1;
edge_tail.u = (r_refdef.vrectright << 20) + 0xFFFFF;
edge_tail_u_shift20 = edge_tail.u >> 20;
edge_tail.u_step = 0;
edge_tail.prev = &edge_head;
edge_tail.next = &edge_aftertail;
edge_tail.surfs[0] = 1;
edge_tail.surfs[1] = 0;
edge_aftertail.u = -1;
edge_aftertail.u_step = 0;
edge_aftertail.next = &edge_sentinel;
edge_aftertail.prev = &edge_tail;
edge_sentinel.u = 2000 << 24;
edge_sentinel.prev = &edge_aftertail;
bottom = r_refdef.vrectbottom - 1;
for (iv = r_refdef.vrect.y; iv < bottom; iv++)
{
current_iv = iv;
fv = (float) iv;
surfaces[1].spanstate = 1;
if (newedges[iv])
{
R_InsertNewEdges(newedges[iv], edge_head.next);
}
(*pdrawfunc)();
if (span_p >= max_span_p)
{
VID_UnlockBuffer();
S_ExtraUpdate();
VID_LockBuffer();
if (r_drawculledpolys)
{
R_DrawCulledPolys();
}
else
{
D_DrawSurfaces();
}
for (s = &surfaces[1]; s < surface_p; s++)
s->spans = 0;
span_p = basespan_p;
}
if (removeedges[iv])
R_RemoveEdges(removeedges[iv]);
if (edge_head.next != (&edge_tail))
R_StepActiveU(edge_head.next);
}
current_iv = iv;
fv = (float) iv;
surfaces[1].spanstate = 1;
if (newedges[iv])
R_InsertNewEdges(newedges[iv], edge_head.next);
(*pdrawfunc)();
if (r_drawculledpolys)
R_DrawCulledPolys();
else
D_DrawSurfaces();
}
void R_RemoveEfrags(entity_t *ent)
{
efrag_t *ef;
efrag_t *old;
efrag_t *walk;
efrag_t **prev;
ef = ent->efrag;
while (ef)
{
prev = &ef->leaf->efrags;
while (1)
{
walk = *prev;
if (!walk)
break;
if (walk == ef)
{
*prev = ef->leafnext;
break;
}
else
prev = &walk->leafnext;
}
old = ef;
ef = ef->entnext;
old->entnext = cl.free_efrags;
cl.free_efrags = old;
}
ent->efrag = 0;
}
static void R_SplitEntityOnNode(mnode_t *node)
{
efrag_t *ef;
mplane_t *splitplane;
mleaf_t *leaf;
int32_t sides;
if (node->contents == CONTENTS_SOLID)
{
return;
}
if (node->contents < 0)
{
if (!r_pefragtopnode)
r_pefragtopnode = node;
leaf = (mleaf_t *) node;
ef = cl.free_efrags;
if (!ef)
{
Con_Printf("Too many efrags!\n");
return;
}
cl.free_efrags = cl.free_efrags->entnext;
ef->entity = r_addent;
*lastlink = ef;
lastlink = &ef->entnext;
ef->entnext = 0;
ef->leaf = leaf;
ef->leafnext = leaf->efrags;
leaf->efrags = ef;
return;
}
splitplane = node->plane;
sides = BOX_ON_PLANE_SIDE(r_emins, r_emaxs, splitplane);
if (sides == 3)
{
if (!r_pefragtopnode)
r_pefragtopnode = node;
}
if (sides & 1)
R_SplitEntityOnNode(node->children[0]);
if (sides & 2)
R_SplitEntityOnNode(node->children[1]);
}
void R_SplitEntityOnNode2(mnode_t *node)
{
mplane_t *splitplane;
int32_t sides;
if (node->visframe != r_visframecount)
return;
if (node->contents < 0)
{
if (node->contents != CONTENTS_SOLID)
r_pefragtopnode = node;
return;
}
splitplane = node->plane;
sides = BOX_ON_PLANE_SIDE(r_emins, r_emaxs, splitplane);
if (sides == 3)
{
r_pefragtopnode = node;
return;
}
if (sides & 1)
R_SplitEntityOnNode2(node->children[0]);
else
R_SplitEntityOnNode2(node->children[1]);
}
void R_AddEfrags(entity_t *ent)
{
model_t *entmodel;
int32_t i;
if (!ent->model)
return;
if (ent == cl_entities)
return;
r_addent = ent;
lastlink = &ent->efrag;
r_pefragtopnode = 0;
entmodel = ent->model;
for (i = 0; i < 3; i++)
{
r_emins[i] = ent->origin[i] + entmodel->mins[i];
r_emaxs[i] = ent->origin[i] + entmodel->maxs[i];
}
R_SplitEntityOnNode(cl.worldmodel->nodes);
ent->topnode = r_pefragtopnode;
}
void R_StoreEfrags(efrag_t **ppefrag)
{
entity_t *pent;
model_t *clmodel;
efrag_t *pefrag;
while ((pefrag = *ppefrag) != 0)
{
pent = pefrag->entity;
clmodel = pent->model;
switch (clmodel->type)
{
case mod_alias:
case mod_brush:
case mod_sprite:
pent = pefrag->entity;
if ((pent->visframe != r_framecount) && (cl_numvisedicts < MAX_VISEDICTS))
{
cl_visedicts[cl_numvisedicts++] = pent;
pent->visframe = r_framecount;
}
ppefrag = &pefrag->leafnext;
break;
default:
Sys_Error("R_StoreEfrags: Bad entity type %d\n", clmodel->type);
}
}
}
void R_AnimateLight(void)
{
int32_t i;
int32_t j;
int32_t k;
i = (int32_t) (cl.time * 10);
for (j = 0; j < MAX_LIGHTSTYLES; j++)
{
if (!cl_lightstyle[j].length)
{
d_lightstylevalue[j] = 256;
continue;
}
k = i % cl_lightstyle[j].length;
k = cl_lightstyle[j].map[k] - 'a';
k = k * 22;
d_lightstylevalue[j] = k;
}
}
void R_MarkLights(dlight_t *light, int32_t bit, mnode_t *node)
{
mplane_t *splitplane;
float dist;
msurface_t *surf;
int32_t i;
if (node->contents < 0)
return;
splitplane = node->plane;
dist = DotProduct(light->origin, splitplane->normal) - splitplane->dist;
if (dist > light->radius)
{
R_MarkLights(light, bit, node->children[0]);
return;
}
if (dist < (-light->radius))
{
R_MarkLights(light, bit, node->children[1]);
return;
}
surf = cl.worldmodel->surfaces + node->firstsurface;
for (i = 0; i < node->numsurfaces; i++, surf++)
{
if (surf->dlightframe != r_dlightframecount)
{
surf->dlightbits = 0;
surf->dlightframe = r_dlightframecount;
}
surf->dlightbits |= bit;
}
R_MarkLights(light, bit, node->children[0]);
R_MarkLights(light, bit, node->children[1]);
}
void R_PushDlights(void)
{
int32_t i;
dlight_t *l;
r_dlightframecount = r_framecount + 1;
l = cl_dlights;
for (i = 0; i < MAX_DLIGHTS; i++, l++)
{
if ((l->die < cl.time) || (!l->radius))
continue;
R_MarkLights(l, 1 << i, cl.worldmodel->nodes);
}
}
static int32_t RecursiveLightPoint(mnode_t *node, vec3_t start, vec3_t end)
{
int32_t r;
float front;
float back;
float frac;
int32_t side;
mplane_t *plane;
vec3_t mid;
msurface_t *surf;
int32_t s;
int32_t t;
int32_t ds;
int32_t dt;
int32_t i;
mtexinfo_t *tex;
uint8_t *lightmap;
uint32_t scale;
int32_t maps;
if (node->contents < 0)
return -1;
plane = node->plane;
front = DotProduct(start, plane->normal) - plane->dist;
back = DotProduct(end, plane->normal) - plane->dist;
side = front < 0;
if ((back < 0) == side)
return RecursiveLightPoint(node->children[side], start, end);
frac = front / (front - back);
mid[0] = start[0] + ((end[0] - start[0]) * frac);
mid[1] = start[1] + ((end[1] - start[1]) * frac);
mid[2] = start[2] + ((end[2] - start[2]) * frac);
r = RecursiveLightPoint(node->children[side], start, mid);
if (r >= 0)
return r;
if ((back < 0) == side)
return -1;
surf = cl.worldmodel->surfaces + node->firstsurface;
for (i = 0; i < node->numsurfaces; i++, surf++)
{
if (surf->flags & SURF_DRAWTILED)
continue;
tex = surf->texinfo;
s = DotProduct(mid, tex->vecs[0]) + tex->vecs[0][3];
t = DotProduct(mid, tex->vecs[1]) + tex->vecs[1][3];
;
if ((s < surf->texturemins[0]) || (t < surf->texturemins[1]))
continue;
ds = s - surf->texturemins[0];
dt = t - surf->texturemins[1];
if ((ds > surf->extents[0]) || (dt > surf->extents[1]))
continue;
if (!surf->samples)
return 0;
ds >>= 4;
dt >>= 4;
lightmap = surf->samples;
r = 0;
if (lightmap)
{
lightmap += (dt * ((surf->extents[0] >> 4) + 1)) + ds;
for (maps = 0; (maps < MAXLIGHTMAPS) && (surf->styles[maps] != 255); maps++)
{
scale = d_lightstylevalue[surf->styles[maps]];
r += (*lightmap) * scale;
lightmap += ((surf->extents[0] >> 4) + 1) * ((surf->extents[1] >> 4) + 1);
}
r >>= 8;
}
return r;
}
return RecursiveLightPoint(node->children[!side], mid, end);
}
int32_t R_LightPoint(vec3_t p)
{
vec3_t end;
int32_t r;
if (!cl.worldmodel->lightdata)
return 255;
end[0] = p[0];
end[1] = p[1];
end[2] = p[2] - 2048;
r = RecursiveLightPoint(cl.worldmodel->nodes, p, end);
if (r == (-1))
r = 0;
if (r < r_refdef.ambientlight)
r = r_refdef.ambientlight;
return r;
}
void R_InitTextures(void)
{
int32_t x;
int32_t y;
int32_t m;
uint8_t *dest;
r_notexture_mip = Hunk_AllocName(((((sizeof(texture_t)) + (16 * 16)) + (8 * 8)) + (4 * 4)) + (2 * 2), "notexture");
r_notexture_mip->width = (r_notexture_mip->height = 16);
r_notexture_mip->offsets[0] = sizeof(texture_t);
r_notexture_mip->offsets[1] = r_notexture_mip->offsets[0] + (16 * 16);
r_notexture_mip->offsets[2] = r_notexture_mip->offsets[1] + (8 * 8);
r_notexture_mip->offsets[3] = r_notexture_mip->offsets[2] + (4 * 4);
for (m = 0; m < 4; m++)
{
dest = ((uint8_t *) r_notexture_mip) + r_notexture_mip->offsets[m];
for (y = 0; y < (16 >> m); y++)
for (x = 0; x < (16 >> m); x++)
{
if ((y < (8 >> m)) ^ (x < (8 >> m)))
*(dest++) = 0;
else
*(dest++) = 0xff;
}
}
}
void R_Init(void)
{
int32_t dummy;
r_stack_start = (uint8_t *) (&dummy);
R_InitTurb();
Cmd_AddCommand("timerefresh", R_TimeRefresh_f);
Cmd_AddCommand("pointfile", R_ReadPointFile_f);
Cvar_RegisterVariable(&r_draworder);
Cvar_RegisterVariable(&r_speeds);
Cvar_RegisterVariable(&r_timegraph);
Cvar_RegisterVariable(&r_graphheight);
Cvar_RegisterVariable(&r_drawflat);
Cvar_RegisterVariable(&r_ambient);
Cvar_RegisterVariable(&r_clearcolor);
Cvar_RegisterVariable(&r_waterwarp);
Cvar_RegisterVariable(&r_fullbright);
Cvar_RegisterVariable(&r_drawentities);
Cvar_RegisterVariable(&r_drawviewmodel);
Cvar_RegisterVariable(&r_aliasstats);
Cvar_RegisterVariable(&r_dspeeds);
Cvar_RegisterVariable(&r_reportsurfout);
Cvar_RegisterVariable(&r_maxsurfs);
Cvar_RegisterVariable(&r_numsurfs);
Cvar_RegisterVariable(&r_reportedgeout);
Cvar_RegisterVariable(&r_maxedges);
Cvar_RegisterVariable(&r_numedges);
Cvar_RegisterVariable(&r_aliastransbase);
Cvar_RegisterVariable(&r_aliastransadj);
Cvar_SetValue("r_maxedges", (float) NUMSTACKEDGES);
Cvar_SetValue("r_maxsurfs", (float) NUMSTACKSURFACES);
view_clipplanes[0].leftedge = 1;
view_clipplanes[1].rightedge = 1;
view_clipplanes[1].leftedge = (view_clipplanes[2].leftedge = (view_clipplanes[3].leftedge = 0));
view_clipplanes[0].rightedge = (view_clipplanes[2].rightedge = (view_clipplanes[3].rightedge = 0));
r_refdef.xOrigin = XCENTERING;
r_refdef.yOrigin = YCENTERING;
R_InitParticles();
D_Init();
}
void R_NewMap(void)
{
int32_t i;
for (i = 0; i < cl.worldmodel->numleafs; i++)
cl.worldmodel->leafs[i].efrags = 0;
r_viewleaf = 0;
R_ClearParticles();
r_cnumsurfs = r_maxsurfs.value;
if (r_cnumsurfs <= MINSURFACES)
r_cnumsurfs = MINSURFACES;
if (r_cnumsurfs > NUMSTACKSURFACES)
{
surfaces = Hunk_AllocName(r_cnumsurfs * (sizeof(surf_t)), "surfaces");
surface_p = surfaces;
surf_max = &surfaces[r_cnumsurfs];
r_surfsonstack = 0;
surfaces--;
R_SurfacePatch();
}
else
{
r_surfsonstack = 1;
}
r_maxedgesseen = 0;
r_maxsurfsseen = 0;
r_numallocatededges = r_maxedges.value;
if (r_numallocatededges < MINEDGES)
r_numallocatededges = MINEDGES;
if (r_numallocatededges <= NUMSTACKEDGES)
{
auxedges = 0;
}
else
{
auxedges = Hunk_AllocName(r_numallocatededges * (sizeof(edge_t)), "edges");
}
r_dowarpold = 0;
r_viewchanged = 0;
}
void R_SetVrect(vrect_t *pvrectin, vrect_t *pvrect, int32_t lineadj)
{
int32_t h;
float size;
size = (scr_viewsize.value > 100) ? (100) : (scr_viewsize.value);
if (cl.intermission)
{
size = 100;
lineadj = 0;
}
size /= 100;
h = pvrectin->height - lineadj;
pvrect->width = pvrectin->width * size;
if (pvrect->width < 96)
{
size = 96.0 / pvrectin->width;
pvrect->width = 96;
}
pvrect->width &= ~7;
pvrect->height = pvrectin->height * size;
if (pvrect->height > (pvrectin->height - lineadj))
pvrect->height = pvrectin->height - lineadj;
pvrect->height &= ~1;
pvrect->x = (pvrectin->width - pvrect->width) / 2;
pvrect->y = (h - pvrect->height) / 2;
{
if (lcd_x.value)
{
pvrect->y >>= 1;
pvrect->height >>= 1;
}
}
}
void R_ViewChanged(vrect_t *pvrect, int32_t lineadj, float aspect)
{
int32_t i;
float res_scale;
r_viewchanged = 1;
R_SetVrect(pvrect, &r_refdef.vrect, lineadj);
r_refdef.horizontalFieldOfView = 2.0 * tan((r_refdef.fov_x / 360) * M_PI);
r_refdef.fvrectx = (float) r_refdef.vrect.x;
r_refdef.fvrectx_adj = ((float) r_refdef.vrect.x) - 0.5;
r_refdef.vrect_x_adj_shift20 = ((r_refdef.vrect.x << 20) + (1 << 19)) - 1;
r_refdef.fvrecty = (float) r_refdef.vrect.y;
r_refdef.fvrecty_adj = ((float) r_refdef.vrect.y) - 0.5;
r_refdef.vrectright = r_refdef.vrect.x + r_refdef.vrect.width;
r_refdef.vrectright_adj_shift20 = ((r_refdef.vrectright << 20) + (1 << 19)) - 1;
r_refdef.fvrectright = (float) r_refdef.vrectright;
r_refdef.fvrectright_adj = ((float) r_refdef.vrectright) - 0.5;
r_refdef.vrectrightedge = ((float) r_refdef.vrectright) - 0.99;
r_refdef.vrectbottom = r_refdef.vrect.y + r_refdef.vrect.height;
r_refdef.fvrectbottom = (float) r_refdef.vrectbottom;
r_refdef.fvrectbottom_adj = ((float) r_refdef.vrectbottom) - 0.5;
r_refdef.aliasvrect.x = (int32_t) (r_refdef.vrect.x * r_aliasuvscale);
r_refdef.aliasvrect.y = (int32_t) (r_refdef.vrect.y * r_aliasuvscale);
r_refdef.aliasvrect.width = (int32_t) (r_refdef.vrect.width * r_aliasuvscale);
r_refdef.aliasvrect.height = (int32_t) (r_refdef.vrect.height * r_aliasuvscale);
r_refdef.aliasvrectright = r_refdef.aliasvrect.x + r_refdef.aliasvrect.width;
r_refdef.aliasvrectbottom = r_refdef.aliasvrect.y + r_refdef.aliasvrect.height;
pixelAspect = aspect;
xOrigin = r_refdef.xOrigin;
yOrigin = r_refdef.yOrigin;
screenAspect = (r_refdef.vrect.width * pixelAspect) / r_refdef.vrect.height;
verticalFieldOfView = r_refdef.horizontalFieldOfView / screenAspect;
xcenter = ((((float) r_refdef.vrect.width) * XCENTERING) + r_refdef.vrect.x) - 0.5;
aliasxcenter = xcenter * r_aliasuvscale;
ycenter = ((((float) r_refdef.vrect.height) * YCENTERING) + r_refdef.vrect.y) - 0.5;
aliasycenter = ycenter * r_aliasuvscale;
xscale = r_refdef.vrect.width / r_refdef.horizontalFieldOfView;
aliasxscale = xscale * r_aliasuvscale;
xscaleinv = 1.0 / xscale;
yscale = xscale * pixelAspect;
aliasyscale = yscale * r_aliasuvscale;
yscaleinv = 1.0 / yscale;
xscaleshrink = (r_refdef.vrect.width - 6) / r_refdef.horizontalFieldOfView;
yscaleshrink = xscaleshrink * pixelAspect;
screenedge[0].normal[0] = (-1.0) / (xOrigin * r_refdef.horizontalFieldOfView);
screenedge[0].normal[1] = 0;
screenedge[0].normal[2] = 1;
screenedge[0].type = PLANE_ANYZ;
screenedge[1].normal[0] = 1.0 / ((1.0 - xOrigin) * r_refdef.horizontalFieldOfView);
screenedge[1].normal[1] = 0;
screenedge[1].normal[2] = 1;
screenedge[1].type = PLANE_ANYZ;
screenedge[2].normal[0] = 0;
screenedge[2].normal[1] = (-1.0) / (yOrigin * verticalFieldOfView);
screenedge[2].normal[2] = 1;
screenedge[2].type = PLANE_ANYZ;
screenedge[3].normal[0] = 0;
screenedge[3].normal[1] = 1.0 / ((1.0 - yOrigin) * verticalFieldOfView);
screenedge[3].normal[2] = 1;
screenedge[3].type = PLANE_ANYZ;
for (i = 0; i < 4; i++)
VectorNormalize(screenedge[i].normal);
res_scale = sqrt(((double) (r_refdef.vrect.width * r_refdef.vrect.height)) / (320.0 * 152.0)) * (2.0 / r_refdef.horizontalFieldOfView);
r_aliastransition = r_aliastransbase.value * res_scale;
r_resfudge = r_aliastransadj.value * res_scale;
if (scr_fov.value <= 90.0)
r_fov_greater_than_90 = 0;
else
r_fov_greater_than_90 = 1;
D_ViewChanged();
}
void R_MarkLeaves(void)
{
uint8_t *vis;
mnode_t *node;
int32_t i;
if (r_oldviewleaf == r_viewleaf)
return;
r_visframecount++;
r_oldviewleaf = r_viewleaf;
vis = Mod_LeafPVS(r_viewleaf, cl.worldmodel);
for (i = 0; i < cl.worldmodel->numleafs; i++)
{
if (vis[i >> 3] & (1 << (i & 7)))
{
node = (mnode_t *) (&cl.worldmodel->leafs[i + 1]);
do
{
if (node->visframe == r_visframecount)
break;
node->visframe = r_visframecount;
node = node->parent;
}
while (node);
}
}
}
void R_DrawEntitiesOnList(void)
{
int32_t i;
int32_t j;
int32_t lnum;
alight_t lighting;
float lightvec[3] = {-1, 0, 0};
vec3_t dist;
float add;
if (!r_drawentities.value)
return;
for (i = 0; i < cl_numvisedicts; i++)
{
currententity = cl_visedicts[i];
if (currententity == (&cl_entities[cl.viewentity]))
continue;
switch (currententity->model->type)
{
case mod_sprite:
VectorCopy(currententity->origin, r_entorigin);
VectorSubtract(r_origin, r_entorigin, modelorg);
R_DrawSprite();
break;
case mod_alias:
VectorCopy(currententity->origin, r_entorigin);
VectorSubtract(r_origin, r_entorigin, modelorg);
if (R_AliasCheckBBox())
{
j = R_LightPoint(currententity->origin);
lighting.ambientlight = j;
lighting.shadelight = j;
lighting.plightvec = lightvec;
for (lnum = 0; lnum < MAX_DLIGHTS; lnum++)
{
if (cl_dlights[lnum].die >= cl.time)
{
VectorSubtract(currententity->origin, cl_dlights[lnum].origin, dist);
add = cl_dlights[lnum].radius - Length(dist);
if (add > 0)
lighting.ambientlight += add;
}
}
if (lighting.ambientlight > 128)
lighting.ambientlight = 128;
if ((lighting.ambientlight + lighting.shadelight) > 192)
lighting.shadelight = 192 - lighting.ambientlight;
R_AliasDrawModel(&lighting);
}
break;
default:
break;
}
}
}
void R_DrawViewModel(void)
{
float lightvec[3] = {-1, 0, 0};
int32_t j;
int32_t lnum;
vec3_t dist;
float add;
dlight_t *dl;
if ((!r_drawviewmodel.value) || r_fov_greater_than_90)
return;
if (cl.items & IT_INVISIBILITY)
return;
if (cl.stats[STAT_HEALTH] <= 0)
return;
currententity = &cl.viewent;
if (!currententity->model)
return;
VectorCopy(currententity->origin, r_entorigin);
VectorSubtract(r_origin, r_entorigin, modelorg);
VectorCopy(vup, viewlightvec);
VectorInverse(viewlightvec);
j = R_LightPoint(currententity->origin);
if (j < 24)
j = 24;
r_viewlighting.ambientlight = j;
r_viewlighting.shadelight = j;
for (lnum = 0; lnum < MAX_DLIGHTS; lnum++)
{
dl = &cl_dlights[lnum];
if (!dl->radius)
continue;
if (!dl->radius)
continue;
if (dl->die < cl.time)
continue;
VectorSubtract(currententity->origin, dl->origin, dist);
add = dl->radius - Length(dist);
if (add > 0)
r_viewlighting.ambientlight += add;
}
if (r_viewlighting.ambientlight > 128)
r_viewlighting.ambientlight = 128;
if ((r_viewlighting.ambientlight + r_viewlighting.shadelight) > 192)
r_viewlighting.shadelight = 192 - r_viewlighting.ambientlight;
r_viewlighting.plightvec = lightvec;
R_AliasDrawModel(&r_viewlighting);
}
int32_t R_BmodelCheckBBox(model_t *clmodel, float *minmaxs)
{
int32_t i;
int32_t *pindex;
int32_t clipflags;
vec3_t acceptpt;
vec3_t rejectpt;
double d;
clipflags = 0;
if ((currententity->angles[0] || currententity->angles[1]) || currententity->angles[2])
{
for (i = 0; i < 4; i++)
{
d = DotProduct(currententity->origin, view_clipplanes[i].normal);
d -= view_clipplanes[i].dist;
if (d <= (-clmodel->radius))
return BMODEL_FULLY_CLIPPED;
if (d <= clmodel->radius)
clipflags |= 1 << i;
}
}
else
{
for (i = 0; i < 4; i++)
{
pindex = pfrustum_indexes[i];
rejectpt[0] = minmaxs[pindex[0]];
rejectpt[1] = minmaxs[pindex[1]];
rejectpt[2] = minmaxs[pindex[2]];
d = DotProduct(rejectpt, view_clipplanes[i].normal);
d -= view_clipplanes[i].dist;
if (d <= 0)
return BMODEL_FULLY_CLIPPED;
acceptpt[0] = minmaxs[pindex[3 + 0]];
acceptpt[1] = minmaxs[pindex[3 + 1]];
acceptpt[2] = minmaxs[pindex[3 + 2]];
d = DotProduct(acceptpt, view_clipplanes[i].normal);
d -= view_clipplanes[i].dist;
if (d <= 0)
clipflags |= 1 << i;
}
}
return clipflags;
}
void R_DrawBEntitiesOnList(void)
{
int32_t i;
int32_t j;
int32_t k;
int32_t clipflags;
vec3_t oldorigin;
model_t *clmodel;
float minmaxs[6];
if (!r_drawentities.value)
return;
VectorCopy(modelorg, oldorigin);
insubmodel = 1;
r_dlightframecount = r_framecount;
for (i = 0; i < cl_numvisedicts; i++)
{
currententity = cl_visedicts[i];
switch (currententity->model->type)
{
case mod_brush:
clmodel = currententity->model;
for (j = 0; j < 3; j++)
{
minmaxs[j] = currententity->origin[j] + clmodel->mins[j];
minmaxs[3 + j] = currententity->origin[j] + clmodel->maxs[j];
}
clipflags = R_BmodelCheckBBox(clmodel, minmaxs);
if (clipflags != BMODEL_FULLY_CLIPPED)
{
VectorCopy(currententity->origin, r_entorigin);
VectorSubtract(r_origin, r_entorigin, modelorg);
VectorCopy(modelorg, r_worldmodelorg);
r_pcurrentvertbase = clmodel->vertexes;
R_RotateBmodel();
if (clmodel->firstmodelsurface != 0)
{
for (k = 0; k < MAX_DLIGHTS; k++)
{
if ((cl_dlights[k].die < cl.time) || (!cl_dlights[k].radius))
{
continue;
}
R_MarkLights(&cl_dlights[k], 1 << k, clmodel->nodes + clmodel->hulls[0].firstclipnode);
}
}
if (r_drawpolys | r_drawculledpolys)
{
R_ZDrawSubmodelPolys(clmodel);
}
else
{
r_pefragtopnode = 0;
for (j = 0; j < 3; j++)
{
r_emins[j] = minmaxs[j];
r_emaxs[j] = minmaxs[3 + j];
}
R_SplitEntityOnNode2(cl.worldmodel->nodes);
if (r_pefragtopnode)
{
currententity->topnode = r_pefragtopnode;
if (r_pefragtopnode->contents >= 0)
{
r_clipflags = clipflags;
R_DrawSolidClippedSubmodelPolygons(clmodel);
}
else
{
R_DrawSubmodelPolygons(clmodel, clipflags);
}
currententity->topnode = 0;
}
}
VectorCopy(base_vpn, vpn);
VectorCopy(base_vup, vup);
VectorCopy(base_vright, vright);
VectorCopy(base_modelorg, modelorg);
VectorCopy(oldorigin, modelorg);
R_TransformFrustum();
}
break;
default:
break;
}
}
insubmodel = 0;
}
void R_EdgeDrawing(void)
{
edge_t ledges[(NUMSTACKEDGES + ((CACHE_SIZE - 1) / (sizeof(edge_t)))) + 1];
surf_t lsurfs[(NUMSTACKSURFACES + ((CACHE_SIZE - 1) / (sizeof(surf_t)))) + 1];
if (auxedges)
{
r_edges = auxedges;
}
else
{
r_edges = (edge_t *) ALIGN_PTR(&ledges[0], CACHE_SIZE);
}
if (r_surfsonstack)
{
surfaces = (surf_t *) ALIGN_PTR(&lsurfs[0], CACHE_SIZE);
surf_max = &surfaces[r_cnumsurfs];
surfaces--;
R_SurfacePatch();
}
R_BeginEdgeFrame();
if (r_dspeeds.value)
{
rw_time1 = Sys_FloatTime();
}
R_RenderWorld();
if (r_drawculledpolys)
R_ScanEdges();
D_TurnZOn();
if (r_dspeeds.value)
{
rw_time2 = Sys_FloatTime();
db_time1 = rw_time2;
}
R_DrawBEntitiesOnList();
if (r_dspeeds.value)
{
db_time2 = Sys_FloatTime();
se_time1 = db_time2;
}
if (!r_dspeeds.value)
{
VID_UnlockBuffer();
S_ExtraUpdate();
VID_LockBuffer();
}
if (!(r_drawpolys | r_drawculledpolys))
R_ScanEdges();
}
void R_RenderView_(void)
{
uint8_t warpbuffer[WARP_WIDTH * WARP_HEIGHT];
r_warpbuffer = warpbuffer;
if ((r_timegraph.value || r_speeds.value) || r_dspeeds.value)
r_time1 = Sys_FloatTime();
R_SetupFrame();
R_MarkLeaves();
Sys_LowFPPrecision();
if ((!cl_entities[0].model) || (!cl.worldmodel))
Sys_Error("R_RenderView: NULL worldmodel");
if (!r_dspeeds.value)
{
VID_UnlockBuffer();
S_ExtraUpdate();
VID_LockBuffer();
}
R_EdgeDrawing();
if (!r_dspeeds.value)
{
VID_UnlockBuffer();
S_ExtraUpdate();
VID_LockBuffer();
}
if (r_dspeeds.value)
{
se_time2 = Sys_FloatTime();
de_time1 = se_time2;
}
R_DrawEntitiesOnList();
if (r_dspeeds.value)
{
de_time2 = Sys_FloatTime();
dv_time1 = de_time2;
}
R_DrawViewModel();
if (r_dspeeds.value)
{
dv_time2 = Sys_FloatTime();
dp_time1 = Sys_FloatTime();
}
R_DrawParticles();
if (r_dspeeds.value)
dp_time2 = Sys_FloatTime();
if (r_dowarp)
D_WarpScreen();
V_SetContentsColor(r_viewleaf->contents);
if (r_timegraph.value)
R_TimeGraph();
if (r_aliasstats.value)
R_PrintAliasStats();
if (r_speeds.value)
R_PrintTimes();
if (r_dspeeds.value)
R_PrintDSpeeds();
if (r_reportsurfout.value && r_outofsurfaces)
Con_Printf("Short %d surfaces\n", r_outofsurfaces);
if (r_reportedgeout.value && r_outofedges)
Con_Printf("Short roughly %d edges\n", (r_outofedges * 2) / 3);
Sys_HighFPPrecision();
}
void R_RenderView(void)
{
int32_t dummy;
ptrdiff_t delta = ((uint8_t *) (&dummy)) - r_stack_start;
if ((delta < (-10000)) || (delta > 10000))
Sys_Error("R_RenderView: called without enough stack");
if (Hunk_LowMark() & 3)
Sys_Error("Hunk is misaligned");
if (((uintptr_t) (&dummy)) & 3u)
Sys_Error("Stack is misaligned");
if (((uintptr_t) (&r_warpbuffer)) & 3u)
Sys_Error("Globals are misaligned");
R_RenderView_();
}
void R_InitTurb(void)
{
int32_t i;
for (i = 0; i < SIN_BUFFER_SIZE; i++)
{
sintable[i] = AMP + (sin(((i * 3.14159) * 2) / CYCLE) * AMP);
intsintable[i] = AMP2 + (sin(((i * 3.14159) * 2) / CYCLE) * AMP2);
}
}
void R_CheckVariables(void)
{
static float oldbright;
if (r_fullbright.value != oldbright)
{
oldbright = r_fullbright.value;
D_FlushCaches();
}
}
void Show(void)
{
vrect_t vr;
vr.x = (vr.y = 0);
vr.width = vid.width;
vr.height = vid.height;
vr.pnext = 0;
VID_Update(&vr);
}
void R_TimeRefresh_f(void)
{
int32_t i;
float start;
float stop;
float time;
int32_t startangle;
vrect_t vr;
startangle = r_refdef.viewangles[1];
start = Sys_FloatTime();
for (i = 0; i < 128; i++)
{
r_refdef.viewangles[1] = (i / 128.0) * 360.0;
VID_LockBuffer();
R_RenderView();
VID_UnlockBuffer();
vr.x = r_refdef.vrect.x;
vr.y = r_refdef.vrect.y;
vr.width = r_refdef.vrect.width;
vr.height = r_refdef.vrect.height;
vr.pnext = 0;
VID_Update(&vr);
}
stop = Sys_FloatTime();
time = stop - start;
Con_Printf("%f seconds (%f fps)\n", time, 128 / time);
r_refdef.viewangles[1] = startangle;
}
void R_LineGraph(int32_t x, int32_t y, int32_t h)
{
int32_t i;
uint8_t *dest;
int32_t s;
x += r_refdef.vrect.x;
y += r_refdef.vrect.y;
dest = (vid.buffer + (vid.rowbytes * y)) + x;
s = r_graphheight.value;
if (h > s)
h = s;
for (i = 0; i < h; i++, dest -= vid.rowbytes * 2)
{
dest[0] = 0xff;
*(dest - vid.rowbytes) = 0x30;
}
for (; i < s; i++, dest -= vid.rowbytes * 2)
{
dest[0] = 0x30;
*(dest - vid.rowbytes) = 0x30;
}
}
void R_TimeGraph(void)
{
static int32_t timex;
int32_t a;
float r_time2;
static uint8_t r_timings[MAX_TIMINGS];
int32_t x;
r_time2 = Sys_FloatTime();
a = (r_time2 - r_time1) / 0.01;
r_timings[timex] = a;
a = timex;
if (r_refdef.vrect.width <= MAX_TIMINGS)
x = r_refdef.vrect.width - 1;
else
x = r_refdef.vrect.width - ((r_refdef.vrect.width - MAX_TIMINGS) / 2);
do
{
R_LineGraph(x, r_refdef.vrect.height - 2, r_timings[a]);
if (x == 0)
break;
x--;
a--;
if (a == (-1))
a = MAX_TIMINGS - 1;
}
while (a != timex);
timex = (timex + 1) % MAX_TIMINGS;
}
void R_PrintTimes(void)
{
float r_time2;
float ms;
r_time2 = Sys_FloatTime();
ms = 1000 * (r_time2 - r_time1);
Con_Printf("%5.1f ms %3i/%3i/%3i poly %3i surf\n", ms, c_faceclip, r_polycount, r_drawnpolycount, c_surf);
c_surf = 0;
}
void R_PrintDSpeeds(void)
{
float ms;
float dp_time;
float r_time2;
float rw_time;
float db_time;
float se_time;
float de_time;
float dv_time;
r_time2 = Sys_FloatTime();
dp_time = (dp_time2 - dp_time1) * 1000;
rw_time = (rw_time2 - rw_time1) * 1000;
db_time = (db_time2 - db_time1) * 1000;
se_time = (se_time2 - se_time1) * 1000;
de_time = (de_time2 - de_time1) * 1000;
dv_time = (dv_time2 - dv_time1) * 1000;
ms = (r_time2 - r_time1) * 1000;
Con_Printf("%3i %4.1fp %3iw %4.1fb %3is %4.1fe %4.1fv\n", (int32_t) ms, dp_time, (int32_t) rw_time, db_time, (int32_t) se_time, de_time, dv_time);
}
void R_PrintAliasStats(void)
{
Con_Printf("%3i polygon model drawn\n", r_amodels_drawn);
}
void WarpPalette(void)
{
int32_t i;
int32_t j;
uint8_t newpalette[768];
int32_t basecolor[3];
basecolor[0] = 130;
basecolor[1] = 80;
basecolor[2] = 50;
for (i = 0; i < 256; i++)
{
for (j = 0; j < 3; j++)
{
newpalette[(i * 3) + j] = (host_basepal[(i * 3) + j] + basecolor[j]) / 2;
}
}
VID_ShiftPalette(newpalette);
}
void R_TransformFrustum(void)
{
int32_t i;
vec3_t v;
vec3_t v2;
for (i = 0; i < 4; i++)
{
v[0] = screenedge[i].normal[2];
v[1] = -screenedge[i].normal[0];
v[2] = screenedge[i].normal[1];
v2[0] = ((v[1] * vright[0]) + (v[2] * vup[0])) + (v[0] * vpn[0]);
v2[1] = ((v[1] * vright[1]) + (v[2] * vup[1])) + (v[0] * vpn[1]);
v2[2] = ((v[1] * vright[2]) + (v[2] * vup[2])) + (v[0] * vpn[2]);
VectorCopy(v2, view_clipplanes[i].normal);
view_clipplanes[i].dist = DotProduct(modelorg, v2);
}
}
void TransformVector(vec3_t in, vec3_t out)
{
out[0] = DotProduct(in, vright);
out[1] = DotProduct(in, vup);
out[2] = DotProduct(in, vpn);
}
void R_TransformPlane(mplane_t *p, float *normal, float *dist)
{
float d;
d = DotProduct(r_origin, p->normal);
*dist = p->dist - d;
TransformVector(p->normal, normal);
}
void R_SetUpFrustumIndexes(void)
{
int32_t i;
int32_t j;
int32_t *pindex;
pindex = r_frustum_indexes;
for (i = 0; i < 4; i++)
{
for (j = 0; j < 3; j++)
{
if (view_clipplanes[i].normal[j] < 0)
{
pindex[j] = j;
pindex[j + 3] = j + 3;
}
else
{
pindex[j] = j + 3;
pindex[j + 3] = j;
}
}
pfrustum_indexes[i] = pindex;
pindex += 6;
}
}
void R_SetupFrame(void)
{
int32_t edgecount;
vrect_t vrect;
float w;
float h;
if (cl.maxclients > 1)
{
Cvar_Set("r_draworder", "0");
Cvar_Set("r_fullbright", "0");
Cvar_Set("r_ambient", "0");
Cvar_Set("r_drawflat", "0");
}
if (r_numsurfs.value)
{
if ((surface_p - surfaces) > r_maxsurfsseen)
r_maxsurfsseen = surface_p - surfaces;
Con_Printf("Used %d of %d surfs; %d max\n", surface_p - surfaces, surf_max - surfaces, r_maxsurfsseen);
}
if (r_numedges.value)
{
edgecount = edge_p - r_edges;
if (edgecount > r_maxedgesseen)
r_maxedgesseen = edgecount;
Con_Printf("Used %d of %d edges; %d max\n", edgecount, r_numallocatededges, r_maxedgesseen);
}
r_refdef.ambientlight = r_ambient.value;
if (r_refdef.ambientlight < 0)
r_refdef.ambientlight = 0;
if (!sv.active)
r_draworder.value = 0;
R_CheckVariables();
R_AnimateLight();
r_framecount++;
numbtofpolys = 0;
VectorCopy(r_refdef.vieworg, modelorg);
VectorCopy(r_refdef.vieworg, r_origin);
AngleVectors(r_refdef.viewangles, vpn, vright, vup);
r_oldviewleaf = r_viewleaf;
r_viewleaf = Mod_PointInLeaf(r_origin, cl.worldmodel);
r_dowarpold = r_dowarp;
r_dowarp = r_waterwarp.value && (r_viewleaf->contents <= CONTENTS_WATER);
if (((r_dowarp != r_dowarpold) || r_viewchanged) || lcd_x.value)
{
if (r_dowarp)
{
if ((vid.width <= vid.maxwarpwidth) && (vid.height <= vid.maxwarpheight))
{
vrect.x = 0;
vrect.y = 0;
vrect.width = vid.width;
vrect.height = vid.height;
R_ViewChanged(&vrect, sb_lines, vid.aspect);
}
else
{
w = vid.width;
h = vid.height;
if (w > vid.maxwarpwidth)
{
h *= ((float) vid.maxwarpwidth) / w;
w = vid.maxwarpwidth;
}
if (h > vid.maxwarpheight)
{
h = vid.maxwarpheight;
w *= ((float) vid.maxwarpheight) / h;
}
vrect.x = 0;
vrect.y = 0;
vrect.width = (int32_t) w;
vrect.height = (int32_t) h;
R_ViewChanged(&vrect, (int32_t) (((float) sb_lines) * (h / ((float) vid.height))), (vid.aspect * (h / w)) * (((float) vid.width) / ((float) vid.height)));
}
}
else
{
vrect.x = 0;
vrect.y = 0;
vrect.width = vid.width;
vrect.height = vid.height;
R_ViewChanged(&vrect, sb_lines, vid.aspect);
}
r_viewchanged = 0;
}
R_TransformFrustum();
VectorCopy(vpn, base_vpn);
VectorCopy(vright, base_vright);
VectorCopy(vup, base_vup);
VectorCopy(modelorg, base_modelorg);
R_SetSkyFrame();
R_SetUpFrustumIndexes();
r_cache_thrash = 0;
c_faceclip = 0;
d_spanpixcount = 0;
r_polycount = 0;
r_drawnpolycount = 0;
r_wholepolycount = 0;
r_amodels_drawn = 0;
r_outofsurfaces = 0;
r_outofedges = 0;
D_SetupFrame();
}
void R_InitParticles(void)
{
int32_t i;
i = COM_CheckParm("-particles");
if (i)
{
r_numparticles = (int32_t) ((int32_t) strtol(com_argv[i + 1], 0, 0));
if (r_numparticles < ABSOLUTE_MIN_PARTICLES)
r_numparticles = ABSOLUTE_MIN_PARTICLES;
}
else
{
r_numparticles = MAX_PARTICLES;
}
particles = (particle_t *) Hunk_AllocName(r_numparticles * (sizeof(particle_t)), "particles");
}
void R_EntityParticles(entity_t *ent)
{
int32_t count;
int32_t i;
particle_t *p;
float angle;
float sr;
float sp;
float sy;
float cr;
float cp;
float cy;
vec3_t forward;
float dist;
dist = 64;
count = 50;
if (!avelocities[0][0])
{
for (i = 0; i < (NUMVERTEXNORMALS * 3); i++)
avelocities[0][i] = (rand() & 255) * 0.01;
}
for (i = 0; i < NUMVERTEXNORMALS; i++)
{
angle = cl.time * avelocities[i][0];
sy = sin(angle);
cy = cos(angle);
angle = cl.time * avelocities[i][1];
sp = sin(angle);
cp = cos(angle);
angle = cl.time * avelocities[i][2];
sr = sin(angle);
cr = cos(angle);
forward[0] = cp * cy;
forward[1] = cp * sy;
forward[2] = -sp;
if (!free_particles)
return;
p = free_particles;
free_particles = p->next;
p->next = active_particles;
active_particles = p;
p->die = cl.time + 0.01;
p->color = 0x6f;
p->type = pt_explode;
p->org[0] = (ent->origin[0] + (r_avertexnormals[i][0] * dist)) + (forward[0] * beamlength);
p->org[1] = (ent->origin[1] + (r_avertexnormals[i][1] * dist)) + (forward[1] * beamlength);
p->org[2] = (ent->origin[2] + (r_avertexnormals[i][2] * dist)) + (forward[2] * beamlength);
}
}
void R_ClearParticles(void)
{
int32_t i;
free_particles = &particles[0];
active_particles = 0;
for (i = 0; i < r_numparticles; i++)
particles[i].next = &particles[i + 1];
particles[r_numparticles - 1].next = 0;
}
void R_ReadPointFile_f(void)
{
FILE *f;
vec3_t org;
int32_t r;
int32_t c;
particle_t *p;
char name[MAX_OSPATH];
sprintf(name, "maps/%s.pts", sv.name);
COM_FOpenFile(name, &f);
if (!f)
{
Con_Printf("couldn't open %s\n", name);
return;
}
Con_Printf("Reading %s...\n", name);
c = 0;
for (;;)
{
r = fscanf(f, "%f %f %f\n", &org[0], &org[1], &org[2]);
if (r != 3)
break;
c++;
if (!free_particles)
{
Con_Printf("Not enough free particles\n");
break;
}
p = free_particles;
free_particles = p->next;
p->next = active_particles;
active_particles = p;
p->die = 99999;
p->color = (-c) & 15;
p->type = pt_static;
VectorCopy(vec3_origin, p->vel);
VectorCopy(org, p->org);
}
fclose(f);
Con_Printf("%i points read\n", c);
}
void R_ParseParticleEffect(void)
{
vec3_t org;
vec3_t dir;
int32_t i;
int32_t count;
int32_t msgcount;
int32_t color;
for (i = 0; i < 3; i++)
org[i] = MSG_ReadCoord();
for (i = 0; i < 3; i++)
dir[i] = MSG_ReadChar() * (1.0 / 16);
msgcount = MSG_ReadByte();
color = MSG_ReadByte();
if (msgcount == 255)
count = 1024;
else
count = msgcount;
R_RunParticleEffect(org, dir, color, count);
}
void R_ParticleExplosion(vec3_t org)
{
int32_t i;
int32_t j;
particle_t *p;
for (i = 0; i < 1024; i++)
{
if (!free_particles)
return;
p = free_particles;
free_particles = p->next;
p->next = active_particles;
active_particles = p;
p->die = cl.time + 5;
p->color = ramp1[0];
p->ramp = rand() & 3;
if (i & 1)
{
p->type = pt_explode;
for (j = 0; j < 3; j++)
{
p->org[j] = org[j] + ((rand() % 32) - 16);
p->vel[j] = (rand() % 512) - 256;
}
}
else
{
p->type = pt_explode2;
for (j = 0; j < 3; j++)
{
p->org[j] = org[j] + ((rand() % 32) - 16);
p->vel[j] = (rand() % 512) - 256;
}
}
}
}
void R_ParticleExplosion2(vec3_t org, int32_t colorStart, int32_t colorLength)
{
int32_t i;
int32_t j;
particle_t *p;
int32_t colorMod = 0;
for (i = 0; i < 512; i++)
{
if (!free_particles)
return;
p = free_particles;
free_particles = p->next;
p->next = active_particles;
active_particles = p;
p->die = cl.time + 0.3;
p->color = colorStart + (colorMod % colorLength);
colorMod++;
p->type = pt_blob;
for (j = 0; j < 3; j++)
{
p->org[j] = org[j] + ((rand() % 32) - 16);
p->vel[j] = (rand() % 512) - 256;
}
}
}
void R_BlobExplosion(vec3_t org)
{
int32_t i;
int32_t j;
particle_t *p;
for (i = 0; i < 1024; i++)
{
if (!free_particles)
return;
p = free_particles;
free_particles = p->next;
p->next = active_particles;
active_particles = p;
p->die = (cl.time + 1) + ((rand() & 8) * 0.05);
if (i & 1)
{
p->type = pt_blob;
p->color = 66 + (rand() % 6);
for (j = 0; j < 3; j++)
{
p->org[j] = org[j] + ((rand() % 32) - 16);
p->vel[j] = (rand() % 512) - 256;
}
}
else
{
p->type = pt_blob2;
p->color = 150 + (rand() % 6);
for (j = 0; j < 3; j++)
{
p->org[j] = org[j] + ((rand() % 32) - 16);
p->vel[j] = (rand() % 512) - 256;
}
}
}
}
void R_RunParticleEffect(vec3_t org, vec3_t dir, int32_t color, int32_t count)
{
int32_t i;
int32_t j;
particle_t *p;
for (i = 0; i < count; i++)
{
if (!free_particles)
return;
p = free_particles;
free_particles = p->next;
p->next = active_particles;
active_particles = p;
if (count == 1024)
{
p->die = cl.time + 5;
p->color = ramp1[0];
p->ramp = rand() & 3;
if (i & 1)
{
p->type = pt_explode;
for (j = 0; j < 3; j++)
{
p->org[j] = org[j] + ((rand() % 32) - 16);
p->vel[j] = (rand() % 512) - 256;
}
}
else
{
p->type = pt_explode2;
for (j = 0; j < 3; j++)
{
p->org[j] = org[j] + ((rand() % 32) - 16);
p->vel[j] = (rand() % 512) - 256;
}
}
}
else
{
p->die = cl.time + (0.1 * (rand() % 5));
p->color = (color & (~7)) + (rand() & 7);
p->type = pt_slowgrav;
for (j = 0; j < 3; j++)
{
p->org[j] = org[j] + ((rand() & 15) - 8);
p->vel[j] = dir[j] * 15;
}
}
}
}
void R_LavaSplash(vec3_t org)
{
int32_t i;
int32_t j;
int32_t k;
particle_t *p;
float vel;
vec3_t dir;
for (i = -16; i < 16; i++)
for (j = -16; j < 16; j++)
for (k = 0; k < 1; k++)
{
if (!free_particles)
return;
p = free_particles;
free_particles = p->next;
p->next = active_particles;
active_particles = p;
p->die = (cl.time + 2) + ((rand() & 31) * 0.02);
p->color = 224 + (rand() & 7);
p->type = pt_slowgrav;
dir[0] = (j * 8) + (rand() & 7);
dir[1] = (i * 8) + (rand() & 7);
dir[2] = 256;
p->org[0] = org[0] + dir[0];
p->org[1] = org[1] + dir[1];
p->org[2] = org[2] + (rand() & 63);
VectorNormalize(dir);
vel = 50 + (rand() & 63);
VectorScale(dir, vel, p->vel);
}
}
void R_TeleportSplash(vec3_t org)
{
int32_t i;
int32_t j;
int32_t k;
particle_t *p;
float vel;
vec3_t dir;
for (i = -16; i < 16; i += 4)
for (j = -16; j < 16; j += 4)
for (k = -24; k < 32; k += 4)
{
if (!free_particles)
return;
p = free_particles;
free_particles = p->next;
p->next = active_particles;
active_particles = p;
p->die = (cl.time + 0.2) + ((rand() & 7) * 0.02);
p->color = 7 + (rand() & 7);
p->type = pt_slowgrav;
dir[0] = j * 8;
dir[1] = i * 8;
dir[2] = k * 8;
p->org[0] = (org[0] + i) + (rand() & 3);
p->org[1] = (org[1] + j) + (rand() & 3);
p->org[2] = (org[2] + k) + (rand() & 3);
VectorNormalize(dir);
vel = 50 + (rand() & 63);
VectorScale(dir, vel, p->vel);
}
}
void R_RocketTrail(vec3_t start, vec3_t end, int32_t type)
{
vec3_t vec;
float len;
int32_t j;
particle_t *p;
int32_t dec;
static int32_t tracercount;
VectorSubtract(end, start, vec);
len = VectorNormalize(vec);
if (type < 128)
dec = 3;
else
{
dec = 1;
type -= 128;
}
while (len > 0)
{
len -= dec;
if (!free_particles)
return;
p = free_particles;
free_particles = p->next;
p->next = active_particles;
active_particles = p;
VectorCopy(vec3_origin, p->vel);
p->die = cl.time + 2;
switch (type)
{
case 0:
p->ramp = rand() & 3;
p->color = ramp3[(int32_t) p->ramp];
p->type = pt_fire;
for (j = 0; j < 3; j++)
p->org[j] = start[j] + ((rand() % 6) - 3);
break;
case 1:
p->ramp = (rand() & 3) + 2;
p->color = ramp3[(int32_t) p->ramp];
p->type = pt_fire;
for (j = 0; j < 3; j++)
p->org[j] = start[j] + ((rand() % 6) - 3);
break;
case 2:
p->type = pt_grav;
p->color = 67 + (rand() & 3);
for (j = 0; j < 3; j++)
p->org[j] = start[j] + ((rand() % 6) - 3);
break;
case 3:
case 5:
p->die = cl.time + 0.5;
p->type = pt_static;
if (type == 3)
p->color = 52 + ((tracercount & 4) << 1);
else
p->color = 230 + ((tracercount & 4) << 1);
tracercount++;
VectorCopy(start, p->org);
if (tracercount & 1)
{
p->vel[0] = 30 * vec[1];
p->vel[1] = 30 * (-vec[0]);
}
else
{
p->vel[0] = 30 * (-vec[1]);
p->vel[1] = 30 * vec[0];
}
break;
case 4:
p->type = pt_grav;
p->color = 67 + (rand() & 3);
for (j = 0; j < 3; j++)
p->org[j] = start[j] + ((rand() % 6) - 3);
len -= 3;
break;
case 6:
p->color = ((9 * 16) + 8) + (rand() & 3);
p->type = pt_static;
p->die = cl.time + 0.3;
for (j = 0; j < 3; j++)
p->org[j] = start[j] + ((rand() & 15) - 8);
break;
}
VectorAdd(start, vec, start);
}
}
void R_DrawParticles(void)
{
particle_t *p;
particle_t *kill;
float grav;
int32_t i;
float time2;
float time3;
float time1;
float dvel;
float frametime;
D_StartParticles();
VectorScale(vright, xscaleshrink, r_pright);
VectorScale(vup, yscaleshrink, r_pup);
VectorCopy(vpn, r_ppn);
frametime = cl.time - cl.oldtime;
time3 = frametime * 15;
time2 = frametime * 10;
time1 = frametime * 5;
grav = (frametime * sv_gravity.value) * 0.05;
dvel = 4 * frametime;
for (;;)
{
kill = active_particles;
if (kill && (kill->die < cl.time))
{
active_particles = kill->next;
kill->next = free_particles;
free_particles = kill;
continue;
}
break;
}
for (p = active_particles; p; p = p->next)
{
for (;;)
{
kill = p->next;
if (kill && (kill->die < cl.time))
{
p->next = kill->next;
kill->next = free_particles;
free_particles = kill;
continue;
}
break;
}
D_DrawParticle(p);
p->org[0] += p->vel[0] * frametime;
p->org[1] += p->vel[1] * frametime;
p->org[2] += p->vel[2] * frametime;
switch (p->type)
{
case pt_static:
break;
case pt_fire:
p->ramp += time1;
if (p->ramp >= 6)
p->die = -1;
else
p->color = ramp3[(int32_t) p->ramp];
p->vel[2] += grav;
break;
case pt_explode:
p->ramp += time2;
if (p->ramp >= 8)
p->die = -1;
else
p->color = ramp1[(int32_t) p->ramp];
for (i = 0; i < 3; i++)
p->vel[i] += p->vel[i] * dvel;
p->vel[2] -= grav;
break;
case pt_explode2:
p->ramp += time3;
if (p->ramp >= 8)
p->die = -1;
else
p->color = ramp2[(int32_t) p->ramp];
for (i = 0; i < 3; i++)
p->vel[i] -= p->vel[i] * frametime;
p->vel[2] -= grav;
break;
case pt_blob:
for (i = 0; i < 3; i++)
p->vel[i] += p->vel[i] * dvel;
p->vel[2] -= grav;
break;
case pt_blob2:
for (i = 0; i < 2; i++)
p->vel[i] -= p->vel[i] * dvel;
p->vel[2] -= grav;
break;
case pt_grav:
case pt_slowgrav:
p->vel[2] -= grav;
break;
}
}
D_EndParticles();
}
void R_InitSky(texture_t *mt)
{
int32_t i;
int32_t j;
uint8_t *src;
src = ((uint8_t *) mt) + mt->offsets[0];
for (i = 0; i < 128; i++)
{
for (j = 0; j < 128; j++)
{
newsky[((i * 256) + j) + 128] = src[((i * 256) + j) + 128];
}
}
for (i = 0; i < 128; i++)
{
for (j = 0; j < 131; j++)
{
if (src[(i * 256) + (j & 0x7F)])
{
bottomsky[(i * 131) + j] = src[(i * 256) + (j & 0x7F)];
bottommask[(i * 131) + j] = 0;
}
else
{
bottomsky[(i * 131) + j] = 0;
bottommask[(i * 131) + j] = 0xff;
}
}
}
r_skysource = newsky;
}
void R_MakeSky(void)
{
int32_t x;
int32_t y;
int32_t ofs;
int32_t baseofs;
int32_t xshift;
int32_t yshift;
uint32_t *pnewsky;
static int32_t xlast = -1;
static int32_t ylast = -1;
xshift = skytime * skyspeed;
yshift = skytime * skyspeed;
if ((xshift == xlast) && (yshift == ylast))
return;
xlast = xshift;
ylast = yshift;
pnewsky = (uint32_t *) (&newsky[0]);
for (y = 0; y < SKYSIZE; y++)
{
baseofs = ((y + yshift) & SKYMASK) * 131;
for (x = 0; x < SKYSIZE; x++)
{
ofs = baseofs + ((x + xshift) & SKYMASK);
*((uint8_t *) pnewsky) = ((*(((uint8_t *) pnewsky) + 128)) & (*((uint8_t *) (&bottommask[ofs])))) | (*((uint8_t *) (&bottomsky[ofs])));
pnewsky = (uint32_t *) (((uint8_t *) pnewsky) + 1);
}
pnewsky += 128 / (sizeof(uint32_t));
}
r_skymade = 1;
}
void R_GenSkyTile(void *pdest)
{
int32_t x;
int32_t y;
int32_t ofs;
int32_t baseofs;
int32_t xshift;
int32_t yshift;
uint32_t *pnewsky;
uint32_t *pd;
xshift = skytime * skyspeed;
yshift = skytime * skyspeed;
pnewsky = (uint32_t *) (&newsky[0]);
pd = (uint32_t *) pdest;
for (y = 0; y < SKYSIZE; y++)
{
baseofs = ((y + yshift) & SKYMASK) * 131;
for (x = 0; x < SKYSIZE; x++)
{
ofs = baseofs + ((x + xshift) & SKYMASK);
*((uint8_t *) pd) = ((*(((uint8_t *) pnewsky) + 128)) & (*((uint8_t *) (&bottommask[ofs])))) | (*((uint8_t *) (&bottomsky[ofs])));
pnewsky = (uint32_t *) (((uint8_t *) pnewsky) + 1);
pd = (uint32_t *) (((uint8_t *) pd) + 1);
}
pnewsky += 128 / (sizeof(uint32_t));
}
}
void R_GenSkyTile16(void *pdest)
{
int32_t x;
int32_t y;
int32_t ofs;
int32_t baseofs;
int32_t xshift;
int32_t yshift;
uint8_t *pnewsky;
uint16_t *pd;
xshift = skytime * skyspeed;
yshift = skytime * skyspeed;
pnewsky = (uint8_t *) (&newsky[0]);
pd = (uint16_t *) pdest;
for (y = 0; y < SKYSIZE; y++)
{
baseofs = ((y + yshift) & SKYMASK) * 131;
for (x = 0; x < SKYSIZE; x++)
{
ofs = baseofs + ((x + xshift) & SKYMASK);
*pd = d_8to16table[((*(pnewsky + 128)) & (*((uint8_t *) (&bottommask[ofs])))) | (*((uint8_t *) (&bottomsky[ofs])))];
pnewsky++;
pd++;
}
pnewsky += TILE_SIZE;
}
}
void R_SetSkyFrame(void)
{
int32_t g;
int32_t s1;
int32_t s2;
float temp;
skyspeed = iskyspeed;
skyspeed2 = iskyspeed2;
g = GreatestCommonDivisor(iskyspeed, iskyspeed2);
s1 = iskyspeed / g;
s2 = iskyspeed2 / g;
temp = (SKYSIZE * s1) * s2;
skytime = cl.time - (((int32_t) (cl.time / temp)) * temp);
r_skymade = 0;
}
void R_RotateSprite(float beamlength)
{
vec3_t vec;
if (beamlength == 0.0)
return;
VectorScale(r_spritedesc.vpn, -beamlength, vec);
VectorAdd(r_entorigin, vec, r_entorigin);
VectorSubtract(modelorg, vec, modelorg);
}
int32_t R_ClipSpriteFace(int32_t nump, clipplane_t *pclipplane)
{
int32_t i;
int32_t outcount;
float dists[MAXWORKINGVERTS + 1];
float frac;
float clipdist;
float *pclipnormal;
float *in;
float *instep;
float *outstep;
float *vert2;
clipdist = pclipplane->dist;
pclipnormal = pclipplane->normal;
if (clip_current)
{
in = clip_verts[1][0];
outstep = clip_verts[0][0];
clip_current = 0;
}
else
{
in = clip_verts[0][0];
outstep = clip_verts[1][0];
clip_current = 1;
}
instep = in;
for (i = 0; i < nump; i++, instep += (sizeof(vec5_t)) / (sizeof(float)))
{
dists[i] = DotProduct(instep, pclipnormal) - clipdist;
}
dists[nump] = dists[0];
memcpy(instep, in, sizeof(vec5_t));
instep = in;
outcount = 0;
for (i = 0; i < nump; i++, instep += (sizeof(vec5_t)) / (sizeof(float)))
{
if (dists[i] >= 0)
{
memcpy(outstep, instep, sizeof(vec5_t));
outstep += (sizeof(vec5_t)) / (sizeof(float));
outcount++;
}
if ((dists[i] == 0) || (dists[i + 1] == 0))
continue;
if ((dists[i] > 0) == (dists[i + 1] > 0))
continue;
frac = dists[i] / (dists[i] - dists[i + 1]);
vert2 = instep + ((sizeof(vec5_t)) / (sizeof(float)));
outstep[0] = instep[0] + (frac * (vert2[0] - instep[0]));
outstep[1] = instep[1] + (frac * (vert2[1] - instep[1]));
outstep[2] = instep[2] + (frac * (vert2[2] - instep[2]));
outstep[3] = instep[3] + (frac * (vert2[3] - instep[3]));
outstep[4] = instep[4] + (frac * (vert2[4] - instep[4]));
outstep += (sizeof(vec5_t)) / (sizeof(float));
outcount++;
}
return outcount;
}
void R_SetupAndDrawSprite()
{
int32_t i;
int32_t nump;
float dot;
float scale;
float *pv;
vec5_t *pverts;
vec3_t left;
vec3_t up;
vec3_t right;
vec3_t down;
vec3_t transformed;
vec3_t local;
emitpoint_t outverts[MAXWORKINGVERTS + 1];
emitpoint_t *pout;
dot = DotProduct(r_spritedesc.vpn, modelorg);
if (dot >= 0)
return;
VectorScale(r_spritedesc.vright, r_spritedesc.pspriteframe->right, right);
VectorScale(r_spritedesc.vup, r_spritedesc.pspriteframe->up, up);
VectorScale(r_spritedesc.vright, r_spritedesc.pspriteframe->left, left);
VectorScale(r_spritedesc.vup, r_spritedesc.pspriteframe->down, down);
pverts = clip_verts[0];
pverts[0][0] = (r_entorigin[0] + up[0]) + left[0];
pverts[0][1] = (r_entorigin[1] + up[1]) + left[1];
pverts[0][2] = (r_entorigin[2] + up[2]) + left[2];
pverts[0][3] = 0;
pverts[0][4] = 0;
pverts[1][0] = (r_entorigin[0] + up[0]) + right[0];
pverts[1][1] = (r_entorigin[1] + up[1]) + right[1];
pverts[1][2] = (r_entorigin[2] + up[2]) + right[2];
pverts[1][3] = sprite_width;
pverts[1][4] = 0;
pverts[2][0] = (r_entorigin[0] + down[0]) + right[0];
pverts[2][1] = (r_entorigin[1] + down[1]) + right[1];
pverts[2][2] = (r_entorigin[2] + down[2]) + right[2];
pverts[2][3] = sprite_width;
pverts[2][4] = sprite_height;
pverts[3][0] = (r_entorigin[0] + down[0]) + left[0];
pverts[3][1] = (r_entorigin[1] + down[1]) + left[1];
pverts[3][2] = (r_entorigin[2] + down[2]) + left[2];
pverts[3][3] = 0;
pverts[3][4] = sprite_height;
nump = 4;
clip_current = 0;
for (i = 0; i < 4; i++)
{
nump = R_ClipSpriteFace(nump, &view_clipplanes[i]);
if (nump < 3)
return;
if (nump >= MAXWORKINGVERTS)
Sys_Error("R_SetupAndDrawSprite: too many points");
}
pv = &clip_verts[clip_current][0][0];
r_spritedesc.nearzi = -999999;
for (i = 0; i < nump; i++)
{
VectorSubtract(pv, r_origin, local);
TransformVector(local, transformed);
if (transformed[2] < NEAR_CLIP)
transformed[2] = NEAR_CLIP;
pout = &outverts[i];
pout->zi = 1.0 / transformed[2];
if (pout->zi > r_spritedesc.nearzi)
r_spritedesc.nearzi = pout->zi;
pout->s = pv[3];
pout->t = pv[4];
scale = xscale * pout->zi;
pout->u = xcenter + (scale * transformed[0]);
scale = yscale * pout->zi;
pout->v = ycenter - (scale * transformed[1]);
pv += (sizeof(vec5_t)) / (sizeof(*pv));
}
r_spritedesc.nump = nump;
r_spritedesc.pverts = outverts;
D_DrawSprite();
}
mspriteframe_t *R_GetSpriteframe(msprite_t *psprite)
{
mspritegroup_t *pspritegroup;
mspriteframe_t *pspriteframe;
int32_t i;
int32_t numframes;
int32_t frame;
float *pintervals;
float fullinterval;
float targettime;
float time;
frame = currententity->frame;
if ((frame >= psprite->numframes) || (frame < 0))
{
Con_Printf("R_DrawSprite: no such frame %d\n", frame);
frame = 0;
}
if (psprite->frames[frame].type == SPR_SINGLE)
{
pspriteframe = psprite->frames[frame].frameptr;
}
else
{
pspritegroup = (mspritegroup_t *) psprite->frames[frame].frameptr;
pintervals = pspritegroup->intervals;
numframes = pspritegroup->numframes;
fullinterval = pintervals[numframes - 1];
time = cl.time + currententity->syncbase;
targettime = time - (((int32_t) (time / fullinterval)) * fullinterval);
for (i = 0; i < (numframes - 1); i++)
{
if (pintervals[i] > targettime)
break;
}
pspriteframe = pspritegroup->frames[i];
}
return pspriteframe;
}
void R_DrawSprite(void)
{
int32_t i;
msprite_t *psprite;
vec3_t tvec;
float dot;
float angle;
float sr;
float cr;
psprite = currententity->model->cache.data;
r_spritedesc.pspriteframe = R_GetSpriteframe(psprite);
sprite_width = r_spritedesc.pspriteframe->width;
sprite_height = r_spritedesc.pspriteframe->height;
if (psprite->type == SPR_FACING_UPRIGHT)
{
tvec[0] = -modelorg[0];
tvec[1] = -modelorg[1];
tvec[2] = -modelorg[2];
VectorNormalize(tvec);
dot = tvec[2];
if ((dot > 0.999848) || (dot < (-0.999848)))
return;
r_spritedesc.vup[0] = 0;
r_spritedesc.vup[1] = 0;
r_spritedesc.vup[2] = 1;
r_spritedesc.vright[0] = tvec[1];
r_spritedesc.vright[1] = -tvec[0];
r_spritedesc.vright[2] = 0;
VectorNormalize(r_spritedesc.vright);
r_spritedesc.vpn[0] = -r_spritedesc.vright[1];
r_spritedesc.vpn[1] = r_spritedesc.vright[0];
r_spritedesc.vpn[2] = 0;
}
else
if (psprite->type == SPR_VP_PARALLEL)
{
for (i = 0; i < 3; i++)
{
r_spritedesc.vup[i] = vup[i];
r_spritedesc.vright[i] = vright[i];
r_spritedesc.vpn[i] = vpn[i];
}
}
else
if (psprite->type == SPR_VP_PARALLEL_UPRIGHT)
{
dot = vpn[2];
if ((dot > 0.999848) || (dot < (-0.999848)))
return;
r_spritedesc.vup[0] = 0;
r_spritedesc.vup[1] = 0;
r_spritedesc.vup[2] = 1;
r_spritedesc.vright[0] = vpn[1];
r_spritedesc.vright[1] = -vpn[0];
r_spritedesc.vright[2] = 0;
VectorNormalize(r_spritedesc.vright);
r_spritedesc.vpn[0] = -r_spritedesc.vright[1];
r_spritedesc.vpn[1] = r_spritedesc.vright[0];
r_spritedesc.vpn[2] = 0;
}
else
if (psprite->type == SPR_ORIENTED)
{
AngleVectors(currententity->angles, r_spritedesc.vpn, r_spritedesc.vright, r_spritedesc.vup);
}
else
if (psprite->type == SPR_VP_PARALLEL_ORIENTED)
{
angle = currententity->angles[ROLL] * ((M_PI * 2) / 360);
sr = sin(angle);
cr = cos(angle);
for (i = 0; i < 3; i++)
{
r_spritedesc.vpn[i] = vpn[i];
r_spritedesc.vright[i] = (vright[i] * cr) + (vup[i] * sr);
r_spritedesc.vup[i] = (vright[i] * (-sr)) + (vup[i] * cr);
}
}
else
{
Sys_Error("R_DrawSprite: Bad sprite type %d", psprite->type);
}
R_RotateSprite(psprite->beamlength);
R_SetupAndDrawSprite();
}
void R_AddDynamicLights(void)
{
msurface_t *surf;
int32_t lnum;
int32_t sd;
int32_t td;
float dist;
float rad;
float minlight;
vec3_t impact;
vec3_t local;
int32_t s;
int32_t t;
int32_t i;
int32_t smax;
int32_t tmax;
mtexinfo_t *tex;
surf = r_drawsurf.surf;
smax = (surf->extents[0] >> 4) + 1;
tmax = (surf->extents[1] >> 4) + 1;
tex = surf->texinfo;
for (lnum = 0; lnum < MAX_DLIGHTS; lnum++)
{
if (!(surf->dlightbits & (1 << lnum)))
continue;
rad = cl_dlights[lnum].radius;
dist = DotProduct(cl_dlights[lnum].origin, surf->plane->normal) - surf->plane->dist;
rad -= fabs(dist);
minlight = cl_dlights[lnum].minlight;
if (rad < minlight)
continue;
minlight = rad - minlight;
for (i = 0; i < 3; i++)
{
impact[i] = cl_dlights[lnum].origin[i] - (surf->plane->normal[i] * dist);
}
local[0] = DotProduct(impact, tex->vecs[0]) + tex->vecs[0][3];
local[1] = DotProduct(impact, tex->vecs[1]) + tex->vecs[1][3];
local[0] -= surf->texturemins[0];
local[1] -= surf->texturemins[1];
for (t = 0; t < tmax; t++)
{
td = local[1] - (t * 16);
if (td < 0)
td = -td;
for (s = 0; s < smax; s++)
{
sd = local[0] - (s * 16);
if (sd < 0)
sd = -sd;
if (sd > td)
dist = sd + (td >> 1);
else
dist = td + (sd >> 1);
if (dist < minlight)
blocklights[(t * smax) + s] += (rad - dist) * 256;
}
}
}
}
void R_BuildLightMap(void)
{
int32_t smax;
int32_t tmax;
int32_t t;
int32_t i;
int32_t size;
uint8_t *lightmap;
uint32_t scale;
int32_t maps;
msurface_t *surf;
surf = r_drawsurf.surf;
smax = (surf->extents[0] >> 4) + 1;
tmax = (surf->extents[1] >> 4) + 1;
size = smax * tmax;
lightmap = surf->samples;
if (r_fullbright.value || (!cl.worldmodel->lightdata))
{
for (i = 0; i < size; i++)
blocklights[i] = 0;
return;
}
for (i = 0; i < size; i++)
blocklights[i] = r_refdef.ambientlight << 8;
if (lightmap)
for (maps = 0; (maps < MAXLIGHTMAPS) && (surf->styles[maps] != 255); maps++)
{
scale = r_drawsurf.lightadj[maps];
for (i = 0; i < size; i++)
blocklights[i] += lightmap[i] * scale;
lightmap += size;
}
if (surf->dlightframe == r_framecount)
R_AddDynamicLights();
for (i = 0; i < size; i++)
{
t = ((255 * 256) - ((int32_t) blocklights[i])) >> (8 - VID_CBITS);
if (t < (1 << 6))
t = 1 << 6;
blocklights[i] = t;
}
}
texture_t *R_TextureAnimation(texture_t *base)
{
int32_t reletive;
int32_t count;
if (currententity->frame)
{
if (base->alternate_anims)
base = base->alternate_anims;
}
if (!base->anim_total)
return base;
reletive = ((int32_t) (cl.time * 10)) % base->anim_total;
count = 0;
while ((base->anim_min > reletive) || (base->anim_max <= reletive))
{
base = base->anim_next;
if (!base)
Sys_Error("R_TextureAnimation: broken cycle");
if ((++count) > 100)
Sys_Error("R_TextureAnimation: infinite cycle");
}
return base;
}
void R_DrawSurface(void)
{
unsigned char *basetptr;
int32_t smax;
int32_t tmax;
int32_t twidth;
int32_t u;
int32_t soffset;
int32_t basetoffset;
int32_t texwidth;
int32_t horzblockstep;
unsigned char *pcolumndest;
void (*pblockdrawer)(void);
texture_t *mt;
R_BuildLightMap();
surfrowbytes = r_drawsurf.rowbytes;
mt = r_drawsurf.texture;
r_source = ((uint8_t *) mt) + mt->offsets[r_drawsurf.surfmip];
texwidth = mt->width >> r_drawsurf.surfmip;
blocksize = 16 >> r_drawsurf.surfmip;
blockdivshift = 4 - r_drawsurf.surfmip;
blockdivmask = (1 << blockdivshift) - 1;
r_lightwidth = (r_drawsurf.surf->extents[0] >> 4) + 1;
r_numhblocks = r_drawsurf.surfwidth >> blockdivshift;
r_numvblocks = r_drawsurf.surfheight >> blockdivshift;
if (r_pixbytes == 1)
{
pblockdrawer = surfmiptable[r_drawsurf.surfmip];
horzblockstep = blocksize;
}
else
{
pblockdrawer = R_DrawSurfaceBlock16;
horzblockstep = blocksize << 1;
}
smax = mt->width >> r_drawsurf.surfmip;
twidth = texwidth;
tmax = mt->height >> r_drawsurf.surfmip;
sourcetstep = texwidth;
r_stepback = tmax * twidth;
r_sourcemax = r_source + (tmax * smax);
soffset = r_drawsurf.surf->texturemins[0];
basetoffset = r_drawsurf.surf->texturemins[1];
soffset = ((soffset >> r_drawsurf.surfmip) + (smax << 16)) % smax;
basetptr = &r_source[(((basetoffset >> r_drawsurf.surfmip) + (tmax << 16)) % tmax) * twidth];
pcolumndest = r_drawsurf.surfdat;
for (u = 0; u < r_numhblocks; u++)
{
r_lightptr = blocklights + u;
prowdestbase = pcolumndest;
pbasesource = basetptr + soffset;
(*pblockdrawer)();
soffset = soffset + blocksize;
if (soffset >= smax)
soffset = 0;
pcolumndest += horzblockstep;
}
}
void R_DrawSurfaceBlock8_mip0(void)
{
int32_t v;
int32_t i;
int32_t b;
int32_t lightstep;
int32_t lighttemp;
int32_t light;
unsigned char pix;
unsigned char *psource;
unsigned char *prowdest;
psource = pbasesource;
prowdest = prowdestbase;
for (v = 0; v < r_numvblocks; v++)
{
lightleft = r_lightptr[0];
lightright = r_lightptr[1];
r_lightptr += r_lightwidth;
lightleftstep = (r_lightptr[0] - lightleft) >> 4;
lightrightstep = (r_lightptr[1] - lightright) >> 4;
for (i = 0; i < 16; i++)
{
lighttemp = lightleft - lightright;
lightstep = lighttemp >> 4;
light = lightright;
for (b = 15; b >= 0; b--)
{
pix = psource[b];
prowdest[b] = ((unsigned char *) vid.colormap)[(light & 0xFF00) + pix];
light += lightstep;
}
psource += sourcetstep;
lightright += lightrightstep;
lightleft += lightleftstep;
prowdest += surfrowbytes;
}
if (psource >= r_sourcemax)
psource -= r_stepback;
}
}
void R_DrawSurfaceBlock8_mip1(void)
{
int32_t v;
int32_t i;
int32_t b;
int32_t lightstep;
int32_t lighttemp;
int32_t light;
unsigned char pix;
unsigned char *psource;
unsigned char *prowdest;
psource = pbasesource;
prowdest = prowdestbase;
for (v = 0; v < r_numvblocks; v++)
{
lightleft = r_lightptr[0];
lightright = r_lightptr[1];
r_lightptr += r_lightwidth;
lightleftstep = (r_lightptr[0] - lightleft) >> 3;
lightrightstep = (r_lightptr[1] - lightright) >> 3;
for (i = 0; i < 8; i++)
{
lighttemp = lightleft - lightright;
lightstep = lighttemp >> 3;
light = lightright;
for (b = 7; b >= 0; b--)
{
pix = psource[b];
prowdest[b] = ((unsigned char *) vid.colormap)[(light & 0xFF00) + pix];
light += lightstep;
}
psource += sourcetstep;
lightright += lightrightstep;
lightleft += lightleftstep;
prowdest += surfrowbytes;
}
if (psource >= r_sourcemax)
psource -= r_stepback;
}
}
void R_DrawSurfaceBlock8_mip2(void)
{
int32_t v;
int32_t i;
int32_t b;
int32_t lightstep;
int32_t lighttemp;
int32_t light;
unsigned char pix;
unsigned char *psource;
unsigned char *prowdest;
psource = pbasesource;
prowdest = prowdestbase;
for (v = 0; v < r_numvblocks; v++)
{
lightleft = r_lightptr[0];
lightright = r_lightptr[1];
r_lightptr += r_lightwidth;
lightleftstep = (r_lightptr[0] - lightleft) >> 2;
lightrightstep = (r_lightptr[1] - lightright) >> 2;
for (i = 0; i < 4; i++)
{
lighttemp = lightleft - lightright;
lightstep = lighttemp >> 2;
light = lightright;
for (b = 3; b >= 0; b--)
{
pix = psource[b];
prowdest[b] = ((unsigned char *) vid.colormap)[(light & 0xFF00) + pix];
light += lightstep;
}
psource += sourcetstep;
lightright += lightrightstep;
lightleft += lightleftstep;
prowdest += surfrowbytes;
}
if (psource >= r_sourcemax)
psource -= r_stepback;
}
}
void R_DrawSurfaceBlock8_mip3(void)
{
int32_t v;
int32_t i;
int32_t b;
int32_t lightstep;
int32_t lighttemp;
int32_t light;
unsigned char pix;
unsigned char *psource;
unsigned char *prowdest;
psource = pbasesource;
prowdest = prowdestbase;
for (v = 0; v < r_numvblocks; v++)
{
lightleft = r_lightptr[0];
lightright = r_lightptr[1];
r_lightptr += r_lightwidth;
lightleftstep = (r_lightptr[0] - lightleft) >> 1;
lightrightstep = (r_lightptr[1] - lightright) >> 1;
for (i = 0; i < 2; i++)
{
lighttemp = lightleft - lightright;
lightstep = lighttemp >> 1;
light = lightright;
for (b = 1; b >= 0; b--)
{
pix = psource[b];
prowdest[b] = ((unsigned char *) vid.colormap)[(light & 0xFF00) + pix];
light += lightstep;
}
psource += sourcetstep;
lightright += lightrightstep;
lightleft += lightleftstep;
prowdest += surfrowbytes;
}
if (psource >= r_sourcemax)
psource -= r_stepback;
}
}
void R_DrawSurfaceBlock16(void)
{
int32_t k;
unsigned char *psource;
int32_t lighttemp;
int32_t lightstep;
int32_t light;
uint16_t *prowdest;
prowdest = (uint16_t *) prowdestbase;
for (k = 0; k < blocksize; k++)
{
uint16_t *pdest;
unsigned char pix;
int32_t b;
psource = pbasesource;
lighttemp = lightright - lightleft;
lightstep = lighttemp >> blockdivshift;
light = lightleft;
pdest = prowdest;
for (b = 0; b < blocksize; b++)
{
pix = *psource;
*pdest = vid.colormap16[(light & 0xFF00) + pix];
psource += sourcesstep;
pdest++;
light += lightstep;
}
pbasesource += sourcetstep;
lightright += lightrightstep;
lightleft += lightleftstep;
prowdest = (uint16_t *) (((int32_t) prowdest) + surfrowbytes);
}
prowdestbase = prowdest;
}
void R_GenTurbTile(pixel_t *pbasetex, void *pdest)
{
int32_t *turb;
int32_t i;
int32_t j;
int32_t s;
int32_t t;
uint8_t *pd;
turb = sintable + (((int32_t) (cl.time * SPEED)) & (CYCLE - 1));
pd = (uint8_t *) pdest;
for (i = 0; i < TILE_SIZE; i++)
{
for (j = 0; j < TILE_SIZE; j++)
{
s = (((j << 16) + turb[i & (CYCLE - 1)]) >> 16) & 63;
t = (((i << 16) + turb[j & (CYCLE - 1)]) >> 16) & 63;
*(pd++) = *((pbasetex + (t << 6)) + s);
}
}
}
void R_GenTurbTile16(pixel_t *pbasetex, void *pdest)
{
int32_t *turb;
int32_t i;
int32_t j;
int32_t s;
int32_t t;
uint16_t *pd;
turb = sintable + (((int32_t) (cl.time * SPEED)) & (CYCLE - 1));
pd = (uint16_t *) pdest;
for (i = 0; i < TILE_SIZE; i++)
{
for (j = 0; j < TILE_SIZE; j++)
{
s = (((j << 16) + turb[i & (CYCLE - 1)]) >> 16) & 63;
t = (((i << 16) + turb[j & (CYCLE - 1)]) >> 16) & 63;
*(pd++) = d_8to16table[*((pbasetex + (t << 6)) + s)];
}
}
}
void R_GenTile(msurface_t *psurf, void *pdest)
{
if (psurf->flags & SURF_DRAWTURB)
{
if (r_pixbytes == 1)
{
R_GenTurbTile((pixel_t *) (((uint8_t *) psurf->texinfo->texture) + psurf->texinfo->texture->offsets[0]), pdest);
}
else
{
R_GenTurbTile16((pixel_t *) (((uint8_t *) psurf->texinfo->texture) + psurf->texinfo->texture->offsets[0]), pdest);
}
}
else
if (psurf->flags & SURF_DRAWSKY)
{
if (r_pixbytes == 1)
{
R_GenSkyTile(pdest);
}
else
{
R_GenSkyTile16(pdest);
}
}
else
{
Sys_Error("Unknown tile type");
}
}
void Sbar_ShowScores(void)
{
if (sb_showscores)
return;
sb_showscores = 1;
sb_updates = 0;
}
void Sbar_DontShowScores(void)
{
sb_showscores = 0;
sb_updates = 0;
}
void Sbar_Changed(void)
{
sb_updates = 0;
}
void Sbar_Init(void)
{
int32_t i;
for (i = 0; i < 10; i++)
{
sb_nums[0][i] = Draw_PicFromWad(va("num_%i", i));
sb_nums[1][i] = Draw_PicFromWad(va("anum_%i", i));
}
sb_nums[0][10] = Draw_PicFromWad("num_minus");
sb_nums[1][10] = Draw_PicFromWad("anum_minus");
sb_colon = Draw_PicFromWad("num_colon");
sb_slash = Draw_PicFromWad("num_slash");
sb_weapons[0][0] = Draw_PicFromWad("inv_shotgun");
sb_weapons[0][1] = Draw_PicFromWad("inv_sshotgun");
sb_weapons[0][2] = Draw_PicFromWad("inv_nailgun");
sb_weapons[0][3] = Draw_PicFromWad("inv_snailgun");
sb_weapons[0][4] = Draw_PicFromWad("inv_rlaunch");
sb_weapons[0][5] = Draw_PicFromWad("inv_srlaunch");
sb_weapons[0][6] = Draw_PicFromWad("inv_lightng");
sb_weapons[1][0] = Draw_PicFromWad("inv2_shotgun");
sb_weapons[1][1] = Draw_PicFromWad("inv2_sshotgun");
sb_weapons[1][2] = Draw_PicFromWad("inv2_nailgun");
sb_weapons[1][3] = Draw_PicFromWad("inv2_snailgun");
sb_weapons[1][4] = Draw_PicFromWad("inv2_rlaunch");
sb_weapons[1][5] = Draw_PicFromWad("inv2_srlaunch");
sb_weapons[1][6] = Draw_PicFromWad("inv2_lightng");
for (i = 0; i < 5; i++)
{
sb_weapons[2 + i][0] = Draw_PicFromWad(va("inva%i_shotgun", i + 1));
sb_weapons[2 + i][1] = Draw_PicFromWad(va("inva%i_sshotgun", i + 1));
sb_weapons[2 + i][2] = Draw_PicFromWad(va("inva%i_nailgun", i + 1));
sb_weapons[2 + i][3] = Draw_PicFromWad(va("inva%i_snailgun", i + 1));
sb_weapons[2 + i][4] = Draw_PicFromWad(va("inva%i_rlaunch", i + 1));
sb_weapons[2 + i][5] = Draw_PicFromWad(va("inva%i_srlaunch", i + 1));
sb_weapons[2 + i][6] = Draw_PicFromWad(va("inva%i_lightng", i + 1));
}
sb_ammo[0] = Draw_PicFromWad("sb_shells");
sb_ammo[1] = Draw_PicFromWad("sb_nails");
sb_ammo[2] = Draw_PicFromWad("sb_rocket");
sb_ammo[3] = Draw_PicFromWad("sb_cells");
sb_armor[0] = Draw_PicFromWad("sb_armor1");
sb_armor[1] = Draw_PicFromWad("sb_armor2");
sb_armor[2] = Draw_PicFromWad("sb_armor3");
sb_items[0] = Draw_PicFromWad("sb_key1");
sb_items[1] = Draw_PicFromWad("sb_key2");
sb_items[2] = Draw_PicFromWad("sb_invis");
sb_items[3] = Draw_PicFromWad("sb_invuln");
sb_items[4] = Draw_PicFromWad("sb_suit");
sb_items[5] = Draw_PicFromWad("sb_quad");
sb_sigil[0] = Draw_PicFromWad("sb_sigil1");
sb_sigil[1] = Draw_PicFromWad("sb_sigil2");
sb_sigil[2] = Draw_PicFromWad("sb_sigil3");
sb_sigil[3] = Draw_PicFromWad("sb_sigil4");
sb_faces[4][0] = Draw_PicFromWad("face1");
sb_faces[4][1] = Draw_PicFromWad("face_p1");
sb_faces[3][0] = Draw_PicFromWad("face2");
sb_faces[3][1] = Draw_PicFromWad("face_p2");
sb_faces[2][0] = Draw_PicFromWad("face3");
sb_faces[2][1] = Draw_PicFromWad("face_p3");
sb_faces[1][0] = Draw_PicFromWad("face4");
sb_faces[1][1] = Draw_PicFromWad("face_p4");
sb_faces[0][0] = Draw_PicFromWad("face5");
sb_faces[0][1] = Draw_PicFromWad("face_p5");
sb_face_invis = Draw_PicFromWad("face_invis");
sb_face_invuln = Draw_PicFromWad("face_invul2");
sb_face_invis_invuln = Draw_PicFromWad("face_inv2");
sb_face_quad = Draw_PicFromWad("face_quad");
Cmd_AddCommand("+showscores", Sbar_ShowScores);
Cmd_AddCommand("-showscores", Sbar_DontShowScores);
sb_sbar = Draw_PicFromWad("sbar");
sb_ibar = Draw_PicFromWad("ibar");
sb_scorebar = Draw_PicFromWad("scorebar");
if (hipnotic)
{
hsb_weapons[0][0] = Draw_PicFromWad("inv_laser");
hsb_weapons[0][1] = Draw_PicFromWad("inv_mjolnir");
hsb_weapons[0][2] = Draw_PicFromWad("inv_gren_prox");
hsb_weapons[0][3] = Draw_PicFromWad("inv_prox_gren");
hsb_weapons[0][4] = Draw_PicFromWad("inv_prox");
hsb_weapons[1][0] = Draw_PicFromWad("inv2_laser");
hsb_weapons[1][1] = Draw_PicFromWad("inv2_mjolnir");
hsb_weapons[1][2] = Draw_PicFromWad("inv2_gren_prox");
hsb_weapons[1][3] = Draw_PicFromWad("inv2_prox_gren");
hsb_weapons[1][4] = Draw_PicFromWad("inv2_prox");
for (i = 0; i < 5; i++)
{
hsb_weapons[2 + i][0] = Draw_PicFromWad(va("inva%i_laser", i + 1));
hsb_weapons[2 + i][1] = Draw_PicFromWad(va("inva%i_mjolnir", i + 1));
hsb_weapons[2 + i][2] = Draw_PicFromWad(va("inva%i_gren_prox", i + 1));
hsb_weapons[2 + i][3] = Draw_PicFromWad(va("inva%i_prox_gren", i + 1));
hsb_weapons[2 + i][4] = Draw_PicFromWad(va("inva%i_prox", i + 1));
}
hsb_items[0] = Draw_PicFromWad("sb_wsuit");
hsb_items[1] = Draw_PicFromWad("sb_eshld");
}
if (rogue)
{
rsb_invbar[0] = Draw_PicFromWad("r_invbar1");
rsb_invbar[1] = Draw_PicFromWad("r_invbar2");
rsb_weapons[0] = Draw_PicFromWad("r_lava");
rsb_weapons[1] = Draw_PicFromWad("r_superlava");
rsb_weapons[2] = Draw_PicFromWad("r_gren");
rsb_weapons[3] = Draw_PicFromWad("r_multirock");
rsb_weapons[4] = Draw_PicFromWad("r_plasma");
rsb_items[0] = Draw_PicFromWad("r_shield1");
rsb_items[1] = Draw_PicFromWad("r_agrav1");
rsb_teambord = Draw_PicFromWad("r_teambord");
rsb_ammo[0] = Draw_PicFromWad("r_ammolava");
rsb_ammo[1] = Draw_PicFromWad("r_ammomulti");
rsb_ammo[2] = Draw_PicFromWad("r_ammoplasma");
}
}
void Sbar_DrawPic(int32_t x, int32_t y, qpic_t *pic)
{
if (cl.gametype == GAME_DEATHMATCH)
Draw_Pic(x, y + (vid.height - SBAR_HEIGHT), pic);
else
Draw_Pic(x + ((vid.width - 320) >> 1), y + (vid.height - SBAR_HEIGHT), pic);
}
void Sbar_DrawTransPic(int32_t x, int32_t y, qpic_t *pic)
{
if (cl.gametype == GAME_DEATHMATCH)
Draw_TransPic(x, y + (vid.height - SBAR_HEIGHT), pic);
else
Draw_TransPic(x + ((vid.width - 320) >> 1), y + (vid.height - SBAR_HEIGHT), pic);
}
void Sbar_DrawCharacter(int32_t x, int32_t y, int32_t num)
{
if (cl.gametype == GAME_DEATHMATCH)
Draw_Character(x + 4, (y + vid.height) - SBAR_HEIGHT, num);
else
Draw_Character((x + ((vid.width - 320) >> 1)) + 4, (y + vid.height) - SBAR_HEIGHT, num);
}
void Sbar_DrawString(int32_t x, int32_t y, char *str)
{
if (cl.gametype == GAME_DEATHMATCH)
Draw_String(x, (y + vid.height) - SBAR_HEIGHT, str);
else
Draw_String(x + ((vid.width - 320) >> 1), (y + vid.height) - SBAR_HEIGHT, str);
}
int32_t Sbar_itoa(int32_t num, char *buf)
{
char *str;
int32_t pow10;
int32_t dig;
str = buf;
if (num < 0)
{
*(str++) = '-';
num = -num;
}
for (pow10 = 10; num >= pow10; pow10 *= 10)
;
do
{
pow10 /= 10;
dig = num / pow10;
*(str++) = '0' + dig;
num -= dig * pow10;
}
while (pow10 != 1);
*str = 0;
return str - buf;
}
void Sbar_DrawNum(int32_t x, int32_t y, int32_t num, int32_t digits, int32_t color)
{
char str[12];
char *ptr;
int32_t l;
int32_t frame;
l = Sbar_itoa(num, str);
ptr = str;
if (l > digits)
ptr += l - digits;
if (l < digits)
x += (digits - l) * 24;
while (*ptr)
{
if ((*ptr) == '-')
frame = STAT_MINUS;
else
frame = (*ptr) - '0';
Sbar_DrawTransPic(x, y, sb_nums[color][frame]);
x += 24;
ptr++;
}
}
void Sbar_SortFrags(void)
{
int32_t i;
int32_t j;
int32_t k;
scoreboardlines = 0;
for (i = 0; i < cl.maxclients; i++)
{
if (cl.scores[i].name[0])
{
fragsort[scoreboardlines] = i;
scoreboardlines++;
}
}
for (i = 0; i < scoreboardlines; i++)
for (j = 0; j < ((scoreboardlines - 1) - i); j++)
if (cl.scores[fragsort[j]].frags < cl.scores[fragsort[j + 1]].frags)
{
k = fragsort[j];
fragsort[j] = fragsort[j + 1];
fragsort[j + 1] = k;
}
}
int32_t Sbar_ColorForMap(int32_t m)
{
return (m < 128) ? (m + 8) : (m + 8);
}
void Sbar_UpdateScoreboard(void)
{
int32_t i;
int32_t k;
int32_t top;
int32_t bottom;
scoreboard_t *s;
Sbar_SortFrags();
memset(scoreboardtext, 0, sizeof(scoreboardtext));
for (i = 0; i < scoreboardlines; i++)
{
k = fragsort[i];
s = &cl.scores[k];
sprintf(&scoreboardtext[i][1], "%3i %s", s->frags, s->name);
top = s->colors & 0xf0;
bottom = (s->colors & 15) << 4;
scoreboardtop[i] = Sbar_ColorForMap(top);
scoreboardbottom[i] = Sbar_ColorForMap(bottom);
}
}
void Sbar_SoloScoreboard(void)
{
char str[80];
int32_t minutes;
int32_t seconds;
int32_t tens;
int32_t units;
int32_t l;
sprintf(str, "Monsters:%3i /%3i", cl.stats[STAT_MONSTERS], cl.stats[STAT_TOTALMONSTERS]);
Sbar_DrawString(8, 4, str);
sprintf(str, "Secrets :%3i /%3i", cl.stats[STAT_SECRETS], cl.stats[STAT_TOTALSECRETS]);
Sbar_DrawString(8, 12, str);
minutes = cl.time / 60;
seconds = cl.time - (60 * minutes);
tens = seconds / 10;
units = seconds - (10 * tens);
sprintf(str, "Time :%3i:%i%i", minutes, tens, units);
Sbar_DrawString(184, 4, str);
l = strlen(cl.levelname);
Sbar_DrawString(232 - (l * 4), 12, cl.levelname);
}
void Sbar_DrawScoreboard(void)
{
Sbar_SoloScoreboard();
if (cl.gametype == GAME_DEATHMATCH)
Sbar_DeathmatchOverlay();
}
void Sbar_DrawInventory(void)
{
int32_t i;
char num[6];
float time;
int32_t flashon;
if (rogue)
{
if (cl.stats[STAT_ACTIVEWEAPON] >= RIT_LAVA_NAILGUN)
Sbar_DrawPic(0, -24, rsb_invbar[0]);
else
Sbar_DrawPic(0, -24, rsb_invbar[1]);
}
else
{
Sbar_DrawPic(0, -24, sb_ibar);
}
for (i = 0; i < 7; i++)
{
if (cl.items & (IT_SHOTGUN << i))
{
time = cl.item_gettime[i];
flashon = (int32_t) ((cl.time - time) * 10);
if (flashon >= 10)
{
if (cl.stats[STAT_ACTIVEWEAPON] == (IT_SHOTGUN << i))
flashon = 1;
else
flashon = 0;
}
else
flashon = (flashon % 5) + 2;
Sbar_DrawPic(i * 24, -16, sb_weapons[flashon][i]);
if (flashon > 1)
sb_updates = 0;
}
}
if (hipnotic)
{
int32_t grenadeflashing = 0;
for (i = 0; i < 4; i++)
{
if (cl.items & (1 << hipweapons[i]))
{
time = cl.item_gettime[hipweapons[i]];
flashon = (int32_t) ((cl.time - time) * 10);
if (flashon >= 10)
{
if (cl.stats[STAT_ACTIVEWEAPON] == (1 << hipweapons[i]))
flashon = 1;
else
flashon = 0;
}
else
flashon = (flashon % 5) + 2;
if (i == 2)
{
if (cl.items & HIT_PROXIMITY_GUN)
{
if (flashon)
{
grenadeflashing = 1;
Sbar_DrawPic(96, -16, hsb_weapons[flashon][2]);
}
}
}
else
if (i == 3)
{
if (cl.items & (IT_SHOTGUN << 4))
{
if (flashon && (!grenadeflashing))
{
Sbar_DrawPic(96, -16, hsb_weapons[flashon][3]);
}
else
if (!grenadeflashing)
{
Sbar_DrawPic(96, -16, hsb_weapons[0][3]);
}
}
else
Sbar_DrawPic(96, -16, hsb_weapons[flashon][4]);
}
else
Sbar_DrawPic(176 + (i * 24), -16, hsb_weapons[flashon][i]);
if (flashon > 1)
sb_updates = 0;
}
}
}
if (rogue)
{
if (cl.stats[STAT_ACTIVEWEAPON] >= RIT_LAVA_NAILGUN)
{
for (i = 0; i < 5; i++)
{
if (cl.stats[STAT_ACTIVEWEAPON] == (RIT_LAVA_NAILGUN << i))
{
Sbar_DrawPic((i + 2) * 24, -16, rsb_weapons[i]);
}
}
}
}
for (i = 0; i < 4; i++)
{
sprintf(num, "%3i", cl.stats[STAT_SHELLS + i]);
if (num[0] != ' ')
Sbar_DrawCharacter((((6 * i) + 1) * 8) - 2, -24, (18 + num[0]) - '0');
if (num[1] != ' ')
Sbar_DrawCharacter((((6 * i) + 2) * 8) - 2, -24, (18 + num[1]) - '0');
if (num[2] != ' ')
Sbar_DrawCharacter((((6 * i) + 3) * 8) - 2, -24, (18 + num[2]) - '0');
}
flashon = 0;
for (i = 0; i < 6; i++)
if (cl.items & (1 << (17 + i)))
{
time = cl.item_gettime[17 + i];
if ((time && (time > (cl.time - 2))) && flashon)
{
sb_updates = 0;
}
else
{
if ((!hipnotic) || (i > 1))
{
Sbar_DrawPic(192 + (i * 16), -16, sb_items[i]);
}
}
if (time && (time > (cl.time - 2)))
sb_updates = 0;
}
if (hipnotic)
{
for (i = 0; i < 2; i++)
if (cl.items & (1 << (24 + i)))
{
time = cl.item_gettime[24 + i];
if ((time && (time > (cl.time - 2))) && flashon)
{
sb_updates = 0;
}
else
{
Sbar_DrawPic(288 + (i * 16), -16, hsb_items[i]);
}
if (time && (time > (cl.time - 2)))
sb_updates = 0;
}
}
if (rogue)
{
for (i = 0; i < 2; i++)
{
if (cl.items & (1 << (29 + i)))
{
time = cl.item_gettime[29 + i];
if ((time && (time > (cl.time - 2))) && flashon)
{
sb_updates = 0;
}
else
{
Sbar_DrawPic(288 + (i * 16), -16, rsb_items[i]);
}
if (time && (time > (cl.time - 2)))
sb_updates = 0;
}
}
}
else
{
for (i = 0; i < 4; i++)
{
if (cl.items & (1 << (28 + i)))
{
time = cl.item_gettime[28 + i];
if ((time && (time > (cl.time - 2))) && flashon)
{
sb_updates = 0;
}
else
Sbar_DrawPic((320 - 32) + (i * 8), -16, sb_sigil[i]);
if (time && (time > (cl.time - 2)))
sb_updates = 0;
}
}
}
}
void Sbar_DrawFrags(void)
{
int32_t i;
int32_t k;
int32_t l;
int32_t top;
int32_t bottom;
int32_t x;
int32_t y;
int32_t f;
int32_t xofs;
char num[12];
scoreboard_t *s;
Sbar_SortFrags();
l = (scoreboardlines <= 4) ? (scoreboardlines) : (4);
x = 23;
if (cl.gametype == GAME_DEATHMATCH)
xofs = 0;
else
xofs = (vid.width - 320) >> 1;
y = (vid.height - SBAR_HEIGHT) - 23;
for (i = 0; i < l; i++)
{
k = fragsort[i];
s = &cl.scores[k];
if (!s->name[0])
continue;
top = s->colors & 0xf0;
bottom = (s->colors & 15) << 4;
top = Sbar_ColorForMap(top);
bottom = Sbar_ColorForMap(bottom);
Draw_Fill((xofs + (x * 8)) + 10, y, 28, 4, top);
Draw_Fill((xofs + (x * 8)) + 10, y + 4, 28, 3, bottom);
f = s->frags;
sprintf(num, "%3i", f);
Sbar_DrawCharacter((x + 1) * 8, -24, num[0]);
Sbar_DrawCharacter((x + 2) * 8, -24, num[1]);
Sbar_DrawCharacter((x + 3) * 8, -24, num[2]);
if (k == (cl.viewentity - 1))
{
Sbar_DrawCharacter((x * 8) + 2, -24, 16);
Sbar_DrawCharacter(((x + 4) * 8) - 4, -24, 17);
}
x += 4;
}
}
void Sbar_DrawFace(void)
{
int32_t f;
int32_t anim;
if (((rogue && (cl.maxclients != 1)) && (teamplay.value > 3)) && (teamplay.value < 7))
{
int32_t top;
int32_t bottom;
int32_t xofs;
char num[12];
scoreboard_t *s;
s = &cl.scores[cl.viewentity - 1];
top = s->colors & 0xf0;
bottom = (s->colors & 15) << 4;
top = Sbar_ColorForMap(top);
bottom = Sbar_ColorForMap(bottom);
if (cl.gametype == GAME_DEATHMATCH)
xofs = 113;
else
xofs = ((vid.width - 320) >> 1) + 113;
Sbar_DrawPic(112, 0, rsb_teambord);
Draw_Fill(xofs, (vid.height - SBAR_HEIGHT) + 3, 22, 9, top);
Draw_Fill(xofs, (vid.height - SBAR_HEIGHT) + 12, 22, 9, bottom);
f = s->frags;
sprintf(num, "%3i", f);
if (top == 8)
{
if (num[0] != ' ')
Sbar_DrawCharacter(109, 3, (18 + num[0]) - '0');
if (num[1] != ' ')
Sbar_DrawCharacter(116, 3, (18 + num[1]) - '0');
if (num[2] != ' ')
Sbar_DrawCharacter(123, 3, (18 + num[2]) - '0');
}
else
{
Sbar_DrawCharacter(109, 3, num[0]);
Sbar_DrawCharacter(116, 3, num[1]);
Sbar_DrawCharacter(123, 3, num[2]);
}
return;
}
if ((cl.items & (IT_INVISIBILITY | IT_INVULNERABILITY)) == (IT_INVISIBILITY | IT_INVULNERABILITY))
{
Sbar_DrawPic(112, 0, sb_face_invis_invuln);
return;
}
if (cl.items & IT_QUAD)
{
Sbar_DrawPic(112, 0, sb_face_quad);
return;
}
if (cl.items & IT_INVISIBILITY)
{
Sbar_DrawPic(112, 0, sb_face_invis);
return;
}
if (cl.items & IT_INVULNERABILITY)
{
Sbar_DrawPic(112, 0, sb_face_invuln);
return;
}
if (cl.stats[STAT_HEALTH] >= 100)
f = 4;
else
f = cl.stats[STAT_HEALTH] / 20;
if (cl.time <= cl.faceanimtime)
{
anim = 1;
sb_updates = 0;
}
else
anim = 0;
Sbar_DrawPic(112, 0, sb_faces[f][anim]);
}
void Sbar_Draw(void)
{
if (scr_con_current == vid.height)
return;
if (sb_updates >= vid.numpages)
return;
scr_copyeverything = 1;
sb_updates++;
if (sb_lines && (vid.width > 320))
Draw_TileClear(0, vid.height - sb_lines, vid.width, sb_lines);
if (sb_lines > 24)
{
Sbar_DrawInventory();
if (cl.maxclients != 1)
Sbar_DrawFrags();
}
if (sb_showscores || (cl.stats[STAT_HEALTH] <= 0))
{
Sbar_DrawPic(0, 0, sb_scorebar);
Sbar_DrawScoreboard();
sb_updates = 0;
}
else
if (sb_lines)
{
Sbar_DrawPic(0, 0, sb_sbar);
if (hipnotic)
{
if (cl.items & IT_KEY1)
Sbar_DrawPic(209, 3, sb_items[0]);
if (cl.items & IT_KEY2)
Sbar_DrawPic(209, 12, sb_items[1]);
}
if (cl.items & IT_INVULNERABILITY)
{
Sbar_DrawNum(24, 0, 666, 3, 1);
Sbar_DrawPic(0, 0, draw_disc);
}
else
{
if (rogue)
{
Sbar_DrawNum(24, 0, cl.stats[STAT_ARMOR], 3, cl.stats[STAT_ARMOR] <= 25);
if (cl.items & RIT_ARMOR3)
Sbar_DrawPic(0, 0, sb_armor[2]);
else
if (cl.items & RIT_ARMOR2)
Sbar_DrawPic(0, 0, sb_armor[1]);
else
if (cl.items & RIT_ARMOR1)
Sbar_DrawPic(0, 0, sb_armor[0]);
}
else
{
Sbar_DrawNum(24, 0, cl.stats[STAT_ARMOR], 3, cl.stats[STAT_ARMOR] <= 25);
if (cl.items & IT_ARMOR3)
Sbar_DrawPic(0, 0, sb_armor[2]);
else
if (cl.items & IT_ARMOR2)
Sbar_DrawPic(0, 0, sb_armor[1]);
else
if (cl.items & IT_ARMOR1)
Sbar_DrawPic(0, 0, sb_armor[0]);
}
}
Sbar_DrawFace();
Sbar_DrawNum(136, 0, cl.stats[STAT_HEALTH], 3, cl.stats[STAT_HEALTH] <= 25);
if (rogue)
{
if (cl.items & RIT_SHELLS)
Sbar_DrawPic(224, 0, sb_ammo[0]);
else
if (cl.items & RIT_NAILS)
Sbar_DrawPic(224, 0, sb_ammo[1]);
else
if (cl.items & RIT_ROCKETS)
Sbar_DrawPic(224, 0, sb_ammo[2]);
else
if (cl.items & RIT_CELLS)
Sbar_DrawPic(224, 0, sb_ammo[3]);
else
if (cl.items & RIT_LAVA_NAILS)
Sbar_DrawPic(224, 0, rsb_ammo[0]);
else
if (cl.items & RIT_PLASMA_AMMO)
Sbar_DrawPic(224, 0, rsb_ammo[1]);
else
if (cl.items & RIT_MULTI_ROCKETS)
Sbar_DrawPic(224, 0, rsb_ammo[2]);
}
else
{
if (cl.items & IT_SHELLS)
Sbar_DrawPic(224, 0, sb_ammo[0]);
else
if (cl.items & IT_NAILS)
Sbar_DrawPic(224, 0, sb_ammo[1]);
else
if (cl.items & IT_ROCKETS)
Sbar_DrawPic(224, 0, sb_ammo[2]);
else
if (cl.items & IT_CELLS)
Sbar_DrawPic(224, 0, sb_ammo[3]);
}
Sbar_DrawNum(248, 0, cl.stats[STAT_AMMO], 3, cl.stats[STAT_AMMO] <= 10);
}
if (vid.width > 320)
{
if (cl.gametype == GAME_DEATHMATCH)
Sbar_MiniDeathmatchOverlay();
}
}
void Sbar_IntermissionNumber(int32_t x, int32_t y, int32_t num, int32_t digits, int32_t color)
{
char str[12];
char *ptr;
int32_t l;
int32_t frame;
l = Sbar_itoa(num, str);
ptr = str;
if (l > digits)
ptr += l - digits;
if (l < digits)
x += (digits - l) * 24;
while (*ptr)
{
if ((*ptr) == '-')
frame = STAT_MINUS;
else
frame = (*ptr) - '0';
Draw_TransPic(x, y, sb_nums[color][frame]);
x += 24;
ptr++;
}
}
void Sbar_DeathmatchOverlay(void)
{
qpic_t *pic;
int32_t i;
int32_t k;
int32_t l;
int32_t top;
int32_t bottom;
int32_t x;
int32_t y;
int32_t f;
char num[12];
scoreboard_t *s;
scr_copyeverything = 1;
scr_fullupdate = 0;
pic = Draw_CachePic("gfx/ranking.lmp");
M_DrawPic((320 - pic->width) / 2, 8, pic);
Sbar_SortFrags();
l = scoreboardlines;
x = 80 + ((vid.width - 320) >> 1);
y = 40;
for (i = 0; i < l; i++)
{
k = fragsort[i];
s = &cl.scores[k];
if (!s->name[0])
continue;
top = s->colors & 0xf0;
bottom = (s->colors & 15) << 4;
top = Sbar_ColorForMap(top);
bottom = Sbar_ColorForMap(bottom);
Draw_Fill(x, y, 40, 4, top);
Draw_Fill(x, y + 4, 40, 4, bottom);
f = s->frags;
sprintf(num, "%3i", f);
Draw_Character(x + 8, y, num[0]);
Draw_Character(x + 16, y, num[1]);
Draw_Character(x + 24, y, num[2]);
if (k == (cl.viewentity - 1))
Draw_Character(x - 8, y, 12);
Draw_String(x + 64, y, s->name);
y += 10;
}
}
void Sbar_MiniDeathmatchOverlay(void)
{
qpic_t *pic;
int32_t i;
int32_t k;
int32_t l;
int32_t top;
int32_t bottom;
int32_t x;
int32_t y;
int32_t f;
char num[12];
scoreboard_t *s;
int32_t numlines;
if ((vid.width < 512) || (!sb_lines))
return;
scr_copyeverything = 1;
scr_fullupdate = 0;
Sbar_SortFrags();
l = scoreboardlines;
y = vid.height - sb_lines;
numlines = sb_lines / 8;
if (numlines < 3)
return;
for (i = 0; i < scoreboardlines; i++)
if (fragsort[i] == (cl.viewentity - 1))
break;
if (i == scoreboardlines)
i = 0;
else
i = i - (numlines / 2);
if (i > (scoreboardlines - numlines))
i = scoreboardlines - numlines;
if (i < 0)
i = 0;
x = 324;
for (; (i < scoreboardlines) && (y < (vid.height - 8)); i++)
{
k = fragsort[i];
s = &cl.scores[k];
if (!s->name[0])
continue;
top = s->colors & 0xf0;
bottom = (s->colors & 15) << 4;
top = Sbar_ColorForMap(top);
bottom = Sbar_ColorForMap(bottom);
Draw_Fill(x, y + 1, 40, 3, top);
Draw_Fill(x, y + 4, 40, 4, bottom);
f = s->frags;
sprintf(num, "%3i", f);
Draw_Character(x + 8, y, num[0]);
Draw_Character(x + 16, y, num[1]);
Draw_Character(x + 24, y, num[2]);
if (k == (cl.viewentity - 1))
{
Draw_Character(x, y, 16);
Draw_Character(x + 32, y, 17);
}
Draw_String(x + 48, y, s->name);
y += 8;
}
}
void Sbar_IntermissionOverlay(void)
{
qpic_t *pic;
int32_t dig;
int32_t num;
scr_copyeverything = 1;
scr_fullupdate = 0;
if (cl.gametype == GAME_DEATHMATCH)
{
Sbar_DeathmatchOverlay();
return;
}
pic = Draw_CachePic("gfx/complete.lmp");
Draw_Pic(64, 24, pic);
pic = Draw_CachePic("gfx/inter.lmp");
Draw_TransPic(0, 56, pic);
dig = cl.completed_time / 60;
Sbar_IntermissionNumber(160, 64, dig, 3, 0);
num = cl.completed_time - (dig * 60);
Draw_TransPic(234, 64, sb_colon);
Draw_TransPic(246, 64, sb_nums[0][num / 10]);
Draw_TransPic(266, 64, sb_nums[0][num % 10]);
Sbar_IntermissionNumber(160, 104, cl.stats[STAT_SECRETS], 3, 0);
Draw_TransPic(232, 104, sb_slash);
Sbar_IntermissionNumber(240, 104, cl.stats[STAT_TOTALSECRETS], 3, 0);
Sbar_IntermissionNumber(160, 144, cl.stats[STAT_MONSTERS], 3, 0);
Draw_TransPic(232, 144, sb_slash);
Sbar_IntermissionNumber(240, 144, cl.stats[STAT_TOTALMONSTERS], 3, 0);
}
void Sbar_FinaleOverlay(void)
{
qpic_t *pic;
scr_copyeverything = 1;
pic = Draw_CachePic("gfx/finale.lmp");
Draw_TransPic((vid.width - pic->width) / 2, 16, pic);
}
void SCR_CenterPrint(char *str)
{
strncpy(scr_centerstring, str, (sizeof(scr_centerstring)) - 1);
scr_centertime_off = scr_centertime.value;
scr_centertime_start = cl.time;
scr_center_lines = 1;
while (*str)
{
if ((*str) == '\n')
scr_center_lines++;
str++;
}
}
void SCR_EraseCenterString(void)
{
int32_t y;
if ((scr_erase_center++) > vid.numpages)
{
scr_erase_lines = 0;
return;
}
if (scr_center_lines <= 4)
y = vid.height * 0.35;
else
y = 48;
scr_copytop = 1;
Draw_TileClear(0, y, vid.width, 8 * scr_erase_lines);
}
void SCR_DrawCenterString(void)
{
char *start;
int32_t l;
int32_t j;
int32_t x;
int32_t y;
int32_t remaining;
if (cl.intermission)
remaining = scr_printspeed.value * (cl.time - scr_centertime_start);
else
remaining = 9999;
scr_erase_center = 0;
start = scr_centerstring;
if (scr_center_lines <= 4)
y = vid.height * 0.35;
else
y = 48;
do
{
for (l = 0; l < 40; l++)
if ((start[l] == '\n') || (!start[l]))
break;
x = (vid.width - (l * 8)) / 2;
for (j = 0; j < l; j++, x += 8)
{
Draw_Character(x, y, start[j]);
if (!(remaining--))
return;
}
y += 8;
while ((*start) && ((*start) != '\n'))
start++;
if (!(*start))
break;
start++;
}
while (1);
}
void SCR_CheckDrawCenterString(void)
{
scr_copytop = 1;
if (scr_center_lines > scr_erase_lines)
scr_erase_lines = scr_center_lines;
scr_centertime_off -= host_frametime;
if ((scr_centertime_off <= 0) && (!cl.intermission))
return;
if (key_dest != key_game)
return;
SCR_DrawCenterString();
}
float CalcFov(float fov_x, float width, float height)
{
float a;
float x;
if ((fov_x < 1) || (fov_x > 179))
Sys_Error("Bad fov: %f", fov_x);
x = width / tan((fov_x / 360) * M_PI);
a = atan(height / x);
a = (a * 360) / M_PI;
return a;
}
static void SCR_CalcRefdef(void)
{
vrect_t vrect;
float size;
scr_fullupdate = 0;
vid.recalc_refdef = 0;
Sbar_Changed();
if (scr_viewsize.value < 30)
Cvar_Set("viewsize", "30");
if (scr_viewsize.value > 120)
Cvar_Set("viewsize", "120");
if (scr_fov.value < 10)
Cvar_Set("fov", "10");
if (scr_fov.value > 170)
Cvar_Set("fov", "170");
r_refdef.fov_x = scr_fov.value;
r_refdef.fov_y = CalcFov(r_refdef.fov_x, r_refdef.vrect.width, r_refdef.vrect.height);
if (cl.intermission)
size = 120;
else
size = scr_viewsize.value;
if (size >= 120)
sb_lines = 0;
else
if (size >= 110)
sb_lines = 24;
else
sb_lines = (24 + 16) + 8;
vrect.x = 0;
vrect.y = 0;
vrect.width = vid.width;
vrect.height = vid.height;
R_SetVrect(&vrect, &scr_vrect, sb_lines);
if (scr_con_current > vid.height)
scr_con_current = vid.height;
R_ViewChanged(&vrect, sb_lines, vid.aspect);
}
void SCR_SizeUp_f(void)
{
Cvar_SetValue("viewsize", scr_viewsize.value + 10);
vid.recalc_refdef = 1;
}
void SCR_SizeDown_f(void)
{
Cvar_SetValue("viewsize", scr_viewsize.value - 10);
vid.recalc_refdef = 1;
}
void SCR_Init(void)
{
Cvar_RegisterVariable(&scr_fov);
Cvar_RegisterVariable(&scr_viewsize);
Cvar_RegisterVariable(&scr_conspeed);
Cvar_RegisterVariable(&scr_showram);
Cvar_RegisterVariable(&scr_showturtle);
Cvar_RegisterVariable(&scr_showpause);
Cvar_RegisterVariable(&scr_centertime);
Cvar_RegisterVariable(&scr_printspeed);
Cmd_AddCommand("screenshot", SCR_ScreenShot_f);
Cmd_AddCommand("sizeup", SCR_SizeUp_f);
Cmd_AddCommand("sizedown", SCR_SizeDown_f);
scr_ram = Draw_PicFromWad("ram");
scr_net = Draw_PicFromWad("net");
scr_turtle = Draw_PicFromWad("turtle");
scr_initialized = 1;
}
void SCR_DrawRam(void)
{
if (!scr_showram.value)
return;
if (!r_cache_thrash)
return;
Draw_Pic(scr_vrect.x + 32, scr_vrect.y, scr_ram);
}
void SCR_DrawTurtle(void)
{
static int32_t count;
if (!scr_showturtle.value)
return;
if (host_frametime < 0.1)
{
count = 0;
return;
}
count++;
if (count < 3)
return;
Draw_Pic(scr_vrect.x, scr_vrect.y, scr_turtle);
}
void SCR_DrawNet(void)
{
if ((realtime - cl.last_received_message) < 0.3)
return;
if (cls.demoplayback)
return;
Draw_Pic(scr_vrect.x + 64, scr_vrect.y, scr_net);
}
void SCR_DrawPause(void)
{
qpic_t *pic;
if (!scr_showpause.value)
return;
if (!cl.paused)
return;
pic = Draw_CachePic("gfx/pause.lmp");
Draw_Pic((vid.width - pic->width) / 2, ((vid.height - 48) - pic->height) / 2, pic);
}
void SCR_DrawLoading(void)
{
qpic_t *pic;
if (!scr_drawloading)
return;
pic = Draw_CachePic("gfx/loading.lmp");
Draw_Pic((vid.width - pic->width) / 2, ((vid.height - 48) - pic->height) / 2, pic);
}
void SCR_SetUpToDrawConsole(void)
{
Con_CheckResize();
if (scr_drawloading)
return;
con_forcedup = (!cl.worldmodel) || (cls.signon != SIGNONS);
if (con_forcedup)
{
scr_conlines = vid.height;
scr_con_current = scr_conlines;
}
else
if (key_dest == key_console)
scr_conlines = vid.height / 2;
else
scr_conlines = 0;
if (scr_conlines < scr_con_current)
{
scr_con_current -= scr_conspeed.value * host_frametime;
if (scr_conlines > scr_con_current)
scr_con_current = scr_conlines;
}
else
if (scr_conlines > scr_con_current)
{
scr_con_current += scr_conspeed.value * host_frametime;
if (scr_conlines < scr_con_current)
scr_con_current = scr_conlines;
}
if ((clearconsole++) < vid.numpages)
{
scr_copytop = 1;
Draw_TileClear(0, (int32_t) scr_con_current, vid.width, vid.height - ((int32_t) scr_con_current));
Sbar_Changed();
}
else
if ((clearnotify++) < vid.numpages)
{
scr_copytop = 1;
Draw_TileClear(0, 0, vid.width, con_notifylines);
}
else
con_notifylines = 0;
}
void SCR_DrawConsole(void)
{
if (scr_con_current)
{
scr_copyeverything = 1;
Con_DrawConsole(scr_con_current, 1);
clearconsole = 0;
}
else
{
if ((key_dest == key_game) || (key_dest == key_message))
Con_DrawNotify();
}
}
void WritePCXfile(char *filename, uint8_t *data, int32_t width, int32_t height, int32_t rowbytes, uint8_t *palette)
{
int32_t i;
int32_t j;
int32_t length;
pcx_t *pcx;
uint8_t *pack;
pcx = Hunk_TempAlloc(((width * height) * 2) + 1000);
if (pcx == 0)
{
Con_Printf("SCR_ScreenShot_f: not enough memory\n");
return;
}
pcx->manufacturer = 0x0a;
pcx->version = 5;
pcx->encoding = 1;
pcx->bits_per_pixel = 8;
pcx->xmin = 0;
pcx->ymin = 0;
pcx->xmax = (int16_t) (width - 1);
pcx->ymax = (int16_t) (height - 1);
pcx->hres = (int16_t) width;
pcx->vres = (int16_t) height;
memset(pcx->palette, 0, sizeof(pcx->palette));
pcx->color_planes = 1;
pcx->bytes_per_line = (int16_t) width;
pcx->palette_type = 2;
memset(pcx->filler, 0, sizeof(pcx->filler));
pack = &pcx->data;
for (i = 0; i < height; i++)
{
for (j = 0; j < width; j++)
{
if (((*data) & 0xc0) != 0xc0)
*(pack++) = *(data++);
else
{
*(pack++) = 0xc1;
*(pack++) = *(data++);
}
}
data += rowbytes - width;
}
*(pack++) = 0x0c;
for (i = 0; i < 768; i++)
*(pack++) = *(palette++);
length = pack - ((uint8_t *) pcx);
COM_WriteFile(filename, pcx, length);
}
void SCR_ScreenShot_f(void)
{
int32_t i;
char pcxname[80];
char checkname[MAX_OSPATH];
strcpy(pcxname, "quake00.pcx");
for (i = 0; i <= 99; i++)
{
pcxname[5] = (i / 10) + '0';
pcxname[6] = (i % 10) + '0';
sprintf(checkname, "%s/%s", com_gamedir, pcxname);
if (Sys_FileTime(checkname) == (-1))
break;
}
if (i == 100)
{
Con_Printf("SCR_ScreenShot_f: Couldn't create a PCX file\n");
return;
}
D_EnableBackBufferAccess();
WritePCXfile(pcxname, vid.buffer, vid.width, vid.height, vid.rowbytes, host_basepal);
D_DisableBackBufferAccess();
Con_Printf("Wrote %s\n", pcxname);
}
void SCR_BeginLoadingPlaque(void)
{
S_StopAllSounds(1);
if (cls.state != ca_connected)
return;
if (cls.signon != SIGNONS)
return;
Con_ClearNotify();
scr_centertime_off = 0;
scr_con_current = 0;
scr_drawloading = 1;
scr_fullupdate = 0;
Sbar_Changed();
SCR_UpdateScreen();
scr_drawloading = 0;
scr_disabled_for_loading = 1;
scr_disabled_time = realtime;
scr_fullupdate = 0;
}
void SCR_EndLoadingPlaque(void)
{
scr_disabled_for_loading = 0;
scr_fullupdate = 0;
Con_ClearNotify();
}
void SCR_DrawNotifyString(void)
{
char *start;
int32_t l;
int32_t j;
int32_t x;
int32_t y;
start = scr_notifystring;
y = vid.height * 0.35;
do
{
for (l = 0; l < 40; l++)
if ((start[l] == '\n') || (!start[l]))
break;
x = (vid.width - (l * 8)) / 2;
for (j = 0; j < l; j++, x += 8)
Draw_Character(x, y, start[j]);
y += 8;
while ((*start) && ((*start) != '\n'))
start++;
if (!(*start))
break;
start++;
}
while (1);
}
int32_t SCR_ModalMessage(char *text)
{
if (cls.state == ca_dedicated)
return 1;
scr_notifystring = text;
scr_fullupdate = 0;
scr_drawdialog = 1;
SCR_UpdateScreen();
scr_drawdialog = 0;
S_ClearBuffer();
do
{
key_count = -1;
Sys_SendKeyEvents();
}
while (((key_lastpress != 'y') && (key_lastpress != 'n')) && (key_lastpress != K_ESCAPE));
scr_fullupdate = 0;
SCR_UpdateScreen();
return key_lastpress == 'y';
}
void SCR_BringDownConsole(void)
{
int32_t i;
scr_centertime_off = 0;
for (i = 0; (i < 20) && (scr_conlines != scr_con_current); i++)
SCR_UpdateScreen();
cl.cshifts[0].percent = 0;
VID_SetPalette(host_basepal);
}
void SCR_UpdateScreen(void)
{
static float oldscr_viewsize;
static float oldlcd_x;
vrect_t vrect;
if (scr_skipupdate || block_drawing)
return;
scr_copytop = 0;
scr_copyeverything = 0;
if (scr_disabled_for_loading)
{
if ((realtime - scr_disabled_time) > 60)
{
scr_disabled_for_loading = 0;
Con_Printf("load failed.\n");
}
else
return;
}
if (cls.state == ca_dedicated)
return;
if ((!scr_initialized) || (!con_initialized))
return;
if (scr_viewsize.value != oldscr_viewsize)
{
oldscr_viewsize = scr_viewsize.value;
vid.recalc_refdef = 1;
}
if (oldfov != scr_fov.value)
{
oldfov = scr_fov.value;
vid.recalc_refdef = 1;
}
if (oldlcd_x != lcd_x.value)
{
oldlcd_x = lcd_x.value;
vid.recalc_refdef = 1;
}
if (oldscreensize != scr_viewsize.value)
{
oldscreensize = scr_viewsize.value;
vid.recalc_refdef = 1;
}
if (vid.recalc_refdef)
{
SCR_CalcRefdef();
}
D_EnableBackBufferAccess();
if ((scr_fullupdate++) < vid.numpages)
{
scr_copyeverything = 1;
Draw_TileClear(0, 0, vid.width, vid.height);
Sbar_Changed();
}
pconupdate = 0;
SCR_SetUpToDrawConsole();
SCR_EraseCenterString();
D_DisableBackBufferAccess();
VID_LockBuffer();
V_RenderView();
VID_UnlockBuffer();
D_EnableBackBufferAccess();
if (scr_drawdialog)
{
Sbar_Draw();
Draw_FadeScreen();
SCR_DrawNotifyString();
scr_copyeverything = 1;
}
else
if (scr_drawloading)
{
SCR_DrawLoading();
Sbar_Draw();
}
else
if ((cl.intermission == 1) && (key_dest == key_game))
{
Sbar_IntermissionOverlay();
}
else
if ((cl.intermission == 2) && (key_dest == key_game))
{
Sbar_FinaleOverlay();
SCR_CheckDrawCenterString();
}
else
if ((cl.intermission == 3) && (key_dest == key_game))
{
SCR_CheckDrawCenterString();
}
else
{
SCR_DrawRam();
SCR_DrawNet();
SCR_DrawTurtle();
SCR_DrawPause();
SCR_CheckDrawCenterString();
Sbar_Draw();
SCR_DrawConsole();
M_Draw();
}
D_DisableBackBufferAccess();
if (pconupdate)
{
D_UpdateRects(pconupdate);
}
V_UpdatePalette();
if (scr_copyeverything)
{
vrect.x = 0;
vrect.y = 0;
vrect.width = vid.width;
vrect.height = vid.height;
vrect.pnext = 0;
VID_Update(&vrect);
}
else
if (scr_copytop)
{
vrect.x = 0;
vrect.y = 0;
vrect.width = vid.width;
vrect.height = vid.height - sb_lines;
vrect.pnext = 0;
VID_Update(&vrect);
}
else
{
vrect.x = scr_vrect.x;
vrect.y = scr_vrect.y;
vrect.width = scr_vrect.width;
vrect.height = scr_vrect.height;
vrect.pnext = 0;
VID_Update(&vrect);
}
}
void SCR_UpdateWholeScreen(void)
{
scr_fullupdate = 0;
SCR_UpdateScreen();
}
void S_AmbientOff(void)
{
snd_ambient = 0;
}
void S_AmbientOn(void)
{
snd_ambient = 1;
}
void S_SoundInfo_f(void)
{
if ((!sound_started) || (!shm))
{
Con_Printf("sound system not started\n");
return;
}
Con_Printf("%5d stereo\n", shm->channels - 1);
Con_Printf("%5d samples\n", shm->samples);
Con_Printf("%5d samplepos\n", shm->samplepos);
Con_Printf("%5d samplebits\n", shm->samplebits);
Con_Printf("%5d submission_chunk\n", shm->submission_chunk);
Con_Printf("%5d speed\n", shm->speed);
Con_Printf("0x%x dma buffer\n", shm->buffer);
Con_Printf("%5d total_channels\n", total_channels);
}
void S_Startup(void)
{
int32_t rc;
if (!snd_initialized)
return;
if (!fakedma)
{
rc = SNDDMA_Init();
if (!rc)
{
Con_Printf("S_Startup: SNDDMA_Init failed.\n");
sound_started = 0;
return;
}
}
sound_started = 1;
}
void S_Init(void)
{
Con_Printf("\nSound Initialization\n");
if (COM_CheckParm("-nosound"))
return;
if (COM_CheckParm("-simsound"))
fakedma = 1;
Cmd_AddCommand("play", S_Play);
Cmd_AddCommand("playvol", S_PlayVol);
Cmd_AddCommand("stopsound", S_StopAllSoundsC);
Cmd_AddCommand("soundlist", S_SoundList);
Cmd_AddCommand("soundinfo", S_SoundInfo_f);
Cvar_RegisterVariable(&nosound);
Cvar_RegisterVariable(&volume);
Cvar_RegisterVariable(&precache);
Cvar_RegisterVariable(&loadas8bit);
Cvar_RegisterVariable(&bgmvolume);
Cvar_RegisterVariable(&bgmbuffer);
Cvar_RegisterVariable(&ambient_level);
Cvar_RegisterVariable(&ambient_fade);
Cvar_RegisterVariable(&snd_noextraupdate);
Cvar_RegisterVariable(&snd_show);
Cvar_RegisterVariable(&_snd_mixahead);
if (host_parms.memsize < 0x800000)
{
Cvar_Set("loadas8bit", "1");
Con_Printf("loading all sounds as 8bit\n");
}
snd_initialized = 1;
S_Startup();
SND_InitScaletable();
known_sfx = Hunk_AllocName(MAX_SFX * (sizeof(sfx_t)), "sfx_t");
num_sfx = 0;
if (fakedma)
{
shm = (void *) Hunk_AllocName(sizeof(*shm), "shm");
shm->splitbuffer = 0;
shm->samplebits = 16;
shm->speed = 22050;
shm->channels = 2;
shm->samples = 32768;
shm->samplepos = 0;
shm->soundalive = 1;
shm->gamealive = 1;
shm->submission_chunk = 1;
shm->buffer = Hunk_AllocName(1 << 16, "shmbuf");
}
Con_Printf("Sound sampling rate: %i\n", shm->speed);
ambient_sfx[AMBIENT_WATER] = S_PrecacheSound("ambience/water1.wav");
ambient_sfx[AMBIENT_SKY] = S_PrecacheSound("ambience/wind2.wav");
S_StopAllSounds(1);
}
void S_Shutdown(void)
{
if (!sound_started)
return;
if (shm)
shm->gamealive = 0;
shm = 0;
sound_started = 0;
if (!fakedma)
{
SNDDMA_Shutdown();
}
}
sfx_t *S_FindName(char *name)
{
int32_t i;
sfx_t *sfx;
if (!name)
Sys_Error("S_FindName: NULL\n");
if (strlen(name) >= MAX_QPATH)
Sys_Error("Sound name too int32_t: %s", name);
for (i = 0; i < num_sfx; i++)
if (!strcmp(known_sfx[i].name, name))
{
return &known_sfx[i];
}
if (num_sfx == MAX_SFX)
Sys_Error("S_FindName: out of sfx_t");
sfx = &known_sfx[i];
strcpy(sfx->name, name);
num_sfx++;
return sfx;
}
void S_TouchSound(char *name)
{
sfx_t *sfx;
if (!sound_started)
return;
sfx = S_FindName(name);
Cache_Check(&sfx->cache);
}
sfx_t *S_PrecacheSound(char *name)
{
sfx_t *sfx;
if ((!sound_started) || nosound.value)
return 0;
sfx = S_FindName(name);
if (precache.value)
S_LoadSound(sfx);
return sfx;
}
channel_t *SND_PickChannel(int32_t entnum, int32_t entchannel)
{
int32_t ch_idx;
int32_t first_to_die;
int32_t life_left;
first_to_die = -1;
life_left = 0x7fffffff;
for (ch_idx = NUM_AMBIENTS; ch_idx < (NUM_AMBIENTS + MAX_DYNAMIC_CHANNELS); ch_idx++)
{
if (((entchannel != 0) && (channels[ch_idx].entnum == entnum)) && ((channels[ch_idx].entchannel == entchannel) || (entchannel == (-1))))
{
first_to_die = ch_idx;
break;
}
if (((channels[ch_idx].entnum == cl.viewentity) && (entnum != cl.viewentity)) && channels[ch_idx].sfx)
continue;
if ((channels[ch_idx].end - paintedtime) < life_left)
{
life_left = channels[ch_idx].end - paintedtime;
first_to_die = ch_idx;
}
}
if (first_to_die == (-1))
return 0;
if (channels[first_to_die].sfx)
channels[first_to_die].sfx = 0;
return &channels[first_to_die];
}
void SND_Spatialize(channel_t *ch)
{
vec_t dot;
vec_t ldist;
vec_t rdist;
vec_t dist;
vec_t lscale;
vec_t rscale;
vec_t scale;
vec3_t source_vec;
sfx_t *snd;
if (ch->entnum == cl.viewentity)
{
ch->leftvol = ch->master_vol;
ch->rightvol = ch->master_vol;
return;
}
snd = ch->sfx;
VectorSubtract(ch->origin, listener_origin, source_vec);
dist = VectorNormalize(source_vec) * ch->dist_mult;
dot = DotProduct(listener_right, source_vec);
if (shm->channels == 1)
{
rscale = 1.0;
lscale = 1.0;
}
else
{
rscale = 1.0 + dot;
lscale = 1.0 - dot;
}
scale = (1.0 - dist) * rscale;
ch->rightvol = (int32_t) (ch->master_vol * scale);
if (ch->rightvol < 0)
ch->rightvol = 0;
scale = (1.0 - dist) * lscale;
ch->leftvol = (int32_t) (ch->master_vol * scale);
if (ch->leftvol < 0)
ch->leftvol = 0;
}
void S_StartSound(int32_t entnum, int32_t entchannel, sfx_t *sfx, vec3_t origin, float fvol, float attenuation)
{
channel_t *target_chan;
channel_t *check;
sfxcache_t *sc;
int32_t vol;
int32_t ch_idx;
int32_t skip;
if (!sound_started)
return;
if (!sfx)
return;
if (nosound.value)
return;
vol = fvol * 255;
target_chan = SND_PickChannel(entnum, entchannel);
if (!target_chan)
return;
memset(target_chan, 0, sizeof(*target_chan));
VectorCopy(origin, target_chan->origin);
target_chan->dist_mult = attenuation / sound_nominal_clip_dist;
target_chan->master_vol = vol;
target_chan->entnum = entnum;
target_chan->entchannel = entchannel;
SND_Spatialize(target_chan);
if ((!target_chan->leftvol) && (!target_chan->rightvol))
return;
sc = S_LoadSound(sfx);
if (!sc)
{
target_chan->sfx = 0;
return;
}
target_chan->sfx = sfx;
target_chan->pos = 0.0;
target_chan->end = paintedtime + sc->length;
check = &channels[NUM_AMBIENTS];
for (ch_idx = NUM_AMBIENTS; ch_idx < (NUM_AMBIENTS + MAX_DYNAMIC_CHANNELS); ch_idx++, check++)
{
if (check == target_chan)
continue;
if ((check->sfx == sfx) && (!check->pos))
{
skip = rand() % ((int32_t) (0.1 * shm->speed));
if (skip >= target_chan->end)
skip = target_chan->end - 1;
target_chan->pos += skip;
target_chan->end -= skip;
break;
}
}
}
void S_StopSound(int32_t entnum, int32_t entchannel)
{
int32_t i;
for (i = 0; i < MAX_DYNAMIC_CHANNELS; i++)
{
if ((channels[i].entnum == entnum) && (channels[i].entchannel == entchannel))
{
channels[i].end = 0;
channels[i].sfx = 0;
return;
}
}
}
void S_StopAllSounds(bool clear)
{
int32_t i;
if (!sound_started)
return;
total_channels = MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS;
for (i = 0; i < MAX_CHANNELS; i++)
if (channels[i].sfx)
channels[i].sfx = 0;
memset(channels, 0, MAX_CHANNELS * (sizeof(channel_t)));
if (clear)
S_ClearBuffer();
}
void S_StopAllSoundsC(void)
{
S_StopAllSounds(1);
}
void S_ClearBuffer(void)
{
int32_t clear;
if (((!sound_started) || (!shm)) || (!shm->buffer))
return;
if (shm->samplebits == 8)
clear = 0x80;
else
clear = 0;
memset(shm->buffer, clear, (shm->samples * shm->samplebits) / 8);
}
void S_StaticSound(sfx_t *sfx, vec3_t origin, float vol, float attenuation)
{
channel_t *ss;
sfxcache_t *sc;
if (!sfx)
return;
if (total_channels == MAX_CHANNELS)
{
Con_Printf("total_channels == MAX_CHANNELS\n");
return;
}
ss = &channels[total_channels];
total_channels++;
sc = S_LoadSound(sfx);
if (!sc)
return;
if (sc->loopstart == (-1))
{
Con_Printf("Sound %s not looped\n", sfx->name);
return;
}
ss->sfx = sfx;
VectorCopy(origin, ss->origin);
ss->master_vol = vol;
ss->dist_mult = (attenuation / 64) / sound_nominal_clip_dist;
ss->end = paintedtime + sc->length;
SND_Spatialize(ss);
}
void S_UpdateAmbientSounds(void)
{
mleaf_t *l;
float vol;
int32_t ambient_channel;
channel_t *chan;
if (!snd_ambient)
return;
if (!cl.worldmodel)
return;
l = Mod_PointInLeaf(listener_origin, cl.worldmodel);
if ((!l) || (!ambient_level.value))
{
for (ambient_channel = 0; ambient_channel < NUM_AMBIENTS; ambient_channel++)
channels[ambient_channel].sfx = 0;
return;
}
for (ambient_channel = 0; ambient_channel < NUM_AMBIENTS; ambient_channel++)
{
chan = &channels[ambient_channel];
chan->sfx = ambient_sfx[ambient_channel];
vol = ambient_level.value * l->ambient_sound_level[ambient_channel];
if (vol < 8)
vol = 0;
if (chan->master_vol < vol)
{
chan->master_vol += host_frametime * ambient_fade.value;
if (chan->master_vol > vol)
chan->master_vol = vol;
}
else
if (chan->master_vol > vol)
{
chan->master_vol -= host_frametime * ambient_fade.value;
if (chan->master_vol < vol)
chan->master_vol = vol;
}
chan->leftvol = (chan->rightvol = chan->master_vol);
}
}
void S_Update(vec3_t origin, vec3_t forward, vec3_t right, vec3_t up)
{
int32_t i;
int32_t j;
int32_t total;
channel_t *ch;
channel_t *combine;
if ((!sound_started) || (snd_blocked > 0))
return;
VectorCopy(origin, listener_origin);
VectorCopy(forward, listener_forward);
VectorCopy(right, listener_right);
VectorCopy(up, listener_up);
S_UpdateAmbientSounds();
combine = 0;
ch = channels + NUM_AMBIENTS;
for (i = NUM_AMBIENTS; i < total_channels; i++, ch++)
{
if (!ch->sfx)
continue;
SND_Spatialize(ch);
if ((!ch->leftvol) && (!ch->rightvol))
continue;
if (i >= (MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS))
{
if (combine && (combine->sfx == ch->sfx))
{
combine->leftvol += ch->leftvol;
combine->rightvol += ch->rightvol;
ch->leftvol = (ch->rightvol = 0);
continue;
}
combine = (channels + MAX_DYNAMIC_CHANNELS) + NUM_AMBIENTS;
for (j = MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS; j < i; j++, combine++)
if (combine->sfx == ch->sfx)
break;
if (j == total_channels)
{
combine = 0;
}
else
{
if (combine != ch)
{
combine->leftvol += ch->leftvol;
combine->rightvol += ch->rightvol;
ch->leftvol = (ch->rightvol = 0);
}
continue;
}
}
}
if (snd_show.value)
{
total = 0;
ch = channels;
for (i = 0; i < total_channels; i++, ch++)
if (ch->sfx && (ch->leftvol || ch->rightvol))
{
total++;
}
Con_Printf("----(%i)----\n", total);
}
S_Update_();
}
void GetSoundtime(void)
{
int32_t samplepos;
static int32_t buffers;
static int32_t oldsamplepos;
int32_t fullsamples;
fullsamples = shm->samples / shm->channels;
samplepos = SNDDMA_GetDMAPos();
if (samplepos < oldsamplepos)
{
buffers++;
if (paintedtime > 0x40000000)
{
buffers = 0;
paintedtime = fullsamples;
S_StopAllSounds(1);
}
}
oldsamplepos = samplepos;
soundtime = (buffers * fullsamples) + (samplepos / shm->channels);
}
void S_ExtraUpdate(void)
{
if (snd_noextraupdate.value)
return;
S_Update_();
}
void S_Update_(void)
{
unsigned endtime;
int32_t samps;
if ((!sound_started) || (snd_blocked > 0))
return;
GetSoundtime();
if (paintedtime < soundtime)
{
paintedtime = soundtime;
}
endtime = soundtime + (_snd_mixahead.value * shm->speed);
samps = shm->samples >> (shm->channels - 1);
if ((endtime - soundtime) > samps)
endtime = soundtime + samps;
S_PaintChannels(endtime);
SNDDMA_Submit();
}
void S_Play(void)
{
static int32_t hash = 345;
int32_t i;
char name[256];
sfx_t *sfx;
i = 1;
while (i < Cmd_Argc())
{
if (!strrchr(Cmd_Argv(i), '.'))
{
strcpy(name, Cmd_Argv(i));
strcat(name, ".wav");
}
else
strcpy(name, Cmd_Argv(i));
sfx = S_PrecacheSound(name);
S_StartSound(hash++, 0, sfx, listener_origin, 1.0, 1.0);
i++;
}
}
void S_PlayVol(void)
{
static int32_t hash = 543;
int32_t i;
float vol;
char name[256];
sfx_t *sfx;
i = 1;
while (i < Cmd_Argc())
{
if (!strrchr(Cmd_Argv(i), '.'))
{
strcpy(name, Cmd_Argv(i));
strcat(name, ".wav");
}
else
strcpy(name, Cmd_Argv(i));
sfx = S_PrecacheSound(name);
vol = (float) strtod(Cmd_Argv(i + 1), 0);
S_StartSound(hash++, 0, sfx, listener_origin, vol, 1.0);
i += 2;
}
}
void S_SoundList(void)
{
int32_t i;
sfx_t *sfx;
sfxcache_t *sc;
int32_t size;
int32_t total;
total = 0;
for (sfx = known_sfx, i = 0; i < num_sfx; i++, sfx++)
{
sc = Cache_Check(&sfx->cache);
if (!sc)
continue;
size = (sc->length * sc->width) * (sc->stereo + 1);
total += size;
if (sc->loopstart >= 0)
Con_Printf("L");
else
Con_Printf(" ");
Con_Printf("(%2db) %6i : %s\n", sc->width * 8, size, sfx->name);
}
Con_Printf("Total resident: %i\n", total);
}
void S_LocalSound(char *sound)
{
sfx_t *sfx;
if (nosound.value)
return;
if (!sound_started)
return;
sfx = S_PrecacheSound(sound);
if (!sfx)
{
Con_Printf("S_LocalSound: can't cache %s\n", sound);
return;
}
S_StartSound(cl.viewentity, -1, sfx, vec3_origin, 1, 1);
}
void S_ClearPrecache(void)
{
}
void S_BeginPrecaching(void)
{
}
void S_EndPrecaching(void)
{
}
void ResampleSfx(sfx_t *sfx, int32_t inrate, int32_t inwidth, uint8_t *data)
{
int32_t outcount;
int32_t srcsample;
float stepscale;
int32_t i;
int32_t sample;
int32_t samplefrac;
int32_t fracstep;
sfxcache_t *sc;
sc = Cache_Check(&sfx->cache);
if (!sc)
return;
stepscale = ((float) inrate) / shm->speed;
outcount = sc->length / stepscale;
sc->length = outcount;
if (sc->loopstart != (-1))
sc->loopstart = sc->loopstart / stepscale;
sc->speed = shm->speed;
if (loadas8bit.value)
sc->width = 1;
else
sc->width = inwidth;
sc->stereo = 0;
if (((stepscale == 1) && (inwidth == 1)) && (sc->width == 1))
{
for (i = 0; i < outcount; i++)
((signed char *) sc->data)[i] = (int32_t) (((unsigned char) data[i]) - 128);
}
else
{
samplefrac = 0;
fracstep = stepscale * 256;
for (i = 0; i < outcount; i++)
{
srcsample = samplefrac >> 8;
samplefrac += fracstep;
if (inwidth == 2)
sample = ((int16_t *) data)[srcsample];
else
sample = ((int32_t) (((unsigned char) data[srcsample]) - 128)) << 8;
if (sc->width == 2)
((int16_t *) sc->data)[i] = sample;
else
((signed char *) sc->data)[i] = sample >> 8;
}
}
}
sfxcache_t *S_LoadSound(sfx_t *s)
{
char namebuffer[256];
uint8_t *data;
wavinfo_t info;
int32_t len;
float stepscale;
sfxcache_t *sc;
uint8_t stackbuf[1 * 1024];
sc = Cache_Check(&s->cache);
if (sc)
return sc;
strcpy(namebuffer, "sound/");
strcat(namebuffer, s->name);
data = COM_LoadStackFile(namebuffer, stackbuf, sizeof(stackbuf));
if (!data)
{
Con_Printf("Couldn't load %s\n", namebuffer);
return 0;
}
info = GetWavinfo(s->name, data, com_filesize);
if (info.channels != 1)
{
Con_Printf("%s is a stereo sample\n", s->name);
return 0;
}
stepscale = ((float) info.rate) / shm->speed;
len = info.samples / stepscale;
len = (len * info.width) * info.channels;
sc = Cache_Alloc(&s->cache, len + (sizeof(sfxcache_t)), s->name);
if (!sc)
return 0;
sc->length = info.samples;
sc->loopstart = info.loopstart;
sc->speed = info.rate;
sc->width = info.width;
sc->stereo = info.channels;
ResampleSfx(s, sc->speed, sc->width, data + info.dataofs);
return sc;
}
int16_t GetLittleShort(void)
{
int16_t val = 0;
val = *data_p;
val = val + ((*(data_p + 1)) << 8);
data_p += 2;
return val;
}
int32_t GetLittleLong(void)
{
int32_t val = 0;
val = *data_p;
val = val + ((*(data_p + 1)) << 8);
val = val + ((*(data_p + 2)) << 16);
val = val + ((*(data_p + 3)) << 24);
data_p += 4;
return val;
}
void FindNextChunk(char *name)
{
while (1)
{
data_p = last_chunk;
if (data_p >= iff_end)
{
data_p = 0;
return;
}
data_p += 4;
iff_chunk_len = GetLittleLong();
if (iff_chunk_len < 0)
{
data_p = 0;
return;
}
data_p -= 8;
last_chunk = (data_p + 8) + ((iff_chunk_len + 1) & (~1));
if (!strncmp(data_p, name, 4))
return;
}
}
void FindChunk(char *name)
{
last_chunk = iff_data;
FindNextChunk(name);
}
void DumpChunks(void)
{
char str[5];
str[4] = 0;
data_p = iff_data;
do
{
memcpy(str, data_p, 4);
data_p += 4;
iff_chunk_len = GetLittleLong();
Con_Printf("0x%x : %s (%d)\n", (int32_t) (data_p - 4), str, iff_chunk_len);
data_p += (iff_chunk_len + 1) & (~1);
}
while (data_p < iff_end);
}
wavinfo_t GetWavinfo(char *name, uint8_t *wav, int32_t wavlength)
{
wavinfo_t info;
int32_t i;
int32_t format;
int32_t samples;
memset(&info, 0, sizeof(info));
if (!wav)
return info;
iff_data = wav;
iff_end = wav + wavlength;
FindChunk("RIFF");
if (!(data_p && (!strncmp(data_p + 8, "WAVE", 4))))
{
Con_Printf("Missing RIFF/WAVE chunks\n");
return info;
}
iff_data = data_p + 12;
FindChunk("fmt ");
if (!data_p)
{
Con_Printf("Missing fmt chunk\n");
return info;
}
data_p += 8;
format = GetLittleShort();
if (format != 1)
{
Con_Printf("Microsoft PCM format only\n");
return info;
}
info.channels = GetLittleShort();
info.rate = GetLittleLong();
data_p += 4 + 2;
info.width = GetLittleShort() / 8;
FindChunk("cue ");
if (data_p)
{
data_p += 32;
info.loopstart = GetLittleLong();
FindNextChunk("LIST");
if (data_p)
{
if (!strncmp(data_p + 28, "mark", 4))
{
data_p += 24;
i = GetLittleLong();
info.samples = info.loopstart + i;
}
}
}
else
info.loopstart = -1;
FindChunk("data");
if (!data_p)
{
Con_Printf("Missing data chunk\n");
return info;
}
data_p += 4;
samples = GetLittleLong() / info.width;
if (info.samples)
{
if (samples < info.samples)
Sys_Error("Sound %s has a bad loop length", name);
}
else
info.samples = samples;
info.dataofs = data_p - wav;
return info;
}
void Snd_WriteLinearBlastStereo16(void)
{
int32_t i;
int32_t val;
for (i = 0; i < snd_linear_count; i += 2)
{
val = (snd_p[i] * snd_vol) >> 8;
if (val > 0x7fff)
snd_out[i] = 0x7fff;
else
if (val < ((int16_t) 0x8000))
snd_out[i] = (int16_t) 0x8000;
else
snd_out[i] = val;
val = (snd_p[i + 1] * snd_vol) >> 8;
if (val > 0x7fff)
snd_out[i + 1] = 0x7fff;
else
if (val < ((int16_t) 0x8000))
snd_out[i + 1] = (int16_t) 0x8000;
else
snd_out[i + 1] = val;
}
}
void S_TransferStereo16(int32_t endtime)
{
int32_t lpos;
int32_t lpaintedtime;
uint32_t *pbuf;
snd_vol = volume.value * 256;
snd_p = (int32_t *) paintbuffer;
lpaintedtime = paintedtime;
pbuf = (uint32_t *) shm->buffer;
while (lpaintedtime < endtime)
{
lpos = lpaintedtime & ((shm->samples >> 1) - 1);
snd_out = ((int16_t *) pbuf) + (lpos << 1);
snd_linear_count = (shm->samples >> 1) - lpos;
if ((lpaintedtime + snd_linear_count) > endtime)
snd_linear_count = endtime - lpaintedtime;
snd_linear_count <<= 1;
Snd_WriteLinearBlastStereo16();
snd_p += snd_linear_count;
lpaintedtime += snd_linear_count >> 1;
}
}
void S_TransferPaintBuffer(int32_t endtime)
{
int32_t out_idx;
int32_t count;
int32_t out_mask;
int32_t *p;
int32_t step;
int32_t val;
int32_t snd_vol;
uint32_t *pbuf;
if ((shm->samplebits == 16) && (shm->channels == 2))
{
S_TransferStereo16(endtime);
return;
}
p = (int32_t *) paintbuffer;
count = (endtime - paintedtime) * shm->channels;
out_mask = shm->samples - 1;
out_idx = (paintedtime * shm->channels) & out_mask;
step = 3 - shm->channels;
snd_vol = volume.value * 256;
pbuf = (uint32_t *) shm->buffer;
if (shm->samplebits == 16)
{
int16_t *out = (int16_t *) pbuf;
while (count--)
{
val = ((*p) * snd_vol) >> 8;
p += step;
if (val > 0x7fff)
val = 0x7fff;
else
if (val < ((int16_t) 0x8000))
val = (int16_t) 0x8000;
out[out_idx] = val;
out_idx = (out_idx + 1) & out_mask;
}
}
else
if (shm->samplebits == 8)
{
unsigned char *out = (unsigned char *) pbuf;
while (count--)
{
val = ((*p) * snd_vol) >> 8;
p += step;
if (val > 0x7fff)
val = 0x7fff;
else
if (val < ((int16_t) 0x8000))
val = (int16_t) 0x8000;
out[out_idx] = (val >> 8) + 128;
out_idx = (out_idx + 1) & out_mask;
}
}
}
void S_PaintChannels(int32_t endtime)
{
int32_t i;
int32_t end;
channel_t *ch;
sfxcache_t *sc;
int32_t ltime;
int32_t count;
while (paintedtime < endtime)
{
end = endtime;
if ((endtime - paintedtime) > PAINTBUFFER_SIZE)
end = paintedtime + PAINTBUFFER_SIZE;
memset(paintbuffer, 0, (end - paintedtime) * (sizeof(portable_samplepair_t)));
ch = channels;
for (i = 0; i < total_channels; i++, ch++)
{
if (!ch->sfx)
continue;
if ((!ch->leftvol) && (!ch->rightvol))
continue;
sc = S_LoadSound(ch->sfx);
if (!sc)
continue;
ltime = paintedtime;
while (ltime < end)
{
if (ch->end < end)
count = ch->end - ltime;
else
count = end - ltime;
if (count > 0)
{
if (sc->width == 1)
SND_PaintChannelFrom8(ch, sc, count);
else
SND_PaintChannelFrom16(ch, sc, count);
ltime += count;
}
if (ltime >= ch->end)
{
if (sc->loopstart >= 0)
{
ch->pos = sc->loopstart;
ch->end = (ltime + sc->length) - ch->pos;
}
else
{
ch->sfx = 0;
break;
}
}
}
}
S_TransferPaintBuffer(end);
paintedtime = end;
}
}
void SND_InitScaletable(void)
{
int32_t i;
int32_t j;
for (i = 0; i < 32; i++)
for (j = 0; j < 256; j++)
snd_scaletable[i][j] = (((signed char) j) * i) * 8;
}
void SND_PaintChannelFrom8(channel_t *ch, sfxcache_t *sc, int32_t count)
{
int32_t data;
int32_t *lscale;
int32_t *rscale;
unsigned char *sfx;
int32_t i;
if (ch->leftvol > 255)
ch->leftvol = 255;
if (ch->rightvol > 255)
ch->rightvol = 255;
lscale = snd_scaletable[ch->leftvol >> 3];
rscale = snd_scaletable[ch->rightvol >> 3];
sfx = ((signed char *) sc->data) + ch->pos;
for (i = 0; i < count; i++)
{
data = sfx[i];
paintbuffer[i].left += lscale[data];
paintbuffer[i].right += rscale[data];
}
ch->pos += count;
}
void SND_PaintChannelFrom16(channel_t *ch, sfxcache_t *sc, int32_t count)
{
int32_t data;
int32_t left;
int32_t right;
int32_t leftvol;
int32_t rightvol;
int16_t *sfx;
int32_t i;
leftvol = ch->leftvol;
rightvol = ch->rightvol;
sfx = ((int16_t *) sc->data) + ch->pos;
for (i = 0; i < count; i++)
{
data = sfx[i];
left = (data * leftvol) >> 8;
right = (data * rightvol) >> 8;
paintbuffer[i].left += left;
paintbuffer[i].right += right;
}
ch->pos += count;
}
bool SNDDMA_Init(void)
{
int buffer_samples = 1024;
snd_inited = 0;
if ((desired_bits != 8) && (desired_bits != 16))
{
Con_Printf("Unknown number of audio bits: %d\n", desired_bits);
return 0;
}
InitAudioDevice();
SetAudioStreamBufferSizeDefault(buffer_samples);
stream = LoadAudioStream(desired_speed, desired_bits, 2);
if (!IsAudioStreamValid(stream))
{
Con_Printf("Could not create audio stream\n");
return 0;
}
PlayAudioStream(stream);
mixbuffer = MemAlloc((buffer_samples * 2) * (desired_bits / 8));
shm = &the_shm;
shm->splitbuffer = 0;
shm->samplebits = desired_bits;
shm->speed = desired_speed;
shm->channels = 2;
shm->samples = buffer_samples * shm->channels;
shm->samplepos = 0;
shm->submission_chunk = 1;
shm->buffer = mixbuffer;
snd_inited = 1;
return 1;
}
int32_t SNDDMA_GetDMAPos(void)
{
return shm->samplepos;
}
void SNDDMA_Shutdown(void)
{
if (snd_inited)
{
MemFree(mixbuffer);
UnloadAudioStream(stream);
CloseAudioDevice();
snd_inited = 0;
}
}
void SNDDMA_Submit(void)
{
if ((!snd_inited) || (!IsAudioStreamProcessed(stream)))
{
return;
}
int sample_frames = shm->samples / shm->channels;
shm->samplepos += sample_frames;
S_PaintChannels(shm->samplepos);
UpdateAudioStream(stream, shm->buffer, sample_frames);
}
void SV_Init(void)
{
int32_t i;
extern cvar_t sv_maxvelocity;
extern cvar_t sv_gravity;
extern cvar_t sv_nostep;
extern cvar_t sv_friction;
extern cvar_t sv_edgefriction;
extern cvar_t sv_stopspeed;
extern cvar_t sv_maxspeed;
extern cvar_t sv_accelerate;
extern cvar_t sv_idealpitchscale;
extern cvar_t sv_aim;
Cvar_RegisterVariable(&sv_maxvelocity);
Cvar_RegisterVariable(&sv_gravity);
Cvar_RegisterVariable(&sv_friction);
Cvar_RegisterVariable(&sv_edgefriction);
Cvar_RegisterVariable(&sv_stopspeed);
Cvar_RegisterVariable(&sv_maxspeed);
Cvar_RegisterVariable(&sv_accelerate);
Cvar_RegisterVariable(&sv_idealpitchscale);
Cvar_RegisterVariable(&sv_aim);
Cvar_RegisterVariable(&sv_nostep);
for (i = 0; i < MAX_MODELS; i++)
sprintf(localmodels[i], "*%i", i);
}
void SV_StartParticle(vec3_t org, vec3_t dir, int32_t color, int32_t count)
{
int32_t i;
int32_t v;
if (sv.datagram.cursize > (MAX_DATAGRAM - 16))
return;
MSG_WriteByte(&sv.datagram, svc_particle);
MSG_WriteCoord(&sv.datagram, org[0]);
MSG_WriteCoord(&sv.datagram, org[1]);
MSG_WriteCoord(&sv.datagram, org[2]);
for (i = 0; i < 3; i++)
{
v = dir[i] * 16;
if (v > 127)
v = 127;
else
if (v < (-128))
v = -128;
MSG_WriteChar(&sv.datagram, v);
}
MSG_WriteByte(&sv.datagram, count);
MSG_WriteByte(&sv.datagram, color);
}
void SV_StartSound(edict_t *entity, int32_t channel, char *sample, int32_t volume, float attenuation)
{
int32_t sound_num;
int32_t field_mask;
int32_t i;
int32_t ent;
if ((volume < 0) || (volume > 255))
Sys_Error("SV_StartSound: volume = %i", volume);
if ((attenuation < 0) || (attenuation > 4))
Sys_Error("SV_StartSound: attenuation = %f", attenuation);
if ((channel < 0) || (channel > 7))
Sys_Error("SV_StartSound: channel = %i", channel);
if (sv.datagram.cursize > (MAX_DATAGRAM - 16))
return;
for (sound_num = 1; (sound_num < MAX_SOUNDS) && sv.sound_precache[sound_num]; sound_num++)
if (!strcmp(sample, sv.sound_precache[sound_num]))
break;
if ((sound_num == MAX_SOUNDS) || (!sv.sound_precache[sound_num]))
{
Con_Printf("SV_StartSound: %s not precacheed\n", sample);
return;
}
ent = NUM_FOR_EDICT(entity);
channel = (ent << 3) | channel;
field_mask = 0;
if (volume != DEFAULT_SOUND_PACKET_VOLUME)
field_mask |= SND_VOLUME;
if (attenuation != DEFAULT_SOUND_PACKET_ATTENUATION)
field_mask |= SND_ATTENUATION;
MSG_WriteByte(&sv.datagram, svc_sound);
MSG_WriteByte(&sv.datagram, field_mask);
if (field_mask & SND_VOLUME)
MSG_WriteByte(&sv.datagram, volume);
if (field_mask & SND_ATTENUATION)
MSG_WriteByte(&sv.datagram, attenuation * 64);
MSG_WriteShort(&sv.datagram, channel);
MSG_WriteByte(&sv.datagram, sound_num);
for (i = 0; i < 3; i++)
MSG_WriteCoord(&sv.datagram, entity->v.origin[i] + (0.5 * (entity->v.mins[i] + entity->v.maxs[i])));
}
void SV_SendServerinfo(client_t *client)
{
char **s;
char message[2048];
MSG_WriteByte(&client->message, svc_print);
sprintf(message, "%c\nVERSION %4.2f SERVER (%i CRC)", 2, VERSION, pr_crc);
MSG_WriteString(&client->message, message);
MSG_WriteByte(&client->message, svc_serverinfo);
MSG_WriteLong(&client->message, PROTOCOL_VERSION);
MSG_WriteByte(&client->message, svs.maxclients);
if ((!coop.value) && deathmatch.value)
MSG_WriteByte(&client->message, GAME_DEATHMATCH);
else
MSG_WriteByte(&client->message, GAME_COOP);
sprintf(message, pr_strings + sv.edicts->v.message);
MSG_WriteString(&client->message, message);
for (s = sv.model_precache + 1; *s; s++)
MSG_WriteString(&client->message, *s);
MSG_WriteByte(&client->message, 0);
for (s = sv.sound_precache + 1; *s; s++)
MSG_WriteString(&client->message, *s);
MSG_WriteByte(&client->message, 0);
MSG_WriteByte(&client->message, svc_cdtrack);
MSG_WriteByte(&client->message, sv.edicts->v.sounds);
MSG_WriteByte(&client->message, sv.edicts->v.sounds);
MSG_WriteByte(&client->message, svc_setview);
MSG_WriteShort(&client->message, NUM_FOR_EDICT(client->edict));
MSG_WriteByte(&client->message, svc_signonnum);
MSG_WriteByte(&client->message, 1);
client->sendsignon = 1;
client->spawned = 0;
}
void SV_ConnectClient(int32_t clientnum)
{
edict_t *ent;
client_t *client;
int32_t edictnum;
struct qsocket_s *netconnection;
int32_t i;
float spawn_parms[NUM_SPAWN_PARMS];
client = svs.clients + clientnum;
Con_DPrintf("Client %s connected\n", client->netconnection->address);
edictnum = clientnum + 1;
ent = EDICT_NUM(edictnum);
netconnection = client->netconnection;
if (sv.loadgame)
memcpy(spawn_parms, client->spawn_parms, sizeof(spawn_parms));
memset(client, 0, sizeof(*client));
client->netconnection = netconnection;
strcpy(client->name, "unconnected");
client->active = 1;
client->spawned = 0;
client->edict = ent;
client->message.data = client->msgbuf;
client->message.maxsize = sizeof(client->msgbuf);
client->message.allowoverflow = 1;
client->privileged = 0;
if (sv.loadgame)
memcpy(client->spawn_parms, spawn_parms, sizeof(spawn_parms));
else
{
PR_ExecuteProgram(pr_global_struct->SetNewParms);
for (i = 0; i < NUM_SPAWN_PARMS; i++)
client->spawn_parms[i] = (&pr_global_struct->parm1)[i];
}
SV_SendServerinfo(client);
}
void SV_CheckForNewClients(void)
{
struct qsocket_s *ret;
int32_t i;
while (1)
{
ret = NET_CheckNewConnections();
if (!ret)
break;
for (i = 0; i < svs.maxclients; i++)
if (!svs.clients[i].active)
break;
if (i == svs.maxclients)
Sys_Error("Host_CheckForNewClients: no free clients");
svs.clients[i].netconnection = ret;
SV_ConnectClient(i);
net_activeconnections++;
}
}
void SV_ClearDatagram(void)
{
SZ_Clear(&sv.datagram);
}
void SV_AddToFatPVS(vec3_t org, mnode_t *node)
{
int32_t i;
uint8_t *pvs;
mplane_t *plane;
float d;
while (1)
{
if (node->contents < 0)
{
if (node->contents != CONTENTS_SOLID)
{
pvs = Mod_LeafPVS((mleaf_t *) node, sv.worldmodel);
for (i = 0; i < fatbytes; i++)
fatpvs[i] |= pvs[i];
}
return;
}
plane = node->plane;
d = DotProduct(org, plane->normal) - plane->dist;
if (d > 8)
node = node->children[0];
else
if (d < (-8))
node = node->children[1];
else
{
SV_AddToFatPVS(org, node->children[0]);
node = node->children[1];
}
}
}
uint8_t *SV_FatPVS(vec3_t org)
{
fatbytes = (sv.worldmodel->numleafs + 31) >> 3;
memset(fatpvs, 0, fatbytes);
SV_AddToFatPVS(org, sv.worldmodel->nodes);
return fatpvs;
}
void SV_WriteEntitiesToClient(edict_t *clent, sizebuf_t *msg)
{
int32_t e;
int32_t i;
int32_t bits;
uint8_t *pvs;
vec3_t org;
float miss;
edict_t *ent;
VectorAdd(clent->v.origin, clent->v.view_ofs, org);
pvs = SV_FatPVS(org);
ent = NEXT_EDICT(sv.edicts);
for (e = 1; e < sv.num_edicts; e++, ent = NEXT_EDICT(ent))
{
if (ent != clent)
{
if ((!ent->v.modelindex) || (!pr_strings[ent->v.model]))
continue;
for (i = 0; i < ent->num_leafs; i++)
if (pvs[ent->leafnums[i] >> 3] & (1 << (ent->leafnums[i] & 7)))
break;
if (i == ent->num_leafs)
continue;
}
if ((msg->maxsize - msg->cursize) < 16)
{
Con_Printf("packet overflow\n");
return;
}
bits = 0;
for (i = 0; i < 3; i++)
{
miss = ent->v.origin[i] - ent->baseline.origin[i];
if ((miss < (-0.1)) || (miss > 0.1))
bits |= U_ORIGIN1 << i;
}
if (ent->v.angles[0] != ent->baseline.angles[0])
bits |= U_ANGLE1;
if (ent->v.angles[1] != ent->baseline.angles[1])
bits |= U_ANGLE2;
if (ent->v.angles[2] != ent->baseline.angles[2])
bits |= U_ANGLE3;
if (ent->v.movetype == MOVETYPE_STEP)
bits |= U_NOLERP;
if (ent->baseline.colormap != ent->v.colormap)
bits |= U_COLORMAP;
if (ent->baseline.skin != ent->v.skin)
bits |= U_SKIN;
if (ent->baseline.frame != ent->v.frame)
bits |= U_FRAME;
if (ent->baseline.effects != ent->v.effects)
bits |= U_EFFECTS;
if (ent->baseline.modelindex != ent->v.modelindex)
bits |= U_MODEL;
if (e >= 256)
bits |= U_LONGENTITY;
if (bits >= 256)
bits |= U_MOREBITS;
MSG_WriteByte(msg, bits | U_SIGNAL);
if (bits & U_MOREBITS)
MSG_WriteByte(msg, bits >> 8);
if (bits & U_LONGENTITY)
MSG_WriteShort(msg, e);
else
MSG_WriteByte(msg, e);
if (bits & U_MODEL)
MSG_WriteByte(msg, ent->v.modelindex);
if (bits & U_FRAME)
MSG_WriteByte(msg, ent->v.frame);
if (bits & U_COLORMAP)
MSG_WriteByte(msg, ent->v.colormap);
if (bits & U_SKIN)
MSG_WriteByte(msg, ent->v.skin);
if (bits & U_EFFECTS)
MSG_WriteByte(msg, ent->v.effects);
if (bits & U_ORIGIN1)
MSG_WriteCoord(msg, ent->v.origin[0]);
if (bits & U_ANGLE1)
MSG_WriteAngle(msg, ent->v.angles[0]);
if (bits & U_ORIGIN2)
MSG_WriteCoord(msg, ent->v.origin[1]);
if (bits & U_ANGLE2)
MSG_WriteAngle(msg, ent->v.angles[1]);
if (bits & U_ORIGIN3)
MSG_WriteCoord(msg, ent->v.origin[2]);
if (bits & U_ANGLE3)
MSG_WriteAngle(msg, ent->v.angles[2]);
}
}
void SV_CleanupEnts(void)
{
int32_t e;
edict_t *ent;
ent = NEXT_EDICT(sv.edicts);
for (e = 1; e < sv.num_edicts; e++, ent = NEXT_EDICT(ent))
{
ent->v.effects = ((int32_t) ent->v.effects) & (~EF_MUZZLEFLASH);
}
}
void SV_WriteClientdataToMessage(edict_t *ent, sizebuf_t *msg)
{
int32_t bits;
int32_t i;
edict_t *other;
int32_t items;
eval_t *val;
if (ent->v.dmg_take || ent->v.dmg_save)
{
other = PROG_TO_EDICT(ent->v.dmg_inflictor);
MSG_WriteByte(msg, svc_damage);
MSG_WriteByte(msg, ent->v.dmg_save);
MSG_WriteByte(msg, ent->v.dmg_take);
for (i = 0; i < 3; i++)
MSG_WriteCoord(msg, other->v.origin[i] + (0.5 * (other->v.mins[i] + other->v.maxs[i])));
ent->v.dmg_take = 0;
ent->v.dmg_save = 0;
}
SV_SetIdealPitch();
if (ent->v.fixangle)
{
MSG_WriteByte(msg, svc_setangle);
for (i = 0; i < 3; i++)
MSG_WriteAngle(msg, ent->v.angles[i]);
ent->v.fixangle = 0;
}
bits = 0;
if (ent->v.view_ofs[2] != DEFAULT_VIEWHEIGHT)
bits |= SU_VIEWHEIGHT;
if (ent->v.idealpitch)
bits |= SU_IDEALPITCH;
val = GetEdictFieldValue(ent, "items2");
if (val)
items = ((int32_t) ent->v.items) | (((int32_t) val->_float) << 23);
else
items = ((int32_t) ent->v.items) | (((int32_t) pr_global_struct->serverflags) << 28);
bits |= SU_ITEMS;
if (((int32_t) ent->v.flags) & FL_ONGROUND)
bits |= SU_ONGROUND;
if (ent->v.waterlevel >= 2)
bits |= SU_INWATER;
for (i = 0; i < 3; i++)
{
if (ent->v.punchangle[i])
bits |= SU_PUNCH1 << i;
if (ent->v.velocity[i])
bits |= SU_VELOCITY1 << i;
}
if (ent->v.weaponframe)
bits |= SU_WEAPONFRAME;
if (ent->v.armorvalue)
bits |= SU_ARMOR;
bits |= SU_WEAPON;
MSG_WriteByte(msg, svc_clientdata);
MSG_WriteShort(msg, bits);
if (bits & SU_VIEWHEIGHT)
MSG_WriteChar(msg, ent->v.view_ofs[2]);
if (bits & SU_IDEALPITCH)
MSG_WriteChar(msg, ent->v.idealpitch);
for (i = 0; i < 3; i++)
{
if (bits & (SU_PUNCH1 << i))
MSG_WriteChar(msg, ent->v.punchangle[i]);
if (bits & (SU_VELOCITY1 << i))
MSG_WriteChar(msg, ent->v.velocity[i] / 16);
}
MSG_WriteLong(msg, items);
if (bits & SU_WEAPONFRAME)
MSG_WriteByte(msg, ent->v.weaponframe);
if (bits & SU_ARMOR)
MSG_WriteByte(msg, ent->v.armorvalue);
if (bits & SU_WEAPON)
MSG_WriteByte(msg, SV_ModelIndex(pr_strings + ent->v.weaponmodel));
MSG_WriteShort(msg, ent->v.health);
MSG_WriteByte(msg, ent->v.currentammo);
MSG_WriteByte(msg, ent->v.ammo_shells);
MSG_WriteByte(msg, ent->v.ammo_nails);
MSG_WriteByte(msg, ent->v.ammo_rockets);
MSG_WriteByte(msg, ent->v.ammo_cells);
if (standard_quake)
{
MSG_WriteByte(msg, ent->v.weapon);
}
else
{
for (i = 0; i < 32; i++)
{
if (((int32_t) ent->v.weapon) & (1 << i))
{
MSG_WriteByte(msg, i);
break;
}
}
}
}
bool SV_SendClientDatagram(client_t *client)
{
uint8_t buf[MAX_DATAGRAM];
sizebuf_t msg;
msg.data = buf;
msg.maxsize = sizeof(buf);
msg.cursize = 0;
MSG_WriteByte(&msg, svc_time);
MSG_WriteFloat(&msg, sv.time);
SV_WriteClientdataToMessage(client->edict, &msg);
SV_WriteEntitiesToClient(client->edict, &msg);
if ((msg.cursize + sv.datagram.cursize) < msg.maxsize)
SZ_Write(&msg, sv.datagram.data, sv.datagram.cursize);
if (NET_SendUnreliableMessage(client->netconnection, &msg) == (-1))
{
SV_DropClient(1);
return 0;
}
return 1;
}
void SV_UpdateToReliableMessages(void)
{
int32_t i;
int32_t j;
client_t *client;
for (i = 0, host_client = svs.clients; i < svs.maxclients; i++, host_client++)
{
if (host_client->old_frags != host_client->edict->v.frags)
{
for (j = 0, client = svs.clients; j < svs.maxclients; j++, client++)
{
if (!client->active)
continue;
MSG_WriteByte(&client->message, svc_updatefrags);
MSG_WriteByte(&client->message, i);
MSG_WriteShort(&client->message, host_client->edict->v.frags);
}
host_client->old_frags = host_client->edict->v.frags;
}
}
for (j = 0, client = svs.clients; j < svs.maxclients; j++, client++)
{
if (!client->active)
continue;
SZ_Write(&client->message, sv.reliable_datagram.data, sv.reliable_datagram.cursize);
}
SZ_Clear(&sv.reliable_datagram);
}
void SV_SendNop(client_t *client)
{
sizebuf_t msg;
uint8_t buf[4];
msg.data = buf;
msg.maxsize = sizeof(buf);
msg.cursize = 0;
MSG_WriteChar(&msg, svc_nop);
if (NET_SendUnreliableMessage(client->netconnection, &msg) == (-1))
SV_DropClient(1);
client->last_message = realtime;
}
void SV_SendClientMessages(void)
{
int32_t i;
SV_UpdateToReliableMessages();
for (i = 0, host_client = svs.clients; i < svs.maxclients; i++, host_client++)
{
if (!host_client->active)
continue;
if (host_client->spawned)
{
if (!SV_SendClientDatagram(host_client))
continue;
}
else
{
if (!host_client->sendsignon)
{
if ((realtime - host_client->last_message) > 5)
SV_SendNop(host_client);
continue;
}
}
if (host_client->message.overflowed)
{
SV_DropClient(1);
host_client->message.overflowed = 0;
continue;
}
if (host_client->message.cursize || host_client->dropasap)
{
if (!NET_CanSendMessage(host_client->netconnection))
{
continue;
}
if (host_client->dropasap)
SV_DropClient(0);
else
{
if (NET_SendMessage(host_client->netconnection, &host_client->message) == (-1))
SV_DropClient(1);
SZ_Clear(&host_client->message);
host_client->last_message = realtime;
host_client->sendsignon = 0;
}
}
}
SV_CleanupEnts();
}
int32_t SV_ModelIndex(char *name)
{
int32_t i;
if ((!name) || (!name[0]))
return 0;
for (i = 0; (i < MAX_MODELS) && sv.model_precache[i]; i++)
if (!strcmp(sv.model_precache[i], name))
return i;
if ((i == MAX_MODELS) || (!sv.model_precache[i]))
Sys_Error("SV_ModelIndex: model %s not precached", name);
return i;
}
void SV_CreateBaseline(void)
{
int32_t i;
edict_t *svent;
int32_t entnum;
for (entnum = 0; entnum < sv.num_edicts; entnum++)
{
svent = EDICT_NUM(entnum);
if (svent->free)
continue;
if ((entnum > svs.maxclients) && (!svent->v.modelindex))
continue;
VectorCopy(svent->v.origin, svent->baseline.origin);
VectorCopy(svent->v.angles, svent->baseline.angles);
svent->baseline.frame = svent->v.frame;
svent->baseline.skin = svent->v.skin;
if ((entnum > 0) && (entnum <= svs.maxclients))
{
svent->baseline.colormap = entnum;
svent->baseline.modelindex = SV_ModelIndex("progs/player.mdl");
}
else
{
svent->baseline.colormap = 0;
svent->baseline.modelindex = SV_ModelIndex(pr_strings + svent->v.model);
}
MSG_WriteByte(&sv.signon, svc_spawnbaseline);
MSG_WriteShort(&sv.signon, entnum);
MSG_WriteByte(&sv.signon, svent->baseline.modelindex);
MSG_WriteByte(&sv.signon, svent->baseline.frame);
MSG_WriteByte(&sv.signon, svent->baseline.colormap);
MSG_WriteByte(&sv.signon, svent->baseline.skin);
for (i = 0; i < 3; i++)
{
MSG_WriteCoord(&sv.signon, svent->baseline.origin[i]);
MSG_WriteAngle(&sv.signon, svent->baseline.angles[i]);
}
}
}
void SV_SendReconnect(void)
{
char data[128];
sizebuf_t msg;
msg.data = data;
msg.cursize = 0;
msg.maxsize = sizeof(data);
MSG_WriteChar(&msg, svc_stufftext);
MSG_WriteString(&msg, "reconnect\n");
NET_SendToAll(&msg, 5);
if (cls.state != ca_dedicated)
Cmd_ExecuteString("reconnect\n", src_command);
}
void SV_SaveSpawnparms(void)
{
int32_t i;
int32_t j;
svs.serverflags = pr_global_struct->serverflags;
for (i = 0, host_client = svs.clients; i < svs.maxclients; i++, host_client++)
{
if (!host_client->active)
continue;
pr_global_struct->self = EDICT_TO_PROG(host_client->edict);
PR_ExecuteProgram(pr_global_struct->SetChangeParms);
for (j = 0; j < NUM_SPAWN_PARMS; j++)
host_client->spawn_parms[j] = (&pr_global_struct->parm1)[j];
}
}
void SV_SpawnServer(char *server)
{
edict_t *ent;
int32_t i;
if (hostname.string[0] == 0)
Cvar_Set("hostname", "UNNAMED");
scr_centertime_off = 0;
Con_DPrintf("SpawnServer: %s\n", server);
svs.changelevel_issued = 0;
if (sv.active)
{
SV_SendReconnect();
}
if (coop.value)
Cvar_SetValue("deathmatch", 0);
current_skill = (int32_t) (skill.value + 0.5);
if (current_skill < 0)
current_skill = 0;
if (current_skill > 3)
current_skill = 3;
Cvar_SetValue("skill", (float) current_skill);
Host_ClearMemory();
memset(&sv, 0, sizeof(sv));
strcpy(sv.name, server);
PR_LoadProgs();
sv.max_edicts = MAX_EDICTS;
sv.edicts = Hunk_AllocName(sv.max_edicts * pr_edict_size, "edicts");
sv.datagram.maxsize = sizeof(sv.datagram_buf);
sv.datagram.cursize = 0;
sv.datagram.data = sv.datagram_buf;
sv.reliable_datagram.maxsize = sizeof(sv.reliable_datagram_buf);
sv.reliable_datagram.cursize = 0;
sv.reliable_datagram.data = sv.reliable_datagram_buf;
sv.signon.maxsize = sizeof(sv.signon_buf);
sv.signon.cursize = 0;
sv.signon.data = sv.signon_buf;
sv.num_edicts = svs.maxclients + 1;
for (i = 0; i < svs.maxclients; i++)
{
ent = EDICT_NUM(i + 1);
svs.clients[i].edict = ent;
}
sv.state = ss_loading;
sv.paused = 0;
sv.time = 1.0;
strcpy(sv.name, server);
sprintf(sv.modelname, "maps/%s.bsp", server);
sv.worldmodel = Mod_ForName(sv.modelname, 0);
if (!sv.worldmodel)
{
Con_Printf("Couldn't spawn server %s\n", sv.modelname);
sv.active = 0;
return;
}
sv.models[1] = sv.worldmodel;
SV_ClearWorld();
sv.sound_precache[0] = pr_strings;
sv.model_precache[0] = pr_strings;
sv.model_precache[1] = sv.modelname;
for (i = 1; i < sv.worldmodel->numsubmodels; i++)
{
sv.model_precache[1 + i] = localmodels[i];
sv.models[i + 1] = Mod_ForName(localmodels[i], 0);
}
ent = EDICT_NUM(0);
memset(&ent->v, 0, progs->entityfields * 4);
ent->free = 0;
ent->v.model = sv.worldmodel->name - pr_strings;
ent->v.modelindex = 1;
ent->v.solid = SOLID_BSP;
ent->v.movetype = MOVETYPE_PUSH;
if (coop.value)
pr_global_struct->coop = coop.value;
else
pr_global_struct->deathmatch = deathmatch.value;
pr_global_struct->mapname = sv.name - pr_strings;
pr_global_struct->serverflags = svs.serverflags;
ED_LoadFromFile(sv.worldmodel->entities);
sv.active = 1;
sv.state = ss_active;
host_frametime = 0.1;
SV_Physics();
SV_Physics();
SV_CreateBaseline();
for (i = 0, host_client = svs.clients; i < svs.maxclients; i++, host_client++)
if (host_client->active)
SV_SendServerinfo(host_client);
Con_DPrintf("Server spawned.\n");
}
bool SV_CheckBottom(edict_t *ent)
{
vec3_t mins;
vec3_t maxs;
vec3_t start;
vec3_t stop;
trace_t trace;
int32_t x;
int32_t y;
float mid;
float bottom;
VectorAdd(ent->v.origin, ent->v.mins, mins);
VectorAdd(ent->v.origin, ent->v.maxs, maxs);
start[2] = mins[2] - 1;
for (x = 0; x <= 1; x++)
for (y = 0; y <= 1; y++)
{
start[0] = (x) ? (maxs[0]) : (mins[0]);
start[1] = (y) ? (maxs[1]) : (mins[1]);
if (SV_PointContents(start) != CONTENTS_SOLID)
goto realcheck;
}
c_yes++;
return 1;
realcheck:
c_no++;
start[2] = mins[2];
start[0] = (stop[0] = (mins[0] + maxs[0]) * 0.5);
start[1] = (stop[1] = (mins[1] + maxs[1]) * 0.5);
stop[2] = start[2] - (2 * STEPSIZE);
trace = SV_Move(start, vec3_origin, vec3_origin, stop, 1, ent);
if (trace.fraction == 1.0)
return 0;
mid = (bottom = trace.endpos[2]);
for (x = 0; x <= 1; x++)
for (y = 0; y <= 1; y++)
{
start[0] = (stop[0] = (x) ? (maxs[0]) : (mins[0]));
start[1] = (stop[1] = (y) ? (maxs[1]) : (mins[1]));
trace = SV_Move(start, vec3_origin, vec3_origin, stop, 1, ent);
if ((trace.fraction != 1.0) && (trace.endpos[2] > bottom))
bottom = trace.endpos[2];
if ((trace.fraction == 1.0) || ((mid - trace.endpos[2]) > STEPSIZE))
return 0;
}
c_yes++;
return 1;
}
bool SV_movestep(edict_t *ent, vec3_t move, bool relink)
{
float dz;
vec3_t oldorg;
vec3_t neworg;
vec3_t end;
trace_t trace;
int32_t i;
edict_t *enemy;
VectorCopy(ent->v.origin, oldorg);
VectorAdd(ent->v.origin, move, neworg);
if (((int32_t) ent->v.flags) & (FL_SWIM | FL_FLY))
{
for (i = 0; i < 2; i++)
{
VectorAdd(ent->v.origin, move, neworg);
enemy = PROG_TO_EDICT(ent->v.enemy);
if ((i == 0) && (enemy != sv.edicts))
{
dz = ent->v.origin[2] - PROG_TO_EDICT(ent->v.enemy)->v.origin[2];
if (dz > 40)
neworg[2] -= 8;
if (dz < 30)
neworg[2] += 8;
}
trace = SV_Move(ent->v.origin, ent->v.mins, ent->v.maxs, neworg, 0, ent);
if (trace.fraction == 1)
{
if ((((int32_t) ent->v.flags) & FL_SWIM) && (SV_PointContents(trace.endpos) == CONTENTS_EMPTY))
return 0;
VectorCopy(trace.endpos, ent->v.origin);
if (relink)
SV_LinkEdict(ent, 1);
return 1;
}
if (enemy == sv.edicts)
break;
}
return 0;
}
neworg[2] += STEPSIZE;
VectorCopy(neworg, end);
end[2] -= STEPSIZE * 2;
trace = SV_Move(neworg, ent->v.mins, ent->v.maxs, end, 0, ent);
if (trace.allsolid)
return 0;
if (trace.startsolid)
{
neworg[2] -= STEPSIZE;
trace = SV_Move(neworg, ent->v.mins, ent->v.maxs, end, 0, ent);
if (trace.allsolid || trace.startsolid)
return 0;
}
if (trace.fraction == 1)
{
if (((int32_t) ent->v.flags) & FL_PARTIALGROUND)
{
VectorAdd(ent->v.origin, move, ent->v.origin);
if (relink)
SV_LinkEdict(ent, 1);
ent->v.flags = ((int32_t) ent->v.flags) & (~FL_ONGROUND);
return 1;
}
return 0;
}
VectorCopy(trace.endpos, ent->v.origin);
if (!SV_CheckBottom(ent))
{
if (((int32_t) ent->v.flags) & FL_PARTIALGROUND)
{
if (relink)
SV_LinkEdict(ent, 1);
return 1;
}
VectorCopy(oldorg, ent->v.origin);
return 0;
}
if (((int32_t) ent->v.flags) & FL_PARTIALGROUND)
{
ent->v.flags = ((int32_t) ent->v.flags) & (~FL_PARTIALGROUND);
}
ent->v.groundentity = EDICT_TO_PROG(trace.ent);
if (relink)
SV_LinkEdict(ent, 1);
return 1;
}
bool SV_StepDirection(edict_t *ent, float yaw, float dist)
{
vec3_t move;
vec3_t oldorigin;
float delta;
ent->v.ideal_yaw = yaw;
PF_changeyaw();
yaw = ((yaw * M_PI) * 2) / 360;
move[0] = cos(yaw) * dist;
move[1] = sin(yaw) * dist;
move[2] = 0;
VectorCopy(ent->v.origin, oldorigin);
if (SV_movestep(ent, move, 0))
{
delta = ent->v.angles[YAW] - ent->v.ideal_yaw;
if ((delta > 45) && (delta < 315))
{
VectorCopy(oldorigin, ent->v.origin);
}
SV_LinkEdict(ent, 1);
return 1;
}
SV_LinkEdict(ent, 1);
return 0;
}
void SV_FixCheckBottom(edict_t *ent)
{
ent->v.flags = ((int32_t) ent->v.flags) | FL_PARTIALGROUND;
}
void SV_NewChaseDir(edict_t *actor, edict_t *enemy, float dist)
{
float deltax;
float deltay;
float d[3];
float tdir;
float olddir;
float turnaround;
olddir = anglemod(((int32_t) (actor->v.ideal_yaw / 45)) * 45);
turnaround = anglemod(olddir - 180);
deltax = enemy->v.origin[0] - actor->v.origin[0];
deltay = enemy->v.origin[1] - actor->v.origin[1];
if (deltax > 10)
d[1] = 0;
else
if (deltax < (-10))
d[1] = 180;
else
d[1] = DI_NODIR;
if (deltay < (-10))
d[2] = 270;
else
if (deltay > 10)
d[2] = 90;
else
d[2] = DI_NODIR;
if ((d[1] != DI_NODIR) && (d[2] != DI_NODIR))
{
if (d[1] == 0)
tdir = (d[2] == 90) ? (45) : (315);
else
tdir = (d[2] == 90) ? (135) : (215);
if ((tdir != turnaround) && SV_StepDirection(actor, tdir, dist))
return;
}
if (((rand() & 3) & 1) || (fabsf(deltay) > fabsf(deltax)))
{
tdir = d[1];
d[1] = d[2];
d[2] = tdir;
}
if (((d[1] != DI_NODIR) && (d[1] != turnaround)) && SV_StepDirection(actor, d[1], dist))
return;
if (((d[2] != DI_NODIR) && (d[2] != turnaround)) && SV_StepDirection(actor, d[2], dist))
return;
if ((olddir != DI_NODIR) && SV_StepDirection(actor, olddir, dist))
return;
if (rand() & 1)
{
for (tdir = 0; tdir <= 315; tdir += 45)
if ((tdir != turnaround) && SV_StepDirection(actor, tdir, dist))
return;
}
else
{
for (tdir = 315; tdir >= 0; tdir -= 45)
if ((tdir != turnaround) && SV_StepDirection(actor, tdir, dist))
return;
}
if ((turnaround != DI_NODIR) && SV_StepDirection(actor, turnaround, dist))
return;
actor->v.ideal_yaw = olddir;
if (!SV_CheckBottom(actor))
SV_FixCheckBottom(actor);
}
bool SV_CloseEnough(edict_t *ent, edict_t *goal, float dist)
{
int32_t i;
for (i = 0; i < 3; i++)
{
if (goal->v.absmin[i] > (ent->v.absmax[i] + dist))
return 0;
if (goal->v.absmax[i] < (ent->v.absmin[i] - dist))
return 0;
}
return 1;
}
void SV_MoveToGoal(void)
{
edict_t *ent;
edict_t *goal;
float dist;
ent = PROG_TO_EDICT(pr_global_struct->self);
goal = PROG_TO_EDICT(ent->v.goalentity);
dist = G_FLOAT(OFS_PARM0);
if (!(((int32_t) ent->v.flags) & ((FL_ONGROUND | FL_FLY) | FL_SWIM)))
{
G_FLOAT(OFS_RETURN) = 0;
return;
}
if ((PROG_TO_EDICT(ent->v.enemy) != sv.edicts) && SV_CloseEnough(ent, goal, dist))
return;
if (((rand() & 3) == 1) || (!SV_StepDirection(ent, ent->v.ideal_yaw, dist)))
{
SV_NewChaseDir(ent, goal, dist);
}
}
void SV_CheckAllEnts(void)
{
int32_t e;
edict_t *check;
check = NEXT_EDICT(sv.edicts);
for (e = 1; e < sv.num_edicts; e++, check = NEXT_EDICT(check))
{
if (check->free)
continue;
if (((check->v.movetype == MOVETYPE_PUSH) || (check->v.movetype == MOVETYPE_NONE)) || (check->v.movetype == MOVETYPE_NOCLIP))
continue;
if (SV_TestEntityPosition(check))
Con_Printf("entity in invalid position\n");
}
}
void SV_CheckVelocity(edict_t *ent)
{
int32_t i;
for (i = 0; i < 3; i++)
{
if (IS_NAN(ent->v.velocity[i]))
{
Con_Printf("Got a NaN velocity on %s\n", pr_strings + ent->v.classname);
ent->v.velocity[i] = 0;
}
if (IS_NAN(ent->v.origin[i]))
{
Con_Printf("Got a NaN origin on %s\n", pr_strings + ent->v.classname);
ent->v.origin[i] = 0;
}
if (ent->v.velocity[i] > sv_maxvelocity.value)
ent->v.velocity[i] = sv_maxvelocity.value;
else
if (ent->v.velocity[i] < (-sv_maxvelocity.value))
ent->v.velocity[i] = -sv_maxvelocity.value;
}
}
bool SV_RunThink(edict_t *ent)
{
float thinktime;
thinktime = ent->v.nextthink;
if ((thinktime <= 0) || (thinktime > (sv.time + host_frametime)))
return 1;
if (thinktime < sv.time)
thinktime = sv.time;
ent->v.nextthink = 0;
pr_global_struct->time = thinktime;
pr_global_struct->self = EDICT_TO_PROG(ent);
pr_global_struct->other = EDICT_TO_PROG(sv.edicts);
PR_ExecuteProgram(ent->v.think);
return !ent->free;
}
void SV_Impact(edict_t *e1, edict_t *e2)
{
int32_t old_self;
int32_t old_other;
old_self = pr_global_struct->self;
old_other = pr_global_struct->other;
pr_global_struct->time = sv.time;
if (e1->v.touch && (e1->v.solid != SOLID_NOT))
{
pr_global_struct->self = EDICT_TO_PROG(e1);
pr_global_struct->other = EDICT_TO_PROG(e2);
PR_ExecuteProgram(e1->v.touch);
}
if (e2->v.touch && (e2->v.solid != SOLID_NOT))
{
pr_global_struct->self = EDICT_TO_PROG(e2);
pr_global_struct->other = EDICT_TO_PROG(e1);
PR_ExecuteProgram(e2->v.touch);
}
pr_global_struct->self = old_self;
pr_global_struct->other = old_other;
}
int32_t ClipVelocity(vec3_t in, vec3_t normal, vec3_t out, float overbounce)
{
float backoff;
float change;
int32_t i;
int32_t blocked;
blocked = 0;
if (normal[2] > 0)
blocked |= 1;
if (!normal[2])
blocked |= 2;
backoff = DotProduct(in, normal) * overbounce;
for (i = 0; i < 3; i++)
{
change = normal[i] * backoff;
out[i] = in[i] - change;
if ((out[i] > (-STOP_EPSILON)) && (out[i] < STOP_EPSILON))
out[i] = 0;
}
return blocked;
}
int32_t SV_FlyMove(edict_t *ent, float time, trace_t *steptrace)
{
int32_t bumpcount;
int32_t numbumps;
vec3_t dir;
float d;
int32_t numplanes;
vec3_t planes[MAX_CLIP_PLANES];
vec3_t primal_velocity;
vec3_t original_velocity;
vec3_t new_velocity;
int32_t i;
int32_t j;
trace_t trace;
vec3_t end;
float time_left;
int32_t blocked;
numbumps = 4;
blocked = 0;
VectorCopy(ent->v.velocity, original_velocity);
VectorCopy(ent->v.velocity, primal_velocity);
numplanes = 0;
time_left = time;
for (bumpcount = 0; bumpcount < numbumps; bumpcount++)
{
if (((!ent->v.velocity[0]) && (!ent->v.velocity[1])) && (!ent->v.velocity[2]))
break;
for (i = 0; i < 3; i++)
end[i] = ent->v.origin[i] + (time_left * ent->v.velocity[i]);
trace = SV_Move(ent->v.origin, ent->v.mins, ent->v.maxs, end, 0, ent);
if (trace.allsolid)
{
VectorCopy(vec3_origin, ent->v.velocity);
return 3;
}
if (trace.fraction > 0)
{
VectorCopy(trace.endpos, ent->v.origin);
VectorCopy(ent->v.velocity, original_velocity);
numplanes = 0;
}
if (trace.fraction == 1)
break;
if (!trace.ent)
Sys_Error("SV_FlyMove: !trace.ent");
if (trace.plane.normal[2] > 0.7)
{
blocked |= 1;
if (trace.ent->v.solid == SOLID_BSP)
{
ent->v.flags = ((int32_t) ent->v.flags) | FL_ONGROUND;
ent->v.groundentity = EDICT_TO_PROG(trace.ent);
}
}
if (!trace.plane.normal[2])
{
blocked |= 2;
if (steptrace)
*steptrace = trace;
}
SV_Impact(ent, trace.ent);
if (ent->free)
break;
time_left -= time_left * trace.fraction;
if (numplanes >= MAX_CLIP_PLANES)
{
VectorCopy(vec3_origin, ent->v.velocity);
return 3;
}
VectorCopy(trace.plane.normal, planes[numplanes]);
numplanes++;
for (i = 0; i < numplanes; i++)
{
ClipVelocity(original_velocity, planes[i], new_velocity, 1);
for (j = 0; j < numplanes; j++)
if (j != i)
{
if (DotProduct(new_velocity, planes[j]) < 0)
break;
}
if (j == numplanes)
break;
}
if (i != numplanes)
{
VectorCopy(new_velocity, ent->v.velocity);
}
else
{
if (numplanes != 2)
{
VectorCopy(vec3_origin, ent->v.velocity);
return 7;
}
CrossProduct(planes[0], planes[1], dir);
d = DotProduct(dir, ent->v.velocity);
VectorScale(dir, d, ent->v.velocity);
}
if (DotProduct(ent->v.velocity, primal_velocity) <= 0)
{
VectorCopy(vec3_origin, ent->v.velocity);
return blocked;
}
}
return blocked;
}
void SV_AddGravity(edict_t *ent)
{
float ent_gravity;
eval_t *val;
val = GetEdictFieldValue(ent, "gravity");
if (val && val->_float)
ent_gravity = val->_float;
else
ent_gravity = 1.0;
ent->v.velocity[2] -= (ent_gravity * sv_gravity.value) * host_frametime;
}
trace_t SV_PushEntity(edict_t *ent, vec3_t push)
{
trace_t trace;
vec3_t end;
VectorAdd(ent->v.origin, push, end);
if (ent->v.movetype == MOVETYPE_FLYMISSILE)
trace = SV_Move(ent->v.origin, ent->v.mins, ent->v.maxs, end, MOVE_MISSILE, ent);
else
if ((ent->v.solid == SOLID_TRIGGER) || (ent->v.solid == SOLID_NOT))
trace = SV_Move(ent->v.origin, ent->v.mins, ent->v.maxs, end, MOVE_NOMONSTERS, ent);
else
trace = SV_Move(ent->v.origin, ent->v.mins, ent->v.maxs, end, MOVE_NORMAL, ent);
VectorCopy(trace.endpos, ent->v.origin);
SV_LinkEdict(ent, 1);
if (trace.ent)
SV_Impact(ent, trace.ent);
return trace;
}
void SV_PushMove(edict_t *pusher, float movetime)
{
int32_t i;
int32_t e;
edict_t *check;
edict_t *block;
vec3_t mins;
vec3_t maxs;
vec3_t move;
vec3_t entorig;
vec3_t pushorig;
int32_t num_moved;
edict_t *moved_edict[MAX_EDICTS];
vec3_t moved_from[MAX_EDICTS];
if (((!pusher->v.velocity[0]) && (!pusher->v.velocity[1])) && (!pusher->v.velocity[2]))
{
pusher->v.ltime += movetime;
return;
}
for (i = 0; i < 3; i++)
{
move[i] = pusher->v.velocity[i] * movetime;
mins[i] = pusher->v.absmin[i] + move[i];
maxs[i] = pusher->v.absmax[i] + move[i];
}
VectorCopy(pusher->v.origin, pushorig);
VectorAdd(pusher->v.origin, move, pusher->v.origin);
pusher->v.ltime += movetime;
SV_LinkEdict(pusher, 0);
num_moved = 0;
check = NEXT_EDICT(sv.edicts);
for (e = 1; e < sv.num_edicts; e++, check = NEXT_EDICT(check))
{
if (check->free)
continue;
if (((check->v.movetype == MOVETYPE_PUSH) || (check->v.movetype == MOVETYPE_NONE)) || (check->v.movetype == MOVETYPE_NOCLIP))
continue;
if (!((((int32_t) check->v.flags) & FL_ONGROUND) && (PROG_TO_EDICT(check->v.groundentity) == pusher)))
{
if ((((((check->v.absmin[0] >= maxs[0]) || (check->v.absmin[1] >= maxs[1])) || (check->v.absmin[2] >= maxs[2])) || (check->v.absmax[0] <= mins[0])) || (check->v.absmax[1] <= mins[1])) || (check->v.absmax[2] <= mins[2]))
continue;
if (!SV_TestEntityPosition(check))
continue;
}
if (check->v.movetype != MOVETYPE_WALK)
check->v.flags = ((int32_t) check->v.flags) & (~FL_ONGROUND);
VectorCopy(check->v.origin, entorig);
VectorCopy(check->v.origin, moved_from[num_moved]);
moved_edict[num_moved] = check;
num_moved++;
pusher->v.solid = SOLID_NOT;
SV_PushEntity(check, move);
pusher->v.solid = SOLID_BSP;
block = SV_TestEntityPosition(check);
if (block)
{
if (check->v.mins[0] == check->v.maxs[0])
continue;
if ((check->v.solid == SOLID_NOT) || (check->v.solid == SOLID_TRIGGER))
{
check->v.mins[0] = (check->v.mins[1] = 0);
VectorCopy(check->v.mins, check->v.maxs);
continue;
}
VectorCopy(entorig, check->v.origin);
SV_LinkEdict(check, 1);
VectorCopy(pushorig, pusher->v.origin);
SV_LinkEdict(pusher, 0);
pusher->v.ltime -= movetime;
if (pusher->v.blocked)
{
pr_global_struct->self = EDICT_TO_PROG(pusher);
pr_global_struct->other = EDICT_TO_PROG(check);
PR_ExecuteProgram(pusher->v.blocked);
}
for (i = 0; i < num_moved; i++)
{
VectorCopy(moved_from[i], moved_edict[i]->v.origin);
SV_LinkEdict(moved_edict[i], 0);
}
return;
}
}
}
void SV_Physics_Pusher(edict_t *ent)
{
float thinktime;
float oldltime;
float movetime;
oldltime = ent->v.ltime;
thinktime = ent->v.nextthink;
if (thinktime < (ent->v.ltime + host_frametime))
{
movetime = thinktime - ent->v.ltime;
if (movetime < 0)
movetime = 0;
}
else
movetime = host_frametime;
if (movetime)
{
SV_PushMove(ent, movetime);
}
if ((thinktime > oldltime) && (thinktime <= ent->v.ltime))
{
ent->v.nextthink = 0;
pr_global_struct->time = sv.time;
pr_global_struct->self = EDICT_TO_PROG(ent);
pr_global_struct->other = EDICT_TO_PROG(sv.edicts);
PR_ExecuteProgram(ent->v.think);
if (ent->free)
return;
}
}
void SV_CheckStuck(edict_t *ent)
{
int32_t i;
int32_t j;
int32_t z;
vec3_t org;
if (!SV_TestEntityPosition(ent))
{
VectorCopy(ent->v.origin, ent->v.oldorigin);
return;
}
VectorCopy(ent->v.origin, org);
VectorCopy(ent->v.oldorigin, ent->v.origin);
if (!SV_TestEntityPosition(ent))
{
Con_DPrintf("Unstuck.\n");
SV_LinkEdict(ent, 1);
return;
}
for (z = 0; z < 18; z++)
for (i = -1; i <= 1; i++)
for (j = -1; j <= 1; j++)
{
ent->v.origin[0] = org[0] + i;
ent->v.origin[1] = org[1] + j;
ent->v.origin[2] = org[2] + z;
if (!SV_TestEntityPosition(ent))
{
Con_DPrintf("Unstuck.\n");
SV_LinkEdict(ent, 1);
return;
}
}
VectorCopy(org, ent->v.origin);
Con_DPrintf("player is stuck.\n");
}
bool SV_CheckWater(edict_t *ent)
{
vec3_t point;
int32_t cont;
point[0] = ent->v.origin[0];
point[1] = ent->v.origin[1];
point[2] = (ent->v.origin[2] + ent->v.mins[2]) + 1;
ent->v.waterlevel = 0;
ent->v.watertype = CONTENTS_EMPTY;
cont = SV_PointContents(point);
if (cont <= CONTENTS_WATER)
{
ent->v.watertype = cont;
ent->v.waterlevel = 1;
point[2] = ent->v.origin[2] + ((ent->v.mins[2] + ent->v.maxs[2]) * 0.5);
cont = SV_PointContents(point);
if (cont <= CONTENTS_WATER)
{
ent->v.waterlevel = 2;
point[2] = ent->v.origin[2] + ent->v.view_ofs[2];
cont = SV_PointContents(point);
if (cont <= CONTENTS_WATER)
ent->v.waterlevel = 3;
}
}
return ent->v.waterlevel > 1;
}
void SV_WallFriction(edict_t *ent, trace_t *trace)
{
vec3_t forward;
vec3_t right;
vec3_t up;
float d;
float i;
vec3_t into;
vec3_t side;
AngleVectors(ent->v.v_angle, forward, right, up);
d = DotProduct(trace->plane.normal, forward);
d += 0.5;
if (d >= 0)
return;
i = DotProduct(trace->plane.normal, ent->v.velocity);
VectorScale(trace->plane.normal, i, into);
VectorSubtract(ent->v.velocity, into, side);
ent->v.velocity[0] = side[0] * (1 + d);
ent->v.velocity[1] = side[1] * (1 + d);
}
int32_t SV_TryUnstick(edict_t *ent, vec3_t oldvel)
{
int32_t i;
vec3_t oldorg;
vec3_t dir;
int32_t clip;
trace_t steptrace;
VectorCopy(ent->v.origin, oldorg);
VectorCopy(vec3_origin, dir);
for (i = 0; i < 8; i++)
{
switch (i)
{
case 0:
dir[0] = 2;
dir[1] = 0;
break;
case 1:
dir[0] = 0;
dir[1] = 2;
break;
case 2:
dir[0] = -2;
dir[1] = 0;
break;
case 3:
dir[0] = 0;
dir[1] = -2;
break;
case 4:
dir[0] = 2;
dir[1] = 2;
break;
case 5:
dir[0] = -2;
dir[1] = 2;
break;
case 6:
dir[0] = 2;
dir[1] = -2;
break;
case 7:
dir[0] = -2;
dir[1] = -2;
break;
}
SV_PushEntity(ent, dir);
ent->v.velocity[0] = oldvel[0];
ent->v.velocity[1] = oldvel[1];
ent->v.velocity[2] = 0;
clip = SV_FlyMove(ent, 0.1, &steptrace);
if ((fabs(oldorg[1] - ent->v.origin[1]) > 4) || (fabs(oldorg[0] - ent->v.origin[0]) > 4))
{
return clip;
}
VectorCopy(oldorg, ent->v.origin);
}
VectorCopy(vec3_origin, ent->v.velocity);
return 7;
}
void SV_WalkMove(edict_t *ent)
{
vec3_t upmove;
vec3_t downmove;
vec3_t oldorg;
vec3_t oldvel;
vec3_t nosteporg;
vec3_t nostepvel;
int32_t clip;
int32_t oldonground;
trace_t steptrace;
trace_t downtrace;
oldonground = ((int32_t) ent->v.flags) & FL_ONGROUND;
ent->v.flags = ((int32_t) ent->v.flags) & (~FL_ONGROUND);
VectorCopy(ent->v.origin, oldorg);
VectorCopy(ent->v.velocity, oldvel);
clip = SV_FlyMove(ent, host_frametime, &steptrace);
if (!(clip & 2))
return;
if ((!oldonground) && (ent->v.waterlevel == 0))
return;
if (ent->v.movetype != MOVETYPE_WALK)
return;
if (sv_nostep.value)
return;
if (((int32_t) sv_player->v.flags) & FL_WATERJUMP)
return;
VectorCopy(ent->v.origin, nosteporg);
VectorCopy(ent->v.velocity, nostepvel);
VectorCopy(oldorg, ent->v.origin);
VectorCopy(vec3_origin, upmove);
VectorCopy(vec3_origin, downmove);
upmove[2] = STEPSIZE;
downmove[2] = (-STEPSIZE) + (oldvel[2] * host_frametime);
SV_PushEntity(ent, upmove);
ent->v.velocity[0] = oldvel[0];
ent->v.velocity[1] = oldvel[1];
ent->v.velocity[2] = 0;
clip = SV_FlyMove(ent, host_frametime, &steptrace);
if (clip)
{
if ((fabs(oldorg[1] - ent->v.origin[1]) < 0.03125) && (fabs(oldorg[0] - ent->v.origin[0]) < 0.03125))
{
clip = SV_TryUnstick(ent, oldvel);
}
}
if (clip & 2)
SV_WallFriction(ent, &steptrace);
downtrace = SV_PushEntity(ent, downmove);
if (downtrace.plane.normal[2] > 0.7)
{
if (ent->v.solid == SOLID_BSP)
{
ent->v.flags = ((int32_t) ent->v.flags) | FL_ONGROUND;
ent->v.groundentity = EDICT_TO_PROG(downtrace.ent);
}
}
else
{
VectorCopy(nosteporg, ent->v.origin);
VectorCopy(nostepvel, ent->v.velocity);
}
}
void SV_Physics_Client(edict_t *ent, int32_t num)
{
if (!svs.clients[num - 1].active)
return;
pr_global_struct->time = sv.time;
pr_global_struct->self = EDICT_TO_PROG(ent);
PR_ExecuteProgram(pr_global_struct->PlayerPreThink);
SV_CheckVelocity(ent);
switch ((int32_t) ent->v.movetype)
{
case MOVETYPE_NONE:
if (!SV_RunThink(ent))
return;
break;
case MOVETYPE_WALK:
if (!SV_RunThink(ent))
return;
if ((!SV_CheckWater(ent)) && (!(((int32_t) ent->v.flags) & FL_WATERJUMP)))
SV_AddGravity(ent);
SV_CheckStuck(ent);
SV_WalkMove(ent);
break;
case MOVETYPE_TOSS:
case MOVETYPE_BOUNCE:
SV_Physics_Toss(ent);
break;
case MOVETYPE_FLY:
if (!SV_RunThink(ent))
return;
SV_FlyMove(ent, host_frametime, 0);
break;
case MOVETYPE_NOCLIP:
if (!SV_RunThink(ent))
return;
VectorMA(ent->v.origin, host_frametime, ent->v.velocity, ent->v.origin);
break;
default:
Sys_Error("SV_Physics_client: bad movetype %i", (int32_t) ent->v.movetype);
}
SV_LinkEdict(ent, 1);
pr_global_struct->time = sv.time;
pr_global_struct->self = EDICT_TO_PROG(ent);
PR_ExecuteProgram(pr_global_struct->PlayerPostThink);
}
void SV_Physics_None(edict_t *ent)
{
SV_RunThink(ent);
}
void SV_Physics_Noclip(edict_t *ent)
{
if (!SV_RunThink(ent))
return;
VectorMA(ent->v.angles, host_frametime, ent->v.avelocity, ent->v.angles);
VectorMA(ent->v.origin, host_frametime, ent->v.velocity, ent->v.origin);
SV_LinkEdict(ent, 0);
}
void SV_CheckWaterTransition(edict_t *ent)
{
int32_t cont;
cont = SV_PointContents(ent->v.origin);
if (!ent->v.watertype)
{
ent->v.watertype = cont;
ent->v.waterlevel = 1;
return;
}
if (cont <= CONTENTS_WATER)
{
if (ent->v.watertype == CONTENTS_EMPTY)
{
SV_StartSound(ent, 0, "misc/h2ohit1.wav", 255, 1);
}
ent->v.watertype = cont;
ent->v.waterlevel = 1;
}
else
{
if (ent->v.watertype != CONTENTS_EMPTY)
{
SV_StartSound(ent, 0, "misc/h2ohit1.wav", 255, 1);
}
ent->v.watertype = CONTENTS_EMPTY;
ent->v.waterlevel = cont;
}
}
void SV_Physics_Toss(edict_t *ent)
{
trace_t trace;
vec3_t move;
float backoff;
if (!SV_RunThink(ent))
return;
if (((int32_t) ent->v.flags) & FL_ONGROUND)
return;
SV_CheckVelocity(ent);
if ((ent->v.movetype != MOVETYPE_FLY) && (ent->v.movetype != MOVETYPE_FLYMISSILE))
SV_AddGravity(ent);
VectorMA(ent->v.angles, host_frametime, ent->v.avelocity, ent->v.angles);
VectorScale(ent->v.velocity, host_frametime, move);
trace = SV_PushEntity(ent, move);
if (trace.fraction == 1)
return;
if (ent->free)
return;
if (ent->v.movetype == MOVETYPE_BOUNCE)
backoff = 1.5;
else
backoff = 1;
ClipVelocity(ent->v.velocity, trace.plane.normal, ent->v.velocity, backoff);
if (trace.plane.normal[2] > 0.7)
{
if ((ent->v.velocity[2] < 60) || (ent->v.movetype != MOVETYPE_BOUNCE))
{
ent->v.flags = ((int32_t) ent->v.flags) | FL_ONGROUND;
ent->v.groundentity = EDICT_TO_PROG(trace.ent);
VectorCopy(vec3_origin, ent->v.velocity);
VectorCopy(vec3_origin, ent->v.avelocity);
}
}
SV_CheckWaterTransition(ent);
}
void SV_Physics_Step(edict_t *ent)
{
bool hitsound;
if (!(((int32_t) ent->v.flags) & ((FL_ONGROUND | FL_FLY) | FL_SWIM)))
{
if (ent->v.velocity[2] < (sv_gravity.value * (-0.1)))
hitsound = 1;
else
hitsound = 0;
SV_AddGravity(ent);
SV_CheckVelocity(ent);
SV_FlyMove(ent, host_frametime, 0);
SV_LinkEdict(ent, 1);
if (((int32_t) ent->v.flags) & FL_ONGROUND)
{
if (hitsound)
SV_StartSound(ent, 0, "demon/dland2.wav", 255, 1);
}
}
SV_RunThink(ent);
SV_CheckWaterTransition(ent);
}
void SV_Physics(void)
{
int32_t i;
edict_t *ent;
pr_global_struct->self = EDICT_TO_PROG(sv.edicts);
pr_global_struct->other = EDICT_TO_PROG(sv.edicts);
pr_global_struct->time = sv.time;
PR_ExecuteProgram(pr_global_struct->StartFrame);
ent = sv.edicts;
for (i = 0; i < sv.num_edicts; i++, ent = NEXT_EDICT(ent))
{
if (ent->free)
continue;
if (pr_global_struct->force_retouch)
{
SV_LinkEdict(ent, 1);
}
if ((i > 0) && (i <= svs.maxclients))
SV_Physics_Client(ent, i);
else
if (ent->v.movetype == MOVETYPE_PUSH)
SV_Physics_Pusher(ent);
else
if (ent->v.movetype == MOVETYPE_NONE)
SV_Physics_None(ent);
else
if (ent->v.movetype == MOVETYPE_NOCLIP)
SV_Physics_Noclip(ent);
else
if (ent->v.movetype == MOVETYPE_STEP)
SV_Physics_Step(ent);
else
if ((((ent->v.movetype == MOVETYPE_TOSS) || (ent->v.movetype == MOVETYPE_BOUNCE)) || (ent->v.movetype == MOVETYPE_FLY)) || (ent->v.movetype == MOVETYPE_FLYMISSILE))
SV_Physics_Toss(ent);
else
Sys_Error("SV_Physics: bad movetype %i", (int32_t) ent->v.movetype);
}
if (pr_global_struct->force_retouch)
pr_global_struct->force_retouch--;
sv.time += host_frametime;
}
void SV_SetIdealPitch(void)
{
float angleval;
float sinval;
float cosval;
trace_t tr;
vec3_t top;
vec3_t bottom;
float z[MAX_FORWARD];
int32_t i;
int32_t j;
int32_t step;
int32_t dir;
int32_t steps;
if (!(((int32_t) sv_player->v.flags) & FL_ONGROUND))
return;
angleval = ((sv_player->v.angles[YAW] * M_PI) * 2) / 360;
sinval = sin(angleval);
cosval = cos(angleval);
for (i = 0; i < MAX_FORWARD; i++)
{
top[0] = sv_player->v.origin[0] + ((cosval * (i + 3)) * 12);
top[1] = sv_player->v.origin[1] + ((sinval * (i + 3)) * 12);
top[2] = sv_player->v.origin[2] + sv_player->v.view_ofs[2];
bottom[0] = top[0];
bottom[1] = top[1];
bottom[2] = top[2] - 160;
tr = SV_Move(top, vec3_origin, vec3_origin, bottom, 1, sv_player);
if (tr.allsolid)
return;
if (tr.fraction == 1)
return;
z[i] = top[2] + (tr.fraction * (bottom[2] - top[2]));
}
dir = 0;
steps = 0;
for (j = 1; j < i; j++)
{
step = z[j] - z[j - 1];
if ((step > (-ON_EPSILON)) && (step < ON_EPSILON))
continue;
if (dir && (((step - dir) > ON_EPSILON) || ((step - dir) < (-ON_EPSILON))))
return;
steps++;
dir = step;
}
if (!dir)
{
sv_player->v.idealpitch = 0;
return;
}
if (steps < 2)
return;
sv_player->v.idealpitch = (-dir) * sv_idealpitchscale.value;
}
void SV_UserFriction(void)
{
float *vel;
float speed;
float newspeed;
float control;
vec3_t start;
vec3_t stop;
float friction;
trace_t trace;
vel = velocity;
speed = sqrt((vel[0] * vel[0]) + (vel[1] * vel[1]));
if (!speed)
return;
start[0] = (stop[0] = origin[0] + ((vel[0] / speed) * 16));
start[1] = (stop[1] = origin[1] + ((vel[1] / speed) * 16));
start[2] = origin[2] + sv_player->v.mins[2];
stop[2] = start[2] - 34;
trace = SV_Move(start, vec3_origin, vec3_origin, stop, 1, sv_player);
if (trace.fraction == 1.0)
friction = sv_friction.value * sv_edgefriction.value;
else
friction = sv_friction.value;
control = (speed < sv_stopspeed.value) ? (sv_stopspeed.value) : (speed);
newspeed = speed - ((host_frametime * control) * friction);
if (newspeed < 0)
newspeed = 0;
newspeed /= speed;
vel[0] = vel[0] * newspeed;
vel[1] = vel[1] * newspeed;
vel[2] = vel[2] * newspeed;
}
void SV_Accelerate(void)
{
int32_t i;
float addspeed;
float accelspeed;
float currentspeed;
currentspeed = DotProduct(velocity, wishdir);
addspeed = wishspeed - currentspeed;
if (addspeed <= 0)
return;
accelspeed = (sv_accelerate.value * host_frametime) * wishspeed;
if (accelspeed > addspeed)
accelspeed = addspeed;
for (i = 0; i < 3; i++)
velocity[i] += accelspeed * wishdir[i];
}
void SV_AirAccelerate(vec3_t wishveloc)
{
int32_t i;
float addspeed;
float wishspd;
float accelspeed;
float currentspeed;
wishspd = VectorNormalize(wishveloc);
if (wishspd > 30)
wishspd = 30;
currentspeed = DotProduct(velocity, wishveloc);
addspeed = wishspd - currentspeed;
if (addspeed <= 0)
return;
accelspeed = (sv_accelerate.value * wishspeed) * host_frametime;
if (accelspeed > addspeed)
accelspeed = addspeed;
for (i = 0; i < 3; i++)
velocity[i] += accelspeed * wishveloc[i];
}
void DropPunchAngle(void)
{
float len;
len = VectorNormalize(sv_player->v.punchangle);
len -= 10 * host_frametime;
if (len < 0)
len = 0;
VectorScale(sv_player->v.punchangle, len, sv_player->v.punchangle);
}
void SV_WaterMove(void)
{
int32_t i;
vec3_t wishvel;
float speed;
float newspeed;
float wishspeed;
float addspeed;
float accelspeed;
AngleVectors(sv_player->v.v_angle, forward, right, up);
for (i = 0; i < 3; i++)
wishvel[i] = (forward[i] * cmd.forwardmove) + (right[i] * cmd.sidemove);
if (((!cmd.forwardmove) && (!cmd.sidemove)) && (!cmd.upmove))
wishvel[2] -= 60;
else
wishvel[2] += cmd.upmove;
wishspeed = Length(wishvel);
if (wishspeed > sv_maxspeed.value)
{
VectorScale(wishvel, sv_maxspeed.value / wishspeed, wishvel);
wishspeed = sv_maxspeed.value;
}
wishspeed *= 0.7;
speed = Length(velocity);
if (speed)
{
newspeed = speed - ((host_frametime * speed) * sv_friction.value);
if (newspeed < 0)
newspeed = 0;
VectorScale(velocity, newspeed / speed, velocity);
}
else
newspeed = 0;
if (!wishspeed)
return;
addspeed = wishspeed - newspeed;
if (addspeed <= 0)
return;
VectorNormalize(wishvel);
accelspeed = (sv_accelerate.value * wishspeed) * host_frametime;
if (accelspeed > addspeed)
accelspeed = addspeed;
for (i = 0; i < 3; i++)
velocity[i] += accelspeed * wishvel[i];
}
void SV_WaterJump(void)
{
if ((sv.time > sv_player->v.teleport_time) || (!sv_player->v.waterlevel))
{
sv_player->v.flags = ((int32_t) sv_player->v.flags) & (~FL_WATERJUMP);
sv_player->v.teleport_time = 0;
}
sv_player->v.velocity[0] = sv_player->v.movedir[0];
sv_player->v.velocity[1] = sv_player->v.movedir[1];
}
void SV_AirMove(void)
{
int32_t i;
vec3_t wishvel;
float fmove;
float smove;
AngleVectors(sv_player->v.angles, forward, right, up);
fmove = cmd.forwardmove;
smove = cmd.sidemove;
if ((sv.time < sv_player->v.teleport_time) && (fmove < 0))
fmove = 0;
for (i = 0; i < 3; i++)
wishvel[i] = (forward[i] * fmove) + (right[i] * smove);
if (((int32_t) sv_player->v.movetype) != MOVETYPE_WALK)
wishvel[2] = cmd.upmove;
else
wishvel[2] = 0;
VectorCopy(wishvel, wishdir);
wishspeed = VectorNormalize(wishdir);
if (wishspeed > sv_maxspeed.value)
{
VectorScale(wishvel, sv_maxspeed.value / wishspeed, wishvel);
wishspeed = sv_maxspeed.value;
}
if (sv_player->v.movetype == MOVETYPE_NOCLIP)
{
VectorCopy(wishvel, velocity);
}
else
if (onground)
{
SV_UserFriction();
SV_Accelerate();
}
else
{
SV_AirAccelerate(wishvel);
}
}
void SV_ClientThink(void)
{
vec3_t v_angle;
if (sv_player->v.movetype == MOVETYPE_NONE)
return;
onground = ((int32_t) sv_player->v.flags) & FL_ONGROUND;
origin = sv_player->v.origin;
velocity = sv_player->v.velocity;
DropPunchAngle();
if (sv_player->v.health <= 0)
return;
cmd = host_client->cmd;
angles = sv_player->v.angles;
VectorAdd(sv_player->v.v_angle, sv_player->v.punchangle, v_angle);
angles[ROLL] = V_CalcRoll(sv_player->v.angles, sv_player->v.velocity) * 4;
if (!sv_player->v.fixangle)
{
angles[PITCH] = (-v_angle[PITCH]) / 3;
angles[YAW] = v_angle[YAW];
}
if (((int32_t) sv_player->v.flags) & FL_WATERJUMP)
{
SV_WaterJump();
return;
}
if ((sv_player->v.waterlevel >= 2) && (sv_player->v.movetype != MOVETYPE_NOCLIP))
{
SV_WaterMove();
return;
}
SV_AirMove();
}
void SV_ReadClientMove(usercmd_t *move)
{
int32_t i;
vec3_t angle;
int32_t bits;
host_client->ping_times[host_client->num_pings % NUM_PING_TIMES] = sv.time - MSG_ReadFloat();
host_client->num_pings++;
for (i = 0; i < 3; i++)
angle[i] = MSG_ReadAngle();
VectorCopy(angle, host_client->edict->v.v_angle);
move->forwardmove = MSG_ReadShort();
move->sidemove = MSG_ReadShort();
move->upmove = MSG_ReadShort();
bits = MSG_ReadByte();
host_client->edict->v.button0 = bits & 1;
host_client->edict->v.button2 = (bits & 2) >> 1;
i = MSG_ReadByte();
if (i)
host_client->edict->v.impulse = i;
}
bool SV_ReadClientMessage(void)
{
int32_t ret;
int32_t cmd;
char *s;
do
{
nextmsg:
ret = NET_GetMessage(host_client->netconnection);
if (ret == (-1))
{
Sys_Printf("SV_ReadClientMessage: NET_GetMessage failed\n");
return 0;
}
if (!ret)
return 1;
MSG_BeginReading();
while (1)
{
if (!host_client->active)
return 0;
if (msg_badread)
{
Sys_Printf("SV_ReadClientMessage: badread\n");
return 0;
}
cmd = MSG_ReadChar();
switch (cmd)
{
case -1:
goto nextmsg;
default:
Sys_Printf("SV_ReadClientMessage: unknown command char\n");
return 0;
case clc_nop:
break;
case clc_stringcmd:
s = MSG_ReadString();
if (host_client->privileged)
ret = 2;
else
ret = 0;
if (strncasecmp(s, "status", 6) == 0)
ret = 1;
else
if (strncasecmp(s, "god", 3) == 0)
ret = 1;
else
if (strncasecmp(s, "notarget", 8) == 0)
ret = 1;
else
if (strncasecmp(s, "fly", 3) == 0)
ret = 1;
else
if (strncasecmp(s, "name", 4) == 0)
ret = 1;
else
if (strncasecmp(s, "noclip", 6) == 0)
ret = 1;
else
if (strncasecmp(s, "say", 3) == 0)
ret = 1;
else
if (strncasecmp(s, "say_team", 8) == 0)
ret = 1;
else
if (strncasecmp(s, "tell", 4) == 0)
ret = 1;
else
if (strncasecmp(s, "color", 5) == 0)
ret = 1;
else
if (strncasecmp(s, "kill", 4) == 0)
ret = 1;
else
if (strncasecmp(s, "pause", 5) == 0)
ret = 1;
else
if (strncasecmp(s, "spawn", 5) == 0)
ret = 1;
else
if (strncasecmp(s, "begin", 5) == 0)
ret = 1;
else
if (strncasecmp(s, "prespawn", 8) == 0)
ret = 1;
else
if (strncasecmp(s, "kick", 4) == 0)
ret = 1;
else
if (strncasecmp(s, "ping", 4) == 0)
ret = 1;
else
if (strncasecmp(s, "give", 4) == 0)
ret = 1;
else
if (strncasecmp(s, "ban", 3) == 0)
ret = 1;
if (ret == 2)
Cbuf_InsertText(s);
else
if (ret == 1)
Cmd_ExecuteString(s, src_client);
else
Con_DPrintf("%s tried to %s\n", host_client->name, s);
break;
case clc_disconnect:
return 0;
case clc_move:
SV_ReadClientMove(&host_client->cmd);
break;
}
}
}
while (ret == 1);
return 1;
}
void SV_RunClients(void)
{
int32_t i;
for (i = 0, host_client = svs.clients; i < svs.maxclients; i++, host_client++)
{
if (!host_client->active)
continue;
sv_player = host_client->edict;
if (!SV_ReadClientMessage())
{
SV_DropClient(0);
continue;
}
if (!host_client->spawned)
{
memset(&host_client->cmd, 0, sizeof(host_client->cmd));
continue;
}
if ((!sv.paused) && ((svs.maxclients > 1) || (key_dest == key_game)))
SV_ClientThink();
}
}
void Sys_DebugNumber(int32_t y, int32_t val)
{
}
void Sys_Printf(char *fmt, ...)
{
va_list argptr;
char text[1024];
va_start(argptr, fmt);
vsnprintf(text, sizeof(text), fmt, argptr);
va_end(argptr);
TraceLog(LOG_INFO, text);
}
void Sys_Quit(void)
{
Host_Shutdown();
exit(0);
}
void Sys_Init(void)
{
}
void Sys_LowFPPrecision(void)
{
}
void Sys_HighFPPrecision(void)
{
}
void Sys_Error(char *error, ...)
{
va_list argptr;
char string[1024];
va_start(argptr, error);
vsnprintf(string, sizeof(string), error, argptr);
va_end(argptr);
TraceLog(LOG_ERROR, string);
Host_Shutdown();
exit(1);
}
void Sys_Warn(char *warning, ...)
{
va_list argptr;
char string[1024];
va_start(argptr, warning);
vsnprintf(string, sizeof(string), warning, argptr);
va_end(argptr);
TraceLog(LOG_WARNING, string);
}
int32_t findhandle(void)
{
for (int i = 1; i < MAX_HANDLES; i++)
{
if (!sys_handles[i])
return i;
}
Sys_Error("out of handles");
return -1;
}
static int32_t Qfilelength(FILE *f)
{
int32_t pos;
int32_t end;
pos = ftell(f);
fseek(f, 0, 2);
end = ftell(f);
fseek(f, pos, 0);
return end;
}
int32_t Sys_FileOpenRead(char *path, int32_t *hndl)
{
FILE *f;
int32_t i = findhandle();
f = fopen(path, "rb");
if (!f)
{
*hndl = -1;
return -1;
}
sys_handles[i] = f;
*hndl = i;
return Qfilelength(f);
}
int32_t Sys_FileOpenWrite(char *path)
{
FILE *f;
int32_t i = findhandle();
f = fopen(path, "wb");
if (!f)
Sys_Error("Error opening %s: %s", path, strerror(errno));
sys_handles[i] = f;
return i;
}
void Sys_FileClose(int32_t handle)
{
if (((handle >= 0) && (handle < MAX_HANDLES)) && sys_handles[handle])
{
fclose(sys_handles[handle]);
sys_handles[handle] = 0;
}
}
void Sys_FileSeek(int32_t handle, int32_t position)
{
if (((handle >= 0) && (handle < MAX_HANDLES)) && sys_handles[handle])
{
fseek(sys_handles[handle], position, 0);
}
}
int32_t Sys_FileRead(int32_t handle, void *dst, int32_t count)
{
if (((handle < 0) || (handle >= MAX_HANDLES)) || (!sys_handles[handle]))
{
return 0;
}
return fread(dst, 1, count, sys_handles[handle]);
}
int32_t Sys_FileWrite(int32_t handle, void *src, int32_t count)
{
if (((handle < 0) || (handle >= MAX_HANDLES)) || (!sys_handles[handle]))
{
return 0;
}
return fwrite(src, 1, count, sys_handles[handle]);
}
int32_t Sys_FileTime(char *path)
{
return (FileExists(path)) ? (1) : (-1);
}
void Sys_mkdir(char *path)
{
mkdir(path, 0777);
}
void Sys_DebugLog(char *file, char *fmt, ...)
{
va_list argptr;
char data[1024];
FILE *fp;
va_start(argptr, fmt);
vsnprintf(data, sizeof(data), fmt, argptr);
va_end(argptr);
fp = fopen(file, "a");
if (fp)
{
fwrite(data, strlen(data), 1, fp);
fclose(fp);
}
}
double Sys_FloatTime(void)
{
return GetTime();
}
int main(int argc, char *argv[])
{
quakeparms_t parms;
parms.memsize = (32 * 1024) * 1024;
parms.membase = malloc(parms.memsize);
parms.basedir = basedir;
parms.cachedir = 0;
COM_InitArgv(argc, argv);
parms.argc = com_argc;
parms.argv = com_argv;
Sys_Init();
Host_Init(&parms);
Cvar_RegisterVariable(&sys_nostdout);
while (!WindowShouldClose())
{
Host_Frame(GetFrameTime());
}
Host_Shutdown();
return 0;
}
void VID_SetPalette(unsigned char *palette_data)
{
for (int i = 0; i < 256; ++i)
{
palette[i].r = *(palette_data++);
palette[i].g = *(palette_data++);
palette[i].b = *(palette_data++);
palette[i].a = 255;
}
}
void VID_ShiftPalette(unsigned char *palette)
{
VID_SetPalette(palette);
}
void VID_Init(unsigned char *palette)
{
int pnum;
int chunk;
uint8_t *cache;
int cachesize;
vid.width = BASEWIDTH;
vid.height = BASEHEIGHT;
vid.maxwarpwidth = WARP_WIDTH;
vid.maxwarpheight = WARP_HEIGHT;
if (pnum = COM_CheckParm("-winsize"))
{
if (pnum >= (com_argc - 2))
Sys_Error("VID: -winsize <width> <height>\n");
vid.width = (int32_t) strtol(com_argv[pnum + 1], 0, 0);
vid.height = (int32_t) strtol(com_argv[pnum + 2], 0, 0);
if ((!vid.width) || (!vid.height))
Sys_Error("VID: Bad window width/height\n");
}
int window_width = BASEWIDTH * 2;
int window_height = BASEHEIGHT * 2;
if (COM_CheckParm("-fullscreen"))
{
SetConfigFlags(FLAG_FULLSCREEN_MODE);
window_width = GetMonitorWidth(0);
window_height = GetMonitorHeight(0);
}
InitWindow(window_width, window_height, "Quake");
image8bpp.data = MemAlloc(vid.width * vid.height);
image8bpp.width = vid.width;
image8bpp.height = vid.height;
image8bpp.format = PIXELFORMAT_UNCOMPRESSED_GRAYSCALE;
image8bpp.mipmaps = 1;
image32bpp = GenImageColor(vid.width, vid.height, (Color){0, 0, 0, 255});
screenTexture = LoadTextureFromImage(image32bpp);
VID_SetPalette(palette);
vid.conwidth = vid.width;
vid.conheight = vid.height;
vid.aspect = (((float) vid.height) / ((float) vid.width)) * (320.0 / 240.0);
vid.numpages = 1;
vid.colormap = host_colormap;
vid.fullbright = 256 - (*(((int *) vid.colormap) + 2048));
vid.buffer = image8bpp.data;
vid.rowbytes = vid.width;
vid.conbuffer = vid.buffer;
vid.conrowbytes = vid.rowbytes;
vid.direct = 0;
chunk = (vid.width * vid.height) * (sizeof(*d_pzbuffer));
cachesize = D_SurfaceCacheForRes(vid.width, vid.height);
chunk += cachesize;
d_pzbuffer = Hunk_HighAllocName(chunk, "video");
if (d_pzbuffer == 0)
Sys_Error("Not enough memory for video mode\n");
cache = ((uint8_t *) d_pzbuffer) + ((vid.width * vid.height) * (sizeof(*d_pzbuffer)));
D_InitCaches(cache, cachesize);
HideCursor();
DisableCursor();
}
void VID_Shutdown(void)
{
UnloadTexture(screenTexture);
UnloadImage(image32bpp);
MemFree(image8bpp.data);
CloseWindow();
}
void VID_Update(vrect_t *rects)
{
unsigned char *src = (unsigned char *) vid.buffer;
Color *dest = (Color *) image32bpp.data;
for (int i = 0; i < (vid.width * vid.height); i++)
{
dest[i] = palette[src[i]];
}
UpdateTexture(screenTexture, dest);
BeginDrawing();
ClearBackground((Color){0, 0, 0, 255});
DrawTexturePro(screenTexture, (Rectangle){0, 0, vid.width, vid.height}, (Rectangle){0, 0, GetScreenWidth(), GetScreenHeight()}, (Vector2){0, 0}, 0, (Color){255, 255, 255, 255});
EndDrawing();
}
static int TranslateKey(int key)
{
switch (key)
{
case KEY_DELETE:
return K_DEL;
case KEY_BACKSPACE:
return K_BACKSPACE;
case KEY_F1:
return K_F1;
case KEY_F2:
return K_F2;
case KEY_F3:
return K_F3;
case KEY_F4:
return K_F4;
case KEY_F5:
return K_F5;
case KEY_F6:
return K_F6;
case KEY_F7:
return K_F7;
case KEY_F8:
return K_F8;
case KEY_F9:
return K_F9;
case KEY_F10:
return K_F10;
case KEY_F11:
return K_F11;
case KEY_F12:
return K_F12;
case KEY_PAUSE:
return K_PAUSE;
case KEY_UP:
return K_UPARROW;
case KEY_DOWN:
return K_DOWNARROW;
case KEY_RIGHT:
return K_RIGHTARROW;
case KEY_LEFT:
return K_LEFTARROW;
case KEY_INSERT:
return K_INS;
case KEY_HOME:
return K_HOME;
case KEY_END:
return K_END;
case KEY_PAGE_UP:
return K_PGUP;
case KEY_PAGE_DOWN:
return K_PGDN;
case KEY_LEFT_SHIFT:
case KEY_RIGHT_SHIFT:
return K_SHIFT;
case KEY_LEFT_CONTROL:
case KEY_RIGHT_CONTROL:
return K_CTRL;
case KEY_LEFT_ALT:
case KEY_RIGHT_ALT:
return K_ALT;
case KEY_KP_DIVIDE:
return '/';
case KEY_KP_MULTIPLY:
return '*';
case KEY_KP_SUBTRACT:
return '-';
case KEY_KP_ADD:
return '+';
case KEY_KP_ENTER:
return K_ENTER;
case KEY_KP_EQUAL:
return '=';
case KEY_ENTER:
return K_ENTER;
default:
return tolower(key);
}
}
void Sys_SendKeyEvents(void)
{
if (WindowShouldClose())
{
CL_Disconnect();
Host_ShutdownServer(0);
Sys_Quit();
}
for (int i = 32; i < 349; i++)
{
if (IsKeyPressed(i))
Key_Event(TranslateKey(i), 1);
if (IsKeyReleased(i))
Key_Event(TranslateKey(i), 0);
}
Vector2 delta = GetMouseDelta();
mouse_x = delta.x * 10;
mouse_y = delta.y * 10;
}
void IN_Init(void)
{
if (COM_CheckParm("-nomouse"))
return;
mouse_x = (mouse_y = 0.0);
mouse_avail = 1;
}
void IN_Shutdown(void)
{
mouse_avail = 0;
}
void IN_Commands(void)
{
int mouse_buttonstate = 0;
if (!mouse_avail)
return;
if (IsMouseButtonDown(MOUSE_BUTTON_LEFT))
mouse_buttonstate |= 1;
if (IsMouseButtonDown(MOUSE_BUTTON_RIGHT))
mouse_buttonstate |= 2;
if (IsMouseButtonDown(MOUSE_BUTTON_MIDDLE))
mouse_buttonstate |= 4;
int quake_buttonstate = ((mouse_buttonstate & (~0x06)) | ((mouse_buttonstate & 0x02) << 1)) | ((mouse_buttonstate & 0x04) >> 1);
for (int i = 0; i < 3; i++)
{
if ((quake_buttonstate & (1 << i)) && (!(mouse_oldbuttonstate & (1 << i))))
Key_Event(K_MOUSE1 + i, 1);
if ((!(quake_buttonstate & (1 << i))) && (mouse_oldbuttonstate & (1 << i)))
Key_Event(K_MOUSE1 + i, 0);
}
mouse_oldbuttonstate = quake_buttonstate;
}
void IN_Move(usercmd_t *cmd)
{
if (!mouse_avail)
return;
mouse_x *= sensitivity.value;
mouse_y *= sensitivity.value;
if ((in_strafe.state & 1) || (lookstrafe.value && (in_mlook.state & 1)))
cmd->sidemove += m_side.value * mouse_x;
else
cl.viewangles[YAW] -= m_yaw.value * mouse_x;
if (in_mlook.state & 1)
V_StopPitchDrift();
if ((in_mlook.state & 1) && (!(in_strafe.state & 1)))
{
cl.viewangles[PITCH] += m_pitch.value * mouse_y;
if (cl.viewangles[PITCH] > 80)
cl.viewangles[PITCH] = 80;
if (cl.viewangles[PITCH] < (-70))
cl.viewangles[PITCH] = -70;
}
else
{
if ((in_strafe.state & 1) && noclip_anglehack)
cmd->upmove -= m_forward.value * mouse_y;
else
cmd->forwardmove -= m_forward.value * mouse_y;
}
mouse_x = 0.0;
mouse_y = 0.0;
}
char *Sys_ConsoleInput(void)
{
return 0;
}
void VID_LockBuffer()
{
}
void VID_UnlockBuffer()
{
}
void VID_HandlePause(bool pause)
{
}
float V_CalcRoll(vec3_t angles, vec3_t velocity)
{
float sign;
float side;
float value;
AngleVectors(angles, forward, right, up);
side = DotProduct(velocity, right);
sign = (side < 0) ? (-1) : (1);
side = fabs(side);
value = cl_rollangle.value;
if (side < cl_rollspeed.value)
side = (side * value) / cl_rollspeed.value;
else
side = value;
return side * sign;
}
float V_CalcBob(void)
{
float bob;
float cycle;
cycle = cl.time - (((int32_t) (cl.time / cl_bobcycle.value)) * cl_bobcycle.value);
cycle /= cl_bobcycle.value;
if (cycle < cl_bobup.value)
cycle = (M_PI * cycle) / cl_bobup.value;
else
cycle = M_PI + ((M_PI * (cycle - cl_bobup.value)) / (1.0 - cl_bobup.value));
bob = sqrt((cl.velocity[0] * cl.velocity[0]) + (cl.velocity[1] * cl.velocity[1])) * cl_bob.value;
bob = (bob * 0.3) + ((bob * 0.7) * sin(cycle));
if (bob > 4)
bob = 4;
else
if (bob < (-7))
bob = -7;
return bob;
}
void V_StartPitchDrift(void)
{
if (cl.laststop == cl.time)
{
return;
}
if (cl.nodrift || (!cl.pitchvel))
{
cl.pitchvel = v_centerspeed.value;
cl.nodrift = 0;
cl.driftmove = 0;
}
}
void V_StopPitchDrift(void)
{
cl.laststop = cl.time;
cl.nodrift = 1;
cl.pitchvel = 0;
}
void V_DriftPitch(void)
{
float delta;
float move;
if ((noclip_anglehack || (!cl.onground)) || cls.demoplayback)
{
cl.driftmove = 0;
cl.pitchvel = 0;
return;
}
if (cl.nodrift)
{
if (fabs(cl.cmd.forwardmove) < cl_forwardspeed.value)
cl.driftmove = 0;
else
cl.driftmove += host_frametime;
if (cl.driftmove > v_centermove.value)
{
V_StartPitchDrift();
}
return;
}
delta = cl.idealpitch - cl.viewangles[PITCH];
if (!delta)
{
cl.pitchvel = 0;
return;
}
move = host_frametime * cl.pitchvel;
cl.pitchvel += host_frametime * v_centerspeed.value;
if (delta > 0)
{
if (move > delta)
{
cl.pitchvel = 0;
move = delta;
}
cl.viewangles[PITCH] += move;
}
else
if (delta < 0)
{
if (move > (-delta))
{
cl.pitchvel = 0;
move = -delta;
}
cl.viewangles[PITCH] -= move;
}
}
void BuildGammaTable(float g)
{
int32_t i;
int32_t inf;
if (g == 1.0)
{
for (i = 0; i < 256; i++)
gammatable[i] = i;
return;
}
for (i = 0; i < 256; i++)
{
inf = (255 * pow((i + 0.5) / 255.5, g)) + 0.5;
if (inf < 0)
inf = 0;
if (inf > 255)
inf = 255;
gammatable[i] = inf;
}
}
bool V_CheckGamma(void)
{
static float oldgammavalue;
if (v_gamma.value == oldgammavalue)
return 0;
oldgammavalue = v_gamma.value;
BuildGammaTable(v_gamma.value);
vid.recalc_refdef = 1;
return 1;
}
void V_ParseDamage(void)
{
int32_t armor;
int32_t blood;
vec3_t from;
int32_t i;
vec3_t forward;
vec3_t right;
vec3_t up;
entity_t *ent;
float side;
float count;
armor = MSG_ReadByte();
blood = MSG_ReadByte();
for (i = 0; i < 3; i++)
from[i] = MSG_ReadCoord();
count = (blood * 0.5) + (armor * 0.5);
if (count < 10)
count = 10;
cl.faceanimtime = cl.time + 0.2;
cl.cshifts[CSHIFT_DAMAGE].percent += 3 * count;
if (cl.cshifts[CSHIFT_DAMAGE].percent < 0)
cl.cshifts[CSHIFT_DAMAGE].percent = 0;
if (cl.cshifts[CSHIFT_DAMAGE].percent > 150)
cl.cshifts[CSHIFT_DAMAGE].percent = 150;
if (armor > blood)
{
cl.cshifts[CSHIFT_DAMAGE].destcolor[0] = 200;
cl.cshifts[CSHIFT_DAMAGE].destcolor[1] = 100;
cl.cshifts[CSHIFT_DAMAGE].destcolor[2] = 100;
}
else
if (armor)
{
cl.cshifts[CSHIFT_DAMAGE].destcolor[0] = 220;
cl.cshifts[CSHIFT_DAMAGE].destcolor[1] = 50;
cl.cshifts[CSHIFT_DAMAGE].destcolor[2] = 50;
}
else
{
cl.cshifts[CSHIFT_DAMAGE].destcolor[0] = 255;
cl.cshifts[CSHIFT_DAMAGE].destcolor[1] = 0;
cl.cshifts[CSHIFT_DAMAGE].destcolor[2] = 0;
}
ent = &cl_entities[cl.viewentity];
VectorSubtract(from, ent->origin, from);
VectorNormalize(from);
AngleVectors(ent->angles, forward, right, up);
side = DotProduct(from, right);
v_dmg_roll = (count * side) * v_kickroll.value;
side = DotProduct(from, forward);
v_dmg_pitch = (count * side) * v_kickpitch.value;
v_dmg_time = v_kicktime.value;
}
void V_cshift_f(void)
{
cshift_empty.destcolor[0] = atoi(Cmd_Argv(1));
cshift_empty.destcolor[1] = atoi(Cmd_Argv(2));
cshift_empty.destcolor[2] = atoi(Cmd_Argv(3));
cshift_empty.percent = atoi(Cmd_Argv(4));
}
void V_BonusFlash_f(void)
{
cl.cshifts[CSHIFT_BONUS].destcolor[0] = 215;
cl.cshifts[CSHIFT_BONUS].destcolor[1] = 186;
cl.cshifts[CSHIFT_BONUS].destcolor[2] = 69;
cl.cshifts[CSHIFT_BONUS].percent = 50;
}
void V_SetContentsColor(int32_t contents)
{
switch (contents)
{
case CONTENTS_EMPTY:
case CONTENTS_SOLID:
cl.cshifts[CSHIFT_CONTENTS] = cshift_empty;
break;
case CONTENTS_LAVA:
cl.cshifts[CSHIFT_CONTENTS] = cshift_lava;
break;
case CONTENTS_SLIME:
cl.cshifts[CSHIFT_CONTENTS] = cshift_slime;
break;
default:
cl.cshifts[CSHIFT_CONTENTS] = cshift_water;
}
}
void V_CalcPowerupCshift(void)
{
if (cl.items & IT_QUAD)
{
cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 0;
cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 0;
cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 255;
cl.cshifts[CSHIFT_POWERUP].percent = 30;
}
else
if (cl.items & IT_SUIT)
{
cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 0;
cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 255;
cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 0;
cl.cshifts[CSHIFT_POWERUP].percent = 20;
}
else
if (cl.items & IT_INVISIBILITY)
{
cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 100;
cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 100;
cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 100;
cl.cshifts[CSHIFT_POWERUP].percent = 100;
}
else
if (cl.items & IT_INVULNERABILITY)
{
cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 255;
cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 255;
cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 0;
cl.cshifts[CSHIFT_POWERUP].percent = 30;
}
else
cl.cshifts[CSHIFT_POWERUP].percent = 0;
}
void V_UpdatePalette(void)
{
int32_t i;
int32_t j;
bool new;
uint8_t *basepal;
uint8_t *newpal;
uint8_t pal[768];
int32_t r;
int32_t g;
int32_t b;
bool force;
V_CalcPowerupCshift();
new = 0;
for (i = 0; i < NUM_CSHIFTS; i++)
{
if (cl.cshifts[i].percent != cl.prev_cshifts[i].percent)
{
new = 1;
cl.prev_cshifts[i].percent = cl.cshifts[i].percent;
}
for (j = 0; j < 3; j++)
if (cl.cshifts[i].destcolor[j] != cl.prev_cshifts[i].destcolor[j])
{
new = 1;
cl.prev_cshifts[i].destcolor[j] = cl.cshifts[i].destcolor[j];
}
}
cl.cshifts[CSHIFT_DAMAGE].percent -= host_frametime * 150;
if (cl.cshifts[CSHIFT_DAMAGE].percent <= 0)
cl.cshifts[CSHIFT_DAMAGE].percent = 0;
cl.cshifts[CSHIFT_BONUS].percent -= host_frametime * 100;
if (cl.cshifts[CSHIFT_BONUS].percent <= 0)
cl.cshifts[CSHIFT_BONUS].percent = 0;
force = V_CheckGamma();
if ((!new) && (!force))
return;
basepal = host_basepal;
newpal = pal;
for (i = 0; i < 256; i++)
{
r = basepal[0];
g = basepal[1];
b = basepal[2];
basepal += 3;
for (j = 0; j < NUM_CSHIFTS; j++)
{
r += (cl.cshifts[j].percent * (cl.cshifts[j].destcolor[0] - r)) >> 8;
g += (cl.cshifts[j].percent * (cl.cshifts[j].destcolor[1] - g)) >> 8;
b += (cl.cshifts[j].percent * (cl.cshifts[j].destcolor[2] - b)) >> 8;
}
newpal[0] = gammatable[r];
newpal[1] = gammatable[g];
newpal[2] = gammatable[b];
newpal += 3;
}
VID_ShiftPalette(pal);
}
float angledelta(float a)
{
a = anglemod(a);
if (a > 180)
a -= 360;
return a;
}
void CalcGunAngle(void)
{
float yaw;
float pitch;
float move;
static float oldyaw = 0;
static float oldpitch = 0;
yaw = r_refdef.viewangles[YAW];
pitch = -r_refdef.viewangles[PITCH];
yaw = angledelta(yaw - r_refdef.viewangles[YAW]) * 0.4;
if (yaw > 10)
yaw = 10;
if (yaw < (-10))
yaw = -10;
pitch = angledelta((-pitch) - r_refdef.viewangles[PITCH]) * 0.4;
if (pitch > 10)
pitch = 10;
if (pitch < (-10))
pitch = -10;
move = host_frametime * 20;
if (yaw > oldyaw)
{
if ((oldyaw + move) < yaw)
yaw = oldyaw + move;
}
else
{
if ((oldyaw - move) > yaw)
yaw = oldyaw - move;
}
if (pitch > oldpitch)
{
if ((oldpitch + move) < pitch)
pitch = oldpitch + move;
}
else
{
if ((oldpitch - move) > pitch)
pitch = oldpitch - move;
}
oldyaw = yaw;
oldpitch = pitch;
cl.viewent.angles[YAW] = r_refdef.viewangles[YAW] + yaw;
cl.viewent.angles[PITCH] = -(r_refdef.viewangles[PITCH] + pitch);
cl.viewent.angles[ROLL] -= (v_idlescale.value * sin(cl.time * v_iroll_cycle.value)) * v_iroll_level.value;
cl.viewent.angles[PITCH] -= (v_idlescale.value * sin(cl.time * v_ipitch_cycle.value)) * v_ipitch_level.value;
cl.viewent.angles[YAW] -= (v_idlescale.value * sin(cl.time * v_iyaw_cycle.value)) * v_iyaw_level.value;
}
void V_BoundOffsets(void)
{
entity_t *ent;
ent = &cl_entities[cl.viewentity];
if (r_refdef.vieworg[0] < (ent->origin[0] - 14))
r_refdef.vieworg[0] = ent->origin[0] - 14;
else
if (r_refdef.vieworg[0] > (ent->origin[0] + 14))
r_refdef.vieworg[0] = ent->origin[0] + 14;
if (r_refdef.vieworg[1] < (ent->origin[1] - 14))
r_refdef.vieworg[1] = ent->origin[1] - 14;
else
if (r_refdef.vieworg[1] > (ent->origin[1] + 14))
r_refdef.vieworg[1] = ent->origin[1] + 14;
if (r_refdef.vieworg[2] < (ent->origin[2] - 22))
r_refdef.vieworg[2] = ent->origin[2] - 22;
else
if (r_refdef.vieworg[2] > (ent->origin[2] + 30))
r_refdef.vieworg[2] = ent->origin[2] + 30;
}
void V_AddIdle(void)
{
r_refdef.viewangles[ROLL] += (v_idlescale.value * sin(cl.time * v_iroll_cycle.value)) * v_iroll_level.value;
r_refdef.viewangles[PITCH] += (v_idlescale.value * sin(cl.time * v_ipitch_cycle.value)) * v_ipitch_level.value;
r_refdef.viewangles[YAW] += (v_idlescale.value * sin(cl.time * v_iyaw_cycle.value)) * v_iyaw_level.value;
}
void V_CalcViewRoll(void)
{
float side;
side = V_CalcRoll(cl_entities[cl.viewentity].angles, cl.velocity);
r_refdef.viewangles[ROLL] += side;
if (v_dmg_time > 0)
{
r_refdef.viewangles[ROLL] += (v_dmg_time / v_kicktime.value) * v_dmg_roll;
r_refdef.viewangles[PITCH] += (v_dmg_time / v_kicktime.value) * v_dmg_pitch;
v_dmg_time -= host_frametime;
}
if (cl.stats[STAT_HEALTH] <= 0)
{
r_refdef.viewangles[ROLL] = 80;
return;
}
}
void V_CalcIntermissionRefdef(void)
{
entity_t *ent;
entity_t *view;
float old;
ent = &cl_entities[cl.viewentity];
view = &cl.viewent;
VectorCopy(ent->origin, r_refdef.vieworg);
VectorCopy(ent->angles, r_refdef.viewangles);
view->model = 0;
old = v_idlescale.value;
v_idlescale.value = 1;
V_AddIdle();
v_idlescale.value = old;
}
void V_CalcRefdef(void)
{
entity_t *ent;
entity_t *view;
int32_t i;
vec3_t forward;
vec3_t right;
vec3_t up;
vec3_t angles;
float bob;
static float oldz = 0;
V_DriftPitch();
ent = &cl_entities[cl.viewentity];
view = &cl.viewent;
ent->angles[YAW] = cl.viewangles[YAW];
ent->angles[PITCH] = -cl.viewangles[PITCH];
bob = V_CalcBob();
VectorCopy(ent->origin, r_refdef.vieworg);
r_refdef.vieworg[2] += cl.viewheight + bob;
r_refdef.vieworg[0] += 1.0 / 32;
r_refdef.vieworg[1] += 1.0 / 32;
r_refdef.vieworg[2] += 1.0 / 32;
VectorCopy(cl.viewangles, r_refdef.viewangles);
V_CalcViewRoll();
V_AddIdle();
angles[PITCH] = -ent->angles[PITCH];
angles[YAW] = ent->angles[YAW];
angles[ROLL] = ent->angles[ROLL];
AngleVectors(angles, forward, right, up);
for (i = 0; i < 3; i++)
r_refdef.vieworg[i] += ((scr_ofsx.value * forward[i]) + (scr_ofsy.value * right[i])) + (scr_ofsz.value * up[i]);
V_BoundOffsets();
VectorCopy(cl.viewangles, view->angles);
CalcGunAngle();
VectorCopy(ent->origin, view->origin);
view->origin[2] += cl.viewheight;
for (i = 0; i < 3; i++)
{
view->origin[i] += (forward[i] * bob) * 0.4;
}
view->origin[2] += bob;
if (scr_viewsize.value == 110)
view->origin[2] += 1;
else
if (scr_viewsize.value == 100)
view->origin[2] += 2;
else
if (scr_viewsize.value == 90)
view->origin[2] += 1;
else
if (scr_viewsize.value == 80)
view->origin[2] += 0.5;
view->model = cl.model_precache[cl.stats[STAT_WEAPON]];
view->frame = cl.stats[STAT_WEAPONFRAME];
view->colormap = vid.colormap;
VectorAdd(r_refdef.viewangles, cl.punchangle, r_refdef.viewangles);
if (cl.onground && ((ent->origin[2] - oldz) > 0))
{
float steptime;
steptime = cl.time - cl.oldtime;
if (steptime < 0)
steptime = 0;
oldz += steptime * 80;
if (oldz > ent->origin[2])
oldz = ent->origin[2];
if ((ent->origin[2] - oldz) > 12)
oldz = ent->origin[2] - 12;
r_refdef.vieworg[2] += oldz - ent->origin[2];
view->origin[2] += oldz - ent->origin[2];
}
else
oldz = ent->origin[2];
if (chase_active.value)
Chase_Update();
}
void V_RenderView(void)
{
if (con_forcedup)
return;
if (cl.maxclients > 1)
{
Cvar_Set("scr_ofsx", "0");
Cvar_Set("scr_ofsy", "0");
Cvar_Set("scr_ofsz", "0");
}
if (cl.intermission)
{
V_CalcIntermissionRefdef();
}
else
{
if (!cl.paused)
V_CalcRefdef();
}
R_PushDlights();
if (lcd_x.value)
{
int32_t i;
vid.rowbytes <<= 1;
vid.aspect *= 0.5;
r_refdef.viewangles[YAW] -= lcd_yaw.value;
for (i = 0; i < 3; i++)
r_refdef.vieworg[i] -= right[i] * lcd_x.value;
R_RenderView();
vid.buffer += vid.rowbytes >> 1;
R_PushDlights();
r_refdef.viewangles[YAW] += lcd_yaw.value * 2;
for (i = 0; i < 3; i++)
r_refdef.vieworg[i] += (2 * right[i]) * lcd_x.value;
R_RenderView();
vid.buffer -= vid.rowbytes >> 1;
r_refdef.vrect.height <<= 1;
vid.rowbytes >>= 1;
vid.aspect *= 2;
}
else
{
R_RenderView();
}
if (crosshair.value)
Draw_Character((scr_vrect.x + (scr_vrect.width / 2)) + cl_crossx.value, (scr_vrect.y + (scr_vrect.height / 2)) + cl_crossy.value, '+');
}
void V_Init(void)
{
Cmd_AddCommand("v_cshift", V_cshift_f);
Cmd_AddCommand("bf", V_BonusFlash_f);
Cmd_AddCommand("centerview", V_StartPitchDrift);
Cvar_RegisterVariable(&lcd_x);
Cvar_RegisterVariable(&lcd_yaw);
Cvar_RegisterVariable(&v_centermove);
Cvar_RegisterVariable(&v_centerspeed);
Cvar_RegisterVariable(&v_iyaw_cycle);
Cvar_RegisterVariable(&v_iroll_cycle);
Cvar_RegisterVariable(&v_ipitch_cycle);
Cvar_RegisterVariable(&v_iyaw_level);
Cvar_RegisterVariable(&v_iroll_level);
Cvar_RegisterVariable(&v_ipitch_level);
Cvar_RegisterVariable(&v_idlescale);
Cvar_RegisterVariable(&crosshair);
Cvar_RegisterVariable(&cl_crossx);
Cvar_RegisterVariable(&cl_crossy);
Cvar_RegisterVariable(&gl_cshiftpercent);
Cvar_RegisterVariable(&scr_ofsx);
Cvar_RegisterVariable(&scr_ofsy);
Cvar_RegisterVariable(&scr_ofsz);
Cvar_RegisterVariable(&cl_rollspeed);
Cvar_RegisterVariable(&cl_rollangle);
Cvar_RegisterVariable(&cl_bob);
Cvar_RegisterVariable(&cl_bobcycle);
Cvar_RegisterVariable(&cl_bobup);
Cvar_RegisterVariable(&v_kicktime);
Cvar_RegisterVariable(&v_kickroll);
Cvar_RegisterVariable(&v_kickpitch);
BuildGammaTable(1.0);
Cvar_RegisterVariable(&v_gamma);
}
void W_CleanupName(char *in, char *out)
{
int32_t i;
int32_t c;
for (i = 0; i < 16; i++)
{
c = in[i];
if (!c)
break;
if ((c >= 'A') && (c <= 'Z'))
c += 'a' - 'A';
out[i] = c;
}
for (; i < 16; i++)
out[i] = 0;
}
void W_LoadWadFile(char *filename)
{
lumpinfo_t *lump_p;
wadinfo_t *header;
uint32_t i;
int32_t infotableofs;
wad_base = COM_LoadHunkFile(filename);
if (!wad_base)
Sys_Error("W_LoadWadFile: couldn't load %s", filename);
header = (wadinfo_t *) wad_base;
if ((((header->identification[0] != 'W') || (header->identification[1] != 'A')) || (header->identification[2] != 'D')) || (header->identification[3] != '2'))
Sys_Error("Wad file %s doesn't have WAD2 id\n", filename);
wad_numlumps = header->numlumps;
infotableofs = header->infotableofs;
wad_lumps = (lumpinfo_t *) (wad_base + infotableofs);
for (i = 0, lump_p = wad_lumps; i < wad_numlumps; i++, lump_p++)
{
lump_p->filepos = lump_p->filepos;
lump_p->size = lump_p->size;
W_CleanupName(lump_p->name, lump_p->name);
if (lump_p->type == TYP_QPIC)
SwapPic((qpic_t *) (wad_base + lump_p->filepos));
}
}
lumpinfo_t *W_GetLumpinfo(char *name)
{
int32_t i;
lumpinfo_t *lump_p;
char clean[16];
W_CleanupName(name, clean);
for (lump_p = wad_lumps, i = 0; i < wad_numlumps; i++, lump_p++)
{
if (!strcmp(clean, lump_p->name))
return lump_p;
}
Sys_Error("W_GetLumpinfo: %s not found", name);
return 0;
}
void *W_GetLumpName(char *name)
{
lumpinfo_t *lump;
lump = W_GetLumpinfo(name);
return (void *) (wad_base + lump->filepos);
}
void *W_GetLumpNum(int32_t num)
{
lumpinfo_t *lump;
if ((num < 0) || (num > wad_numlumps))
Sys_Error("W_GetLumpNum: bad number: %i", num);
lump = wad_lumps + num;
return (void *) (wad_base + lump->filepos);
}
void SwapPic(qpic_t *pic)
{
pic->width = pic->width;
pic->height = pic->height;
}
void SV_InitBoxHull(void)
{
int32_t i;
int32_t side;
box_hull.clipnodes = box_clipnodes;
box_hull.planes = box_planes;
box_hull.firstclipnode = 0;
box_hull.lastclipnode = 5;
for (i = 0; i < 6; i++)
{
box_clipnodes[i].planenum = i;
side = i & 1;
box_clipnodes[i].children[side] = CONTENTS_EMPTY;
if (i != 5)
box_clipnodes[i].children[side ^ 1] = i + 1;
else
box_clipnodes[i].children[side ^ 1] = CONTENTS_SOLID;
box_planes[i].type = i >> 1;
box_planes[i].normal[i >> 1] = 1;
}
}
hull_t *SV_HullForBox(vec3_t mins, vec3_t maxs)
{
box_planes[0].dist = maxs[0];
box_planes[1].dist = mins[0];
box_planes[2].dist = maxs[1];
box_planes[3].dist = mins[1];
box_planes[4].dist = maxs[2];
box_planes[5].dist = mins[2];
return &box_hull;
}
hull_t *SV_HullForEntity(edict_t *ent, vec3_t mins, vec3_t maxs, vec3_t offset)
{
model_t *model;
vec3_t size;
vec3_t hullmins;
vec3_t hullmaxs;
hull_t *hull;
if (ent->v.solid == SOLID_BSP)
{
if (ent->v.movetype != MOVETYPE_PUSH)
Sys_Error("SOLID_BSP without MOVETYPE_PUSH");
model = sv.models[(int32_t) ent->v.modelindex];
if ((!model) || (model->type != mod_brush))
Sys_Error("MOVETYPE_PUSH with a non bsp model");
VectorSubtract(maxs, mins, size);
if (size[0] < 3)
hull = &model->hulls[0];
else
if (size[0] <= 32)
hull = &model->hulls[1];
else
hull = &model->hulls[2];
VectorSubtract(hull->clip_mins, mins, offset);
VectorAdd(offset, ent->v.origin, offset);
}
else
{
VectorSubtract(ent->v.mins, maxs, hullmins);
VectorSubtract(ent->v.maxs, mins, hullmaxs);
hull = SV_HullForBox(hullmins, hullmaxs);
VectorCopy(ent->v.origin, offset);
}
return hull;
}
areanode_t *SV_CreateAreaNode(int32_t depth, vec3_t mins, vec3_t maxs)
{
areanode_t *anode;
vec3_t size;
vec3_t mins1;
vec3_t maxs1;
vec3_t mins2;
vec3_t maxs2;
anode = &sv_areanodes[sv_numareanodes];
sv_numareanodes++;
ClearLink(&anode->trigger_edicts);
ClearLink(&anode->solid_edicts);
if (depth == AREA_DEPTH)
{
anode->axis = -1;
anode->children[0] = (anode->children[1] = 0);
return anode;
}
VectorSubtract(maxs, mins, size);
if (size[0] > size[1])
anode->axis = 0;
else
anode->axis = 1;
anode->dist = 0.5 * (maxs[anode->axis] + mins[anode->axis]);
VectorCopy(mins, mins1);
VectorCopy(mins, mins2);
VectorCopy(maxs, maxs1);
VectorCopy(maxs, maxs2);
maxs1[anode->axis] = (mins2[anode->axis] = anode->dist);
anode->children[0] = SV_CreateAreaNode(depth + 1, mins2, maxs2);
anode->children[1] = SV_CreateAreaNode(depth + 1, mins1, maxs1);
return anode;
}
void SV_ClearWorld(void)
{
SV_InitBoxHull();
memset(sv_areanodes, 0, sizeof(sv_areanodes));
sv_numareanodes = 0;
SV_CreateAreaNode(0, sv.worldmodel->mins, sv.worldmodel->maxs);
}
void SV_UnlinkEdict(edict_t *ent)
{
if (!ent->area.prev)
return;
RemoveLink(&ent->area);
ent->area.prev = (ent->area.next = 0);
}
void SV_TouchLinks(edict_t *ent, areanode_t *node)
{
link_t *l;
link_t *next;
edict_t *touch;
int32_t old_self;
int32_t old_other;
for (l = node->trigger_edicts.next; l != (&node->trigger_edicts); l = next)
{
next = l->next;
touch = EDICT_FROM_AREA(l);
if (touch == ent)
continue;
if ((!touch->v.touch) || (touch->v.solid != SOLID_TRIGGER))
continue;
if ((((((ent->v.absmin[0] > touch->v.absmax[0]) || (ent->v.absmin[1] > touch->v.absmax[1])) || (ent->v.absmin[2] > touch->v.absmax[2])) || (ent->v.absmax[0] < touch->v.absmin[0])) || (ent->v.absmax[1] < touch->v.absmin[1])) || (ent->v.absmax[2] < touch->v.absmin[2]))
continue;
old_self = pr_global_struct->self;
old_other = pr_global_struct->other;
pr_global_struct->self = EDICT_TO_PROG(touch);
pr_global_struct->other = EDICT_TO_PROG(ent);
pr_global_struct->time = sv.time;
PR_ExecuteProgram(touch->v.touch);
pr_global_struct->self = old_self;
pr_global_struct->other = old_other;
}
if (node->axis == (-1))
return;
if (ent->v.absmax[node->axis] > node->dist)
SV_TouchLinks(ent, node->children[0]);
if (ent->v.absmin[node->axis] < node->dist)
SV_TouchLinks(ent, node->children[1]);
}
void SV_FindTouchedLeafs(edict_t *ent, mnode_t *node)
{
mplane_t *splitplane;
mleaf_t *leaf;
int32_t sides;
int32_t leafnum;
if (node->contents == CONTENTS_SOLID)
return;
if (node->contents < 0)
{
if (ent->num_leafs == MAX_ENT_LEAFS)
return;
leaf = (mleaf_t *) node;
leafnum = (leaf - sv.worldmodel->leafs) - 1;
ent->leafnums[ent->num_leafs] = leafnum;
ent->num_leafs++;
return;
}
splitplane = node->plane;
sides = BOX_ON_PLANE_SIDE(ent->v.absmin, ent->v.absmax, splitplane);
if (sides & 1)
SV_FindTouchedLeafs(ent, node->children[0]);
if (sides & 2)
SV_FindTouchedLeafs(ent, node->children[1]);
}
void SV_LinkEdict(edict_t *ent, bool touch_triggers)
{
areanode_t *node;
if (ent->area.prev)
SV_UnlinkEdict(ent);
if (ent == sv.edicts)
return;
if (ent->free)
return;
VectorAdd(ent->v.origin, ent->v.mins, ent->v.absmin);
VectorAdd(ent->v.origin, ent->v.maxs, ent->v.absmax);
if (((int32_t) ent->v.flags) & FL_ITEM)
{
ent->v.absmin[0] -= 15;
ent->v.absmin[1] -= 15;
ent->v.absmax[0] += 15;
ent->v.absmax[1] += 15;
}
else
{
ent->v.absmin[0] -= 1;
ent->v.absmin[1] -= 1;
ent->v.absmin[2] -= 1;
ent->v.absmax[0] += 1;
ent->v.absmax[1] += 1;
ent->v.absmax[2] += 1;
}
ent->num_leafs = 0;
if (ent->v.modelindex)
SV_FindTouchedLeafs(ent, sv.worldmodel->nodes);
if (ent->v.solid == SOLID_NOT)
return;
node = sv_areanodes;
while (1)
{
if (node->axis == (-1))
break;
if (ent->v.absmin[node->axis] > node->dist)
node = node->children[0];
else
if (ent->v.absmax[node->axis] < node->dist)
node = node->children[1];
else
break;
}
if (ent->v.solid == SOLID_TRIGGER)
InsertLinkBefore(&ent->area, &node->trigger_edicts);
else
InsertLinkBefore(&ent->area, &node->solid_edicts);
if (touch_triggers)
SV_TouchLinks(ent, sv_areanodes);
}
int32_t SV_HullPointContents(hull_t *hull, int32_t num, vec3_t p)
{
float d;
dclipnode_t *node;
mplane_t *plane;
while (num >= 0)
{
if ((num < hull->firstclipnode) || (num > hull->lastclipnode))
Sys_Error("SV_HullPointContents: bad node number");
node = hull->clipnodes + num;
plane = hull->planes + node->planenum;
if (plane->type < 3)
d = p[plane->type] - plane->dist;
else
d = DotProduct(plane->normal, p) - plane->dist;
if (d < 0)
num = node->children[1];
else
num = node->children[0];
}
return num;
}
int32_t SV_PointContents(vec3_t p)
{
int32_t cont;
cont = SV_HullPointContents(&sv.worldmodel->hulls[0], 0, p);
if ((cont <= CONTENTS_CURRENT_0) && (cont >= CONTENTS_CURRENT_DOWN))
cont = CONTENTS_WATER;
return cont;
}
int32_t SV_TruePointContents(vec3_t p)
{
return SV_HullPointContents(&sv.worldmodel->hulls[0], 0, p);
}
edict_t *SV_TestEntityPosition(edict_t *ent)
{
trace_t trace;
trace = SV_Move(ent->v.origin, ent->v.mins, ent->v.maxs, ent->v.origin, 0, ent);
if (trace.startsolid)
return sv.edicts;
return 0;
}
bool SV_RecursiveHullCheck(hull_t *hull, int32_t num, float p1f, float p2f, vec3_t p1, vec3_t p2, trace_t *trace)
{
dclipnode_t *node;
mplane_t *plane;
float t1;
float t2;
float frac;
int32_t i;
vec3_t mid;
int32_t side;
float midf;
if (num < 0)
{
if (num != CONTENTS_SOLID)
{
trace->allsolid = 0;
if (num == CONTENTS_EMPTY)
trace->inopen = 1;
else
trace->inwater = 1;
}
else
trace->startsolid = 1;
return 1;
}
if ((num < hull->firstclipnode) || (num > hull->lastclipnode))
Sys_Error("SV_RecursiveHullCheck: bad node number");
node = hull->clipnodes + num;
plane = hull->planes + node->planenum;
if (plane->type < 3)
{
t1 = p1[plane->type] - plane->dist;
t2 = p2[plane->type] - plane->dist;
}
else
{
t1 = DotProduct(plane->normal, p1) - plane->dist;
t2 = DotProduct(plane->normal, p2) - plane->dist;
}
if ((t1 >= 0) && (t2 >= 0))
return SV_RecursiveHullCheck(hull, node->children[0], p1f, p2f, p1, p2, trace);
if ((t1 < 0) && (t2 < 0))
return SV_RecursiveHullCheck(hull, node->children[1], p1f, p2f, p1, p2, trace);
if (t1 < 0)
frac = (t1 + DIST_EPSILON) / (t1 - t2);
else
frac = (t1 - DIST_EPSILON) / (t1 - t2);
if (frac < 0)
frac = 0;
if (frac > 1)
frac = 1;
midf = p1f + ((p2f - p1f) * frac);
for (i = 0; i < 3; i++)
mid[i] = p1[i] + (frac * (p2[i] - p1[i]));
side = t1 < 0;
if (!SV_RecursiveHullCheck(hull, node->children[side], p1f, midf, p1, mid, trace))
return 0;
if (SV_HullPointContents(hull, node->children[side ^ 1], mid) != CONTENTS_SOLID)
return SV_RecursiveHullCheck(hull, node->children[side ^ 1], midf, p2f, mid, p2, trace);
if (trace->allsolid)
return 0;
if (!side)
{
VectorCopy(plane->normal, trace->plane.normal);
trace->plane.dist = plane->dist;
}
else
{
VectorSubtract(vec3_origin, plane->normal, trace->plane.normal);
trace->plane.dist = -plane->dist;
}
while (SV_HullPointContents(hull, hull->firstclipnode, mid) == CONTENTS_SOLID)
{
frac -= 0.1;
if (frac < 0)
{
trace->fraction = midf;
VectorCopy(mid, trace->endpos);
Con_DPrintf("backup past 0\n");
return 0;
}
midf = p1f + ((p2f - p1f) * frac);
for (i = 0; i < 3; i++)
mid[i] = p1[i] + (frac * (p2[i] - p1[i]));
}
trace->fraction = midf;
VectorCopy(mid, trace->endpos);
return 0;
}
trace_t SV_ClipMoveToEntity(edict_t *ent, vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end)
{
trace_t trace;
vec3_t offset;
vec3_t start_l;
vec3_t end_l;
hull_t *hull;
memset(&trace, 0, sizeof(trace_t));
trace.fraction = 1;
trace.allsolid = 1;
VectorCopy(end, trace.endpos);
hull = SV_HullForEntity(ent, mins, maxs, offset);
VectorSubtract(start, offset, start_l);
VectorSubtract(end, offset, end_l);
SV_RecursiveHullCheck(hull, hull->firstclipnode, 0, 1, start_l, end_l, &trace);
if ((ent->v.solid == SOLID_BSP) && ((ent->v.angles[0] || ent->v.angles[1]) || ent->v.angles[2]))
{
vec3_t a;
vec3_t forward;
vec3_t right;
vec3_t up;
vec3_t temp;
if (trace.fraction != 1)
{
VectorSubtract(vec3_origin, ent->v.angles, a);
AngleVectors(a, forward, right, up);
VectorCopy(trace.endpos, temp);
trace.endpos[0] = DotProduct(temp, forward);
trace.endpos[1] = -DotProduct(temp, right);
trace.endpos[2] = DotProduct(temp, up);
VectorCopy(trace.plane.normal, temp);
trace.plane.normal[0] = DotProduct(temp, forward);
trace.plane.normal[1] = -DotProduct(temp, right);
trace.plane.normal[2] = DotProduct(temp, up);
}
}
if (trace.fraction != 1)
VectorAdd(trace.endpos, offset, trace.endpos);
if ((trace.fraction < 1) || trace.startsolid)
trace.ent = ent;
return trace;
}
void SV_ClipToLinks(areanode_t *node, moveclip_t *clip)
{
link_t *l;
link_t *next;
edict_t *touch;
trace_t trace;
for (l = node->solid_edicts.next; l != (&node->solid_edicts); l = next)
{
next = l->next;
touch = EDICT_FROM_AREA(l);
if (touch->v.solid == SOLID_NOT)
continue;
if (touch == clip->passedict)
continue;
if (touch->v.solid == SOLID_TRIGGER)
Sys_Error("Trigger in clipping list");
if ((clip->type == MOVE_NOMONSTERS) && (touch->v.solid != SOLID_BSP))
continue;
if ((((((clip->boxmins[0] > touch->v.absmax[0]) || (clip->boxmins[1] > touch->v.absmax[1])) || (clip->boxmins[2] > touch->v.absmax[2])) || (clip->boxmaxs[0] < touch->v.absmin[0])) || (clip->boxmaxs[1] < touch->v.absmin[1])) || (clip->boxmaxs[2] < touch->v.absmin[2]))
continue;
if ((clip->passedict && clip->passedict->v.size[0]) && (!touch->v.size[0]))
continue;
if (clip->trace.allsolid)
return;
if (clip->passedict)
{
if (PROG_TO_EDICT(touch->v.owner) == clip->passedict)
continue;
if (PROG_TO_EDICT(clip->passedict->v.owner) == touch)
continue;
}
if (((int32_t) touch->v.flags) & FL_MONSTER)
trace = SV_ClipMoveToEntity(touch, clip->start, clip->mins2, clip->maxs2, clip->end);
else
trace = SV_ClipMoveToEntity(touch, clip->start, clip->mins, clip->maxs, clip->end);
if ((trace.allsolid || trace.startsolid) || (trace.fraction < clip->trace.fraction))
{
trace.ent = touch;
if (clip->trace.startsolid)
{
clip->trace = trace;
clip->trace.startsolid = 1;
}
else
clip->trace = trace;
}
else
if (trace.startsolid)
clip->trace.startsolid = 1;
}
if (node->axis == (-1))
return;
if (clip->boxmaxs[node->axis] > node->dist)
SV_ClipToLinks(node->children[0], clip);
if (clip->boxmins[node->axis] < node->dist)
SV_ClipToLinks(node->children[1], clip);
}
void SV_MoveBounds(vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, vec3_t boxmins, vec3_t boxmaxs)
{
int32_t i;
for (i = 0; i < 3; i++)
{
if (end[i] > start[i])
{
boxmins[i] = (start[i] + mins[i]) - 1;
boxmaxs[i] = (end[i] + maxs[i]) + 1;
}
else
{
boxmins[i] = (end[i] + mins[i]) - 1;
boxmaxs[i] = (start[i] + maxs[i]) + 1;
}
}
}
trace_t SV_Move(vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int32_t type, edict_t *passedict)
{
moveclip_t clip;
int32_t i;
memset(&clip, 0, sizeof(moveclip_t));
clip.trace = SV_ClipMoveToEntity(sv.edicts, start, mins, maxs, end);
clip.start = start;
clip.end = end;
clip.mins = mins;
clip.maxs = maxs;
clip.type = type;
clip.passedict = passedict;
if (type == MOVE_MISSILE)
{
for (i = 0; i < 3; i++)
{
clip.mins2[i] = -15;
clip.maxs2[i] = 15;
}
}
else
{
VectorCopy(mins, clip.mins2);
VectorCopy(maxs, clip.maxs2);
}
SV_MoveBounds(start, clip.mins2, clip.maxs2, end, clip.boxmins, clip.boxmaxs);
SV_ClipToLinks(sv_areanodes, &clip);
return clip.trace;
}
void Z_ClearZone(memzone_t *zone, int32_t size)
{
memblock_t *block;
zone->blocklist.next = (zone->blocklist.prev = (block = (memblock_t *) (((uint8_t *) zone) + (sizeof(memzone_t)))));
zone->blocklist.tag = 1;
zone->blocklist.id = 0;
zone->blocklist.size = 0;
zone->rover = block;
block->prev = (block->next = &zone->blocklist);
block->tag = 0;
block->id = ZONEID;
block->size = size - (sizeof(memzone_t));
}
void Z_Free(void *ptr)
{
memblock_t *block;
memblock_t *other;
if (!ptr)
Sys_Error("Z_Free: NULL pointer");
block = (memblock_t *) (((uint8_t *) ptr) - (sizeof(memblock_t)));
if (block->id != ZONEID)
Sys_Error("Z_Free: freed a pointer without ZONEID");
if (block->tag == 0)
Sys_Error("Z_Free: freed a freed pointer");
block->tag = 0;
other = block->prev;
if (!other->tag)
{
other->size += block->size;
other->next = block->next;
other->next->prev = other;
if (block == mainzone->rover)
mainzone->rover = other;
block = other;
}
other = block->next;
if (!other->tag)
{
block->size += other->size;
block->next = other->next;
block->next->prev = block;
if (other == mainzone->rover)
mainzone->rover = block;
}
}
void *Z_Malloc(int32_t size)
{
void *buf;
Z_CheckHeap();
buf = Z_TagMalloc(size, 1);
if (!buf)
Sys_Error("Z_Malloc: failed on allocation of %i bytes", size);
memset(buf, 0, size);
return buf;
}
void *Z_TagMalloc(int32_t size, int32_t tag)
{
int32_t extra;
memblock_t *start;
memblock_t *rover;
memblock_t *new;
memblock_t *base;
if (!tag)
Sys_Error("Z_TagMalloc: tried to use a 0 tag");
size += sizeof(memblock_t);
size += 4;
size = (size + 7) & (~7);
base = (rover = mainzone->rover);
start = base->prev;
do
{
if (rover == start)
return 0;
if (rover->tag)
base = (rover = rover->next);
else
rover = rover->next;
}
while (base->tag || (base->size < size));
extra = base->size - size;
if (extra > MINFRAGMENT)
{
new = (memblock_t *) (((uint8_t *) base) + size);
new->size = extra;
new->tag = 0;
new->prev = base;
new->id = ZONEID;
new->next = base->next;
new->next->prev = new;
base->next = new;
base->size = size;
}
base->tag = tag;
mainzone->rover = base->next;
base->id = ZONEID;
*((int32_t *) ((((uint8_t *) base) + base->size) - 4)) = ZONEID;
return (void *) (((uint8_t *) base) + (sizeof(memblock_t)));
}
void Z_Print(memzone_t *zone)
{
memblock_t *block;
Con_Printf("zone size: %i location: %p\n", mainzone->size, mainzone);
for (block = zone->blocklist.next;; block = block->next)
{
Con_Printf("block:%p size:%7i tag:%3i\n", block, block->size, block->tag);
if (block->next == (&zone->blocklist))
break;
if ((((uint8_t *) block) + block->size) != ((uint8_t *) block->next))
Con_Printf("ERROR: block size does not touch the next block\n");
if (block->next->prev != block)
Con_Printf("ERROR: next block doesn't have proper back link\n");
if ((!block->tag) && (!block->next->tag))
Con_Printf("ERROR: two consecutive free blocks\n");
}
}
void Z_CheckHeap(void)
{
memblock_t *block;
for (block = mainzone->blocklist.next;; block = block->next)
{
if (block->next == (&mainzone->blocklist))
break;
if ((((uint8_t *) block) + block->size) != ((uint8_t *) block->next))
Sys_Error("Z_CheckHeap: block size does not touch the next block\n");
if (block->next->prev != block)
Sys_Error("Z_CheckHeap: next block doesn't have proper back link\n");
if ((!block->tag) && (!block->next->tag))
Sys_Error("Z_CheckHeap: two consecutive free blocks\n");
}
}
void Hunk_Check(void)
{
hunk_t *h;
for (h = (hunk_t *) hunk_base; ((uint8_t *) h) != (hunk_base + hunk_low_used);)
{
if (h->sentinal != HUNK_SENTINAL)
Sys_Error("Hunk_Check: trahsed sentinal");
if ((h->size < 16) || (((h->size + ((uint8_t *) h)) - hunk_base) > hunk_size))
Sys_Error("Hunk_Check: bad size");
h = (hunk_t *) (((uint8_t *) h) + h->size);
}
}
void Hunk_Print(bool all)
{
hunk_t *h;
hunk_t *next;
hunk_t *endlow;
hunk_t *starthigh;
hunk_t *endhigh;
int32_t count;
int32_t sum;
int32_t totalblocks;
char name[9];
name[8] = 0;
count = 0;
sum = 0;
totalblocks = 0;
h = (hunk_t *) hunk_base;
endlow = (hunk_t *) (hunk_base + hunk_low_used);
starthigh = (hunk_t *) ((hunk_base + hunk_size) - hunk_high_used);
endhigh = (hunk_t *) (hunk_base + hunk_size);
Con_Printf(" :%8i total hunk size\n", hunk_size);
Con_Printf("-------------------------\n");
while (1)
{
if (h == endlow)
{
Con_Printf("-------------------------\n");
Con_Printf(" :%8i REMAINING\n", (hunk_size - hunk_low_used) - hunk_high_used);
Con_Printf("-------------------------\n");
h = starthigh;
}
if (h == endhigh)
break;
if (h->sentinal != HUNK_SENTINAL)
Sys_Error("Hunk_Check: trahsed sentinal");
if ((h->size < 16) || (((h->size + ((uint8_t *) h)) - hunk_base) > hunk_size))
Sys_Error("Hunk_Check: bad size");
next = (hunk_t *) (((uint8_t *) h) + h->size);
count++;
totalblocks++;
sum += h->size;
memcpy(name, h->name, 8);
if (all)
Con_Printf("%8p :%8i %8s\n", h, h->size, name);
if (((next == endlow) || (next == endhigh)) || strncmp(h->name, next->name, 8))
{
if (!all)
Con_Printf(" :%8i %8s (TOTAL)\n", sum, name);
count = 0;
sum = 0;
}
h = next;
}
Con_Printf("-------------------------\n");
Con_Printf("%8i total blocks\n", totalblocks);
}
void *Hunk_AllocName(int32_t size, char *name)
{
hunk_t *h;
if (size < 0)
Sys_Error("Hunk_Alloc: bad size: %i", size);
size = (sizeof(hunk_t)) + ((size + 15) & (~15));
if (((hunk_size - hunk_low_used) - hunk_high_used) < size)
Sys_Error("Hunk_Alloc: failed on %i bytes", size);
h = (hunk_t *) (hunk_base + hunk_low_used);
hunk_low_used += size;
Cache_FreeLow(hunk_low_used);
memset(h, 0, size);
h->size = size;
h->sentinal = HUNK_SENTINAL;
strncpy(h->name, name, 8);
return (void *) (h + 1);
}
void *Hunk_Alloc(int32_t size)
{
return Hunk_AllocName(size, "unknown");
}
int32_t Hunk_LowMark(void)
{
return hunk_low_used;
}
void Hunk_FreeToLowMark(int32_t mark)
{
if ((mark < 0) || (mark > hunk_low_used))
Sys_Error("Hunk_FreeToLowMark: bad mark %i", mark);
memset(hunk_base + mark, 0, hunk_low_used - mark);
hunk_low_used = mark;
}
int32_t Hunk_HighMark(void)
{
if (hunk_tempactive)
{
hunk_tempactive = 0;
Hunk_FreeToHighMark(hunk_tempmark);
}
return hunk_high_used;
}
void Hunk_FreeToHighMark(int32_t mark)
{
if (hunk_tempactive)
{
hunk_tempactive = 0;
Hunk_FreeToHighMark(hunk_tempmark);
}
if ((mark < 0) || (mark > hunk_high_used))
Sys_Error("Hunk_FreeToHighMark: bad mark %i", mark);
memset((hunk_base + hunk_size) - hunk_high_used, 0, hunk_high_used - mark);
hunk_high_used = mark;
}
void *Hunk_HighAllocName(int32_t size, char *name)
{
hunk_t *h;
if (size < 0)
Sys_Error("Hunk_HighAllocName: bad size: %i", size);
if (hunk_tempactive)
{
Hunk_FreeToHighMark(hunk_tempmark);
hunk_tempactive = 0;
}
size = (sizeof(hunk_t)) + ((size + 15) & (~15));
if (((hunk_size - hunk_low_used) - hunk_high_used) < size)
{
Con_Printf("Hunk_HighAlloc: failed on %i bytes\n", size);
return 0;
}
hunk_high_used += size;
Cache_FreeHigh(hunk_high_used);
h = (hunk_t *) ((hunk_base + hunk_size) - hunk_high_used);
memset(h, 0, size);
h->size = size;
h->sentinal = HUNK_SENTINAL;
strncpy(h->name, name, 8);
return (void *) (h + 1);
}
void *Hunk_TempAlloc(int32_t size)
{
void *buf;
size = (size + 15) & (~15);
if (hunk_tempactive)
{
Hunk_FreeToHighMark(hunk_tempmark);
hunk_tempactive = 0;
}
hunk_tempmark = Hunk_HighMark();
buf = Hunk_HighAllocName(size, "temp");
hunk_tempactive = 1;
return buf;
}
void Cache_Move(cache_system_t *c)
{
cache_system_t *new;
new = Cache_TryAlloc(c->size, 1);
if (new)
{
memcpy(new + 1, c + 1, c->size - (sizeof(cache_system_t)));
new->user = c->user;
memcpy(new->name, c->name, sizeof(new->name));
Cache_Free(c->user);
new->user->data = (void *) (new + 1);
}
else
{
Cache_Free(c->user);
}
}
void Cache_FreeLow(int32_t new_low_hunk)
{
cache_system_t *c;
while (1)
{
c = cache_head.next;
if (c == (&cache_head))
return;
if (((uint8_t *) c) >= (hunk_base + new_low_hunk))
return;
Cache_Move(c);
}
}
void Cache_FreeHigh(int32_t new_high_hunk)
{
cache_system_t *c;
cache_system_t *prev;
prev = 0;
while (1)
{
c = cache_head.prev;
if (c == (&cache_head))
return;
if ((((uint8_t *) c) + c->size) <= ((hunk_base + hunk_size) - new_high_hunk))
return;
if (c == prev)
Cache_Free(c->user);
else
{
Cache_Move(c);
prev = c;
}
}
}
void Cache_UnlinkLRU(cache_system_t *cs)
{
if ((!cs->lru_next) || (!cs->lru_prev))
Sys_Error("Cache_UnlinkLRU: NULL link");
cs->lru_next->lru_prev = cs->lru_prev;
cs->lru_prev->lru_next = cs->lru_next;
cs->lru_prev = (cs->lru_next = 0);
}
void Cache_MakeLRU(cache_system_t *cs)
{
if (cs->lru_next || cs->lru_prev)
Sys_Error("Cache_MakeLRU: active link");
cache_head.lru_next->lru_prev = cs;
cs->lru_next = cache_head.lru_next;
cs->lru_prev = &cache_head;
cache_head.lru_next = cs;
}
cache_system_t *Cache_TryAlloc(int32_t size, bool nobottom)
{
cache_system_t *cs;
cache_system_t *new;
if ((!nobottom) && (cache_head.prev == (&cache_head)))
{
if (((hunk_size - hunk_high_used) - hunk_low_used) < size)
Sys_Error("Cache_TryAlloc: %i is greater then free hunk", size);
new = (cache_system_t *) (hunk_base + hunk_low_used);
memset(new, 0, sizeof(*new));
new->size = size;
cache_head.prev = (cache_head.next = new);
new->prev = (new->next = &cache_head);
Cache_MakeLRU(new);
return new;
}
new = (cache_system_t *) (hunk_base + hunk_low_used);
cs = cache_head.next;
do
{
if ((!nobottom) || (cs != cache_head.next))
{
if ((((uint8_t *) cs) - ((uint8_t *) new)) >= size)
{
memset(new, 0, sizeof(*new));
new->size = size;
new->next = cs;
new->prev = cs->prev;
cs->prev->next = new;
cs->prev = new;
Cache_MakeLRU(new);
return new;
}
}
new = (cache_system_t *) (((uint8_t *) cs) + cs->size);
cs = cs->next;
}
while (cs != (&cache_head));
if ((((hunk_base + hunk_size) - hunk_high_used) - ((uint8_t *) new)) >= size)
{
memset(new, 0, sizeof(*new));
new->size = size;
new->next = &cache_head;
new->prev = cache_head.prev;
cache_head.prev->next = new;
cache_head.prev = new;
Cache_MakeLRU(new);
return new;
}
return 0;
}
void Cache_Flush(void)
{
while (cache_head.next != (&cache_head))
Cache_Free(cache_head.next->user);
}
void Cache_Print(void)
{
cache_system_t *cd;
for (cd = cache_head.next; cd != (&cache_head); cd = cd->next)
{
Con_Printf("%8i : %s\n", cd->size, cd->name);
}
}
void Cache_Report(void)
{
Con_DPrintf("%4.1f megabyte data cache\n", ((hunk_size - hunk_high_used) - hunk_low_used) / ((float) (1024 * 1024)));
}
void Cache_Compact(void)
{
}
void Cache_Init(void)
{
cache_head.next = (cache_head.prev = &cache_head);
cache_head.lru_next = (cache_head.lru_prev = &cache_head);
Cmd_AddCommand("flush", Cache_Flush);
}
void Cache_Free(cache_user_t *c)
{
cache_system_t *cs;
if (!c->data)
Sys_Error("Cache_Free: not allocated");
cs = ((cache_system_t *) c->data) - 1;
cs->prev->next = cs->next;
cs->next->prev = cs->prev;
cs->next = (cs->prev = 0);
c->data = 0;
Cache_UnlinkLRU(cs);
}
void *Cache_Check(cache_user_t *c)
{
cache_system_t *cs;
if (!c->data)
return 0;
cs = ((cache_system_t *) c->data) - 1;
Cache_UnlinkLRU(cs);
Cache_MakeLRU(cs);
return c->data;
}
void *Cache_Alloc(cache_user_t *c, int32_t size, char *name)
{
cache_system_t *cs;
if (c->data)
Sys_Error("Cache_Alloc: allready allocated");
if (size <= 0)
Sys_Error("Cache_Alloc: size %i", size);
size = ((size + (sizeof(cache_system_t))) + 15) & (~15);
while (1)
{
cs = Cache_TryAlloc(size, 0);
if (cs)
{
strncpy(cs->name, name, (sizeof(cs->name)) - 1);
c->data = (void *) (cs + 1);
cs->user = c;
break;
}
if (cache_head.lru_prev == (&cache_head))
Sys_Error("Cache_Alloc: out of memory");
Cache_Free(cache_head.lru_prev->user);
}
return Cache_Check(c);
}
void Memory_Init(void *buf, int32_t size)
{
int32_t p;
int32_t zonesize = DYNAMIC_SIZE;
hunk_base = buf;
hunk_size = size;
hunk_low_used = 0;
hunk_high_used = 0;
Cache_Init();
p = COM_CheckParm("-zone");
if (p)
{
if (p < (com_argc - 1))
zonesize = ((int32_t) strtol(com_argv[p + 1], 0, 0)) * 1024;
else
Sys_Error("Memory_Init: you must specify a size in KB after -zone");
}
mainzone = Hunk_AllocName(zonesize, "zone");
Z_ClearZone(mainzone, zonesize);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment